我有這個 JavaFX Circle,它根據鍵盤的箭頭移動。所有AnimationTimer沒有被重繪 每一幀的圓圈位置。
我發現0.1每次KeyEvent觸發a 時的運動對于影片來說都足夠平滑,但是它的移動速度非常慢。另一方面,如果我將運動更改為1.0或者10.0,它無疑會更快,但也會更加不穩定(您可以清楚地看到它開始以離散值移動)。
我希望能夠保持最多0.1每幀平移的平滑度,但也能夠在每次觸發鍵時更改它應該移動多少空間。
以下是描述問題的mre:
public class MainFX extends Application {
private double playerX;
private double playerY;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
AnchorPane pane = new AnchorPane();
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
playerX = pane.getWidth()/2;
playerY = pane.getHeight()/2;
Circle player = new Circle(playerX,playerY,10);
pane.getChildren().add(player);
scene.addEventHandler(KeyEvent.KEY_PRESSED, this::animate);
AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long now) {
player.setCenterX(playerX);
player.setCenterY(playerY);
}
};
timer.start();
}
private void animate(KeyEvent key){
if (key.getCode() == KeyCode.UP) {
playerY-=0.1;
}
if (key.getCode() == KeyCode.DOWN) {
playerY =0.1;
}
if (key.getCode() == KeyCode.RIGHT) {
playerX =0.1;
}
if (key.getCode() == KeyCode.LEFT) {
playerX-=0.1;
}
}
}
uj5u.com熱心網友回復:
所述AnimationTimer的handle()方法被呼叫,在每個幀渲染。假設 FX 應用程式執行緒沒有被其他作業淹沒,這將以大約 60 幀/秒的速度發生。使用此方法更新視圖將提供相對平滑的影片。
相比之下,按鍵事件處理程式在每個按鍵(或釋放等)事件上被呼叫。通常,當一個鍵被按下時,本機系統會以某種取決于系統(通常是用戶可配置的)的速率發出重復的按鍵事件,并且通常比影片幀慢得多(通常每半秒左右)。從這里更改 UI 元素的位置將導致抖動。
您當前的代碼從playerX和 中的playerY變數更新 UI 元素的位置AnimationTimer:但是您只更改這些變數是關鍵事件處理程式。因此,如果AnimationTimer以 60fps 運行,并且關鍵事件每 0.5 秒發生一次(例如),您將使用每個新值“更新”UI 元素 30 次,每秒僅更改實際位置兩次。
更好的方法是使用鍵事件處理程式僅維護變數的狀態,指示每個鍵是否被按下。在 中AnimationTimer,根據鍵的狀態以及自上次更新以來經過的時間來更新 UI。
這是使用此方法的代碼版本:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class MainFX extends Application {
private boolean leftPressed ;
private boolean rightPressed ;
private boolean upPressed ;
private boolean downPressed ;
private static final double SPEED = 100 ; // pixels/second
private static final double PLAYER_RADIUS = 10 ;
private AnchorPane pane;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
pane = new AnchorPane();
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
double playerX = pane.getWidth() / 2;
double playerY = pane.getHeight() / 2;
Circle player = new Circle(playerX, playerY, PLAYER_RADIUS);
pane.getChildren().add(player);
scene.addEventHandler(KeyEvent.KEY_PRESSED, this::press);
scene.addEventHandler(KeyEvent.KEY_RELEASED, this::release);
AnimationTimer timer = new AnimationTimer() {
private long lastUpdate = System.nanoTime() ;
@Override
public void handle(long now) {
double elapsedSeconds = (now - lastUpdate) / 1_000_000_000.0 ;
int deltaX = 0 ;
int deltaY = 0 ;
if (leftPressed) deltaX -= 1 ;
if (rightPressed) deltaX = 1 ;
if (upPressed) deltaY -= 1 ;
if (downPressed) deltaY = 1 ;
Point2D translationVector = new Point2D(deltaX, deltaY)
.normalize()
.multiply(SPEED * elapsedSeconds);
player.setCenterX(clampX(player.getCenterX() translationVector.getX()));
player.setCenterY(clampY(player.getCenterY() translationVector.getY()));
lastUpdate = now ;
}
};
timer.start();
}
private double clampX(double value) {
return clamp(value, PLAYER_RADIUS, pane.getWidth() - PLAYER_RADIUS);
}
private double clampY(double value) {
return clamp(value, PLAYER_RADIUS, pane.getHeight() - PLAYER_RADIUS);
}
private double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
private void press(KeyEvent event) {
handle(event.getCode(), true);
}
private void release(KeyEvent event) {
handle(event.getCode(), false);
}
private void handle(KeyCode key, boolean press) {
switch(key) {
case UP: upPressed = press ; break ;
case DOWN: downPressed = press ; break ;
case LEFT: leftPressed = press ; break ;
case RIGHT: rightPressed = press ; break ;
default: ;
}
}
}
uj5u.com熱心網友回復:
您沒有正確使用影片計時器。您的關鍵代碼處理應該用于設定物件的速度。釋放鍵時將速度設定為 0。在影片計時器代碼中,根據經過的時間和速度更改位置。
計時器將以幀速率觸發事件 - 可能約為 60fps。如果您想要平滑的運動,您需要調整每一幀的位置。相反,您使用它來將位置設定為預先計算的值。那樣做沒有任何用處。您可以輕松地在關鍵處理代碼中設定位置并獲得與現在相同的效果。
如果您不想讓用戶按住按鍵移動。也就是說,您想要點擊一次并讓物件移動 10.0。您可以在按鍵處理代碼中設定目標位置。一次跳躍10個目標位置。然后讓影片計時器以適當的速度將當前位置移向目標位置,到達目標位置時停止。
也許是這樣的:
AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long now) {
double curX = player.getCenterX();
double curY = player.getCenterY();
double diffX = playerX-curX;
double diffY = playerY-curY;
if (diffX > 1.0) {
curX = 1.0;
else if (diffX < -1.0) {
curX -= 1.0;
} else {
curX = playerX;
}
if (diffY > 1.0) {
curY = 1.0;
else if (diffY < -1.0) {
curY -= 1.0;
} else {
curY = playerY;
}
player.setCenterX(curX);
player.setCenterY(curY);
}
};
這是一個原始的例子......并注意它會使對角線運動比軸對齊運動更快。(在該示例中,對角線運動的速度矢量幅度是 sqrt(2) 而不是 1。)基本上,您希望根據為影片計時器的滴答之間的間隔調整的速度矢量更新位置。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/324357.html
