翻译形状问题:反应缓慢,有时运动缓慢

时间:2015-12-04 01:52:22

标签: animation javafx

正如标题所说,我想知道我做错了什么。我缺少什么,等等。该程序应该是一个左右移动的简单移动游戏。它做得很好,但有一个缺陷。有时它反应缓慢,例如当我按下Left然后向右,它仍然向左移动然后向右移动,或者当我之前执行动作时,移动速度降低。请注意,我也尝试使用布尔标志。

package sample;

import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application
{
private static Rectangle rct_main_player = new Rectangle();
private static TranslateTransition tTrans_main_player = new TranslateTransition(Duration.millis(2000),rct_main_player);
private static double dbl_player_x = 140, dbl_player_y = 360;
private static int int_player_w = 120, int_player_h = 20;

@Override
public void start(Stage stg_main)
{
    Group grp_main = new Group();

    subRoute_drawPlayer(grp_main);

    Scene scn_main = new Scene(grp_main,400,400);

    scn_main.setOnKeyPressed(e->
    {
        subRoute_movePlayer(e.getCode());
    });

    scn_main.setOnKeyReleased(e->
    {
        subRoute_stopPlayer();
    });

    stg_main.setScene(scn_main);
    stg_main.setResizable(false);
    stg_main.show();
}

private void subRoute_drawPlayer(Group grp_rcvd)
{
    rct_main_player.setFill(Color.BLACK);
    rct_main_player.setWidth(int_player_w);
    rct_main_player.setHeight(int_player_h);
    rct_main_player.setX(dbl_player_x);
    rct_main_player.setY(dbl_player_y);

    grp_rcvd.getChildren().add(rct_main_player);
}

private void subRoute_movePlayer(KeyCode kc_rcvd)
{
    if(kc_rcvd == KeyCode.RIGHT)
    {
        tTrans_main_player.setToX(dbl_player_x += 1);
        tTrans_main_player.play();
    }
    else if(kc_rcvd == KeyCode.LEFT)
    {
        tTrans_main_player.setToX(dbl_player_x -= 1);
        tTrans_main_player.play();
    }
    else
    {
        subRoute_stopPlayer();
    }
}

private void subRoute_stopPlayer()
{
    tTrans_main_player.pause();
}

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

1 个答案:

答案 0 :(得分:0)

问题是你使用的翻译持续2秒。除此之外,翻译总是与起源有关。翻译绝对不会移动,而是相对而言。然后你必须"修复"翻译后玩家的位置。

简而言之,就像这样:

rct_main_player.setLayoutX( rct_main_player.getLayoutX() + rct_main_player.getTranslateX());
rct_main_player.setTranslateX(0);

if (kc_rcvd == KeyCode.RIGHT) {
    tTrans_main_player.setToX( +1);
    tTrans_main_player.play();
} else if (kc_rcvd == KeyCode.LEFT) {
    tTrans_main_player.setToX( -1);
    tTrans_main_player.play();
} else {
    subRoute_stopPlayer();
}

然后来自setToX()文档:

  

无法更改正在运行的TranslateTransition的X.如果   对于正在运行的TranslateTransition,更改了toX的值   动画必须停止并再次启动以获取新动画   值。

但真正的问题是你首先要使用翻译。对于移动游戏,您应该使用AnimationTimer

您可能还想看一下这篇文章:"In JavaFX how do I move a sprite across the screen?"和jewelsea在"How to write a KeyListener for JavaFX"中的答案,这些答案不同,但也可能是您正在寻找的内容。

前段时间我做了一个Pong游戏示例,这里有一些代码可以帮助你入门。

SpriteBase.java:

public abstract class SpriteBase {

    Image image;
    ImageView imageView;

    Pane layer;

    double x;
    double y;
    double r;

    double dx;
    double dy;
    double dr;

    double health;
    double damage;

    boolean removable = false;

    double w;
    double h;

    boolean canMove = true;

    double score = 0;

    public SpriteBase(Pane layer, Image image, double x, double y, double r, double dx, double dy, double dr, double health, double damage) {

        this.layer = layer;
        this.image = image;
        this.x = x;
        this.y = y;
        this.r = r;
        this.dx = dx;
        this.dy = dy;
        this.dr = dr;

        this.health = health;
        this.damage = damage;

        this.imageView = new ImageView(image);
        this.imageView.relocate(x, y);
        this.imageView.setRotate(r);

        this.w = image.getWidth(); // imageView.getBoundsInParent().getWidth();
        this.h = image.getHeight(); // imageView.getBoundsInParent().getHeight();

        addToLayer();

    }

    public void addToLayer() {
        this.layer.getChildren().add(this.imageView);
    }

    public void removeFromLayer() {
        this.layer.getChildren().remove(this.imageView);
    }

    public Pane getLayer() {
        return layer;
    }

    public void setLayer(Pane layer) {
        this.layer = layer;
    }

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    public double getR() {
        return r;
    }

    public void setR(double r) {
        this.r = r;
    }

    public double getDx() {
        return dx;
    }

    public void setDx(double dx) {
        this.dx = dx;
    }

    public double getDy() {
        return dy;
    }

    public void setDy(double dy) {
        this.dy = dy;
    }

    public double getDr() {
        return dr;
    }

    public void setDr(double dr) {
        this.dr = dr;
    }

    public double getHealth() {
        return health;
    }

    public double getDamage() {
        return damage;
    }

    public void setDamage(double damage) {
        this.damage = damage;
    }

    public void setHealth(double health) {
        this.health = health;
    }

    public boolean isRemovable() {
        return removable;
    }

    public void setRemovable(boolean removable) {
        this.removable = removable;
    }

    public void move() {

        if( !canMove)
            return;

        x += dx;
        y += dy;
        r += dr;

    }

    public boolean isAlive() {
        return Double.compare(health, 0) > 0;
    }

    public ImageView getView() {
        return imageView;
    }

    public void updateUI() {

        imageView.relocate(x, y);
        imageView.setRotate(r);

    }

    public double getWidth() {
        return w;
    }

    public double getHeight() {
        return h;
    }

    public double getCenterX() {
        return x + w * 0.5;
    }

    public double getCenterY() {
        return y + h * 0.5;
    }

    // TODO: per-pixel-collision
    public boolean collidesWith( SpriteBase otherSprite) {

        return ( otherSprite.x + otherSprite.w >= x && otherSprite.y + otherSprite.h >= y && otherSprite.x <= x + w && otherSprite.y <= y + h);

    }

    /**
     * Reduce health by the amount of damage that the given sprite can inflict
     * @param sprite
     */
    public void getDamagedBy( SpriteBase sprite) {
        health -= sprite.getDamage();
    }

    /**
     * Set health to 0
     */
    public void kill() {
        setHealth( 0);
    }

    /**
     * Set flag that the sprite can be removed from the UI.
     */
    public void remove() {
        setRemovable(true);
    }

    /**
     * Set flag that the sprite can't move anymore.
     */
    public void stopMovement() {
        this.canMove = false;
    }

    public void addScore( double value) {
        this.score += value;
    }

    public double getScore() {
        return score;
    }
    public abstract void checkRemovability();

}

Player.java:

public class Player extends SpriteBase {

    double playerShipMinX;
    double playerShipMaxX;
    double playerShipMinY;
    double playerShipMaxY;

    Input input;

    double speed;

    public Player(Pane layer, Image image, double x, double y, double r, double dx, double dy, double dr, double health, double damage, double speed, Input input) {

        super(layer, image, x, y, r, dx, dy, dr, health, damage);

        this.speed = speed;
        this.input = input;

        init();
    }


    private void init() {

        // calculate movement bounds of the player ship
        playerShipMinX = x; // limit to vertical movement 
        playerShipMaxX = x; // limit to vertical movement 
        playerShipMinY = 0;
        playerShipMaxY = Settings.SCENE_HEIGHT -image.getHeight();

    }

    public void processInput() {

        // ------------------------------------
        // movement
        // ------------------------------------

        // vertical direction
        if( input.isMoveUp()) {
            dy = -speed;
        } else if( input.isMoveDown()) {
            dy = speed;
        } else {
            dy = 0d;
        }

        // horizontal direction
        if( input.isMoveLeft()) {
            dx = -speed;
        } else if( input.isMoveRight()) {
            dx = speed;
        } else {
            dx = 0d;
        }

    }

    @Override
    public void move() {

        super.move();

        // ensure the ship can't move outside of the screen
        checkBounds();


    }

    protected void checkBounds() {

        // vertical
        if( Double.compare( y, playerShipMinY) < 0) {
            y = playerShipMinY;
        } else if( Double.compare(y, playerShipMaxY) > 0) {
            y = playerShipMaxY;
        }

        // horizontal
        if( Double.compare( x, playerShipMinX) < 0) {
            x = playerShipMinX;
        } else if( Double.compare(x, playerShipMaxX) > 0) {
            x = playerShipMaxX;
        }

    }


    @Override
    public void checkRemovability() {
    }

}

Enemy.java

public class Enemy extends Player {

    private enum Direction {
        UP,
        DOWN;
    }

    Direction direction = Direction.UP;

    public Enemy(Pane layer, Image image, double x, double y, double r, double dx, double dy, double dr, double health, double damage, double speed) {
        super(layer, image, x, y, r, dx, dy, dr, health, damage, speed, null);
    }

    @Override
    public void processInput() {

        if( direction == Direction.UP) {
            dy = -speed;
        } else {
            dy = speed;
        }

    }

    @Override
    protected void checkBounds() {

        super.checkBounds();

        if( y == playerShipMinY) {
            direction = Direction.DOWN;
        } else if( y == playerShipMaxY) {
            direction = Direction.UP;
        }

    }
}

Ball.java

public class Ball extends SpriteBase {

    public Ball(Pane layer, Image image, double x, double y, double r, double dx, double dy, double dr, double health, double damage) {
        super(layer, image, x, y, r, dx, dy, dr, health, damage);
    }

    public void bounceOff( SpriteBase sprite) {

        // TODO: bounce off in all directions and ensure the ball doesn't get stuck inside a player
        dx = -dx;
    }

    @Override
    public void checkRemovability() {

        if( Double.compare( getX(), 0) < 0 || Double.compare( getX(), Settings.SCENE_WIDTH) > 0 || Double.compare( getY(), 0) < 0 || Double.compare( getY(), Settings.SCENE_HEIGHT) > 0) {
            setRemovable(true);
        }


    }
}

Input.java

public class Input {

    /**
     * Bitset which registers if any {@link KeyCode} keeps being pressed or if it is released.
     */
    private BitSet keyboardBitSet = new BitSet();

    // -------------------------------------------------
    // default key codes
    // will vary when you let the user customize the key codes or when you add support for a 2nd player
    // -------------------------------------------------

    private KeyCode upKey = KeyCode.UP;
    private KeyCode downKey = KeyCode.DOWN;
    private KeyCode leftKey = KeyCode.LEFT;
    private KeyCode rightKey = KeyCode.RIGHT;
    private KeyCode primaryWeaponKey = KeyCode.SPACE;
    private KeyCode secondaryWeaponKey = KeyCode.CONTROL;

    Scene scene;

    public Input( Scene scene) {
        this.scene = scene;
    }

    public void addListeners() {

        scene.addEventFilter(KeyEvent.KEY_PRESSED, keyPressedEventHandler);
        scene.addEventFilter(KeyEvent.KEY_RELEASED, keyReleasedEventHandler);

    }

    public void removeListeners() {

        scene.removeEventFilter(KeyEvent.KEY_PRESSED, keyPressedEventHandler);
        scene.removeEventFilter(KeyEvent.KEY_RELEASED, keyReleasedEventHandler);

    }

    /**
     * "Key Pressed" handler for all input events: register pressed key in the bitset
     */
    private EventHandler<KeyEvent> keyPressedEventHandler = new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent event) {

            // register key down
            keyboardBitSet.set(event.getCode().ordinal(), true);

        }
    };

    /**
     * "Key Released" handler for all input events: unregister released key in the bitset
     */
    private EventHandler<KeyEvent> keyReleasedEventHandler = new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent event) {

            // register key up
            keyboardBitSet.set(event.getCode().ordinal(), false);

        }
    };


    // -------------------------------------------------
    // Evaluate bitset of pressed keys and return the player input.
    // If direction and its opposite direction are pressed simultaneously, then the direction isn't handled.
    // -------------------------------------------------

    public boolean isMoveUp() {
        return keyboardBitSet.get( upKey.ordinal()) && !keyboardBitSet.get( downKey.ordinal());
    }

    public boolean isMoveDown() {
        return keyboardBitSet.get( downKey.ordinal()) && !keyboardBitSet.get( upKey.ordinal());
    }

    public boolean isMoveLeft() {
        return keyboardBitSet.get( leftKey.ordinal()) && !keyboardBitSet.get( rightKey.ordinal());  
    }

    public boolean isMoveRight() {
        return keyboardBitSet.get( rightKey.ordinal()) && !keyboardBitSet.get( leftKey.ordinal());
    }

    public boolean isFirePrimaryWeapon() {
        return keyboardBitSet.get( primaryWeaponKey.ordinal());
    }

    public boolean isFireSecondaryWeapon() {
        return keyboardBitSet.get( secondaryWeaponKey.ordinal());
    }

}

