javafx_JavaFX 2 GameTutorial第2部分

javafx

介绍

Ť他的是一系列与一个JavaFX 2游戏教程博客条目的第二批。 如果您尚未阅读第1部分,请参阅JavaFX 2游戏教程的简介部分。 在第1部分中,我提到了游戏的一些方面以及原型飞船的简单演示(原型由简单的形状组成),它可以通过鼠标进行导航。 免责声明 :这是一个很长的教程,因此如果您只想运行演示,请单击“ 此处” 。 该演示称为Atom Smasher,您可以在其中生成碰撞的原子(球体)。 您可以冻结游戏以添加更多原子。 目的是使一个以上的原子活着并弹跳。 文本显示当前漂浮的原子数。 在开始讨论游戏循环之前,我想向您介绍一些有关游戏和动画的背景历史。

历史

早在20世纪80年代至90年代期间,许多尝试制作图像动画的游戏程序员都曾遇到过臭名昭著的屏幕闪烁问题。 这是您的精灵 (图形图像)经常闪烁的地方,并使游戏看起来很糟糕。 所有监视器的刷新率均以一定间隔重绘像素(称为垂直回描CRT )。 例如,如果刷新率是80 Hz,则刷新率大约是每秒80次,屏幕将重新绘制。 如果要在屏幕上进行修改,则由于处于刷新间隔的中间,通常可能会不同步。 您应该怎么做? 好吧,实际上有两件事可以帮助解决这个问题(双缓冲和知道周期何时发生)。 一些聪明的开发人员创建了一种称为双缓冲的技术。 双重缓冲是一种由两个表面组成的技术,其中两个表面依次轮流变为可显示表面,另一个表面外区域(缓冲表面)。 此技术实际上是一种数字技巧,开发人员可以在其中预先计算要在屏幕外表面绘制的精灵及其位置。 一旦在屏幕外缓冲区上完成绘制,代码便会将其切换为可显示表面。 需要指出的重要一点是,由于在刷新间隔即将开始重绘过程时需要通知我们,因此仍然存在问题。 在Java中,此功能是通过BufferStrategy API内置的。 那么,我要去哪里呢? 有时解释过去的策略将有助于我们欣赏今天的状况。 我们需要在JavaFX中执行此操作吗? 不。 不用担心JavaFX在这里! 我已经提到的所有问题都通过使用JavaFX的场景图API来解决。 但是,大多数游戏仍会使用旧的方式来制作图形动画和更新游戏世界,称为“ 游戏循环”

游戏循环

简单地说,游戏循环负责更新子画面(图形),检查碰撞和清理。 较早的游戏循环将在循环中检查按键和鼠标事件。 由于JavaFX对事件进行抽象以允许Scene或单个节点处理事件,因此在我们的游戏循环中不必监听低级事件。 下面显示的是一个典型游戏循环的源代码片段,它将在每个周期更新精灵,检查碰撞和清理精灵。 您会注意到JavaFX 2.x中的Duration对象,该对象表示60除以1000毫秒或每秒60帧(FPS)。 每个帧都会调用JavaFX的EventHandler接口的handle()方法,以更新游戏世界。 假设地,我创建了三个方法updateSprites()checkCollisions()cleanupSprites() ,它们将被调用以处理游戏中的精灵。

final Duration oneFrameAmt = Duration.millis(1000/60);final KeyFrame oneFrame = new KeyFrame(oneFrameAmt,new EventHandler() {@Overridepublic void handle(javafx.event.ActionEvent event) {// update actorsupdateSprites();// check for collisioncheckCollisions();// removed dead thingscleanupSprites();}}); // oneFrame// sets the game world's game loop (Timeline)TimelineBuilder.create().cycleCount(Animation.INDEFINITE).keyFrames(oneFrame).build().play();

上面的代码片段实际上是创建简单游戏或动画所需的全部。 但是,您可能希望将事情带到一个新的高度。 您可能想要创建一个可以管理精灵和游戏世界状态的游戏引擎。

游戏引擎

游戏引擎是负责封装游戏世界,运行游戏循环,管理精灵,物理等的实用程序或库的奇特名称。这实际上是一个小型游戏框架,允许您扩展或重用,因此您无需从头开始创建2D游戏时必须重新发明轮子。 为了快速前进,我创建了游戏引擎设计的UML类图。

下面显示的是图1 JavaFX Game Engine类图。

图1. JavaFX 2游戏引擎设计

在图1 JavaFX 2游戏引擎设计中,您会注意到GameWorldSpriteManagerSprite这三个类。 GameWorld类负责初始化游戏状态,执行游戏循环,更新精灵,处理精灵碰撞以及清理。 接下来是SpriteManager类,该类负责通过添加,删除以及其他内部冲突管理来管理Sprite。 最后是Sprite类,该类负责维护图像(演员)的状态。 在2D世界中,子画面可以包含对象的速度,旋转,场景节点或最终在每个周期(关键帧/秒)渲染的图像。

请快速提醒一下UML表示法:

  • 加号“ + ”表示班级成员是公开的。
  • 减号“ ”表示班级成员是私人的
  • 哈希符号“ ”表示类成员受到保护。

游戏世界

下面是GameWorld类的源代码实现。 单击以展开。 稍后,您将看到一个类图,描述了将扩展GameWorld类的简单演示游戏(请参阅AtomSmasher)。

package carlfx.gameengine;import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.TimelineBuilder;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.util.Duration;/*** This application demonstrates a JavaFX 2.x Game Loop.* Shown below are the methods which comprise of the fundamentals to a* simple game loop in JavaFX:
**  <strong>initialize()</strong> - Initialize the game world.*  <strong>beginGameLoop()</strong> - Creates a JavaFX Timeline object containing the game life cycle.*  <strong>updateSprites()</strong> - Updates the sprite objects each period (per frame)*  <strong>checkCollisions()</strong> - Method will determine objects that collide with each other.*  <strong>cleanupSprites()</strong> - Any sprite objects needing to be removed from play.** @author cdea*/
public abstract class GameWorld {/** The JavaFX Scene as the game surface */private Scene gameSurface;/** All nodes to be displayed in the game window. */private Group sceneNodes;/** The game loop using JavaFX's <code>Timeline</code> API.*/private static Timeline gameLoop;/** Number of frames per second. */private final int framesPerSecond;/** Title in the application window.*/private final String windowTitle;/*** The sprite manager.*/private final SpriteManager spriteManager = new SpriteManager();/*** Constructor that is called by the derived class. This will* set the frames per second, title, and setup the game loop.* @param fps - Frames per second.* @param title - Title of the application window.*/public GameWorld(final int fps, final String title) {framesPerSecond = fps;windowTitle = title;// create and set timeline for the game loopbuildAndSetGameLoop();}/*** Builds and sets the game loop ready to be started.*/protected final void buildAndSetGameLoop() {final Duration oneFrameAmt = Duration.millis(1000/getFramesPerSecond());final KeyFrame oneFrame = new KeyFrame(oneFrameAmt,new EventHandler() {@Overridepublic void handle(javafx.event.ActionEvent event) {// update actorsupdateSprites();// check for collisioncheckCollisions();// removed dead thingscleanupSprites();}}); // oneFrame// sets the game world's game loop (Timeline)setGameLoop(TimelineBuilder.create().cycleCount(Animation.INDEFINITE).keyFrames(oneFrame).build());}/*** Initialize the game world by update the JavaFX Stage.* @param primaryStage*/public abstract void initialize(final Stage primaryStage);/**Kicks off (plays) the Timeline objects containing one key frame* that simply runs indefinitely with each frame invoking a method* to update sprite objects, check for collisions, and cleanup sprite* objects.**/public void beginGameLoop() {getGameLoop().play();}/*** Updates each game sprite in the game world. This method will* loop through each sprite and passing it to the handleUpdate()* method. The derived class should override handleUpdate() method.**/protected void updateSprites() {for (Sprite sprite:spriteManager.getAllSprites()){handleUpdate(sprite);}}/** Updates the sprite object's information to position on the game surface.* @param sprite - The sprite to update.*/protected void handleUpdate(Sprite sprite) {}/*** Checks each game sprite in the game world to determine a collision* occurred. The method will loop through each sprite and* passing it to the handleCollision()* method. The derived class should override handleCollision() method.**/protected void checkCollisions() {// check other sprite's collisionsspriteManager.resetCollisionsToCheck();// check each sprite against other sprite objects.for (Sprite spriteA:spriteManager.getCollisionsToCheck()){for (Sprite spriteB:spriteManager.getAllSprites()){if (handleCollision(spriteA, spriteB)) {// The break helps optimize the collisions//  The break statement means one object only hits another// object as opposed to one hitting many objects.// To be more accurate comment out the break statement.break;}}}}/*** When two objects collide this method can handle the passed in sprite* objects. By default it returns false, meaning the objects do not* collide.* @param spriteA - called from checkCollision() method to be compared.* @param spriteB - called from checkCollision() method to be compared.* @return boolean True if the objects collided, otherwise false.*/protected boolean handleCollision(Sprite spriteA, Sprite spriteB) {return false;}/*** Sprites to be cleaned up.*/protected void cleanupSprites() {spriteManager.cleanupSprites();}/*** Returns the frames per second.* @return int The frames per second.*/protected int getFramesPerSecond() {return framesPerSecond;}/*** Returns the game's window title.* @return String The game's window title.*/public String getWindowTitle() {return windowTitle;}/*** The game loop (Timeline) which is used to update, check collisions, and* cleanup sprite objects at every interval (fps).* @return Timeline An animation running indefinitely representing the game* loop.*/protected static Timeline getGameLoop() {return gameLoop;}/*** The sets the current game loop for this game world.* @param gameLoop Timeline object of an animation running indefinitely* representing the game loop.*/protected static void setGameLoop(Timeline gameLoop) {GameWorld.gameLoop = gameLoop;}/*** Returns the sprite manager containing the sprite objects to* manipulate in the game.* @return SpriteManager The sprite manager.*/protected SpriteManager getSpriteManager() {return spriteManager;}/*** Returns the JavaFX Scene. This is called the game surface to* allow the developer to add JavaFX Node objects onto the Scene.* @return*/public Scene getGameSurface() {return gameSurface;}/*** Sets the JavaFX Scene. This is called the game surface to* allow the developer to add JavaFX Node objects onto the Scene.* @param gameSurface The main game surface (JavaFX Scene).*/protected void setGameSurface(Scene gameSurface) {this.gameSurface = gameSurface;}/*** All JavaFX nodes which are rendered onto the game surface(Scene) is* a JavaFX Group object.* @return Group The root containing many child nodes to be displayed into* the Scene area.*/public Group getSceneNodes() {return sceneNodes;}/*** Sets the JavaFX Group that will hold all JavaFX nodes which are rendered* onto the game surface(Scene) is a JavaFX Group object.* @param sceneNodes The root container having many children nodes* to be displayed into the Scene area.*/protected void setSceneNodes(Group sceneNodes) {this.sceneNodes = sceneNodes;}}

精灵管理器

Sprite Manager类是帮助程序类,可帮助游戏循环跟踪Sprite。 通常,一个精灵管理器将包含所有精灵,每个精灵都包含一个JavaFX节点,该节点显示在“场景”图上。
下面显示的是源代码。 单击以展开。

package carlfx.gameengine;import java.util.*;/*** Sprite manager is responsible for holding all sprite objects, and cleaning up* sprite objects to be removed. All collections are used by the JavaFX* application thread. During each cycle (animation frame) sprite management* occurs. This assists the user of the API to not have to create lists to* later be garbage collected. Should provide some performance gain.* @author cdea*/
public class SpriteManager {/** All the sprite objects currently in play */private final static List GAME_ACTORS = new ArrayList<>();/** A global single threaded list used to check collision against other* sprite objects.*/private final static List CHECK_COLLISION_LIST = new ArrayList<>();/** A global single threaded set used to cleanup or remove sprite objects* in play.*/private final static Set CLEAN_UP_SPRITES = new HashSet<>();/** */public List getAllSprites() {return GAME_ACTORS;}/*** VarArgs of sprite objects to be added to the game.* @param sprites*/public void addSprites(Sprite... sprites) {GAME_ACTORS.addAll(Arrays.asList(sprites));}/*** VarArgs of sprite objects to be removed from the game.* @param sprites*/public void removeSprites(Sprite... sprites) {GAME_ACTORS.removeAll(Arrays.asList(sprites));}/** Returns a set of sprite objects to be removed from the GAME_ACTORS.* @return CLEAN_UP_SPRITES*/public Set getSpritesToBeRemoved() {return CLEAN_UP_SPRITES;}/*** Adds sprite objects to be removed* @param sprites varargs of sprite objects.*/public void addSpritesToBeRemoved(Sprite... sprites) {if (sprites.length > 1) {CLEAN_UP_SPRITES.addAll(Arrays.asList((Sprite[]) sprites));} else {CLEAN_UP_SPRITES.add(sprites[0]);}}/*** Returns a list of sprite objects to assist in collision checks.* This is a temporary and is a copy of all current sprite objects* (copy of GAME_ACTORS).* @return CHECK_COLLISION_LIST*/public List getCollisionsToCheck() {return CHECK_COLLISION_LIST;}/*** Clears the list of sprite objects in the collision check collection* (CHECK_COLLISION_LIST).*/public void resetCollisionsToCheck() {CHECK_COLLISION_LIST.clear();CHECK_COLLISION_LIST.addAll(GAME_ACTORS);}/*** Removes sprite objects and nodes from all* temporary collections such as:* CLEAN_UP_SPRITES.* The sprite to be removed will also be removed from the* list of all sprite objects called (GAME_ACTORS).*/public void cleanupSprites() {// remove from actors listGAME_ACTORS.removeAll(CLEAN_UP_SPRITES);// reset the clean up spritesCLEAN_UP_SPRITES.clear();}
}

雪碧

Sprite类表示要显示在JavaFX Scene图形上的图像或节点。 在2D游戏中,子画面将包含其他信息,例如当对象在场景区域中移动时对象的速度。 游戏循环将在关键帧的每个时间间隔调用update()和collide()方法。
下面显示的是源代码。 单击以展开。

package carlfx.gameengine;import java.util.ArrayList;
import java.util.List;
import javafx.animation.Animation;
import javafx.scene.Node;/*** The Sprite class represents a image or node to be displayed.* In a 2D game a sprite will contain a velocity for the image to* move across the scene area. The game loop will call the update()* and collide() method at every interval of a key frame. A list of* animations can be used during different situations in the game* such as rocket thrusters, walking, jumping, etc.* @author cdea*/
public abstract class Sprite {/** Animation for the node */public List animations = new ArrayList<>();/** Current display node */public Node node;/** velocity vector x direction */public double vX = 0;/** velocity vector y direction */public double vY = 0;/** dead? */public boolean isDead = false;/*** Updates this sprite object's velocity, or animations.*/public abstract void update();/*** Did this sprite collide into the other sprite?** @param other - The other sprite.* @return*/public boolean collide(Sprite other) {return false;}
}

JavaFX 2游戏循环演示– Atom Smasher

ew! 如果您已经走了这么远,那么您就是一个勇敢的灵魂。 让我们休息一下,尝试使用上面的游戏引擎创建的演示。
下面显示的是一个Java Webstart按钮,用于启动游戏演示。 稍后,您将看到设计和源代码,详细说明了它是如何创建的。

要求:

  • Java 7或更高版本
  • JavaFX 2.0.2 2.1或更高版本
  • Windows XP或更高版本(应该很快可用于Linux / MacOS)
AtomSmasher游戏循环演示
演示版

GameLoopPart2设计

下面是名为Atom Smasher的游戏演示的类图,该类使用前面提到的游戏引擎框架。
下面显示的是图2 Atom Smasher类图。

图2. Atom Smasher类图

GameLoopPart2

GameLoopPart2是运行游戏的驱动程序或主要JavaFX应用程序。 这将创建一个要初始化的GameWorld对象,并开始游戏循环。
下面显示的是源代码。 单击以展开。

package carlfx;import carlfx.gameengine.GameWorld;
import javafx.application.Application;
import javafx.stage.Stage;/*** The main driver of the game.* @author cdea*/
public class GameLoopPart2 extends Application {GameWorld gameWorld = new AtomSmasher(60, "JavaFX 2 GameTutorial Part 2 - Game Loop");/*** @param args the command line arguments*/public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage primaryStage) {// setup title, scene, stats, controls, and actors.gameWorld.initialize(primaryStage);// kick off the game loopgameWorld.beginGameLoop();// display windowprimaryStage.show();}}

原子粉碎机

AtomSmasher是GameWorld类的派生类。 它创建许多以随机速度,颜色和位置进行动画处理的球。 按钮控件使用户可以生成更多的“原子”(JavaFX Circle节点)。 当每个原子相互碰撞时,它们将调用implode()方法,该方法生成淡入淡出过渡动画。 您将注意到,仅通过实现initialize(),handleUpdate(),handleCollision()cleanupSprites()方法即可轻松实现此游戏。 一旦实施,游戏引擎就会完成其余的工作。 initialize()方法为用户创建按钮控件。 要更新精灵的位置或更改游戏状态,您将实现handleUpdate()方法。 要比较所有精灵相互碰撞的情况,您将实现handleCollision() 。 游戏循环生命周期的最后一部分是清理精灵。 清理意味着更新精灵管理器并更新JavaFX Scene(删除节点)。
下面显示的是源代码。 单击以展开。

package carlfx;import carlfx.gameengine.GameWorld;
import carlfx.gameengine.Sprite;
import java.util.Random;
import javafx.animation.Timeline;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBoxBuilder;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import static javafx.animation.Animation.Status.RUNNING;
import static javafx.animation.Animation.Status.STOPPED;/*** This is a simple game world simulating a bunch of spheres looking* like atomic particles colliding with each other. When the game loop begins* the user will notice random spheres (atomic particles) floating and* colliding. The user is able to press a button to generate more* atomic particles. Also, the user can freeze the game.** @author cdea*/
public class AtomSmasher extends GameWorld {/** Read only field to show the number of sprite objects are on the field*/private final static Label NUM_SPRITES_FIELD = new Label();public AtomSmasher(int fps, String title){super(fps, title);}/*** Initialize the game world by adding sprite objects.* @param primaryStage*/@Overridepublic void initialize(final Stage primaryStage) {// Sets the window titleprimaryStage.setTitle(getWindowTitle());// Create the scenesetSceneNodes(new Group());setGameSurface(new Scene(getSceneNodes(), 640, 580));primaryStage.setScene(getGameSurface());// Create many spheresgenerateManySpheres(150);// Display the number of spheres visible.// Create a button to add more spheres.// Create a button to freeze the game loop.final Timeline gameLoop = getGameLoop();VBox stats = VBoxBuilder.create().spacing(5).translateX(10).translateY(10).children(HBoxBuilder.create().spacing(5).children(new Label("Number of Particles: "), // show no. particlesNUM_SPRITES_FIELD).build(),// button to build more spheresButtonBuilder.create().text("Regenerate").onMousePressed(new EventHandler() {@Overridepublic void handle(MouseEvent arg0) {generateManySpheres(150);}}).build(),// button to freeze game loopButtonBuilder.create().text("Freeze/Resume").onMousePressed(new EventHandler() {@Overridepublic void handle(MouseEvent arg0) {switch (gameLoop.getStatus()) {case RUNNING:gameLoop.stop();break;case STOPPED:gameLoop.play();break;}}}).build()).build(); // (VBox) stats on children// lay down the controlsgetSceneNodes().getChildren().add(stats);}/*** Make some more space spheres (Atomic particles)*/private void generateManySpheres(int numSpheres) {Random rnd = new Random();Scene gameSurface = getGameSurface();for (int i=0; i (gameSurface.getWidth() - (circle.getRadius() * 2))) {newX = gameSurface.getWidth() - (circle.getRadius()  * 2);}// check for the bottom of screen the height newY is greater than height// minus radius times 2(height of sprite)double newY = rnd.nextInt((int) gameSurface.getHeight());if (newY > (gameSurface.getHeight() - (circle.getRadius() * 2))) {newY = gameSurface.getHeight() - (circle.getRadius() * 2);}circle.setTranslateX(newX);circle.setTranslateY(newY);circle.setVisible(true);circle.setId(b.toString());// add to actors in play (sprite objects)getSpriteManager().addSprites(b);// add sprite'sgetSceneNodes().getChildren().add(0, b.node);}}/*** Each sprite will update it's velocity and bounce off wall borders.* @param sprite - An atomic particle (a sphere).*/@Overrideprotected void handleUpdate(Sprite sprite) {if (sprite instanceof Atom) {Atom sphere = (Atom) sprite;// advance the spheres velocitysphere.update();// bounce off the walls when outside of boundariesif (sphere.node.getTranslateX() > (getGameSurface().getWidth()  -sphere.node.getBoundsInParent().getWidth()) ||sphere.node.getTranslateX() < 0 ) {                 sphere.vX = sphere.vX * -1;             }             if (sphere.node.getTranslateY() > getGameSurface().getHeight()-sphere.node.getBoundsInParent().getHeight() ||sphere.node.getTranslateY() < 0) {sphere.vY = sphere.vY * -1;}}}/*** How to handle the collision of two sprite objects. Stops the particle* by zeroing out the velocity if a collision occurred.* @param spriteA* @param spriteB* @return*/@Overrideprotected boolean handleCollision(Sprite spriteA, Sprite spriteB) {if (spriteA.collide(spriteB)) {((Atom)spriteA).implode(this);((Atom)spriteB).implode(this);getSpriteManager().addSpritesToBeRemoved(spriteA, spriteB);return true;}return false;}/*** Remove dead things.*/@Overrideprotected void cleanupSprites() {// removes from the scene and backend storesuper.cleanupSprites();// let user know how many sprites are showing.NUM_SPRITES_FIELD.setText(String.valueOf(getSpriteManager().getAllSprites().size()));}
}

原子

Atom类是Sprite类的扩展。 原子是一个精灵,看起来像一个在场景中移动的球形物体。 原子将具有随机的半径,颜色和速度。 当每个原子精灵与另一个原子碰撞时,它们将为淡入淡出过渡设置动画(implode()方法)。
下面显示的是源代码。 单击以展开。

package carlfx;import carlfx.gameengine.GameWorld;
import carlfx.gameengine.Sprite;
import javafx.animation.FadeTransitionBuilder;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.paint.Color;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.RadialGradientBuilder;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CircleBuilder;
import javafx.util.Duration;/*** A spherical looking object (Atom) with a random radius, color, and velocity.* When two atoms collide each will fade and become removed from the scene. The* method called implode() implements a fade transition effect.** @author cdea*/
public class Atom extends Sprite {public Atom(double radius, Color fill) {Circle sphere = CircleBuilder.create().centerX(radius).centerY(radius).radius(radius).cache(true).build();RadialGradient rgrad = RadialGradientBuilder.create().centerX(sphere.getCenterX() - sphere.getRadius() / 3).centerY(sphere.getCenterY() - sphere.getRadius() / 3).radius(sphere.getRadius()).proportional(false).stops(new Stop(0.0, fill), new Stop(1.0, Color.BLACK)).build();sphere.setFill(rgrad);// set javafx node to a circlenode = sphere;}/*** Change the velocity of the atom particle.*/@Overridepublic void update() {node.setTranslateX(node.getTranslateX() + vX);node.setTranslateY(node.getTranslateY() + vY);}@Overridepublic boolean collide(Sprite other) {if (other instanceof Atom) {return collide((Atom)other);}return false;}/*** When encountering another Atom to determine if they collided.* @param other Another atom* @return boolean true if this atom and other atom has collided,* otherwise false.*/private boolean collide(Atom other) {// if an object is hidden they didn't collide.if (!node.isVisible() ||!other.node.isVisible() ||this == other) {return false;}// determine it's sizeCircle otherSphere = other.getAsCircle();Circle thisSphere =  getAsCircle();double dx = otherSphere.getTranslateX() - thisSphere.getTranslateX();double dy = otherSphere.getTranslateY() - thisSphere.getTranslateY();double distance = Math.sqrt( dx * dx + dy * dy );double minDist  = otherSphere.getRadius() + thisSphere.getRadius() + 3;return (distance < minDist);}/*** Returns a node casted as a JavaFX Circle shape.* @return Circle shape representing JavaFX node for convenience.*/public Circle getAsCircle() {return (Circle) node;}/*** Animate an implosion. Once done remove from the game world* @param gameWorld - game world*/public void implode(final GameWorld gameWorld) {vX = vY = 0;FadeTransitionBuilder.create().node(node).duration(Duration.millis(300)).fromValue(node.getOpacity()).toValue(0).onFinished(new EventHandler() {@Overridepublic void handle(ActionEvent arg0) {isDead = true;gameWorld.getSceneNodes().getChildren().remove(node);}}).build().play();}
}

结论

希望您有机会了解游戏循环的基础,并在以后通过实施强大的游戏引擎来应用这些知识。 虽然,我简要提到了碰撞,但是我将其保存在这些教程的第4部分中。 请继续关注第3部分,我们将在此使用键盘或鼠标进行输入。 随时尝试。 让我知道你的想法。

要获取源代码,请使用浏览器中的“ 将链接另存为 ”选项, 将链接下载到以下jar文件中。 如果您使用的是Windows系统,则可以将扩展名从jar更改为zip以便轻松扩展。 它将包含带有源代码的目录“ src ”。

源代码位置

http://www.jroller.com/carldea/resource/javafx2.0_games/part2source_code.jar

源代码的发布版本位于GitHub上,名为( JFXGen ),供您克隆并复制到您的心中内容(可以在其中使用自己的项目)。 请享用。

https://github.com/carldea/JFXGen

git clonegit@github.com:carldea / JFXGen.git

参考:来自我们的JCG合作伙伴 Carl Dea的JavaFX 2 GameTutorial第2部分 ,位于Carl's FX Blog博客上。


翻译自: https://www.javacodegeeks.com/2012/05/javafx-2-gametutorial-part-2.html

javafx

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/355361.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql 如何根据月份查询数据总数_什么是慢查询?如何通过慢查询日志优化?

日志就跟人们写的日记一样&#xff0c;记录着过往的事情。但是人的日记是主观的(记自己想记的内容)&#xff0c;而数据库的日志是客观的&#xff0c;根据记录内容分为以下好几种日志&#xff1a;a、错误日志&#xff1a;记录启动、运行或停止mysqld时出现的问题。b、通用日志&a…

查询分析器在哪里_你应该知道的3种Node.js分析器类型

Node.js类似于许多其他编码语言&#xff0c;因为它需要与正确的工具结合使用来调试程序&#xff0c;克服任何瓶颈并优化其功能。使用正确的分析器&#xff0c;您可以毫不费力地实现这一目标&#xff0c;尽管它们都不是完美的。在编码中&#xff0c;分析器是一种动态程序分析软件…

全国计算机一级选择题汇总,全国计算机一级考试选择题训练及答案

全国计算机一级考试选择题训练及答案练习题一1、 将二进制数110B左移一位后其等值的十进制数为()。A、6 B、1100 C、12 D、32、 某微型机的CPU中含有32条地址线、28位数据线及若干条控制信号线&#xff0c;对内存按字节寻址&#xff0c;其最大内存空间是()。A、4MBB、256MBC、2…

python url拼接_教你写python爬虫——用python爬原图

环境&#xff1a;python3.6 pycharm“猎物”&#xff1a;http://www.polayoutu.com &#xff08;仅学习用&#xff09;动机1&#xff1a;想要爬一些尺寸比较大&#xff08;不是尺度&#xff09;的美图养养眼&#xff0c;仅此而已&#xff1b;动机2&#xff1a;学习python爬虫&…

Devoxx的Red Hat Engineers提供了更多Java EE

现在是Devoxx比利时时间&#xff01; 最后。 今天的一切都随着大学会议的开始而开始&#xff0c;会议本身将于明天早晨开始。 我们都非常兴奋&#xff0c;因为这是欧洲的重大事件。 红帽很荣幸今年再次成为赞助商。 我们将在现场举行14次会议&#xff0c;一个展位–提供啤酒&am…

济宁医学院计算机专业好就业吗,山东这3所医学院实力强,就业率高,中等生可捡漏...

原标题&#xff1a;山东这3所医学院实力强&#xff0c;就业率高&#xff0c;中等生可捡漏山东的医药类院校中&#xff0c;实力较强的是山东大学医学院、青岛大学医学院、山东第一医科大学、山东中医药大学&#xff0c;这4所医学院的录取分数都比较高。今天给大家介绍山东3所二批…

php读取西门子plc_PLC对模拟量信号,是怎么进行处理的?

模拟量信号是自动化过程控制系统中最基本的过程信号(压力、温度、流量等)输入形式。系统中的过程信号通过变送器&#xff0c;将这些检测信号转换为统一的电压、电流信号&#xff0c;并将这些信号实时的传送至控制器(PLC)。PLC通过计算转换&#xff0c;将这些模拟量信号转换为内…

Java 8 Streams:过滤和谓词否定

最近&#xff0c;有关LJC 邮件列表的成员在流中.filter方法中使用谓词否定的有趣讨论&#xff0c;因此我认为值得在博客文章中进行总结。 讨论的主题是使用.filter和否定谓词的方法。 这篇文章的代码可以在我的github帐户中找到 。 也许这就是您可能会想到的方式&#xff0c;…

sql server2008如何修改mac地址_如何查看本机的MAC地址和IP地址?

MAC地址是每台电脑或是手机设备中唯一进行识别的编码&#xff0c;IP地址则是路由器分配给电脑或是手机设备联网使用的一个编码。当我们电脑或无法上网&#xff0c;都需要查看IP地址是否正确。那如何查看MAC地址和IP地址呢&#xff1f;方法一&#xff1a;如何电脑已经成功联网了…

vlookup练习_大胆合并吧!VLOOKUP坐字法专做单元格合并查找

回复[目录]学习113篇Excel教程全套Excel视频教程&#xff0c;微信扫码观看编按&#xff1a;前面才讲了Excel家规&#xff0c;数据源表不能有合并单元格&#xff0c;今天就嘚瑟地呼吁“大胆合并”。为何&#xff1f;因为老菜鸟给大家带来了单元格合并查找利器VLOOKUP坐字法查找。…

第一代计算机主要应用领域为数据处理,第一代计算机主要应用领域为____。    A.数据处理 B.人工智能 C.科学计算 D.过程控制...

C、科学计算。一代主要用于科学计算&#xff0c;二代用与数据处理。计算机发明者约翰冯诺依曼。计算机是20世纪最先进的科学技术发明之一&#xff0c;对人类的生产活动和社会活动产生了极其重要的影响&#xff0c;并以强大的生命力飞速发展。它的应用领域从最初的军事科研应用扩…

vscode 书签_10个我必备的 VS code 拓展

推荐&#xff1a;Vue 3中令人激动的新功能&#xff1a;FragmentSuspense多v-modelVS Code是我最喜欢的文本编辑器。它是目前最可扩展的、最流行的代码编辑器。而且令人惊讶的是&#xff0c;它是由微软制作的(我知道)。在我看来&#xff0c;没有任何其他IDE或编辑器能比VS Code更…

u盘显示项目不在计算机zhng,能装在u盘上的操作系统有哪些?

满意答案zhng9211推荐于 2016.08.27采纳率&#xff1a;51% 等级&#xff1a;11已帮助&#xff1a;7556人可装在U盘上的操作系统现在的计算机技术简直日新月异&#xff0c;U盘&#xff0c;固态硬盘等等存储介质做的可以越来越大&#xff0c;并越来越便宜。电脑已经不是稀罕物…

推荐 -- 极客导航,让工作学习更有效率

给大家推荐一个导航&#xff0c;很实用&#xff1b;极客导航精心挑选网址&#xff0c;让您的工作更有效率&#xff1b; 传送门&#xff1a; http://www.gogeeks.cn/ 转载于:https://www.cnblogs.com/JsonShare/p/7064438.html

iphone型号表_机器人造iPhone,苹果奋斗八年,还是败给了富士康工人

8 年前&#xff0c;当蒂姆 库克在富士康观看机器人进行 iPad 实验生产视频时&#xff0c;似乎看到了新时代的希望。郭台铭曾表示&#xff0c;预计在 2014 年可以在富士康工厂中部署 100 万台机器人&#xff0c;进而实现高度自动化的「无人工厂」。机器之心报道&#xff0c;参与…

笔记本如何选择修复计算机,如何处理旧的笔记本电脑?这些操作必不可少!

原标题&#xff1a;如何处理旧的笔记本电脑&#xff1f;这些操作必不可少&#xff01;如何处理旧的笔记本电脑&#xff1f;不要把它丢掉。那台旧笔记本电脑至少应适当回收。就算是打算仅回收旧笔记本电脑&#xff0c;也请确保有效擦除所有数据。备份如今&#xff0c;云服务如此…

Android开源工具项目集合

最近因为要去外派了&#xff0c;工欲善其事&#xff0c;必先利其器&#xff01;所以又回顾了一下自己github上所收藏的项目&#xff0c;也算是温故而知新吧。 最流行的Android组件大全 http://www.open-open.com/lib/view/open1409108030307.html android开源项目分类汇总 http…

Android-Universal-Image-Loader学习笔记(3)--内存缓存

前面的两篇博客写了文件缓存。如今说说Android-Universal-Image-Loader的内存缓存。该内存缓存涉及到的类如图所看到的 这些类的继承关系例如以下图所看到的&#xff1a; 如同文件缓存一样&#xff0c;内存缓存涉及的接口也有两个&#xff1a;MemoryCacheAware 和MemoryCache&a…

css设置字体颜色怎么设,css里面怎么设置字体颜色?

在html中我们经常要用到css样式来美化html标签的一些不足之处&#xff0c;今天我们就来说说如何用css的基本样式来改变文本的颜色&#xff0c;文章通俗易懂&#xff0c;希望大家看完后能练习练习加深印象。一、首先我们先给大家看个html字体的基础代码&#xff1a;HTML中文网字…

visio科学图形包_科学网—科研必备:几款好用的流程图工具,助力你的论文/科研绘图...

写SCI论文常常会遇到一个问题——如何才能让读者通俗易懂地理解自己的研究&#xff1f;如果不能让读者了解研究过程&#xff0c;读者会对研究结果是否合理、如何应用存在疑问&#xff1b;尤其是期刊编辑和审稿人如果不能通过咱们的叙述&#xff0c;清楚地了解研究内容&#xff…