2017-02-26 29 views
0

我是JavaFX的新手,但對面向對象Java有很好的理解。下面的程序是兩個例子的組合,一個是動畫和移動形狀,另一個是通過鼠標按鈕動畫一個對象。許多功能已被刪除或更改爲我的需要。使用JavaFX在鍵盤輸入上動畫製作精靈

我已經通過很多例子進行了搜索,但還沒有找到一個我完全理解的關於移動一個精靈和動畫的關鍵新聞。在我的程序中我確定我沒有使用正確的類來創建遊戲對象,即使有一些調整,我相信它可以工作。

我添加了一些println函數來測試動畫。問題似乎是walkSouth動畫中的KeyFrame部分不能正常工作/播放。

我的問題是:

  1. 我應該使用不同的JavaFX類創建精靈表動畫?
  2. 該代碼是否可以輕鬆適應功能,以便更好地理解JavaFX的工作原理。

這裏是主類:

package testing; 

import javafx.animation.KeyFrame; 
import javafx.animation.Timeline; 
import javafx.application.Application; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.image.Image; 
import javafx.scene.layout.Pane; 
import javafx.stage.Stage; 
import javafx.util.Duration; 

public class Main extends Application { 

    private enum UserAction{ 
     NONE,NORTH,SOUTH; 
    } 
    private static int APP_W = 200; 
    private static int APP_H = 200; 

    private Scene scene; 

    private UserAction action = UserAction.NONE; 

    private Timeline timeline = new Timeline(); 
    private boolean running = true; 
    private int FPS = 60; 

    private Parent createContent(){ 
     Pane root = new Pane(); 
     root.setPrefSize(APP_W,APP_H); 

     Image cat_image = new Image("file:res/cata.png"); 
     GameObject obj = new GameObject(cat_image,12,8); 
     obj.setTranslateX(100); 
     obj.setTranslateY(100); 

     KeyFrame frame = new KeyFrame(Duration.millis(1000/FPS), event -> { 
      if(!running) 
       return; 

      switch(action){ 
       case NORTH: 
        obj.setTranslateY(obj.getTranslateY()-1); 
        break; 

       case SOUTH: 
        obj.walkSouth(); 
        obj.setTranslateY(obj.getTranslateY()+1); 
        break; 
       case NONE: 
        obj.pauseAnimation(); 
        break; 
      } 
     }); 

     timeline.getKeyFrames().add(frame); 
     timeline.setCycleCount(Timeline.INDEFINITE); 

     root.getChildren().add(obj); 

     return root; 
    } 

    private void restartGame(){ 
     stopGame(); 
     startGame(); 
    } 
    private void stopGame(){ 
     running = false; 
     timeline.stop(); 
    } 
    private void startGame(){ 
     timeline.play(); 
     running = true; 
    } 

    public void start(Stage primaryStage) throws Exception{ 
     scene = new Scene(createContent()); 

     scene.setOnKeyPressed(event -> { 
      switch (event.getCode()) { 
       case W: 
        action = UserAction.NORTH; 
        break; 
       case S: 
        action = UserAction.SOUTH; 
        break; 
      } 
     }); 

     scene.setOnKeyReleased(event -> { 
      switch (event.getCode()) { 
       case W: 
        action = UserAction.NONE; 
        break; 
       case S: 
        action = UserAction.NONE; 
        break; 
      } 
     }); 

     primaryStage.setTitle("Simple Animation"); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
     startGame(); 
    } 

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

這裏是遊戲對象類:

package testing; 

import javafx.animation.KeyFrame; 
import javafx.animation.Timeline; 
import javafx.beans.property.IntegerProperty; 
import javafx.beans.property.SimpleIntegerProperty; 
import javafx.geometry.Rectangle2D; 
import javafx.scene.image.Image; 
import javafx.scene.image.ImageView; 
import javafx.scene.layout.Pane; 
import javafx.util.Duration; 


/** 
* Created by matt on 26/02/17. 
*/ 
public class GameObject extends Pane { 

    ObjectImage objectImage; 

    public GameObject(Image image, int columns, int rows){ 
     objectImage = new ObjectImage(image,columns,rows); 
     getChildren().setAll(objectImage); 
    } 

    public void pauseAnimation(){ 
     getChildren().setAll(objectImage); 
     objectImage.pauseAnimation(); 
    } 

    public void walkSouth(){ 
     getChildren().setAll(objectImage); 
     objectImage.walkSouth(); 
    } 

} 

class ObjectImage extends ImageView { 

