【问题标题】:How do I implement game-loop using timeline in Javafx?如何在 Javafx 中使用时间轴实现游戏循环?
【发布时间】:2018-06-22 02:29:11
【问题描述】:

我是初学者。我正在使用 Javafx 制作一个简单的颜色匹配 2D 游戏,其中一个圆圈将穿过矩形的障碍。
圆必须穿过具有相同颜色的矩形。
通过后,圆的颜色和矩形的颜色顺序会发生变化。
我已经完成了以下代码。现在我不知道如何更改每个时间线循环的矩形障碍物的颜色顺序和圆圈的颜色。
另外如何检测圆圈和障碍物的颜色是否匹配...需要快速帮助

import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.*;
import javafx.scene.control.Label;
import javafx.scene.input.*;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ColorRun extends Application {

    private static final int KEYBOARD_MOVEMENT_DELTA = 20;
    private static final Duration TRANSLATE_DURATION = Duration.seconds(0.25);

    @Override
    public void start(Stage primaryStage) {

        for (int i = 0; i < 12; i++) {
        Circle circle = new Circle();
        circle.setCenterX(650);
        circle.setCenterY(500);
        circle.setRadius(40);
        circle.setFill(Color.RED);

        Rectangle rectangle1 = new Rectangle(0, 0, 200, 70);
        rectangle1.setFill(Color.RED);
        rectangle1.setStroke(Color.BLACK);
        rectangle1.setArcWidth(10);
        rectangle1.setArcHeight(10);

        Rectangle rectangle2 = new Rectangle(200, 0, 200, 70);
        rectangle2.setFill(Color.GREEN);
        rectangle2.setStroke(Color.BLACK);
        rectangle2.setArcWidth(10);
        rectangle2.setArcHeight(10);

        Rectangle rectangle3 = new Rectangle(400, 0, 200, 70);
        rectangle3.setFill(Color.BLUE);
        rectangle3.setStroke(Color.BLACK);
        rectangle3.setArcWidth(10);
        rectangle3.setArcHeight(10);

        Rectangle rectangle4 = new Rectangle(600, 0, 200, 70);
        rectangle4.setFill(Color.YELLOW);
        rectangle4.setStroke(Color.BLACK);
        rectangle4.setArcWidth(10);
        rectangle4.setArcHeight(10);

        Pane root = new Pane();
        root.getChildren().addAll(circle, rectangle1, rectangle2, rectangle3, rectangle4);
        final Scene scene = new Scene(root, 800, 800, Color.GREY);
        primaryStage.setTitle("Color Run");
        primaryStage.setScene(scene);
        moveCircleOnKeyPress(scene, circle);

            Timeline timeline1 = new Timeline();
            timeline1.setCycleCount(Timeline.INDEFINITE);
            timeline1.setAutoReverse(false);
            final KeyValue kv1 = new KeyValue(rectangle1.yProperty(), 800);
            final KeyValue kv2 = new KeyValue(rectangle2.yProperty(), 800);
            final KeyValue kv3 = new KeyValue(rectangle3.yProperty(), 800);
            final KeyValue kv4 = new KeyValue(rectangle4.yProperty(), 800);
            final KeyFrame kf = new KeyFrame(Duration.millis(2000), kv1, kv2, kv3, kv4);
            timeline1.getKeyFrames().add(kf);
            timeline1.play();


        primaryStage.show();
        }
    }

    public void moveCircleOnKeyPress(Scene scene, Circle circle) {
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                switch (event.getCode()) {

                    case RIGHT:
                        circle.setCenterX(circle.getCenterX() + KEYBOARD_MOVEMENT_DELTA);
                        break;
                    case LEFT:
                        circle.setCenterX(circle.getCenterX() - KEYBOARD_MOVEMENT_DELTA);
                        break;
                }
            }
        });
    }

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

    }
}

【问题讨论】:

  • 急需帮助 - 多快?
  • 一两天之内,或许更多……我需要尽快学习

标签: java animation javafx event-handling


【解决方案1】:

KeyFrame 有一个可以接受事件处理程序的构造函数。 要更改颜色,请将事件处理程序添加到(最后一个)KeyFrame

final KeyFrame kf = new KeyFrame(Duration.millis(2000),(evt -> changeColors()), kv1, kv2, kv3, kv4);

并添加处理颜色变化的方法:

private void changeColors() {
    //todo handle color change
    System.out.println("timeline cycle finished");
}           

