2017-02-26 28 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