Game.java

public class Game extends Application {

    Random rnd = new Random();

    Pane playfieldLayer;
    AnchorPane scoreLayer;

    Image playerImage;
    Image enemyImage;
    Image ballImage;

    List<Player> players = new ArrayList<>();
    List<Ball> balls = new ArrayList<>();

    Text playerScoreText = new Text();
    Text enemyScoreText = new Text();

    Map<Player,Text> scoreDisplay = new HashMap<>();

    Scene scene;

    @Override
    public void start(Stage primaryStage) {

        Group root = new Group();

        // create layers
        playfieldLayer = new Pane();
        playfieldLayer.setPrefSize(Settings.SCENE_WIDTH, Settings.SCENE_HEIGHT);

        scoreLayer = new AnchorPane();
        scoreLayer.setPrefSize(Settings.SCENE_WIDTH, Settings.SCENE_HEIGHT);

        root.getChildren().add( playfieldLayer);
        root.getChildren().add( scoreLayer);

        scene = new Scene( root, Settings.SCENE_WIDTH, Settings.SCENE_HEIGHT);

        primaryStage.setScene( scene);
        primaryStage.show();

        loadGame();

        createScoreLayer();
        createPlayers();

        AnimationTimer gameLoop = new AnimationTimer() {

            @Override
            public void handle(long now) {

                // player input
                players.forEach(sprite -> sprite.processInput());

                // add random enemies
                spawnBalls();

                // movement
                players.forEach(sprite -> sprite.move());
                balls.forEach(sprite -> sprite.move());

                // check collisions
                checkCollisions();

                // update sprites in scene
                players.forEach(sprite -> sprite.updateUI());
                balls.forEach(sprite -> sprite.updateUI());

                // check if sprite can be removed
                balls.forEach(sprite -> sprite.checkRemovability());

                // remove removables from list, layer, etc
                removeSprites( balls);

                // update score, health, etc
                updateScore();
            }

        };
        gameLoop.start();

    }

