博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Java FXGL构建太空游侠游戏
阅读量:2533 次
发布时间:2019-05-11

本文共 12984 字,大约阅读时间需要 43 分钟。

In this simple FXGL tutorial, we will develop a game called “Space Ranger”.

在这个简单的FXGL教程中,我们将开发一个名为“ Space Ranger”的游戏。

什么是FXGL? (What is FXGL?)

FXGL is a game engine built on top JavaFX, which is a GUI toolkit for desktop, mobile, and embedded systems.

FXGL是基于顶级JavaFX构建的游戏引擎,JavaFX是用于台式机,移动和嵌入式系统的GUI工具包。

为什么在JavaFX上使用FXGL? (Why use FXGL over JavaFX?)

On its own, JavaFX provides general-purpose rendering and UI capabilities. On top of it, FXGL brings real-world game development techniques and tools, making it easy to develop cross-platform games.

JavaFX本身提供了通用的呈现和UI功能。 最重要的是,FXGL带来了现实世界的游戏开发技术和工具,使开发跨平台游戏变得容易。

如何下载FXGL JAR? (How to Download FXGL JARs?)

FXGL can be downloaded as a Maven or Gradle dependency. For example, Maven coordinates are as follows, which can be used with Java 11+.

FXGL可以作为Maven或Gradle依赖项下载。 例如,Maven坐标如下,可与Java 11+一起使用。

com.github.almasb
fxgl
11.8

If you get stuck at any point, you can find the full source code link at the end of this tutorial.

如果您有任何困难,可以在本教程的结尾找到完整的源代码链接。

太空游侠游戏 (Space Rangers Game)

Our game idea is relatively simple. We have a bunch of enemies who are trying to get to our base and we have a single base protector — the player. Given this is an introduction tutorial, we will not use any assets, such as images, sounds, and other external resources. When completed, the game will look like this:

我们的游戏思路比较简单。 我们有一堆试图进入我们基地的敌人,而我们只有一个基地保护者-玩家。 鉴于这是一个入门教程,因此我们将不使用任何资产,例如图像,声音和其他外部资源。 完成后,游戏将如下所示:

Let us begin!

让我们开始吧!

所需进口 (Required Imports)

First, let’s take care of all the imports, so we can focus on the code aspect of the tutorial. For simplicity, all of the code will be in a single file, however you may wish to place each class in its own file.

首先,让我们处理所有导入,因此我们可以专注于本教程的代码方面。 为了简单起见,所有代码都在一个文件中,但是您可能希望将每个类放在自己的文件中。

Create a file SpaceRangerApp.java and place the following imports:

创建一个文件SpaceRangerApp.java并放置以下导入:

import com.almasb.fxgl.animation.Interpolators;import com.almasb.fxgl.app.GameApplication;import com.almasb.fxgl.app.GameSettings;import com.almasb.fxgl.core.math.FXGLMath;import com.almasb.fxgl.dsl.components.ProjectileComponent;import com.almasb.fxgl.entity.Entity;import com.almasb.fxgl.entity.EntityFactory;import com.almasb.fxgl.entity.SpawnData;import com.almasb.fxgl.entity.Spawns;import javafx.geometry.Point2D;import javafx.scene.input.KeyCode;import javafx.scene.input.MouseButton;import javafx.scene.paint.Color;import javafx.scene.shape.Rectangle;import javafx.util.Duration;import static com.almasb.fxgl.dsl.FXGL.*;

That’s it, our first step is done. Note that the last static import simplifies a lot of calls made to FXGL and is the recommended approach.

就是这样,我们的第一步已经完成。 请注意,最后一次静态导入简化了对FXGL的许多调用,这是推荐的方法。

游戏代码 (Game Code)

We will now begin writing some code. As with any other application, we start by specifying the entry point to the program. We will also provide some basic settings to FXGL, such as width, height and the title of our game.

现在,我们将开始编写一些代码。 与任何其他应用程序一样,我们首先指定程序的入口点。 我们还将为FXGL提供一些基本设置,例如宽度,高度和游戏标题。

public class SpaceRangerApp extends GameApplication {    @Override    protected void initSettings(GameSettings settings) {        settings.setTitle("Space Ranger");        settings.setWidth(800);        settings.setHeight(600);    }    public static void main(String[] args) {        launch(args);    }}

