正如标题所说,我想知道我做错了什么。我缺少什么,等等。该程序应该是一个左右移动的简单移动游戏。它做得很好,但有一个缺陷。有时它反应缓慢,例如当我按下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);
}
}
答案 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;
}
截图: