我目前正在使用JavaFX进行类型速度游戏,其中单词应该从顶部开始下降,用户必须在降到最低点之前尽可能快地键入它们。我准备好了游戏的基本设置。我唯一要努力的是如何让单词从顶部掉落并向下移动(目前它们从下到上)。而且我希望在它们之间的特定时间间隔(例如30毫秒)从顶部的随机位置(不是同一个原点)落下多个单词。我到目前为止的代码:
public void showWords() throws InterruptedException
{
int missedWords = 0; // number of words the user failed to type
while (missedWords != 10)
{
dequedWord = queue.dequeue(); // the word that the Text object will contain
Text runWord = new Text(dequedWord);
wordsPane.getChildren().add(runWord); // the canvas in which the words will travel from top to bottom
double PaneHeight = wordsPane.getHeight();
//double PaneWidth = wordsPane.getWidth();
double runWordWidth = runWord.getLayoutBounds().getWidth();
KeyValue initKeyValue = new KeyValue(runWord.translateYProperty(), PaneHeight);
KeyFrame initFrame = new KeyFrame(Duration.ZERO, initKeyValue);
KeyValue endKeyValue = new KeyValue(runWord.translateYProperty(), -1.0 * runWordWidth);
KeyFrame endFrame = new KeyFrame(Duration.seconds(12), endKeyValue);
Timeline timeline = new Timeline(initFrame, endFrame);
timeline.setCycleCount(1);
timeline.play();
// add code to check whether user typed the word in the Text object
missedWords++;
}
}
我是动画新手,所以我对Timeline,KeyValue和KeyFrame类知之甚少。我尝试阅读API的文档,但对我没什么帮助。任何帮助是极大的赞赏。谢谢:))
答案 0 :(得分:1)
坐标系的y轴指向向下(这在计算机图形学中很常见)。这就是您的节点向错误方向移动的原因。此外,Timeline
似乎并不适合这里,因为您需要为每个单词运行Timeline
而另一个Timeline
用于添加新单词。
我建议使用AnimationTimer
,其中包含为每个帧调用的方法,它允许您更新位置,删除旧单词并根据时间添加新单词。
示例:
@Override
public void start(Stage primaryStage) {
final Queue<String> words = new LinkedList<>(Arrays.asList(
"Hello",
"World",
"foo",
"bar"
));
final Pane wordsPane = new Pane();
wordsPane.setPrefSize(800, 400);
final long wordDelay = 500_000_000L; // 500 ms
final long fallDuration = 12_000_000_000L; // 12 s
AnimationTimer animation = new AnimationTimer() {
private long lastWordAdd = Long.MIN_VALUE; // never added a word before
private final Map<Text, Long> nodes = new LinkedHashMap<>();
private double nextX = 0;
private void assignXPosition(Text text) {
text.setTranslateX(nextX);
nextX += text.getBoundsInLocal().getWidth();
}
@Override
public void handle(long now) {
// updates & cleanup
long deletionLimit = now - fallDuration;
for (Iterator<Map.Entry<Text, Long>> iter = nodes.entrySet().iterator(); iter.hasNext();) {
Map.Entry<Text, Long> entry = iter.next();
final Text text = entry.getKey();
final long startTime = entry.getValue();
if (startTime < deletionLimit) {
// delete old word
iter.remove();
wordsPane.getChildren().remove(text);
} else {
// update existing word
double factor = ((double) (now - startTime)) / fallDuration;
Bounds bounds = text.getBoundsInLocal();
text.setTranslateY((wordsPane.getHeight() + bounds.getHeight()) * factor - bounds.getMaxY());
}
}
if (words.isEmpty()) {
if (nodes.isEmpty()) {
stop(); // end animation since there are no more words
}
} else if (lastWordAdd + wordDelay <= now) {
lastWordAdd = now;
// add new word
Text text = new Text(words.remove());
wordsPane.getChildren().add(text);
assignXPosition(text);
text.setTranslateY(-text.getBoundsInLocal().getMaxY());
nodes.put(text, now);
}
}
};
animation.start();
Scene scene = new Scene(wordsPane);
primaryStage.setScene(scene);
primaryStage.show();
}