游戏对象/实体 (Game Objects / Entities)

Each game engine has its own terminology for in-game objects. In FXGL, which uses the Entity-Component model, game objects are called entities. Each entity typically has a type, for example, the player, an enemy, a projectile and so on. In our game we are going to have exactly these types just described:

每个游戏引擎都有自己的用于游戏中对象的术语。 在使用实体组件模型的FXGL中,游戏对象称为实体。 每个实体通常具有一种类型,例如,玩家,敌人,弹丸等。 在我们的游戏中,我们将恰好描述了以下类型:

public enum EntityType {    PLAYER, ENEMY, PROJECTILE}

FXGL needs to know how to construct these types. To do that we create a factory. As mentioned earlier we are keeping all classes in the same file. At this point, if you prefer, you can move the class “SpaceRangerFactory” into its own file (if you do, just remove the “static” keyword).

FXGL需要知道如何构造这些类型。 为此,我们创建了一家工厂。 如前所述,我们将所有类保留在同一文件中。 此时,如果您愿意,可以将“ SpaceRangerFactory”类移到其自己的文件中(如果这样做,只需删除“ static”关键字)。

public class SpaceRangerApp extends GameApplication {    public static class SpaceRangerFactory implements EntityFactory {    }}

There is a streamlined process for defining entities. Inside “SpaceRangerFactory”, we define a new method that accepts a SpawnData object, returns an Entity object and has a Spawns annotation:

有一个简化的流程来定义实体。 在“ SpaceRangerFactory”内部,我们定义了一个新方法,该方法接受一个SpawnData对象,返回一个Entity对象,并具有一个Spawns注释:

@Spawns("player")public Entity newPlayer(SpawnData data) {    var top = new Rectangle(60, 20, Color.BLUE);    top.setStroke(Color.GRAY);    var body = new Rectangle(25, 60, Color.BLUE);    body.setStroke(Color.GRAY);    var bot = new Rectangle(60, 20, Color.BLUE);    bot.setStroke(Color.GRAY);    bot.setTranslateY(40);    return entityBuilder()            .type(EntityType.PLAYER)            .from(data)            .view(body)            .view(top)            .view(bot)            .build();}

As readily seen from the annotation, this method spawns a player object (entity). We will now consider the method in detail.

从注释中可以很容易地看出,该方法产生了一个播放器对象(实体)。 现在,我们将详细考虑该方法。

First, we construct the three parts of the player view, which are standard JavaFX rectangles. Note that we are using the “var” syntax. Using the “entityBuilder()”, we specify the type, the data from which to set the position and views of the player entity. This fluent API allows us to build entities in a concise way.

首先,我们构造播放器视图的三个部分,它们是标准的JavaFX矩形。 请注意,我们使用的是“ var”语法。 使用“ entityBuilder()”,我们指定类型,用于设置玩家实体位置和视图的数据。 这种流畅的API使我们能够以简洁的方式构建实体。

Our next step is to define the projectiles in our game:

我们的下一步是在游戏中定义弹丸:

@Spawns("projectile")public Entity newProjectile(SpawnData data) {    var view = new Rectangle(30, 3, Color.LIGHTBLUE);    view.setStroke(Color.WHITE);    view.setArcWidth(15);    view.setArcHeight(10);    return entityBuilder()            .type(EntityType.PROJECTILE)            .from(data)            .viewWithBBox(view)            .collidable()            .zIndex(-5)            .with(new ProjectileComponent(new Point2D(1, 0), 760))            .build();}

The view is, again, a JavaFX rectangle with rounded corners. This time we call “viewWithBBox()”, rather than just “view()”. The former method automatically generates a bounding box based on the view. Neat!

该视图再次是带有圆角的JavaFX矩形。 这次我们称“ viewWithBBox()”,而不仅仅是“ view()”。 前一种方法会根据视图自动生成边界框。 整齐!

Next, using “collidable()”, we mark our projectile as an entity that can collide with other entities. We will come back to collisions later on. We set the z-index to a negative value so that it gets drawn before the player (by default each entity has z-index of 0).

