0

Below is a simplified example of what I have so far. It's a 2d array which can display blocks based on row/col in the array data. The user can move around the scene forward, back, up and down, and turn left/right 45 degrees using the arrow keys.

  • up arrow = forward
  • back arrow = backward
  • left arrow = turn left
  • right arrow = turn right
  • PgUp = look upward
  • PgDown = look downward
  • Ins = move up
  • Del = move down

The place I'm stuck is how can I add transition animations for these movements? Whenever I try everything goes out whack.

Here's the copy/paste code (running JavaFX with Java 17).

Thanks!

import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;

import java.util.Random;

public class MazeAnimationApp extends Application {

    private World world;
    private Stage stage;
    private Scene scene;

    static final Random random = new Random(1);

    private final BorderPane mainPane = new BorderPane();

    @Override
    public void start(Stage stage) throws Exception {
        this.stage = stage;

        world = new World(getData());

        setupScene();
        setupStage();
        stage.show();
    }

    private String[][] getData() {
        String[][] data = new String[32][32];
        for (int j = 0; j < 32; j++) {
            for (int k = 0; k < 32; k++) {
                if (random.nextInt(32) % 3 == 0 && random.nextInt(32) % 3 == 0) {
                    data[j][k] = "X";
                }
            }
        }
        return data;
    }

    private void setupScene() {

        scene = new Scene(mainPane, 1024, 768);

        mainPane.setCenter(world.getScene());

        Button btn = new Button("press arrows");
        btn.setTranslateX(0);
        btn.setTranslateY(0);
        mainPane.setBottom(btn);
        btn.setOnKeyPressed(event -> {
            keyPressed(event.getCode());
        });


    }

    private void setupStage() {
        stage.setTitle("Demo of something");
        stage.setFullScreenExitHint("");
        stage.setWidth(1024);
        stage.setHeight(768);

        stage.setScene(scene);

    }

    private void keyPressed(KeyCode keyCode) {
        System.out.println("keypressed " + keyCode);
        var camera = world.getCamera();
        switch (keyCode) {

            case KP_UP, NUMPAD8, UP -> {
                // forward
                camera.addToPos(1);
            }
            case KP_DOWN, NUMPAD2, DOWN -> {
                // back
                camera.addToPos(-1);
            }

            case KP_LEFT, LEFT, NUMPAD4 -> {
                // left
                camera.addToHAngle(-90);
            }

            case KP_RIGHT, RIGHT, NUMPAD6 -> {
                // right
                camera.addToHAngle(90);
            }

            case PAGE_UP -> {
                // look up
                camera.addToVAngle(45);
            }

            case PAGE_DOWN -> {
                // look down
                camera.addToVAngle(-45);
            }

            case INSERT -> {
                // go up
                camera.elevate(-0.5);
            }

            case DELETE -> {
                // go down
                camera.elevate(0.5);
            }
        }

    }


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


    private static class MazeCamera extends PerspectiveCamera {

        private final Translate pos = new Translate();

        /**
         * Direction on the horizontal plane.
         */
        private final Rotate hdir = new Rotate(-180, Rotate.Y_AXIS);

        /**
         * Direction on the vertical plane.
         */
        private final Rotate vdir = new Rotate(0, Rotate.X_AXIS);

        public MazeCamera() {
            super(true);

            setFieldOfView(100);
            setVerticalFieldOfView(false);
            setNearClip(0.001);
            setFarClip(30);
            getTransforms().addAll(pos, hdir, vdir);
            // y = - up + down
            // z = - forward + back
            // x = - left + right
        }

        public void setPos(final double x, final double y, final double z) {
            pos.setX(x);
            pos.setY(y);
            pos.setZ(z);
        }

        public void setHAngle(final double hangle) {
            hdir.setAngle(hangle);
        }

        public void addToHAngle(final double hdelta) {
            hdir.setAngle(hdir.getAngle() + hdelta);
        }

        public void setVAngle(final double vangle) {
            vdir.setAngle(vangle);
        }

