发布时间:2024-06-03 17:07:04 浏览量:238次
《开源精选》是我们分享Github、Gitee等开源社区中优质项目的栏目,包括技术、学习、实用与各种有趣的内容。本期推荐的是使用JavaFX / Kotlin开发的2D和3D游戏引擎——FXGL。
FXGL是一个基于JavaFX的2D和3D游戏引擎,适合开发任何类型的2D游戏,包括横向卷轴、平台、街机、RPG等。并提供了大量的游戏示例来帮助我们掌握游戏开发技能。
Maven
<dependency> <groupId>com.github.almasb</groupId> <artifactId>fxgl</artifactId> <version>11.17</version></dependency>
Gradle
compile 'com.github.almasb:fxgl:11.17'
默认情况下,FXGL 将游戏大小设置为 800x600,这适用于我们的游戏。您可以通过 更改这些设置和各种其他设置settings.setXXX()。现在,我们将只设置标题并添加入口点 - main()。
public class PongApp extends GameApplication { @Override protected void initSettings(GameSettings settings) { settings.setTitle("Pong"); } public static void main(String[] args) { launch(args); }}
接下来,我们将定义一些不言自明的常量。
private static final int PADDLE_WIDTH = 30;private static final int PADDLE_HEIGHT = 100;private static final int BALL_SIZE = 20;private static final int PADDLE_SPEED = 5;private static final int BALL_SPEED = 5;
我们有三个游戏对象,特别是两个桨和一个球。FXGL 中的游戏对象称为Entity. 所以,让我们定义我们的实体。
private Entity paddle1;private Entity paddle2;private Entity ball;
接下来,我们将查看输入。与某些框架不同,不需要手动查询输入状态。在 FXGL 中,我们通过定义动作(游戏应该做什么)并将它们绑定到输入触发器(按下某物时)来处理输入。例如:
@Overrideprotected void initInput() { getInput().addAction(new UserAction("Up 1") { @Override protected void onAction() { paddle1.translateY(-PADDLE_SPEED); } }, KeyCode.W); // ...}
上面的意思是,当W被按下时,paddle在 Y 轴上移动-PADDLE_SPEED,这实际上意味着向上移动桨。剩下的输入:
getInput().addAction(new UserAction("Down 1") { @Override protected void onAction() { paddle1.translateY(PADDLE_SPEED); }}, KeyCode.S);getInput().addAction(new UserAction("Up 2") { @Override protected void onAction() { paddle2.translateY(-PADDLE_SPEED); }}, KeyCode.UP);getInput().addAction(new UserAction("Down 2") { @Override protected void onAction() { paddle2.translateY(PADDLE_SPEED); }}, KeyCode.DOWN);
我们现在将添加游戏变量来记录玩家 1 和玩家 2 的分数。我们可以使用int score1;. 但是,FXGL 提供了一个强大的属性概念,它建立在 JavaFX 属性之上。澄清一下,FXGL 中的每个变量都在内部存储为 JavaFX 属性,因此是可观察和可绑定的。我们声明变量如下:
@Overrideprotected void initGameVars(Map vars) { vars.put("score1", 0); vars.put("score2", 0);}
FXGL 将根据默认值推断每个变量的类型。在这种情况下 0 是 type int,因此score1将被分配int类型。稍后我们将看到这些变量与原始 Java 类型相比有多么强大。
@Overrideprotected void initGame() { paddle1 = spawnBat(0, getHeight() / 2 - PADDLE_HEIGHT / 2); paddle2 = spawnBat(getWidth() - PADDLE_WIDTH, getHeight() / 2 - PADDLE_HEIGHT / 2); ball = spawnBall(getWidth() / 2 - BALL_SIZE / 2, getHeight() / 2 - BALL_SIZE / 2);}private Entity spawnBat(double x, double y) { return Entities.builder() .at(x, y) .viewFromNodeWithBBox(new Rectangle(PADDLE_WIDTH, PADDLE_HEIGHT)) .buildAndAttach();}private Entity spawnBall(double x, double y) { return Entities.builder() .at(x, y) .viewFromNodeWithBBox(new Rectangle(BALL_SIZE, BALL_SIZE)) .with("velocity", new Point2D(BALL_SPEED, BALL_SPEED)) .buildAndAttach();}
我们要求Entities建造者:
此后,我们设计了由两个Text对象组成的 UI 。重要的是,我们将这些对象的 text 属性绑定到我们之前创建的两个变量。这是 FXGL 变量提供的强大功能之一。更具体地说,当score1更新时,textScore1UI 对象的文本将自动更新。
@Overrideprotected void initUI() { Text textScore1 = getUIFactory().newText("", Color.BLACK, 22); Text textScore2 = getUIFactory().newText("", Color.BLACK, 22); textScore1.setTranslateX(10); textScore1.setTranslateY(50); textScore2.setTranslateX(getWidth() - 30); textScore2.setTranslateY(50); textScore1.textProperty().bind(getGameState().intProperty("score1").asString()); textScore2.textProperty().bind(getGameState().intProperty("score2").asString()); getGameScene().addUINodes(textScore1, textScore2);}
这个游戏的最后一部分是更新滴答声。通常,FXGL 游戏将使用Components 为每一帧上的实体提供功能。所以根本不需要更新代码。在这种情况下,作为一个简单的例子,我们将使用传统的更新方法,如下所示:
@Overrideprotected void onUpdate(double tpf) { Point2D velocity = ball.getObject("velocity"); ball.translate(velocity); if (ball.getX() == paddle1.getRightX() && ball.getY() paddle1.getY()) { ball.setProperty("velocity", new Point2D(-velocity.getX(), velocity.getY())); } if (ball.getRightX() == paddle2.getX() && ball.getY() paddle2.getY()) { ball.setProperty("velocity", new Point2D(-velocity.getX(), velocity.getY())); } if (ball.getX() = getWidth()) { getGameState().increment("score1", +1); resetBall(); } if (ball.getY() = getHeight()) { ball.setY(getHeight() - BALL_SIZE); ball.setProperty("velocity", new Point2D(velocity.getX(), -velocity.getY())); }}
我们获取球的“速度”属性并使用它在每一帧上平移(移动)球。然后,我们对球相对于游戏窗口和球拍的位置进行各种检查。如果球击中窗口的顶部或底部,则我们在 Y 轴上反转。类似地,如果球击中桨,那么我们会反转 X 轴。最后,如果球未击中球拍并击中屏幕的一侧,则相反地球拍得分并重置球。复位方法如下:
private void resetBall() { ball.setPosition(getWidth() / 2 - BALL_SIZE / 2, getHeight() / 2 - BALL_SIZE / 2); ball.setProperty("velocity", new Point2D(BALL_SPEED, BALL_SPEED));}
运行后游戏将如下所示:
另外,FXGL还提供了大量的完整游戏示例,包括炸弹人、飞翔的小鸟、马里奥、太空游侠、吃豆人等,如果你想学习如何开发一个2D游戏,可以参考一下这些示例。
项目地址:
https://github.com/AlmasB/FXGLGames
开源地址:github.com/AlmasB/FXGL
想学习更多关于游戏开发的知识吗?欢迎来火星时代教育,我们提供游戏设计培训、动画培训、UI设计培训等多种课程,点击咨询获取更多信息!
热门资讯
探讨游戏引擎的文章,介绍了10款游戏引擎及其代表作品,涵盖了RAGE Engine、Naughty Dog Game Engine、The Dead Engine、Cry Engine、Avalanche Engine、Anvil Engine、IW Engine、Frostbite Engine、Creation引擎、Unreal Engine等引擎。借此分析引出了游戏设计领域和数字艺术教育的重要性,欢迎点击咨询报名。
2. 手机游戏如何开发(如何制作传奇手游,都需要准备些什么?)
如何制作传奇手游,都需要准备些什么?提到传奇手游相信大家都不陌生,他是许多80、90后的回忆;从起初的端游到现在的手游,说明时代在进步游戏在更新,更趋于方便化移动化。而如果我们想要制作一款传奇手游的
3. B站视频剪辑软件「必剪」:免费、炫酷特效,小白必备工具
B站视频剪辑软件「必剪」,完全免费、一键制作炫酷特效,适合新手小白。快来试试!
游戏中玩家将面临武侠人生的挣扎抉择,战或降?杀或放?每个抉定都将触发更多爱恨纠葛的精彩奇遇。《天命奇御》具有多线剧情多结局,不限主线发展,高自由...
5. Bigtime加密游戏经济体系揭秘,不同玩家角色的经济活动
Bigtime加密游戏经济模型分析,探讨游戏经济特点,帮助玩家更全面了解这款GameFi产品。
6. 3D动画软件你知道几个?3ds Max、Blender、Maya、Houdini大比拼
当提到3D动画软件或动画工具时,指的是数字内容创建工具。它是用于造型、建模以及绘制3D美术动画的软件程序。但是,在3D动画软件中还包含了其他类型的...
7. 3D动漫建模全过程,不是一般人能学的会的,会的多不是人?
步骤01:面部,颈部,身体在一起这次我不准备设计图片,我从雕刻进入。这一次,它将是一种纯粹关注建模而非整体绘画的形式。像往常一样,我从Sphere创建它...
8. 如何自己开发一款游戏(游戏开发入门必看:五大独立游戏开发技巧)
游戏开发入门必看:五大独立游戏开发技巧无论您是刚刚起步开发自己的第一款游戏,还是已经制作了几款游戏,本篇文章中的5大独立游戏开发技巧都可以帮助您更好地设计下一款游戏。无论你对游戏有着什么样的概念,都
三昧动漫对于著名ARPG游戏《巫师》系列,最近CD Projekt 的高层回应并不会推出《巫师4》。因为《巫师》系列在策划的时候一直定位在“三部曲”的故事框架,所以在游戏的出品上不可能出现《巫师4》
想让你的3D打印模型更坚固?不妨尝试一下Cura参数设置和设计技巧,让你轻松掌握!
同学您好!