1つのゲームキャラクタを管理するクラスとして、次のFxSpriteクラスを定義した。
座標(x,y)、描画の回転角度(angle)、回転の中心点座標(cx , cy)の情報を持って、
自身のキャラクタを描画するdrawメソッドと、自身の振る舞いを定義するactionメソッドを持つ。
drawメソッドとactionメソッドは、作品のフレームレイト用タイマーなどで、繰り返して呼び出されることを前提している。
package application; import java.net.URL; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.Image; public class FxSprite {//スプライト public javafx.scene.image.Image image; public double x = 0, y=0; //位置(左上座標) //public static String myName;//IPアドレス:ポート番号 public double angle;//回転角度(単位:デグリー 1回転が360度)----------- public double cx =0, cy=0; //回転角度 の中心座標----------- public FxSprite(Image image, double x, double y){ this.image = image; this.x = x; this.y = y; } public void draw(GraphicsContext gc){ gc.save(); // 座標系記憶 gc.translate(this.x, this.y);//描画位置(左上端)指定 //スケールと回転処理 gc.scale(1, 1);//スケール変更なし //もとの画像の点(this.cx, this.cy)を中心に回転させる。 gc.translate(this.cx, this.cy); gc.rotate(this.angle); gc.translate(-this.cx, -this.cy); gc.drawImage(this.image, 0, 0);//描画する。 gc.restore(); // 座標系戻す } public void action() { } }
このクラスを利用する作品で、次のようなLinkedHashMapのインスタンス変数を用意する。
LinkedHashMap
これに作品で使うすべてのFxSpriteインスタンスを、それぞれに付けるユニークな名前をキーにして登録します。
そして、タイマーで、mapSpriteの全ての要素のdarwメソッドで描画して、
全ての要素のactionメソッドを呼び出すようにして利用します。
この例として、2つの銛の画像("harpoon2.png")を使うFxSpriteの継承クラス(MySprite)のインスタンスを用意し、
クリック位置へ10カウントで移動する作品例で示します。
なお、2つのMySpriteのインスタンスは、
"A"と"B"の名前を付けて、mapSpriteで管理します。
そしてこの文字列を表示する2つのRadioButtonを
用意して選択した方の文字列のMySpriteを
移動する対象にする。
下記ではPaneを継承したMyPaneを定義して、
この子としてCanvasを用意して、それを
描画対象にしている。
package application; import java.net.URL; import java.util.LinkedHashMap; import java.util.Random; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Point2D; import javafx.scene.Scene; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.RadioButton; import javafx.scene.control.ToggleGroup; import javafx.scene.image.Image; import javafx.scene.layout.Pane; import javafx.stage.Stage; import javafx.util.Duration; class MySprite extends FxSprite {//スプライト static Image image; static { URL url = SpriteA.class.getResource("harpoon2.png");//画像素材を取得 image = new Image(url.toExternalForm()); } Point2D targetPt;//移動目標の点 int downCount;//上記に移動するまでのフレーム回数 MySprite(){ super(image,0,0); this.cx = 37; this.cy = 37; } @Override public void action() {//downCount回で、targetPtの位置まで移動するための1回アクション if(targetPt != null){ if(downCount == 1){ this.x = targetPt.getX(); this.y = targetPt.getY(); targetPt = null; } else { double dx = (targetPt.getX() - this.x)/downCount;//移動量算出 double dy = (targetPt.getY() - this.y)/downCount; this.angle = Math.atan2(dy, dx) * 180 / Math.PI; this.x += dx; this.y += dy; } downCount--; } } } class MyPane extends Pane{ Random rnd = new Random(); ToggleGroup togleGroup = new ToggleGroup(); RadioButton rbA = new RadioButton("A");; RadioButton rbB = new RadioButton("B");; Canvas canvas = new Canvas(400,400);//(400,400)のサイズのキャンバス GraphicsContext gc;//上記canvasに描画するとめのツールを管理する LinkedHashMap <String,FxSprite>mapSprite = new LinkedHashMap <String,FxSprite>(); EventHandler<ActionEvent> actionHandler = new EventHandler<ActionEvent>(){ @Override public void handle(ActionEvent event) {// アニメーション用のタイマー処理 //System.out.println("actionHandler"); draw(); action(); } }; KeyFrame keyFrame = new KeyFrame(Duration.millis(100),actionHandler);//0.1秒間の指定 Timeline timer = new Timeline(keyFrame); MyPane(){ this.getChildren().addAll(this.canvas,this.rbA,this.rbB); this.rbB.setLayoutX(100);//位置変更 this.rbA.setToggleGroup(this.togleGroup); this.rbB.setToggleGroup(this.togleGroup); mapSprite.put("A", new MySprite()); mapSprite.put("B", new MySprite()); this.timer.setCycleCount(Timeline.INDEFINITE);//stopされるまで無期限で繰り返す指定 this.timer.play();//タイマースタート this.setOnMousePressed(e->{ RadioButton toggle = (RadioButton)this.togleGroup.getSelectedToggle(); if(toggle == null) return; MySprite sprite = (MySprite)mapSprite.get(toggle.getText()); sprite.targetPt = new Point2D( e.getX()-37, e.getY()-37); sprite.downCount = 10;//10回でsprite.targetPtまで移動する指定 }); } synchronized void draw(){//各スプライトをキャンバスへの描画処理 gc =canvas.getGraphicsContext2D();//上記キャンバスへの描画ツール取得 gc.clearRect(0, 0, 400, 400); for(String key: mapSprite.keySet()){ FxSprite sprite = mapSprite.get(key); sprite.draw(gc); } } synchronized void action(){//各スプライトを動かす String [] keys = mapSprite.keySet().toArray(new String[0]); for(int i=0; i<keys.length; i++){ FxSprite sprite = mapSprite.get(keys[i]); if(sprite != null)sprite.action(); } } } public class FxSpriteTest extends Application { MyPane myPane = new MyPane(); @Override public void start(Stage primaryStage) throws Exception { Scene scene = new Scene(myPane,400,400); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args);//上記atartを起動する。 } }