質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.48%

JavaFXでImageを回転させたい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 3,046

score 11

だめぽ

JavaFX8でゲームを作ろうと思い立ったはいいんですが、DirectXで言うDrawRotaGraphが見当たらずひぃひぃ言ってます。

1時間ほどリファレンスとにらめっこして、どうやらGraphicsContextごと回せばいいらしいことはわかりましたがこれでは「複数の画像を表示して1枚だけ傾ける」といった実装ができません。

そこで「GraphicContextが2つあればいんじゃね?」と安易な発想でImageを2つ表示してみましたが2枚の画像は一緒に回ってしまいました。お手上げです。誰か助けてください……。

//抜粋

        Canvas canvas = new Canvas( 1500, 800 );
        root.getChildren().add( canvas );
        Image image=new Image("file:C:\\Users\\Admin\\Pictures\\test.png");

        GraphicsContext gc = canvas.getGraphicsContext2D();
        GraphicsContext gc2= canvas.getGraphicsContext2D();

        final long startNanoTime = System.nanoTime();

        new AnimationTimer()
        {
            public void handle(long currentNanoTime)
            {
                double t = (currentNanoTime - startNanoTime) / 1000000000.0;

                Affine af=gc.getTransform();
                af.appendRotation(t,500,300);
                gc.setTransform(af);
                gc.drawImage(image,500,300);

                Affine af2=af.clone();
                af2.appendRotation(0);
                gc2.setTransform(af2);
                gc2.drawImage(image,800,500);
            }
        }.start();
        theStage.show();


抜粋

全文

//全文

package sample;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.transform.Affine;
import javafx.stage.Stage;


public class Main extends Application {


    int ScreeenFlg=0;
    public static Main singleton;
    public Stage stage;


    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage theStage) throws Exception {

        theStage.setTitle("画像回転テスト");
        Group root = new Group();
        Scene theScene = new Scene( root );
        theStage.setScene( theScene );

        Canvas canvas = new Canvas( 1500, 800 );
        root.getChildren().add( canvas );
        Image image=new Image("file:C:\\Users\\Admin\\Pictures\\test.png");

        GraphicsContext gc = canvas.getGraphicsContext2D();
        GraphicsContext gc2= canvas.getGraphicsContext2D();

        final long startNanoTime = System.nanoTime();

        new AnimationTimer()
        {
            public void handle(long currentNanoTime)
            {
                double t = (currentNanoTime - startNanoTime) / 1000000000.0;

                Affine af=gc.getTransform();
                af.appendRotation(t,500,300);
                gc.setTransform(af);
                gc.drawImage(image,500,300);

                Affine af2=af.clone();
                af2.appendRotation(0);
                gc2.setTransform(af2);
                gc2.drawImage(image,800,500);
            }
        }.start();
        theStage.show();
    }
}

駄目元でCanvasを2つにしてみる

 GraphicsContext gc2= canvas2.getGraphicsContext2D();


にしても駄目でした。ひーん。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

GraphicContextが2つあれば

CanvasインスタンスにはGraphicContextインスタンスは1つしか生成されないと思います。そのためアフィン変換を繰り返し適用すると、その効果がどんどん蓄積されるわけで、複数のオブジェクトに違う変換を施したり、刻々と位置や向きが変化するアニメーションを表現するには扱いづらいですね。

こうした場合はGraphicContext#save/restoreメソッドを用いて、次のような3ステップで個々の描画を行うとよいと思います。

(1) GraphicContextの状態をsave
(2) アフィン変換を適用して描画
(3) GraphicContextの状態をrestore

ご質問のコードで言えば以下のような感じでしょうか。

+ gc.save();
  Affine af=gc.getTransform();
  af.appendRotation(t,500,300);
  gc.setTransform(af);
  gc.drawImage(image,500,300);
+ gc.restore();

  gc2.drawImage(image,800,500);

ところで、個々の描画で一々save/restoreすると煩わしいですし、saveかrestoreをし忘れたとたん惨憺たる結果になるため、メソッド化しておくのがよいかも知れません。例えばDirectXのDrawRotaGraphの汎用版みたいな関数は次のように書けます。(蛇足ですが、ここでのtranslate/rotate/scaleは、描画オブジェクトに対する変換ではなく、座標系に対する変換ですので、呼び出す順番にご注意ください)

static void draw(GraphicsContext gc, Consumer<GraphicsContext> operation,
                 double tx, double ty, double scale, double angle, double alpha, boolean turn) {
  gc.save();
  gc.translate(tx, ty);
  gc.rotate(angle);
  gc.scale(scale * (turn ? -1 : 1), scale);
  gc.setGlobalAlpha(alpha);
  operation.accept(gc);
  gc.restore();
}


これを利用すれば以下のようにできます。

// 原点が画像の中心となるよう画像を描画
static void drawImageAtOrigin(GraphicsContext gc, Image image) {
  gc.drawImage(image, -0.5 * image.getWidth(), -0.5 * image.getHeight());
}

...
GraphicsContext gc = canvas.getGraphicsContext2D();
draw(gc, gc2 -> drawImageAtOrigin(gc2, image), 500, 300, 1.0, angle, 1.0, false);
draw(gc, gc2 -> drawImageAtOrigin(gc2, image), 800, 500, 0.5, 0, 1.0, false);


save/restoreは入れ子にできますので、例えば「動きのあるオブジェク1の周りをさらに回っているオブジェクト2」といったものも書きやすくなると思います。以下のtx2, ty2, scale2, angle2はそれぞれオブジェクト1に対するオブジェクト2のアフィン変換ですから、より単純な計算式で表現できます。この考え方は階層的な関係を持つオブジェクトグループを描画する際によく用いられるものだと思います。

draw(gc,
     gc2 -> {
       オブジェクト1を描画;
       draw(gc2, gc3 -> {
            オブジェクト2を描画;
            }, tx2, ty2, scale2, angle2, 1.0, false);
     }, tx1, ty1, scale1, angle1, 1.0, false);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/15 11:33

    うおおおおおおおおおおおおおお動いた動いたぜ動きました!!!!!!!!
    ありがとうございます!!!!!

    丁寧にDirectXと似たような書き方までできるようしにていただき本当に助かりました!ありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る