接下来,使用“ collidable()”,将射弹标记为可以与其他实体碰撞的实体。 稍后我们将回到冲突中。 我们将z-index设置为负值,以便在玩家之前绘制它(默认情况下,每个实体的z-index为0)。

Finally, we add a ProjectileComponent, with a speed equal to 760 and a directional vector (1, 0), which means 1 in the X-axis and 0 in the Y-axis, which in turn means to move to the right.

最后,我们添加一个ProjectileComponent,其速度等于760,方向矢量为(1,0),这意味着X轴为1,Y轴为0,这又意味着向右移动。

Our last entity to define is of type enemy:

我们最后定义的实体是敌人类型:

@Spawns("enemy")public Entity newEnemy(SpawnData data) {    var view = new Rectangle(80, 20, Color.RED);    view.setStroke(Color.GRAY);    view.setStrokeWidth(0.5);    animationBuilder()            .interpolator(Interpolators.SMOOTH.EASE_OUT())            .duration(Duration.seconds(0.5))            .repeatInfinitely()            .animate(view.fillProperty())            .from(Color.RED)            .to(Color.DARKRED)            .buildAndPlay();    return entityBuilder()            .type(EntityType.ENEMY)            .from(data)            .viewWithBBox(view)            .collidable()            .with(new ProjectileComponent(new Point2D(-1, 0), FXGLMath.random(50, 150)))            .build();}

We already covered the fluent API methods, such as “type()” and “collidable()”, so we will focus on the animation instead.

我们已经介绍了流畅的API方法,例如“ type()”和“ collidable()”,因此我们将重点放在动画上。

As you can see, the animation builder also follows a similar fluent API convention. It allows us to set various animation settings, such as duration and how many times to repeat.

如您所见,动画构建器还遵循类似的流利API约定。 它允许我们设置各种动画设置,例如持续时间和重复次数。

We can observe that the animation operates on the “fillProperty()” on the rectangular view, which we are using to represent the enemy. In particular, the fill is animated from RED to DARKRED every 0.5 seconds. Feel free to adjust the animation settings to see what works best for your game.

我们可以观察到动画在矩形视图的“ fillProperty()”上运行,我们用它来表示敌人。 特别是,填充会每0.5秒从红色变为暗红色。 随意调整动画设置,以查看最适合您的游戏的动画。

Our factory class is now complete and we will start bringing our code together.

我们的工厂课程现已完成,我们将开始将我们的代码整合在一起。

输入项 (Input)

All of the FXGL input is typically handled inside the “initInput” method, as follows:

通常,所有FXGL输入都在“ initInput”方法内部进行处理,如下所示:

@Overrideprotected void initInput() {    onKey(KeyCode.W, () -> getGameWorld().getSingleton(EntityType.PLAYER).translateY(-5));    onKey(KeyCode.S, () -> getGameWorld().getSingleton(EntityType.PLAYER).translateY(5));    onBtnDown(MouseButton.PRIMARY, () -> {        double y = getGameWorld().getSingleton(EntityType.PLAYER).getY();        spawn("projectile", 0, y + 10);        spawn("projectile", 0, y + 50);    });}

The first two calls set up our player movement. More specifically, W and S keys are going to move the player up and down respectively. Our last call sets up the player action, which is shooting. When the primary mouse button is pressed, we spawn our projectiles, which we defined earlier. The two last arguments to the “spawn” function are the x and y values of where to spawn the projectile.

前两个呼叫设置了我们的玩家移动。 更具体地说,W和S键将分别向上和向下移动播放器。 我们的最后一个通话设置了玩家的动作,即射击。 当按下主鼠标按钮时,我们会生成我们先前定义的弹丸。 “ spawn”函数的最后两个参数是生成弹丸的位置的x和y值。

游戏逻辑 (Game Logic)

Before we can start the game, we need to initialize some game logic, which we can do as follows:

在开始游戏之前,我们需要初始化一些游戏逻辑,我们可以执行以下操作:

@Overrideprotected void initGame() {    getGameScene().setBackgroundColor(Color.BLACK);    getGameWorld().addEntityFactory(new SpaceRangerFactory());    spawn("player", 0, getAppHeight() / 2 - 30);    run(() -> {        double x = getAppWidth();        double y = FXGLMath.random(0, getAppHeight() - 20);        spawn("enemy", x, y);    }, Duration.seconds(0.25));}

