JavaFXのアニメーションを使ったハノイの塔のサンプル

#サンプルの紹介

ハノイの塔というパズルがあります。[wikipediaのページ](https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%8E%E3%82%A4%E3%81%AE%E5%A1%94"wikipediaのページ")の説明が分かりやすいです。3つの円盤を使ったアニメーション画像があります。

ハノイの塔のサンプルをJavaFXでアニメーションを使って作りました。円盤を移動させるアニメーションを実装しました。ただし、横から見た図を想定してますので、表示は長方形です。

ハノイの塔のアルゴリズムの参考にしたサイトは以下のサイトです。

http://www.atmarkit.co.jp/ait/articles/0810/01/news135_3.html

作ったプログラムは以下のGitHubリポジトリにソースとjarファイルを置きました。

https://github.com/java-beginner/hanoi-JavaFX


以下は起動直後の画面です。

![first.png](https://qiita-image-store.s3.amazonaws.com/0/133921/67b27da9-919a-a634-84b8-c13125a60c75.png)


コンボボックスからディスクの数を選択できます。選択できるのは2, 3, 4のみです。

![combo.png](https://qiita-image-store.s3.amazonaws.com/0/133921/ddead614-606e-6617-c0ab-900d77a03c07.png)


選択した数のディスクが一番左にセットされます。黒い長方形は台を表します。

![selected.png](https://qiita-image-store.s3.amazonaws.com/0/133921/ed249bf6-1cf0-d386-b049-e83478afdcef.png)


「移動」ボタンを押すとディスクが移動します。アニメーションします。

![move.png](https://qiita-image-store.s3.amazonaws.com/0/133921/e6fccb43-664a-d860-1b3b-7aec8255e621.png)


「初期化」ボタンを押下するか、コンボボックスで異なる値を選択すると、最初の位置に全て戻ります。


#アニメーションの実装方法

アニメーションの実装部分についてのみ説明しようと思います。

実装にはSequentialTransitionクラスを使いました。Animationsのリストを順番に再生するというクラスです。サンプルでは、このクラスを使ってディスク（実装としてはRectangleクラスのインスタンスなので長方形）を上、右、下と動かしています。TranslateTransitionクラスのインスタンスを3つ生成し、上、右、下に動くように座標を設定して、SequentialTransitionクラスのインスタンスに登録します。playメソッドで動かすことができます。

以下、SequentialTransitionクラスを使った例を挙げます。

##setByX、setByYメソッド

以下は長方形を上、右、下、左と移動し元の位置に戻る例です。

```java:例
    @FXML Rectangle targetRectangle;

    @FXML
    private void move1(ActionEvent event) {

        Duration duration = new Duration(500);

        TranslateTransition t1 = new TranslateTransition(duration, targetRectangle);
        t1.setByY(-100);

        TranslateTransition t2 = new TranslateTransition(duration, targetRectangle);
        t2.setByX(200);

        TranslateTransition t3 = new TranslateTransition(duration, targetRectangle);
        t3.setByY(100);

        TranslateTransition t4 = new TranslateTransition(duration, targetRectangle);
        t4.setByX(-200);

        SequentialTransition sequentialTransition = new SequentialTransition();
        sequentialTransition.getChildren().addAll(t1, t2, t3, t4);
        sequentialTransition.play();

    }
```

![animationSample.png](https://qiita-image-store.s3.amazonaws.com/0/133921/ce190a8c-59b3-1d10-924c-efad134e8dde.png)


TranslateTransitionのインスタンスの生成にはコンストラクタTranslateTransition(Duration duration, Node node)を使ってます。Durationクラスは期間を定義するクラスです。第2引数にはRectangleをすることができます。RectangleはShapeを継承しShapeはNodeを継承しています。

アニメーションさせるためにはSequentialTransitionクラスのインスタンスを生成します。getChildren().addAll()でTranslateTransitionクラスのインスタンスを順番に指定します。play()メソッドを呼び出すとアニメーションが始まります。

上記例では移動の仕方をTranslateTransitionクラスの以下のメソッドで設定しています。

+ void setByX(double value)
+ void setByY(double value)

それぞれ、プロパティbyX、byYを指定するメソッドです。開始からの増分された停止X座標値、Y座標値を指定するプロパティです。

##setToX、setToYメソッド

次の例も長方形が上、右、下、左と移動し元の位置に戻ります。

```java:例
    @FXML Rectangle targetRectangle;

    @FXML
    private void move2(ActionEvent event) {

        Duration duration = new Duration(500);

        TranslateTransition t1 = new TranslateTransition(duration, targetRectangle);
        t1.setToY(-100);

        TranslateTransition t2 = new TranslateTransition(duration, targetRectangle);
        t2.setToX(200);

        TranslateTransition t3 = new TranslateTransition(duration, targetRectangle);
        t3.setToY(0);

        TranslateTransition t4 = new TranslateTransition(duration, targetRectangle);
        t4.setToX(0);

        SequentialTransition sequentialTransition = new SequentialTransition();
        sequentialTransition.getChildren().addAll(t1, t2, t3, t4);
        sequentialTransition.play();

    }
```

TranslateTransitionクラスの以下のメソッドで移動を設定しています。

+ void setToX(double value)
+ void setToY(double value)

それぞれプロパティtoX、toYの値を設定します。これらのプロパティは停止座標値を指定します。動きから判断すると、引数に指定する座標値は相対座標です。

##setOnFinishedメソッド

SequentialTransitionクラスはTransitionクラスを継承し、TransitionクラスはAnimationクラスを継承しています。なのでAnimationのsetOnFinishedメソッドが使えます。このメソッドでアニメーションの最後に実行されるアクションを設定することができます。ハノイの塔のサンプルではボタン類の活性制御をするために設定をしています。

以下のような使い方です。

```java:例
    @FXML Button button1;

    private void move1(ActionEvent event) {

        button1.setDisable(true);
        ・
        ・
        ・
        SequentialTransition sequentialTransition = new SequentialTransition();
        sequentialTransition.getChildren().addAll(t1, t2, t3, t4);
        sequentialTransition.onFinishedProperty().set(e -> {
            button1.setDisable(false);
        });

        sequentialTransition.play();
```

アニメーションの最後に「button1.setDisable(false);」が実行され、ボタンが活性状態になります。