【讨论】:

    【解决方案2】:

    更接近游戏循环。触发EventHandler 更频繁地更新场景并使用它来检查圆形和矩形之间的交叉点。当矩形到达底部时,将它们重置为顶部并更改颜色。

    private static final double KEYBOARD_MOVEMENT_DELTA = 5;
    private static final double RECT_WIDTH = 200;
    private static final double RECT_HEIGHT = 70;
    private static final double RECT_MAX_Y = 800;
    
    private static Rectangle createRectangle(double x) {
        Rectangle rect = new Rectangle(x, 0, RECT_WIDTH, RECT_HEIGHT);
        rect.setStroke(Color.BLACK);
        rect.setArcWidth(10);
        rect.setArcHeight(10);
        return rect;
    }
    
    private final Random random = new Random();
    
    private void randomizeColors(Rectangle[] rects, Circle circle, List<Color> colors) {
        Collections.shuffle(colors, random);
        for (int i = 0; i < rects.length; i++) {
            rects[i].setFill(colors.get(i));
        }
        circle.setFill(colors.get(random.nextInt(colors.size())));
    }
    
    @Override
    public void start(Stage primaryStage) {
        List<Color> colors = Arrays.asList(Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW);
    
        Circle circle = new Circle(650, 500, 40);
        Rectangle[] rectangles = new Rectangle[4];
        for (int i = 0; i < rectangles.length; i++) {
            rectangles[i] = createRectangle(RECT_WIDTH * i);
        }
    
        Pane root = new Pane();
        root.setPrefHeight(RECT_MAX_Y);
        for (Rectangle rect : rectangles) {
            root.getChildren().add(rect);
        }
        root.getChildren().add(circle);
    
        final double frameDuration = 16;
        final double iterationDuration = 5000;
        final int framesPerIteration = (int) (iterationDuration / frameDuration + 1);
        randomizeColors(rectangles, circle, colors);
    
        Timeline timeline = new Timeline();
    
        class FrameHandler implements EventHandler<ActionEvent> {
    
            KeyCode code;
            private int frame = 1;
    
            @Override
            public void handle(ActionEvent event) {
                if (frame == 0) {
                    randomizeColors(rectangles, circle, colors); // change colors when iteration is done
                }
    
                // move circle, if key is pressed
                if (code != null) {
                    switch (code) {
                        case RIGHT:
                            circle.setCenterX(circle.getCenterX() + KEYBOARD_MOVEMENT_DELTA);
                            break;
                        case LEFT:
                            circle.setCenterX(circle.getCenterX() - KEYBOARD_MOVEMENT_DELTA);
                            break;
                    }
                }
    
                // move rects & check intersection
                final Paint color = circle.getFill();
                final double cx = circle.getCenterX();
                final double cy = circle.getCenterY();
                final double r2 = circle.getRadius() * circle.getRadius();
                boolean lost = false;
                for (Rectangle rect : rectangles) {
                    rect.setY(frame * RECT_MAX_Y / framesPerIteration);
                    // check for intersections with rects of wrong color
                    if (rect.getFill() != color) {
    
                        double dy = Math.min(Math.abs(rect.getY() - cy),
                                Math.abs(rect.getY() + rect.getHeight() - cy));
                        dy = dy * dy;
    
                        if (dy > r2) {
                            continue; // y-distance too great for intersection
                        }
                        if (cx >= rect.getX() && cx <= rect.getX() + rect.getWidth()) {
                            lost = true;
                        } else {
                            double dx = Math.min(Math.abs(rect.getX() - cx),
                                    Math.abs(rect.getX() + rect.getWidth() - cx));
                            if (dx * dx + dy <= r2) {
                                lost = true;
                            }
                        }
                    }
                }
                frame = (frame + 1) % framesPerIteration;
                if (lost) {
                    timeline.stop();
                }
    
            }
        }
    
        FrameHandler frameHandler = new FrameHandler();
    
        Scene scene = new Scene(root);
    
        // keep track of the state of the arrow keys
        scene.setOnKeyPressed(evt -> {
            KeyCode code = evt.getCode();
            switch (code) {
                case RIGHT:
                case LEFT:
                    frameHandler.code = code;
                    break;
            }
        });
        scene.setOnKeyReleased(evt -> {
            KeyCode code = evt.getCode();
            if (frameHandler.code == code) {
                frameHandler.code = null;
            }
        });
    
        primaryStage.setScene(scene);
    
        timeline.getKeyFrames()
                .add(new KeyFrame(Duration.millis(frameDuration), frameHandler));
        timeline.setCycleCount(Timeline.INDEFINITE);
    
        timeline.play();
    
        primaryStage.show();
    }
    

    【讨论】:

    • 非常感谢...我会尝试实现它....但是如果我想添加分数、生命值并显示游戏结束怎么办?我该怎么做?
    • 您可以向事件处理程序添加不同的检查,或者对与错误矩形相交的圆做出不同的反应。除了随机化颜色之外,您还可以调整分数。您只需要弄清楚正确的条件和反应...
    • 这段代码中frameDuration、iterationDuration和framePerIteration的作用是什么?你能解释一下吗?
    猜你喜欢
    • 2014-06-01
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多