We set the game scene background to color black (you may choose a different color to suit your needs).

我们将游戏场景背景设置为黑色(您可以根据需要选择其他颜色)。

Next, we add our entity factory — FXGL needs to know how to spawn our entities.

接下来,我们添加实体工厂-FXGL需要知道如何生成我们的实体。

Thereafter, we spawn our player on the left side of the screen since the x value is 0.

此后,由于x值为0,我们在屏幕左侧生成了播放器。

Lastly, we set up a timer action that runs every 0.25 seconds. The action is to spawn an enemy at a random Y location.

最后,我们设置了一个计时器动作,该动作每0.25秒运行一次。 动作是在一个随机的Y位置生成一个敌人。

物理 (Physics)

Our physics code is trivial since there are not many things that will be colliding in our game.

我们的物理代码是微不足道的,因为在我们的游戏中不会发生很多冲突。

@Overrideprotected void initPhysics() {    onCollisionBegin(EntityType.PROJECTILE, EntityType.ENEMY, (proj, enemy) -> {        proj.removeFromWorld();        enemy.removeFromWorld();    });}

As can be seen above, the only two entity types we care about with respect to collisions are projectile and enemy. We set up a handler that is called when these two types collide, giving us the references to specific entities that collided. Note the order in which the types are defined. This is the order in which entity references are passed in. We want to remove both from the world when they collide.

从上面可以看出,关于碰撞,我们关心的仅有的两种实体类型是射弹和敌人。 我们设置了当这两种类型发生冲突时调用的处理程序,为我们提供了对发生冲突的特定实体的引用。 注意定义类型的顺序。 这是实体引用传递的顺序。当它们碰撞时,我们希望将两者从世界中删除。

更新资料 (Update)

There are not many things in our game loop. We only want to know when an enemy has reached our base, i.e. the enemy’s X value is less than 0.

我们的游戏循环中没有很多东西。 我们只想知道敌人何时到达我们的基地,即敌人的X值小于0。

@Overrideprotected void onUpdate(double tpf) {    var enemiesThatReachedBase = getGameWorld().getEntitiesFiltered(e -> e.isType(EntityType.ENEMY) && e.getX() < 0);    if (!enemiesThatReachedBase.isEmpty()) {        showMessage("Game Over!", () -> getGameController().startNewGame());    }}

To achieve this, we query the game world to give us all entities whose type is enemy and whose X value is less than 0. If the list is not empty then we lost the game, so we show the appropriate message and restart.

为此,我们查询游戏世界以提供所有类型为敌人且X值小于0的实体。如果列表不为空,则我们输了游戏,因此我们显示适当的消息并重新启动。

结论 (Conclusion)

That is the end of this tutorial. You should now be able to run the game in your IDE. The FXGL game engine is fully open-source and the source is available at .

到此结束。 您现在应该可以在IDE中运行游戏。 FXGL游戏引擎是完全开源的,该资源可从 。

The full source code of this tutorial is available at .

本教程的完整源代码可在 。

翻译自:

转载地址:http://imozd.baihongyu.com/

你可能感兴趣的文章
openssl 升级
查看>>
ASP.NET MVC:通过 FileResult 向 浏览器 发送文件
查看>>
CVE-2010-2883Adobe Reader和Acrobat CoolType.dll栈缓冲区溢出漏洞分析
查看>>
使用正确的姿势跨域
查看>>
AccountManager教程
查看>>
Android学习笔记(十一)——从意图返回结果
查看>>
算法导论笔记(四)算法分析常用符号
查看>>
ultraedit激活
查看>>
总结(6)--- python基础知识点小结(细全)
查看>>
亿级曝光品牌视频的幕后设定
查看>>
ARPA
查看>>
JSP开发模式
查看>>
我的Android进阶之旅------&gt;Android嵌入图像InsetDrawable的使用方法
查看>>
Detours信息泄漏漏洞
查看>>
win32使用拖放文件
查看>>
Android 动态显示和隐藏软键盘
查看>>
raid5什么意思?怎样做raid5?raid5 几块硬盘?
查看>>
【转】how can i build fast
查看>>
null?对象?异常?到底应该如何返回错误信息
查看>>
django登录验证码操作
查看>>