    private Rectangle2D[] clips; 
    private double width,height; 
    private Timeline timeline = new Timeline(); 

    public ObjectImage(Image image,int columns,int rows){ 

     width = image.getWidth()/columns; 
     height = image.getHeight()/rows; 

     clips = new Rectangle2D[rows*columns]; 
     int count=0; 
     for(int row =0;row < rows;row++) 
      for(int column = 0 ; column < columns; column++,count++) 
       clips[count] = new Rectangle2D(width * column, height * row,width,height); 

     setImage(image); 
     setViewport(clips[0]); 

    } 

    public void pauseAnimation(){ 
     timeline.pause(); 
    } 

    public void walkSouth(){ 
     System.out.println("walk south test"); 
     IntegerProperty count = new SimpleIntegerProperty(0); 

     KeyFrame frame = new KeyFrame(Duration.millis(1000/5), event -> { 
      if(count.get() < 2) count.set(count.get()+1); 
      else count.set(0); 
      setViewport(clips[count.get()]); 
      System.out.println("frame test"); 
     }); 

     timeline.setCycleCount(timeline.INDEFINITE); 
     timeline.getKeyFrames(); 
     timeline.play(); 
    } 
} 

This is the sprite-sheet image I'm working with

This is the outcome

+0

我可能無法立即回答您的兩個問題,但我可以看到你忘了加上框架在walkSouth方法你的時間表,這就是爲什麼動畫不能正常工作。 –

回答

0

由於暗示的評論,你做忘記添加在walkSouth方法中的框架。 (另外你在walkSouth方法各畫面設置爲200ms的你有沒有打算改變這種狀況?)下面的代碼改變後:

public void walkSouth(){ 
    System.out.println("walk south test"); 
    IntegerProperty count = new SimpleIntegerProperty(0); 

    KeyFrame frame = new KeyFrame(Duration.millis(1000/FPS), event -> { 
     if(count.get() < 2) count.set(count.get()+1); 
     else count.set(0); 
     setViewport(clips[count.get()]); 
    }); 

    timeline.setCycleCount(timeline.INDEFINITE); 
    timeline.getKeyFrames().add(frame); //This was the offending line. 
    timeline.play(); 
} 

要回答你的第一個問題,是有類你可以許多其他的選擇使用。你可以做的兩個選擇是使用AnimationTimerTransition類。這兩個(代碼示例)的簡要說明。

AnimationTimer被稱爲每個週期或渲染的幀,我相信你可能會想這一個:

public void walkSouth(){ 
    System.out.println("walk south test"); 
    IntegerProperty count = new SimpleIntegerProperty(0); 

    AnimationTimer tmr = new AnimationTimer() { 
     @Override 
     public void handle(long nanoTime) 
     { 
      //nanoTime specifies the current time at the beginning of the frame in nano seconds. 
      if(count.get() < 2) count.set(count.get()+1); 
      else count.set(0); 
      setViewport(clips[count.get()]); 
     } 

    }; 
    tmr.start(); 
    //call tmr.stop() to stop/ pause timer. 
} 

但是,如果你不想被稱爲每一幀的動畫,你可以擴展Transition。一個轉換具有範圍從0到1的frac(小數)值,其相對於時間增加。我不會詳細討論,但我相信你可以在api上查找更多的信息。

public void walkSouth(){ 
    System.out.println("walk south test"); 
    IntegerProperty count = new SimpleIntegerProperty(0); 

    Transition trans = new Transition() { 
     { 
      setCycleDuration(Duration.millis(1000/60.0)); 
     } 
     @Override 
     public void interpolate(double frac) 
     { 
      if (frac != 1) 
       return; 
      //End of one cycle. 
      if(count.get() < 2) count.set(count.get()+1); 
      else count.set(0); 
      setViewport(clips[count.get()]); 
     } 

    }; 
    trans.setCycleCount(Animation.INDEFINITE); 
    trans.playFromStart(); 
    //Use trans.pause to pause, trans.stop to stop. 
} 
+0

太好了,謝謝你我現在正在工作,一定會嘗試將它轉換爲使用動畫和轉換類。 有一點需要注意,'walkSouth'方法中的'IntegerProperty',我應該在方法之外創建並初始化它,因爲每次調用它時都會重置計數週期,即使按住按鈕時也是如此。 – mattstack

+0

然後您可以嘗試使用常規實例整數變量 – theKidOfArcrania