    private void loadGame() {

        WritableImage wi;

        // create paddle image
        // -------------------------
        double w = Settings.PADDLE_WIDTH;
        double h = Settings.PADDLE_HEIGHT;

        Rectangle rect = new Rectangle( w, h);

        wi = new WritableImage( (int) w, (int) h);
        rect.snapshot(null, wi);

        playerImage = wi;
        enemyImage = wi;

        // create ball image
        // -------------------------
        double r = Settings.BALL_RADIUS;

        Circle circle = new Circle( r);

        wi = new WritableImage( (int) r * 2, (int) r * 2);
        circle.snapshot(null, wi);

        ballImage = wi;

    }

    private void createScoreLayer() {

        playerScoreText.setFont( Font.font( null, FontWeight.BOLD, 32));
        enemyScoreText.setFont( Font.font( null, FontWeight.BOLD, 32));

        AnchorPane.setTopAnchor(playerScoreText, 0.0);
        AnchorPane.setLeftAnchor(playerScoreText, 10.0);

        AnchorPane.setTopAnchor(enemyScoreText, 0.0);
        AnchorPane.setRightAnchor(enemyScoreText, 10.0);

        scoreLayer.getChildren().add( playerScoreText);
        scoreLayer.getChildren().add( enemyScoreText);

    }

