Animation 在关键点输入上使用JavaFX设置精灵动画

Animation 在关键点输入上使用JavaFX设置精灵动画,animation,javafx,sprite,timeline,keyframe,Animation,Javafx,Sprite,Timeline,Keyframe,我是JavaFX新手,但对面向对象的Java有很好的理解。下面的程序是两个示例的组合,一个用于设置和移动形状的动画,另一个用于在按下鼠标按钮时设置对象的动画。许多功能已被删除或更改,以满足我的需要 我已经搜索了很多例子,但没有找到一个我完全理解的关于移动精灵和按键动画的例子。在我的程序中,我确信我没有使用正确的类来创建游戏对象,即使经过一些调整,我确信它可以工作 我添加了一些println函数来测试动画。问题似乎是walkSouth动画中的关键帧部分无法工作/播放 我的问题是: 我应该使用不同的

我是JavaFX新手,但对面向对象的Java有很好的理解。下面的程序是两个示例的组合,一个用于设置和移动形状的动画,另一个用于在按下鼠标按钮时设置对象的动画。许多功能已被删除或更改,以满足我的需要

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

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

我的问题是:

  • 我应该使用不同的JavaFX类来创建精灵表动画吗
  • 这段代码可以很容易地适应函数,这样我就可以更好地理解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);
        }
    }
    
    以下是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();
        }
    }
    
    封装测试;
    导入javafx.animation.KeyFrame;
    导入javafx.animation.Timeline;
    导入javafx.beans.property.IntegerProperty;
    导入javafx.beans.property.SimpleIntegerProperty;
    导入javafx.geometry.Rectangle2D;
    导入javafx.scene.image.image;
    导入javafx.scene.image.ImageView;
    导入javafx.scene.layout.Pane;
    导入javafx.util.Duration;
    /**
    *马特于2017年2月26日创作。
    */
    公共类游戏对象扩展窗格{
    客观形象客观形象;
    公共游戏对象(图像、整数列、整数行){
    objectImage=新的objectImage(图像、列、行);
    getChildren().setAll(objectImage);
    }
    公共无效暂停激活(){
    getChildren().setAll(objectImage);
    objectImage.pauseAnimation();
    }
    公共图书馆(南){
    getChildren().setAll(objectImage);
    objectImage.walkSouth();
    }
    }
    类ObjectImage扩展了ImageView{
    私人矩形2D[]剪辑;
    私人双宽,高;
    私有时间线=新时间线();
    公共对象图像(图像图像、int列、int行){
    宽度=image.getWidth()/列;
    高度=image.getHeight()/行;
    clips=新矩形2D[行*列];
    整数计数=0;
    对于(int row=0;row{
    if(count.get()<2)count.set(count.get()+1);
    else计数集(0);
    setViewport(剪辑[count.get()]);
    系统输出打印项次(“帧测试”);
    });
    timeline.setCycleCount(timeline.unfinite);
    timeline.getKeyFrames();
    timeline.play();
    }
    }
    


    正如评论所暗示的,您确实忘记了在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();
    }
    
    但是,如果您不希望在每一帧中调用动画,则可以扩展
    Transition
    。转换的
    frac
    (分数)值范围为0到1,随时间增加。我不打算讲太多细节,但我相信你可以在网上查到更多的信息

    public void walkSouth(){
    System.out.println(“向南行走测试”);
    IntegerProperty计数=新的SimpleIntegerProperty(0);
    Transition trans=新的Transition(){
    {
    设置循环次数(持续时间.毫秒(1000/60.0));
    }
    @凌驾
    公共空隙插值(双分形)
    {
    如果(分数!=1)
    返回;
    //一个周期的结束。
    if(count.get()<2)count.set(count.get()+1);
    else计数集(0);
    setViewport(剪辑[count.get()]);
    }
    };
    trans.setCycleCount(Animation.unfinite);
    trans.playFromStart();
    //使用trans.pause暂停,trans.stop停止。
    }
    
    我现在可能无法回答您的2个问题,但我可以看到您忘记在walkSouth方法中的时间线中添加帧,这就是动画无法工作的原因。太好了,谢谢您,我现在可以工作了,一定会尝试将其转换为使用动画和过渡类。需要注意的一点是,
    IntegerProperty
    walksuth
    方法中,我应该在方法之外创建并初始化它,因为每次调用它时,它都会重置计数周期,即使在按住按钮时也是如此。您可以尝试使用常规实例整数变量
    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.
    }
    
    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.
    }