package jp.yucchi.dukewalk;
import com.interactivemesh.jfx.importer.ImportException;
import com.interactivemesh.jfx.importer.obj.ObjModelImporter;
import java.net.URL;
import java.util.Random;
import java.util.stream.IntStream;
import javafx.animation.AnimationTimer;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.When;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.scene.AmbientLight;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Accordion;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TitledPane;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.HBox;
import javafx.scene.media.AudioSpectrumListener;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class DukeWalk extends Application implements AudioSpectrumListener {
// 移動
private final DoubleProperty xPos = new SimpleDoubleProperty(0);
private final DoubleProperty yPos = new SimpleDoubleProperty(0);
private final DoubleProperty zPos = new SimpleDoubleProperty(-1_000);
// スケール
private final DoubleProperty sxPos = new SimpleDoubleProperty(1.0);
private final DoubleProperty syPos = new SimpleDoubleProperty(1.0);
private final DoubleProperty szPos = new SimpleDoubleProperty(1.0);
// 回転
private final DoubleProperty rxPos = new SimpleDoubleProperty(0);
private final DoubleProperty ryPos = new SimpleDoubleProperty(0);
private final DoubleProperty rzPos = new SimpleDoubleProperty(0);
// 表示モード
private final BooleanProperty showWireframe = new SimpleBooleanProperty(false);
// 座標
private double anchorAngleX;
private double anchorAngleY;
private final DoubleProperty angleX = new SimpleDoubleProperty(0);
private final DoubleProperty angleY = new SimpleDoubleProperty(0);
private double anchorX;
private double anchorY;
// Duke 反転処理用
private final DoubleProperty turnAngleY = new SimpleDoubleProperty(0);
// Duke 転倒処理用
private final DoubleProperty turnAngleX = new SimpleDoubleProperty(0);
// Duke 手足アニメーション用
DoubleProperty angleWalk_r_X = new SimpleDoubleProperty(0);
DoubleProperty angleWalk_l_X = new SimpleDoubleProperty(0);
DoubleProperty angleWalk_l_arm = new SimpleDoubleProperty(0);
DoubleProperty angleWalk_r_arm = new SimpleDoubleProperty(0);
// 歩行モーションタイムライン
private Timeline dukeAnim;
// カメラ用アニメーションタイマー
private AnimationTimer walkTimer;
// 方位角
private double azimuth;
// 前回時間
private long previousHandledTime;
// 回転速度
private final DoubleProperty azimuthRateProperty = new SimpleDoubleProperty(15);
// カメラ高さ
private final DoubleProperty elevationProperty = new SimpleDoubleProperty(7);
// 半径
private static final double SUBCAMERA_RADIUS = 5_500;
// Duke 反転アニメーション用タイムライン
private Timeline rightTurn;
private Timeline leftTurn;
// BGM
private final Media bgm = new Media(getClass().getResource("music/bgm.mp3").toExternalForm());
// メディアプレーヤー
private MediaPlayer mp_bgm;
private AudioSpectrumListener audioSpectrumListener;
// 文字オブジェクト用メッシュビュー配列
private MeshView[] backMesh;
// Game 用 Duke の移動スピード
private double d_vx = -5.0;
private double d_vz = -6.0;
// Game 用アニメーションタイマー
private AnimationTimer walkTimer_g;
// Game 用 Duke ターンアニメーション用タイムライン
private Timeline turn_g;
// Game 用 Duke 転倒タイムライン
private Timeline fall;
// GAMEOVER 用回転アングル
private final DoubleProperty g_turnAngleX = new SimpleDoubleProperty(0);
// Score カウンター
private int score;
// メディアプレーヤー ゲームオーバー
private MediaPlayer mp_o;
private final Media o_mp = new Media(getClass().getResource("sound/o.mp3").toExternalForm());
// スコア専用タイムライン
private Timeline scoreTimeline;
@Override
public void start(Stage primaryStage) {
Group root = new Group();
Label translateLabel = new Label("Translate");
Label xLabel = new Label("X");
Label yLabel = new Label("Y");
Label zLabel = new Label("Z");
Label scaleLabel = new Label("Scale "); // 良い子はこんなことしちゃだめよ!
Label sxLabel = new Label("X");
Label syLabel = new Label("Y");
Label szLabel = new Label("Z");
Label rotateLabel = new Label("Rotate "); // 良い子はこんなことしちゃだめよ!
Label rxLabel = new Label("X");
Label ryLabel = new Label("Y");
Label rzLabel = new Label("Z");
Label drawModeLabel = new Label("Wireframe");
Label resetLabel = new Label(" Reset"); // 良い子はこんなことしちゃだめよ!
Label walkLabel = new Label(" Duke Walk!"); // 良い子はこんなことしちゃだめよ!
Label stopLabel = new Label(" Duke Stop!"); // 良い子はこんなことしちゃだめよ!
Label gameLabel = new Label(" Duke Walk Game"); // 良い子はこんなことしちゃだめよ!
Label scoreLabel = new Label(" L Key < RIGHT > J Key < LEFT > Please enjoy!"); // 良い子はこんなことしちゃだめよ!
Slider xSlider = new Slider(-3000, 3000, 0);
Slider ySlider = new Slider(-3000, 3000, 0);
Slider zSlider = new Slider(-5000, 5000, 0);
Slider sxSlider = new Slider(0.01, 10, 1.0);
Slider sySlider = new Slider(0.01, 10, 1.0);
Slider szSlider = new Slider(0.01, 10, 1.0);
Slider rxSlider = new Slider(-360, 360, 0);
Slider rySlider = new Slider(-360, 360, 0);
Slider rzSlider = new Slider(-360, 360, 0);
xSlider.setPrefSize(533, 0);
ySlider.setPrefSize(533, 0);
zSlider.setPrefSize(533, 0);
sxSlider.setPrefSize(533, 0);
sySlider.setPrefSize(533, 0);
szSlider.setPrefSize(533, 0);
rxSlider.setPrefSize(533, 0);
rySlider.setPrefSize(533, 0);
rzSlider.setPrefSize(533, 0);
xSlider.setShowTickLabels(true);
xSlider.setMajorTickUnit(500);
ySlider.setShowTickLabels(true);
ySlider.setMajorTickUnit(500);
zSlider.setShowTickLabels(true);
zSlider.setMajorTickUnit(500);
sxSlider.setShowTickLabels(true);
sxSlider.setMajorTickUnit(2.0);
sySlider.setShowTickLabels(true);
sySlider.setMajorTickUnit(2.0);
szSlider.setShowTickLabels(true);
szSlider.setMajorTickUnit(2.0);
rxSlider.setShowTickLabels(true);
rxSlider.setMajorTickUnit(90);
rySlider.setShowTickLabels(true);
rySlider.setMajorTickUnit(90);
rzSlider.setShowTickLabels(true);
rzSlider.setMajorTickUnit(90);
CheckBox meshCheckBox = new CheckBox("Wireframe");
Button resetBtn = new Button();
resetBtn.setText("Reset");
Button walkBtn = new Button();
walkBtn.setText("Walk!");
Button stopBtn = new Button();
stopBtn.setText("Stop!");
Button gameBtn = new Button();
gameBtn.setText("Game Start!");
final Accordion accordion = new Accordion();
final TitledPane translatePane = new TitledPane("Camera Translate", new HBox(30, translateLabel, xLabel, xSlider, yLabel, ySlider, zLabel, zSlider));
final TitledPane scalePane = new TitledPane("Camera Scale", new HBox(30, scaleLabel, sxLabel, sxSlider, syLabel, sySlider, szLabel, szSlider));
final TitledPane rotatePane = new TitledPane("Camera Rotate", new HBox(30, rotateLabel, rxLabel, rxSlider, ryLabel, rySlider, rzLabel, rzSlider));
final TitledPane drawModePane = new TitledPane("DrawMode & Reset & Duke Walk! & Duke Walk Game", new HBox(20, drawModeLabel, meshCheckBox, resetLabel, resetBtn, walkLabel, walkBtn, stopLabel, stopBtn, gameLabel, gameBtn, scoreLabel));
translatePane.setAnimated(true);
scalePane.setAnimated(true);
rotatePane.setAnimated(true);
drawModePane.setAnimated(true);
accordion.getPanes().addAll(translatePane, scalePane, rotatePane, drawModePane);
accordion.setExpandedPane(translatePane);
root.getChildren().add(accordion);
accordion.setLayoutX(17);
accordion.setLayoutY(873);
Scene scene = new Scene(root, 1910, 1020, true);
scene.setFill(Color.PINK);
PerspectiveCamera cam = new PerspectiveCamera(false);
scene.setCamera(cam);
root.getChildren().add(cam);
// サブシーン用のサブカメラ
PerspectiveCamera subCam = new PerspectiveCamera(true);
// Field of View
subCam.setFieldOfView(45.5);
// Clipping Planes
subCam.setNearClip(1.0);
subCam.setFarClip(1_000_000);
AnchorPane subRoot = new AnchorPane();
subRoot.setBackground(Background.EMPTY);
Group allGroup = new Group();
// 3D モデルをインポート
// Duke
ObjModelImporter dukeImporter = new ObjModelImporter();
// Bord
ObjModelImporter boadImporter = new ObjModelImporter();
// 文字オブジェクト
ObjModelImporter backImporter = new ObjModelImporter();
// ラケット
ObjModelImporter racketImporter = new ObjModelImporter();
// ゲームオーバー
ObjModelImporter game_overImporter = new ObjModelImporter();
try {
URL objUrl = this.getClass().getResource("Duke/duke.obj");
dukeImporter.read(objUrl);
} catch (ImportException e) {
// G'catch! :p
e.printStackTrace();
return;
}
MeshView[] dukeMesh = dukeImporter.getImport();
dukeImporter.close();
Group dukeGroup = new Group();
for (MeshView _dukeMesh : dukeMesh) {
dukeGroup.getChildren().addAll(_dukeMesh);
_dukeMesh.drawModeProperty().bind(new When(showWireframe).then(DrawMode.LINE).otherwise(DrawMode.FILL));
}
try {
URL objUrl = this.getClass().getResource("Duke_base/base.obj");
boadImporter.read(objUrl);
} catch (ImportException e) {
// G'catch! :p
e.printStackTrace();
return;
}
MeshView[] boadMesh = boadImporter.getImport();
boadImporter.close();
Group boadGroup = new Group();
for (MeshView _boadMesh : boadMesh) {
boadGroup.getChildren().addAll(_boadMesh);
_boadMesh.drawModeProperty().bind(new When(showWireframe).then(DrawMode.LINE).otherwise(DrawMode.FILL));
}
try {
URL objUrl = this.getClass().getResource("back_duke/back_duke.obj");
backImporter.read(objUrl);
} catch (ImportException e) {
// G'catch! :p
e.printStackTrace();
return;
}
backMesh = backImporter.getImport();
backImporter.close();
Group backGroup = new Group();
for (MeshView _backMesh : backMesh) {
backGroup.getChildren().addAll(_backMesh);
_backMesh.drawModeProperty().bind(new When(showWireframe).then(DrawMode.LINE).otherwise(DrawMode.FILL));
}
try {
URL objUrl = this.getClass().getResource("Racket/racket.obj");
racketImporter.read(objUrl);
} catch (ImportException e) {
// G'catch! :p
e.printStackTrace();
return;
}
MeshView[] racketMesh = racketImporter.getImport();
racketImporter.close();
Group racketGroup = new Group();
for (MeshView _racketMesh : racketMesh) {
racketGroup.getChildren().addAll(_racketMesh);
_racketMesh.drawModeProperty().bind(new When(showWireframe).then(DrawMode.LINE).otherwise(DrawMode.FILL));
}
try {
URL objUrl = this.getClass().getResource("game_over/game_over.obj");
game_overImporter.read(objUrl);
} catch (ImportException e) {
// G'catch! :p
e.printStackTrace();
return;
}
MeshView[] game_overMesh = game_overImporter.getImport();
game_overImporter.close();
Group game_overGroup = new Group();
for (MeshView _game_overMesh : game_overMesh) {
game_overGroup.getChildren().addAll(_game_overMesh);
_game_overMesh.drawModeProperty().bind(new When(showWireframe).then(DrawMode.LINE).otherwise(DrawMode.FILL));
}
allGroup.getChildren().addAll(dukeGroup, boadGroup, backGroup, racketGroup, game_overGroup);
// Duke 歩行ターニングポイント
// オフセットマージン (適当に設定)
int offsetMargin = (int) (dukeGroup.getLayoutBounds().getDepth() / 6);
// ターニングポイント -Z
int bordTurnPoint = (int) (boadGroup.getLayoutBounds().getMinZ() - dukeGroup.getLayoutBounds().getMinZ());
// ターニングポイント +Z
int bordEndPoint = (int) (boadGroup.getLayoutBounds().getMaxZ() - dukeGroup.getLayoutBounds().getDepth() / 2 - offsetMargin);
// ボードを非表示
boadGroup.setVisible(false);
// 文字を非表示
backGroup.setVisible(false);
// ラケットを非表示
racketGroup.setVisible(false);
// GAMEOVER 非表示
game_overGroup.setVisible(false);
// 環境光設定
AmbientLight ambient = new AmbientLight();
ambient.setColor(Color.rgb(150, 150, 150, 0.6));
ambient.getScope().addAll(allGroup);
// ポイントライト設定
PointLight point = new PointLight();
point.setColor(Color.rgb(255, 255, 255, 1.0));
point.setLayoutX(-700);
point.setLayoutY(-1_000);
point.setTranslateZ(-1_200);
point.getScope().addAll(allGroup);
subRoot.getChildren().addAll(allGroup, subCam, ambient, point);
// SubScene 作成
SubScene subScene = new SubScene(subRoot, 1920, 860, true, SceneAntialiasing.BALANCED);
subScene.setFill(Color.ALICEBLUE);
subScene.setCamera(subCam);
root.getChildren().addAll(subScene);
// マウスで allGroup 操作用
Rotate xRotate;
Rotate yRotate;
allGroup.getTransforms().setAll(
xRotate = new Rotate(0, Rotate.X_AXIS),
yRotate = new Rotate(0, Rotate.Y_AXIS)
);
xRotate.angleProperty().bind(angleX);
yRotate.angleProperty().bind(angleY);
subScene.setOnMousePressed(event -> {
anchorX = event.getSceneX();
anchorY = event.getSceneY();
anchorAngleX = angleX.get();
anchorAngleY = angleY.get();
});
subScene.setOnMouseDragged(event -> {
angleX.set(anchorAngleX - (anchorY - event.getSceneY()));
angleY.set(anchorAngleY + anchorX - event.getSceneX());
});
// サブカメラ操作用
// 移動
xSlider.valueProperty().bindBidirectional(xPos);
subCam.translateXProperty().bind(xPos);
ySlider.valueProperty().bindBidirectional(yPos);
subCam.translateYProperty().bind(yPos);
zSlider.valueProperty().bindBidirectional(zPos);
subCam.translateZProperty().bind(zPos);
// 拡大縮小
sxSlider.valueProperty().bindBidirectional(sxPos);
subCam.scaleXProperty().bind(sxPos);
sySlider.valueProperty().bindBidirectional(syPos);
subCam.scaleYProperty().bind(syPos);
szSlider.valueProperty().bindBidirectional(szPos);
subCam.scaleZProperty().bind(szPos);
// 回転
Rotate rxRotate;
Rotate ryRotate;
Rotate rzRotate;
subCam.getTransforms().setAll(
rxRotate = new Rotate(0, Rotate.X_AXIS),
ryRotate = new Rotate(0, Rotate.Y_AXIS),
rzRotate = new Rotate(0, Rotate.Z_AXIS)
);
rxRotate.angleProperty().bind(rxPos);
ryRotate.angleProperty().bind(ryPos);
rzRotate.angleProperty().bind(rzPos);
rxSlider.valueProperty().bindBidirectional(rxPos);
rySlider.valueProperty().bindBidirectional(ryPos);
rzSlider.valueProperty().bindBidirectional(rzPos);
// Duke モデル手足動作用
Rotate r_legRotate;
Rotate l_legRotate;
Rotate r_armRotate;
Rotate l_armRotate;
dukeMesh[2].getTransforms().setAll(
r_legRotate = new Rotate(0, Rotate.X_AXIS));
dukeMesh[3].getTransforms().setAll(
l_legRotate = new Rotate(0, Rotate.X_AXIS));
dukeMesh[4].getTransforms().setAll(
r_armRotate = new Rotate(0, Rotate.Z_AXIS));
dukeMesh[5].getTransforms().setAll(
l_armRotate = new Rotate(0, Rotate.X_AXIS));
r_legRotate.angleProperty().bind(angleWalk_r_X);
l_legRotate.angleProperty().bind(angleWalk_l_X);
l_armRotate.angleProperty().bind(angleWalk_l_arm);
r_armRotate.angleProperty().bind(angleWalk_r_arm);
// Duke ターン用
Rotate tyRotate;
Rotate txRotate;
dukeGroup.getTransforms().setAll(
tyRotate = new Rotate(0, Rotate.Y_AXIS),
txRotate = new Rotate(0, Rotate.X_AXIS)
);
tyRotate.angleProperty().bind(turnAngleY);
txRotate.angleProperty().bind(turnAngleX);
// GAMEOVER 回転用
Rotate gxRotate;
game_overGroup.getTransforms().setAll(
gxRotate = new Rotate(0, Rotate.X_AXIS));
gxRotate.angleProperty().bind(g_turnAngleX);
// 表示モード
meshCheckBox.selectedProperty().bindBidirectional(showWireframe);
// ラケット キー操作
root.setOnKeyPressed((KeyEvent key) -> {
if (key.getCode() == KeyCode.L) {
if (racketGroup.getBoundsInParent().getMaxX() < boadGroup.getBoundsInParent().getMaxX()) {
racketGroup.setTranslateX(racketGroup.getTranslateX() + 15);
}
}
if (key.getCode() == KeyCode.J) {
if (racketGroup.getBoundsInParent().getMinX() > boadGroup.getBoundsInParent().getMinX()) {
racketGroup.setTranslateX(racketGroup.getTranslateX() - 15);
}
}
});
// リセット
resetBtn.setOnAction((ActionEvent event) -> {
// Icon 変更
Image myIcon = new Image(this.getClass().getResource("icon/duke_3.png").toString());
primaryStage.getIcons().add(myIcon);
// タイムライン、メディアプレーヤーのおまじない
stopLine();
// Score ラベル初期化
scoreLabel.setText(" L Key < RIGHT > J Key < LEFT > Please enjoy!");
// ポイントライト位置初期化
point.setTranslateZ(-1_200);
// ボード非表示
boadGroup.setVisible(false);
// 文字を非表示
backGroup.setVisible(false);
// ラケットを非表示
racketGroup.setVisible(false);
// GAMEOVER 非表示
game_overGroup.setVisible(false);
// Duke の位置を初期化
dukeGroup.setTranslateX(0);
dukeGroup.setTranslateZ(0);
// Duke の向きを初期化
turnAngleY.set(0);
turnAngleX.set(0);
// ラケットの初期化
racketGroup.setTranslateX(0);
// GAMEOVER 用回転アングル初期化
g_turnAngleX.set(0);
// サブカメラ位置初期化
xPos.set(0);
yPos.set(0);
zPos.set(-1_000);
sxPos.set(1.0);
syPos.set(1.0);
szPos.set(1.0);
rxPos.set(0);
ryPos.set(0);
rzPos.set(0);
// Duke 手足の初期化
angleWalk_r_X.set(0);
angleWalk_l_X.set(0);
angleWalk_l_arm.set(0);
angleWalk_r_arm.set(0);
// allGroup 初期化
anchorAngleX = 0;
anchorAngleY = 0;
angleX.set(0);
angleY.set(0);
});
// Duke Walk 開始
walkBtn.setOnAction((ActionEvent event) -> {
// Icon 変更
Image myIcon = new Image(this.getClass().getResource("icon/duke_1.png").toString());
primaryStage.getIcons().add(myIcon);
// タイムライン、メディアプレーヤーのおまじない
stopLine();
// 音楽再生、スペクトラムリスナー実装
dance();
// Score ラベル初期化
scoreLabel.setText(" L Key < RIGHT > J Key < LEFT > Please enjoy!");
// ポイントライト位置変更
point.setTranslateZ(-2_500);
// ボードを表示
boadGroup.setVisible(true);
// 文字を表示
backGroup.setVisible(true);
// ラケットを非表示
racketGroup.setVisible(false);
// GAMEOVER 非表示
game_overGroup.setVisible(false);
// Duke の位置を初期化
dukeGroup.setTranslateX(0);
dukeGroup.setTranslateZ(0);
// Duke の向きを初期化
turnAngleY.set(0);
turnAngleX.set(0);
// ラケットの初期化
racketGroup.setTranslateX(0);
// GAMEOVER 用回転アングル初期化
g_turnAngleX.set(0);
// 設定初期化
azimuth = 0;
previousHandledTime = 0L;
// allGroup 初期化
anchorAngleX = 0;
anchorAngleY = 0;
angleX.set(0);
angleY.set(0);
// Duke 手足の初期化
angleWalk_r_X.set(0);
angleWalk_l_X.set(0);
angleWalk_l_arm.set(0);
angleWalk_r_arm.set(0);
// サブカメラ位置初期化
xPos.set(0);
yPos.set(0);
zPos.set(-1_200);
sxPos.set(1.0);
syPos.set(1.0);
szPos.set(1.0);
rxPos.set(0);
ryPos.set(0);
rzPos.set(0);
// 歩行モーション
walkMotion();
// 歩行
walkTimer = new AnimationTimer() {
@Override
public void handle(long now) {
update(now);
}
private void update(long now) {
if (previousHandledTime == 0) {
previousHandledTime = now;
return;
}
// サブカメラを半径 SUBCAMERA_RADIUS の円の中心むけて回転撮影する。
azimuth += azimuthRateProperty.get() * (now - previousHandledTime) / 1_000_000_000;
previousHandledTime = now;
xPos.set(Math.sin(Math.toRadians(azimuth)) * SUBCAMERA_RADIUS);
zPos.set(-1 * Math.cos(Math.toRadians(azimuth)) * SUBCAMERA_RADIUS);
yPos.set(-1 * Math.sin(Math.toRadians(elevationProperty.getValue())) * SUBCAMERA_RADIUS);
ryPos.set(-1 * azimuth);
// 歩行制御
if (dukeGroup.getTranslateZ() > bordTurnPoint && turnAngleY.get() == 0) {
dukeGroup.setTranslateZ(dukeGroup.getTranslateZ() + -5);
if (dukeGroup.getTranslateZ() < bordTurnPoint + 1) {
rightTurn();
}
}
// 歩行制御
if (dukeGroup.getTranslateZ() < bordEndPoint && turnAngleY.get() == 180) {
dukeGroup.setTranslateZ(dukeGroup.getTranslateZ() + 5);
if (dukeGroup.getTranslateZ() > bordEndPoint - 1) {
leftTurn();
}
}
}
// 歩行右反転処理
private void rightTurn() {
rightTurn = new Timeline(
new KeyFrame(
new Duration(700),
new KeyValue(turnAngleY, 180, Interpolator.EASE_BOTH)
));
rightTurn.play();
}
// 歩行左反転処理
private void leftTurn() {
leftTurn = new Timeline(
new KeyFrame(
new Duration(700),
new KeyValue(turnAngleY, 0, Interpolator.EASE_BOTH)
));
leftTurn.play();
}
};
walkTimer.start();
});
// 歩行ストップ
stopBtn.setOnAction((ActionEvent event) -> {
// Icon 変更
Image myIcon = new Image(this.getClass().getResource("icon/duke_2.png").toString());
primaryStage.getIcons().add(myIcon);
// タイムライン、メディアプレーヤーのおまじない
stopLine();
});
// Game 開始
gameBtn.setOnAction((ActionEvent event) -> {
// Icon 変更
Image myIcon = new Image(this.getClass().getResource("icon/duke_g.png").toString());
primaryStage.getIcons().add(myIcon);
// タイムライン、メディアプレーヤーのおまじない
stopLine();
// 音楽再生、スペクトラムリスナー実装
dance();
// Score ラベル設定
scoreLabel.setText("Your Score: " + score);
// ポイントライト位置変更
point.setTranslateZ(-2_500);
// 歩行ベクトル設定
d_vx = -5.0;
d_vz = -6.0;
// ボードを表示
boadGroup.setVisible(true);
// 文字を表示
backGroup.setVisible(true);
// ラケットを表示
racketGroup.setVisible(true);
// GAMEOVER 非表示
game_overGroup.setVisible(false);
// Duke の位置を初期化
dukeGroup.setTranslateX(0);
Random r = new Random();
dukeGroup.setTranslateZ(-r.nextInt(700));
// Duke の向きを初期化
turnAngleY.set(0);
turnAngleX.set(0);
// ラケットの初期化
racketGroup.setTranslateX(0);
// GAMEOVER 用回転アングル初期化
g_turnAngleX.set(0);
// 設定初期化
azimuth = 0;
previousHandledTime = 0L;
// allGroup 初期化
anchorAngleX = 0;
anchorAngleY = 0;
angleX.set(0);
angleY.set(0);
// Duke 手足の初期化
angleWalk_r_X.set(0);
angleWalk_l_X.set(0);
angleWalk_l_arm.set(0);
angleWalk_r_arm.set(0);
// サブカメラ位置初期化
xPos.set(0);
yPos.set(0);
zPos.set(-1_200);
sxPos.set(1.0);
syPos.set(1.0);
szPos.set(1.0);
rxPos.set(0);
ryPos.set(0);
rzPos.set(0);
// サブカメラ位置設定
zPos.set(-1 * Math.cos(Math.toRadians(azimuth)) * SUBCAMERA_RADIUS);
yPos.set(-1 * Math.sin(Math.toRadians(elevationProperty.getValue())) * SUBCAMERA_RADIUS);
ryPos.set(-1 * azimuth);
// ボードの大きさ
double bord_left = boadGroup.getLayoutBounds().getMinX();
double bord_right = boadGroup.getLayoutBounds().getMaxX();
double bord_front = boadGroup.getLayoutBounds().getMinZ();
double bord_back = boadGroup.getLayoutBounds().getMaxZ();
// Duke Turn アングル
turnAngleY.set(Math.toDegrees(Math.atan(d_vz / d_vx)));
// 歩行モーション
walkMotion();
// Score 専用タイムライン
scoreTimeline = new Timeline(
new KeyFrame(
new Duration(1_000), (ActionEvent e) -> {
if (score < Integer.MAX_VALUE) {
score++;
scoreLabel.setText("Your Score: " + score);
} else {
scoreLabel.setText("そろそろ終わりにしたら?");
score = Integer.MAX_VALUE - 1;
}
}));
scoreTimeline.setCycleCount(Timeline.INDEFINITE);
scoreTimeline.play();
// 歩行
walkTimer_g = new AnimationTimer() {
@Override
public void handle(long now) {
update(now);
}
private void update(long now) {
if (previousHandledTime == 0) {
previousHandledTime = now;
return;
}
// Duke 移動
dukeGroup.setTranslateX(dukeGroup.getTranslateX() + d_vx);
dukeGroup.setTranslateZ(dukeGroup.getTranslateZ() + d_vz);
// 左または右に当たったらx方向速度の符号を反転させる
if (dukeGroup.getBoundsInParent().getMinX() < bord_left || dukeGroup.getBoundsInParent().getMaxX() > bord_right) {
d_vx = -d_vx;
turn();
}
// ラケットに当たったらy方向速度の符号を反転させる
if (dukeGroup.getBoundsInParent().getMinZ() < bord_front) {
if (dukeGroup.getBoundsInParent().getMaxX() > racketGroup.getBoundsInParent().getMinX() && dukeGroup.getBoundsInParent().getMinX() < racketGroup.getBoundsInParent().getMaxX()) {
d_vz = -d_vz;
turn();
} else {
gameOver();
}
}
// 奥に当たったらy方向速度の符号を反転させる
if (dukeGroup.getBoundsInParent().getMaxZ() > bord_back) {
d_vz = -d_vz;
turn();
}
}
// ターンモーション
private void turn() {
double h = 0;
if (d_vz > 0) {
h = -180;
}
turn_g = new Timeline(
new KeyFrame(
new Duration(250),
new KeyValue(turnAngleY, h + Math.toDegrees(Math.atan(d_vz / d_vx)), Interpolator.EASE_BOTH)
));
turn_g.play();
}
// ゲームオーバー
private void gameOver() {
if (scoreTimeline != null) {
scoreTimeline.stop();
}
if (walkTimer_g != null) {
walkTimer_g.stop();
}
if (turn_g != null) {
turn_g.stop();
}
// 転倒位置調整
dukeGroup.setTranslateZ(dukeGroup.getBoundsInParent().getMinZ() - 100);
fall = new Timeline(
new KeyFrame(
new Duration(350),
new KeyValue(turnAngleX, 140, Interpolator.EASE_BOTH)),
new KeyFrame(new Duration(1_000),
new KeyValue(game_overGroup.visibleProperty(), true, Interpolator.EASE_BOTH)),
new KeyFrame(new Duration(2_000),
new KeyValue(g_turnAngleX, 360, Interpolator.EASE_BOTH)
));
fall.play();
// 音楽停止
if (mp_bgm != null) {
mp_bgm.stop();
}
// ゲームオーバー用音楽
mp_o = new MediaPlayer(o_mp);
mp_o.play();
if (dukeAnim != null) {
dukeAnim.stop();
}
}
};
walkTimer_g.start();
});
// Icon 設定
Image myIcon = new Image(this.getClass().getResource("icon/cludia_icon.png").toString());
primaryStage.getIcons().add(myIcon);
primaryStage.setTitle("Duke Walk +");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
// バックの文字オブジェクトのスケーリング
@Override
public void spectrumDataUpdate(double timestamp, double duration, float[] magnitudes, float[] phases) {
IntStream.range(0, magnitudes.length).forEach(i -> {
backMesh[i].setScaleX((magnitudes[i] + 60.01) * 0.1 + 1.0);
backMesh[i].setScaleY((magnitudes[i] + 60.01) * 0.1 + 1.0);
backMesh[i].setScaleZ((magnitudes[i] + 60.01) * 0.1 + 1.0);
});
}
// いろいろ停止
private void stopLine() {
if (mp_bgm != null) {
mp_bgm.stop();
}
if (mp_o != null) {
mp_o.stop();
}
if (walkTimer != null) {
walkTimer.stop();
}
if (dukeAnim != null) {
dukeAnim.stop();
}
if (leftTurn != null) {
leftTurn.stop();
}
if (rightTurn != null) {
rightTurn.stop();
}
if (walkTimer_g != null) {
walkTimer_g.stop();
}
if (turn_g != null) {
turn_g.stop();
}
if (fall != null) {
fall.stop();
}
if (scoreTimeline != null) {
scoreTimeline.stop();
}
}
// 音楽再生、スペクトラムリスナー実装
private void dance() {
audioSpectrumListener = this;
mp_bgm = new MediaPlayer(bgm);
mp_bgm.setAudioSpectrumListener(audioSpectrumListener);
mp_bgm.setAudioSpectrumInterval(0.01);
mp_bgm.setAudioSpectrumNumBands(8);
mp_bgm.setVolume(0.9);
mp_bgm.setAutoPlay(true);
mp_bgm.setCycleCount(MediaPlayer.INDEFINITE);
}
// 歩行モーション
private void walkMotion() {
dukeAnim = new Timeline(
new KeyFrame(
new Duration(300),
new KeyValue(angleWalk_r_X, -25),
new KeyValue(angleWalk_l_X, 25),
new KeyValue(angleWalk_l_arm, -20),
new KeyValue(angleWalk_r_arm, 12, Interpolator.EASE_BOTH)
),
new KeyFrame(
new Duration(600),
new KeyValue(angleWalk_r_X, 0),
new KeyValue(angleWalk_l_X, 0),
new KeyValue(angleWalk_l_arm, 0),
new KeyValue(angleWalk_r_arm, 0, Interpolator.EASE_BOTH)
),
new KeyFrame(
new Duration(900),
new KeyValue(angleWalk_r_X, 25),
new KeyValue(angleWalk_l_X, -25),
new KeyValue(angleWalk_l_arm, 20),
new KeyValue(angleWalk_r_arm, 12, Interpolator.EASE_BOTH)
),
new KeyFrame(
new Duration(1_200),
new KeyValue(angleWalk_r_X, 0),
new KeyValue(angleWalk_l_X, 0),
new KeyValue(angleWalk_l_arm, 0),
new KeyValue(angleWalk_r_arm, 0, Interpolator.EASE_BOTH)
)
);
dukeAnim.setCycleCount(Timeline.INDEFINITE);
dukeAnim.play();
}
}