    private void createPlayers() {

        // create player instances
        Player player = createPlayer();
        Player enemy = createEnemy();

        // register player
        players.add( player);
        players.add( enemy);

        // assign score display
        scoreDisplay.put(player, playerScoreText);
        scoreDisplay.put(enemy, enemyScoreText);

    }

    private Player createPlayer() {

        // player input
        Input input = new Input( scene);

        // register input listeners
        input.addListeners(); // TODO: remove listeners on game over

        Image image = playerImage;

        // offset x position, center vertically
        double x = Settings.PADDLE_OFFSET_X;
        double y = (Settings.SCENE_HEIGHT - image.getHeight()) * 0.5;

        // create player
        Player player = new Player(playfieldLayer, image, x, y, 0, 0, 0, 0, 1, 0, Settings.PADDLE_SPEED, input);

        return player;

    }

    private Player createEnemy() {

        Image image = enemyImage;

        // offset x position, center vertically
        double x = Settings.SCENE_WIDTH - Settings.PADDLE_OFFSET_X - image.getWidth();
        double y = (Settings.SCENE_HEIGHT - image.getHeight()) * 0.5;

        // create player
        Enemy player = new Enemy(playfieldLayer, image, x, y, 0, 0, 0, 0, 1, 0, Settings.PADDLE_SPEED);

        return player;

    }

