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+一起使用。


<dependency><groupId>com.github.almasb</groupId><artifactId>fxgl</artifactId><version>11.8</version>
</dependency>

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 {@Overrideprotected 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”方法内部进行处理,如下所示:


@Override
protected 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:

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


@Override
protected 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.

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


@Override
protected 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。


@Override
protected 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 https://github.com/AlmasB/FXGL.

到此结束。 您现在应该可以在IDE中运行游戏。 FXGL游戏引擎是完全开源的,该资源可从https://github.com/AlmasB/FXGL获得 。

The full source code of this tutorial is available at https://github.com/AlmasB/FXGLGames/tree/master/SpaceRanger.

本教程的完整源代码可在https://github.com/AlmasB/FXGLGames/tree/master/SpaceRanger中找到 。

翻译自: https://www.journaldev.com/40219/space-rangers-game-java-fxgl

使用Java FXGL构建太空游侠游戏相关推荐

  1. 太空java游戏_JavaSpaceWar 一个基于 语言编写的太空战争游戏,包含所有图像和声音资源,便 习理解使用 Game Program 238万源代码下载- www.pudn.com...

    文件名称: JavaSpaceWar下载 收藏√  [ 5  4  3  2  1 ] 开发工具: Java 文件大小: 506 KB 上传时间: 2014-03-18 下载次数: 0 提 供 者: ...

  2. 树莓派zero玩JAVA游戏,用树莓派编写“太空入侵者”游戏教程,这个元旦简单太爽了……...

    几乎人人都玩过太空入侵者,这款游戏还可能是有些人玩的第一个电脑游戏. 这个"太空入侵者"游戏模式需要相当多的不同编码技术才能完成.一直以来,当人们需要学习一种新的编码语言,常常会写 ...

  3. java毕业设计——基于java+Java awt+swing的愤怒的小鸟游戏设计与实现(毕业论文+程序源码)——愤怒的小鸟游戏

    基于java+Java awt+swing的愤怒的小鸟游戏设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+Java awt+swing的愤怒的小鸟游戏设计与实现,文章末尾附有本毕 ...

  4. 【java毕业设计】基于java+swing+Eclipse的俄罗斯方块游戏GUI设计与实现(毕业论文+程序源码)——俄罗斯方块游戏

    基于java+swing+Eclipse的俄罗斯方块游戏GUI设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+swing+Eclipse的俄罗斯方块游戏GUI设计与实现,文章末尾 ...

  5. 太空动作游戏《Phobos Vector Prime》创作经验分享

    本文将由来自Gunstar Studio的几位技术大牛将分享他们在太空动作游戏<Phobos Vector Prime>中制作爆炸效果.角色纹理和构建关卡的方法技巧. 项目简介 Gunst ...

  6. JAVA课程设计(小游戏贪吃蛇)完整源码附素材(二)

    目录 JAVA课程设计(小游戏贪吃蛇)完整源码附素材(一) JAVA课程设计(小游戏贪吃蛇)完整源码附素材(二) JAVA课程设计(小游戏贪吃蛇)完整源码附素材(三) 前言 1. 任务描述 1.1  ...

  7. 【java毕业设计】基于java+swing+GUI的连连看游戏设计与实现(毕业论文+程序源码)——连连看游戏

    基于java+swing+GUI的连连看游戏设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+swing+GUI的连连看游戏设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦 ...

  8. mac太空射击游戏:Nova Drift (新星漂移)中文版

    Nova Drift是一款激动人心的太空射击游戏. Nova Drift Mac整合了现代独立游戏风格和游戏历史上最深的根源.它不仅具有时尚和性感的外观,而且具有平稳的控制功能.玩家控制着一艘不断开发 ...

  9. 【Java】Java基础飞机大战小游戏完整代码

    Java基础飞机大战小游戏完整代码 先来展示一下代码实现结果图 主函数ShootGame 初始化游戏原始背景图片,游戏人物图片,游戏开始结束图片:构建产生敌人算法:产生英雄机算法:发射子弹算法:判断是 ...

最新文章

  1. 构建高可用服务器之二 Keepalive参数详解
  2. 涨知识了!网络原来是这样连接的
  3. Java集合学习_Java集合学习(适合新手)
  4. 【控制】《多智能体系统的协同群集运动控制》陈杰老师-第8章-高阶非线性多智能体分布式自适应鲁棒控制
  5. RocketMQ:消息ACK机制源码解析
  6. C语言中 *.c和*.h文件的区别!
  7. 数据结构与算法--数字在排序数组中出现次数
  8. 工作111:对element-form的理解
  9. centos7挂载ntfs文件系统_MacOS读写挂载NTFS磁盘,几行代码就行了
  10. 快速傅立叶变换(FFT)算法(原来这就是蝶形变换)
  11. 95-38-035-Buffer-AbstractByteBuf
  12. 租用服务器怎么免去后顾之忧?
  13. 【SQL Server】 SQL Server Management Studio不允许保存更改,组织保存要求解决方案
  14. 基于python的学生信息管理系统毕业论文_学生信息管理系统毕业论文
  15. IT计算机行业都有哪些证可以考
  16. TDB和SPARQL
  17. 名帖194 米芾 行书《珊瑚帖》
  18. DBeaver配置phoenix
  19. python培训费用多少钱?学习python课程价格?
  20. Excel笔记(2)常用函数1-10

热门文章

  1. python3_configparser模块详解
  2. byte[]与Image Image与 byte[] 之间的转换
  3. 理科本科,无考试,读诗《宣州谢脁楼饯别校书叔云》李白
  4. 提高mysql数据库速度方法(转)
  5. 史上最细的FIFO最小深度计算,(大多数笔试题中都会涉及)
  6. PLL与PHY的连接:通道绑定或者不绑定
  7. 通过宝塔webhook,实现git自动拉取服务器代码
  8. java-成员变量的属性与成员函数的覆盖
  9. Cordova开发总结(插件篇)
  10. 2101 Problem A Snake Filled