        public void addToVAngle(final double vdelta) {
            final double vangle = vdir.getAngle() + vdelta;
            if (vangle < -90 || vangle > 90) {
                return;
            }

            vdir.setAngle(vdir.getAngle() + vdelta);
        }

        /**
         * Adds the specified amount to the camera's horizontal position, toward the camera's current horizontal direction.
         *
         * @param helta horizontal distance to be added to the camera's current horizontal position
         */
        public void addToPos(final double helta) {
            addToPos(helta, hdir.getAngle());
        }

        /**
         * Adds the specified amount to the camera's horizontal position, toward the specified horizontal direction.
         *
         * @param hdelta horizontal distance to be added to the camera's current horizontal position
         */
        public void addToPos(double hdelta, final double hangle) {

            final double rad = Math.toRadians(hangle);

            pos.setX(pos.getX() + hdelta * Math.sin(rad));
            pos.setZ(pos.getZ() + hdelta * Math.cos(rad));
        }

        /**
         * Elevates the camera: adds the specified vertical delta to its y position.
         *
         * @param vdelta (vertical) elevation to be added to the camera's current vertical position
         */
        public void elevate(final double vdelta) {
            pos.setY(pos.getY() + vdelta);
        }

        public Translate getPos() {
            return pos;
        }

        public Rotate getHDir() {
            return hdir;
        }

    }

    private record BlockPos(int row, int col) {
        static int maxDistance = 32;
    }


    private static class World {
        private final Group root = new Group();

        private final SubScene scene = new SubScene(root, 800, 600, true, SceneAntialiasing.BALANCED);

        private final MazeCamera camera = new MazeCamera();

        private final PointLight pointLight = new PointLight(Color.gray(0.3));

        private final AmbientLight ambientLight = new AmbientLight(Color.ANTIQUEWHITE);

        private final Material material1 = new PhongMaterial(Color.RED, null, null, null, null);
        private final Material material2 = new PhongMaterial(Color.GREEN, null, null, null, null);
        private final Material material3 = new PhongMaterial(Color.BLUE, null, null, null, null);

        private final String[][] data;

        public World(String[][] data) {
            this.data = data;
            initScene();
        }

        private void initScene() {
            root.getChildren().clear();
            pointLight.getTransforms().clear();

            camera.setVAngle(0);
            camera.setHAngle(0);

            root.getChildren().add(camera);
            root.getChildren().add(ambientLight);
            root.getChildren().add(pointLight);

            scene.setCamera(camera);
            scene.setFill(Color.LIGHTBLUE);

            int row = 5;
            int col = 5;
            camera.setPos(col + 0.5, 0, BlockPos.maxDistance - row + 0.5);

            for (int r = 0; r < BlockPos.maxDistance; r++) {
                for (int c = 0; c < BlockPos.maxDistance; c++) {
                    if ("X".equalsIgnoreCase(data[r][c])) {
                        BlockPos pos = new BlockPos(r, c);
                        root.getChildren().add(createBlock(pos));
                    }

                }
            }

        }

        private Node createBlock(BlockPos pos) {
            Box box = new Box(1, 1, 1);
            Material material = null;
            int r = random.nextInt(3);
            switch (r) {
                case 0 -> material = material1;
                case 1 -> material = material2;
                case 2 -> material = material3;
            }
            box.setMaterial(material);
            box.setTranslateX(pos.col() + 0.5);
            box.setTranslateZ((BlockPos.maxDistance - pos.row()) + 0.5);
            return box;
        }

        public SubScene getScene() {
            return scene;
        }

        public MazeCamera getCamera() {
            return camera;
        }
    }


}
sproketboy
  • 8,967
  • 18
  • 65
  • 95
  • 1
    A simple `Timeline` animation is shown [here](https://stackoverflow.com/a/37516327/230513); more [here](https://stackoverflow.com/a/37370840/230513). – trashgod Jul 16 '23 at 23:25

0 Answers0