    private void spawnBalls() {
        if( balls.size() == 0) {
            createBall();
        }
    }
    private void createBall() {

        Image image = ballImage;

        // offset x position, center vertically
        double x = (Settings.SCENE_WIDTH - image.getWidth()) / 2;
        double y = (Settings.SCENE_HEIGHT - image.getHeight()) / 2;

        // create ball
        Ball ball = new Ball( playfieldLayer, image, x, y, 0, -Settings.BALL_SPEED, 0, 0, 1, 1);

        // register ball
        balls.add(ball);

    }

    private void removeSprites(  List<? extends SpriteBase> spriteList) {
        Iterator<? extends SpriteBase> iter = spriteList.iterator();
        while( iter.hasNext()) {
            SpriteBase sprite = iter.next();

            if( sprite.isRemovable()) {

                // remove from layer
                sprite.removeFromLayer();

                // remove from list
                iter.remove();
            }
        }
    }

    private void checkCollisions() {

        for( Player player: players) {
            for( Ball ball: balls) {
                if( player.collidesWith(ball)) {

                    // bounce ball
                    ball.bounceOff(player);

                    // add score
                    // TODO: proper score handling: score only to the player if the ball leaves screen afterwards
                    player.addScore( 1); 

                }
            }
        }
    }

    private void updateScore() {

        for( Player player: players) {
            scoreDisplay.get( player).setText( "" + (int) player.getScore());           
        }

    }

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

}

Settings.java

public class Settings {

    public static double SCENE_WIDTH = 600;
    public static double SCENE_HEIGHT = 400;

    public static double PADDLE_WIDTH = 20;
    public static double PADDLE_HEIGHT = 100;

    public static double BALL_RADIUS = 10;

    public static double PADDLE_OFFSET_X = 50;

    public static double PADDLE_SPEED = 4.0;
    public static double BALL_SPEED = 4.0;

}

截图:

enter image description here