在键输入

时间:2017-02-26 23:23:16

标签: animation javafx sprite timeline keyframe

我是JavaFX的新手,但对面向对象的Java有很好的理解。以下程序是两个示例的组合,一个是动画和移动形状,另一个是按下鼠标按钮上的对象动画。已根据我的需要删除或更改了大部分功能。

我已经搜索过很多例子,但是我找不到一个关于在按键上移动精灵和动画的完全理解。在我的程序中,我确定我没有使用正确的类来创建游戏对象,即使经过一些调整我确信它可以工作。

我添加了一些println函数来测试动画。问题似乎是KeyFrame动画中的walkSouth部分无法正常工作/播放。

我的问题是:

  1. 我应该使用不同的JavaFX类来创建精灵表动画吗?
  2. 这段代码可以很容易地适应功能,这样我就可以更好地理解JavaFX的工作原理。
  3. 这是主要课程:

    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);
        }
    }
    

    这是GameObject类:

    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

1 个答案:

答案 0 :(得分:0)

正如评论所暗示的,你确实忘了在walkSouth方法中添加框架。 (此外,您将walkSouth方法中的每个图片框设置为200毫秒。您是否打算更改它?)以下是更改后的代码:

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.
}