简体   繁体   中英

How do I implement game-loop using timeline in Javafx?

I am a beginner. I am making a simple color matching 2D game using Javafx where a circle will pass through obstacles of rectangles.
The circle must pass through the rectangle which has the same color.
After passing, the color of the circle and the sequence of colors of rectangles will change.
I have done the following code. Now I don't have any clue how can I change sequence of colors of the rectangle obstacles and the color of the circle for each cycle of timeline.
Also how can I detect if the color of the circle and obstacle has matched or not... need help quickly

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

    }
}

KeyFrame has a constructor that can accept event handler. To change colors add a event handler to the (last) KeyFrame :

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

And add a method to handle color changes:

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

Go closer to a game loop. Trigger a EventHandler that updates the scene more frequently and use it to check for intersections between circle and rectangles. When the rectangles reach the bottom, reset them to the top and change the colors.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM