实体类型

大多数游戏都有不同类型的实体,例如玩家、子弹、敌人等。

该类型可能是一个枚举,例如可能如下所示:

 public enum EntityType {PLAYER, BULLET, ENEMY}

枚举类的名称可以取成你想要的,但应该遵守规范。

你可以用MyGameNameType代替EntityTyp,这取决于什么更适合你。例如,一个太空入侵者的克隆体SpaceInvadersType枚举的名称。

实体工厂

在你的游戏中为不同集合类型创建实体时,建议分别使用一个单独的类来负责。例如,你可以创建一个只制造敌人的类,然后创建一个只制造能量的类,再创建一个专门制造背景和装饰实体的类。这种创建实体的类称为实体工厂。

 public class MyBlockFactory implements EntityFactory {​@Spawns("block")public Entity newBlock(SpawnData data) {return FXGL.entityBuilder(data).viewFromNode(new Rectangle(70, 70)).build();}}

您可以向游戏世界添加工厂,如下所示:getGameWorld().addEntityFactory(new MyBlockFactory());。然后您可以使用getGameWorld().spawn("block");生成一个实体,它又调用用@Spawns("block").

实体组件(FXGL 11)

实体

任何你能想到的游戏物体(玩家、硬币、电源、墙壁、灰尘、粒子、武器等等)。是一个实体。一个实体本身只不过是一个普通的对象。组件允许我们将实体塑造成我们喜欢的任何形状。

组件作为数据

在我们的上下文中,组件是实体的一部分。严格地说,组件是一种数据结构,包括修改和查询数据的方法。这类似于如何将实例字段添加到类定义中:

 class Car {int moveSpeed;Color color;}

Car类包含一个实例字段moveSpeed,暗示它可以移动。现在想象我们也有一个玩家角色,它也可以移动。我们可以加上moveSpeedPlayer对象。然而,还有许多其他可移动的游戏对象。所以也许我们应该用继承来代替,有一个抽象类MovableGameObject,或者更好的接口Movable。这很好,但是如果我们想要一个有时能移动,有时不能移动的物体,例如,玩家控制的汽车可以移动,但它自己不能移动。我们不能只是移除一个接口,然后在需要的时候再把它装回去。这就是组件的用武之地。你可能听说过“组成高于继承”。嗯,这是它的极端版本。组件允许我们在运行时“附加”字段,这多酷啊?考虑上面的片段,用组件重写。

 Entity entity = new Entity();entity.addComponent(new MoveSpeedComponent()); // 为实体对象添加新的移动组件entity.addComponent(new ColorComponent());  // 为实体对象添加新的颜色组件​// some time later we can make it immovableentity.removeComponent(MoveSpeedComponent.class);  //移除移动组件​// or change a component's value like a normal fieldentity.getComponentOptional(ColorComponent.class).ifPresent(color -> {color.setValue(Color.RED);});

此外,这种方法隔离了每个组件。这意味着我们可以将它附加到任何的实体并自行修改它。

请注意,最好在实体创建期间添加您知道实体将拥有的任何组件。然后,您可以根据需要启用/禁用组件。举个例子,CollidableComponent表示一个实体可能会与某物发生碰撞。假设我们不希望我们的实体在创建时是可聚合的。与其在实体变得可碰撞时添加组件,不如在创建时添加组件并禁用它。通过这样做,我们可以避免使用getComponentOptional()和使用getComponent().

组件作为行为

我们讨论了添加组件就像添加字段一样。嗯,添加一个组件也类似于添加一个方法。组件允许我们让一个实体做一些事情,本质上定义了实体的行为。假设我们想让一个实体成为升降机,这样它就可以把玩家载到山顶。

 entity.addComponent(new LiftComponent());

A ComponentonUpdate()可以实现以提供功能的方法。这就是lift组件可能实现的方式(省略不相关的代码):

 public class LiftComponent extends Component {@Overridepublic void onUpdate(double tpf) {if (timer.elapsed(duration)) {  //如果 (计时器.过去时间(持续时间))goingUp = !goingUp;timer.capture();}​entity.translateY(goingUp ? -speed * tpf : speed * tpf);}}

某些组件将具有需要手动触发的方法。例如,一个非常简单的播放器组件:

 public class PlayerComponent extends Component {​// note that this component is injected automaticallyprivate TransformComponent position;​private double speed = 0;​@Overridepublic void onUpdate(double tpf) {speed = tpf * 60;}​public void up() {position.translateY(-5 * speed);}​public void down() {position.translateY(5 * speed);}​public void left() {position.translateX(-5 * speed);}​public void right() {position.translateX(5 * speed);}}

因此,在输入处理部分,我们可以将键绑定到方法up, down等。然后,用户将能够通过其组件来移动玩家实体。在添加组件的过程中会检查所需的组件,因此确保首先添加所有必需的组件非常重要。

输入(FXGL 11)

有多种方法允许您处理用户输入。

注意:我们只讨论FXGL输入处理。但是,如果出于任何原因需要,您可以获取对底层JavaFX场景对象的引用:getGameScene().getRoot().getScene()并使用JavaFX处理。

FXGL输入处理模型

任何输入触发器(键盘、鼠标或虚拟的)都由FXGL在内部捕获,并由开发人员指定的UserAction处理。用户操作有三种状态:

  • 开始(1次刻度)

  • 动作(一个或多个刻度)

  • 结束(1个刻度)

这些状态对应于用户按压、保持和释放触发器。这个三态系统允许我们提供一个简单的高级API来处理任何类型的输入。考虑一个击打高尔夫球的用户动作:

 UserAction hitBall = new UserAction("Hit") {@Overrideprotected void onActionBegin() {// action just started (key has just been pressed), play swinging animation}​@Overrideprotected void onAction() {// action continues (key is held), increase swing power}​@Overrideprotected void onActionEnd() {// action finished (key is released), play hitting animation based on swing power}};

注册用户操作

既然我们已经创建了一个操作hitBall,我们可以要求输入服务注册它:

 @Overrideprotected void initInput() {Input input = getInput();​input.addAction(hitBall, KeyCode.F);}

注意:重要的是,所有操作都要在initInput()中注册,因为这是在FXGL初始化例程的早期调用的,所以菜单可以正确显示所有现有的命令。

的第二个参数addAction()是导火索。触发器可以是按键或鼠标按钮。我们说“动作”必然会“触发”,所以在这种情况下hitBall动作被绑定到F钥匙。

使用触发器监听器

您还可以监听一般的触发事件。开始-行动-结束模型同上。

 getInput().addTriggerListener(new TriggerListener() {@Overrideprotected void onActionBegin(Trigger trigger) {System.out.println("Begin: " + trigger);}​@Overrideprotected void onAction(Trigger trigger) {System.out.println("On: " + trigger);}​@Overrideprotected void onActionEnd(Trigger trigger) {System.out.println("End: " + trigger);}});

使用上面的监听器,你也可以检查在这个框架中哪个键/按钮被按下了。例如,在onAction()您可以添加:

 if (trigger.isKey()) {var keyTrigger = (KeyTrigger) trigger;​// key is being pressed nowvar key = keyTrigger.getKey();​} else {var mouseTrigger = (MouseTrigger) trigger;​// btn is being pressed nowvar btn = mouseTrigger.getButton();}

查询鼠标状态

您可以随时检查光标位置:

 Point2D cursorPointInWorld = input.getMouseXYWorld();Point2D cursorPointInUI = input.getMouseXYUI();

如你所见,我们不仅可以在游戏世界(场景)中查询光标位置,还可以在UI叠加中查询光标位置。对于屏幕不动的游戏,坐标是一样的。然而,想象一下像马里奥这样的平台游戏,你水平移动,世界(摄像机)也跟着你移动。那么世界中的光标可能是x = 3400,y = 450,而UI坐标中的光标将是x = 400,y = 450。当你的游戏世界层需要与UI层交互时,这非常有用。比方说,你捡起一枚硬币,然后硬币慢慢地向计算你收集的硬币的UI对象移动,随后与该对象合并以创建一个流畅的动画。

模仿用户输入

因为FXGL有自己的输入处理机制层,所以您也可以模拟输入。这在教程级别或电影动作中非常有用,也就是说,你需要用播放器做一些事情,并且有代码来做,但是你不想让用户去做。只需拨打:

 // behaves exactly the same as if the user pressed 'W' on the keyboardinput.mockKeyPress(KeyCode.W);​// behaves exactly the same as if the user released 'W' on the keyboardinput.mockKeyRelease(KeyCode.W);

可以用类似的方式模仿鼠标事件。假设输入绑定,也就是你注册的动作,会相应地被触发。

重新绑定系统操作

您可以将屏幕截图操作(或任何其他操作)重新绑定到您自己的触发器。在…里initInput():

 getInput().rebind(getInput().getActionByName("Screenshot"), KeyCode.F11);

输入修饰符(CTRL、ALT、SHIFT)

一个例子:

 input.addAction(action, keyCode, InputModifier.CTRL);

只有当修饰符按键被按下。

按键序列(组合)

         Input input = FXGL.getInput();​var sequence = new InputSequence(KeyCode.Q, KeyCode.W, KeyCode.E, KeyCode.R);​// the action fires only when the sequence above (Q, W, E, R) is complete// useful for input combosinput.addAction(new UserAction("Print Line") {@Overrideprotected void onActionBegin() {System.out.println("Action Begin");}​@Overrideprotected void onAction() {System.out.println("On Action");}​@Overrideprotected void onActionEnd() {System.out.println("Action End");}}, sequence);

要记住的事情

  • 动作必须有唯一且有意义的名称。

  • 一个动作<->一个触发器策略,即一个动作绑定到一个触发器,并且一个触发器只能绑定一个动作。

Assets

游戏世界(FXGL 11)

游戏世界负责添加、更新和删除实体。它还提供了通过各种标准查询实体的方法。游戏世界中的实体被认为active(这可以检查:entity.isActive()).一旦一个实体从世界上被删除,它就不再可用并被清除。游戏世界实例可以通过调用FXGL.getGameWorld().

添加和删除实体

要将实体添加到游戏世界中,使其成为游戏的一部分,只需调用:

 GameWorld world = getGameWorld();Entity e = ...world.addEntity(e);

你可以用类似的方法移除游戏世界中存在的实体:

 world.removeEntity(e);

每个实体都知道它所依附的世界。所以,代替上面的,你可以调用一个更方便的版本:

 e.removeFromWorld();

上面的两个调用在语义上是等价的。

问题

下面的代码片段允许您从世界上请求特定的实体。每个查询对实体组件都有一定的先决条件。例如,如果您基于以下内容进行查询TypeComponent,那么没有该组件的所有实体将被自动从搜索中过滤掉。一些查询返回实体列表,其他的-Optional<Entity>表示这种实体可能不存在。

按类型

示例:我们有一个枚举EntityType它包含游戏中实体的有效类型。

 List<Entity> enemies = world.getEntitiesByType(EntityType.ENEMY);

按ID

示例:您可能允许实体的副本。这对于RPG类型的游戏尤其有用,在这类游戏中你会有很多副本。因此,也许我们在游戏世界中放置了多个伪造品,每个都有自己独特的IDComponent。虽然名称相同-“Forge”,但其数字id是不同的。

 Optional<Entity> forge123 = world.getEntityByID("Forge", 123);

按单个

示例:假设我们知道只有一个“玩家”类型的实体。

 Entity player = world.getSingleton(EntityType.PLAYER);

随机地

例子:假设我们知道一个随机的敌人。

 Optional<Entity> enemy = world.getRandom(EntityType.ENEMY);

注意:返回类型是Optional因为我们可能根本没有敌人。

按组件

示例:您想要具有特定组件的实体。

 List<Entity> entityAbove = world.getEntitiesByComponent(MyComponent.class);

按范围

示例:您想要特定选择框中的实体。有助于选择多个实体,查看爆炸物是否应该摧毁一定范围内的物体,或者查看玩家是否可以与某个物体互动。

 List<Entity> entitiesNearby = world.getEntitiesInRange(new Rectangle2D(50, 50, 100, 100));

按过滤器

示例:您有自己的实体规格说明,您希望这些规格说明不属于上述任何类别。

 List<Entity> items = world.getEntitiesFiltered(e -> e.getInt("hp") == 10);

游戏世界属性全局可观察变量(FXGL 11)

您可以在应用程序类中声明游戏变量,如下所示:

     @Overrideprotected void initGameVars(Map<String, Object> vars) {// this creates an observable integer variable with value 3vars.put("lives", 3);}PropertyMap state = FXGL.getWorldProperties();

有五种类型的已用值:

  • boolean

  • int

  • double

  • String

  • Object(任何不是来自上面的都属于这一类)

每个值都有一个与之关联的名称。可以通过以下方式访问原始值:

 int lives = state.getInt("lives");

每个值都由JavaFX备份Property。这意味着您可以监听更改,也可以绑定到自动更新的属性。属性值可以通过以下方式访问:

 IntegerProperty livesProperty = state.intProperty("lives");

最后,您可以使用以下命令编辑这些值:

 state.setValue("lives", 5);

物理世界 (FXGL 11)

FXGL,像许多其他2D游戏框架一样,使用jbox2d来处理物理。我们只介绍如何在FXGL中使用jbox2d,因为该库有自己的文档。

物理世界 (FXGL 11) ·AlmasB/FXGL Wiki ·GitHub

视窗

FXGL 中的视口承担摄像机的责任。您可以按如下方式获取对视口的引用:

 Viewport viewport = getGameScene().getViewport();

您可以通过调用以下内容手动更改视口的 和 值:x``y

 viewport.setX(...);viewport.setY(...);

最有用的功能之一是绑定视口以跟随实体:

 Entity player = ...;​// distX and distY is how far the viewport origin should be from the entityviewport.bindToEntity(player, distX, distY);

当视口跟随玩家时,它可能会偏离关卡边界。您可以将边界设置为视口可以"徘徊"的距离:

 viewport.setBounds(minX, minY, maxX, maxY);

计时器操作 (FXGL 11)

在游戏中,您通常希望在一段时间或某个时间间隔后运行操作。例如,可以使用以下内容将某些操作安排在 1 秒后仅运行一次。该操作将在 JavaFX 线程上运行。

 getGameTimer().runOnceAfter(() -> {// code to run once after 1 second}, Duration.seconds(1));

请注意,以上内容等效于FXGL DSL:

 import static com.almasb.fxgl.dsl.FXGL.*;​runOnce(() -> {// code to run once after 1 second}, Duration.seconds(1))

类似的 DSL 函数可用于其他计时器操作。如果您希望某些内容持续运行,则可以使用:

 getGameTimer().runAtInterval(() -> {// code to run every 300 ms}, Duration.millis(300));

请注意,这些操作由状态计时器控制,这意味着如果您的游戏已暂停,则计时器也会暂停。每个状态 (, 等) 都有自己的计时器,因此您可以选择所需的计时器。GAME``GAME_MENU``MAIN_MENU

计划操作后,将返回对该操作的引用。

 TimerAction timerAction = getGameTimer().runAtInterval(() -> {// ...}, Duration.seconds(0.5));

您可以在需要时暂停和恢复计时器操作。

timerAction.pause();
timerAction.resume();

最后,如果您不再需要该操作,可以使其过期。

timerAction.expire();

数学函数

数学函数 (FXGL 11) ·AlmasB/FXGL Wiki ·GitHub

保存和加载 (FXGL 11)

import com.almasb.fxgl.app.GameApplication; import com.almasb.fxgl.app.GameSettings; import com.almasb.fxgl.app.MenuItem; import com.almasb.fxgl.core.serialization.Bundle; import com.almasb.fxgl.profile.DataFile; import com.almasb.fxgl.profile.SaveLoadHandler; import javafx.scene.input.KeyCode; import javafx.scene.paint.Color; import javafx.util.Duration;

import java.util.EnumSet; import java.util.Map;

import static com.almasb.fxgl.dsl.FXGL.*;

public class SaveLoadSample extends GameApplication { @Override protected void initSettings(GameSettings settings) { settings.setMainMenuEnabled(true); settings.setEnabledMenuItems(EnumSet.allOf(MenuItem.class)); }

 @Overrideprotected void initGameVars(Map<String, Object> vars) {vars.put("time", 0.0);}​@Overrideprotected void onPreInit() {getSaveLoadService().addHandler(new SaveLoadHandler() {@Overridepublic void onSave(DataFile data) {// create a new bundle to store your datavar bundle = new Bundle("gameData");​// store some datadouble time = getd("time");bundle.put("time", time);​// give the bundle to data filedata.putBundle(bundle);}​@Overridepublic void onLoad(DataFile data) {// get your previously saved bundlevar bundle = data.getBundle("gameData");​// retrieve some datadouble time = bundle.get("time");​// update your game with saved dataset("time", time);}});}​@Overrideprotected void initInput() {onKeyDown(KeyCode.F, "Save", () -> {getSaveLoadService().saveAndWriteTask("save1.sav").run();});​onKeyDown(KeyCode.G, "Load", () -> {getSaveLoadService().readAndLoadTask("save1.sav").run();});}​@Overrideprotected void initGame() {run(() -> inc("time", 1.0), Duration.seconds(1.0));}​@Overrideprotected void initUI() {var text = getUIFactoryService().newText("", Color.BLACK, 18.0);text.textProperty().bind(getdp("time").asString());​addUINode(text, 100, 100);}​public static void main(String[] args) {launch(args);}

}

。。。。。。。。

JavaFXGL框架笔记相关推荐

  1. Java基础知识强化之集合框架笔记76:ConcurrentHashMap之 ConcurrentHashMap简介

    1. ConcurrentHashMap简介: ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和Hashtable功能相同但是线程安全的方法.Conc ...

  2. ET框架笔记 (笑览世界写)(转)

    客户端 1.使用unity2017.2.0版本及以上2018版以下,编译原始版ET客户端报没有引用错误时,删除hoxfit下引用的UnityEngine.dll和UnityEngine.UI.dll, ...

  3. msm8996平台的 camera 框架笔记

    msm8996平台的 camera 框架笔记 标签(空格分隔): camera 文章目录 msm8996平台的 camera 框架笔记 @[toc] 0 前言 1 kernel中的camera结构 1 ...

  4. 解读基金—读书框架笔记①

    解读基金-读书框架笔记① 基础概念 投资目标的确定 基金风险 投资金额比例 & 投资时间 复利计算"72"法则 & 基金选择的关键点 基金基础知识 & 品种 ...

  5. Android O Treble框架笔记(基于高通845平台)

    Android O Treble框架笔记(基于高通845平台) tags: android 文章目录 Android O Treble框架笔记(基于高通845平台) @[toc] **0 前言** * ...

  6. Android O 的camera framework-hal层框架笔记(基于高通845平台)

    Android O 的camera framework/hal层框架笔记(基于高通845平台) tags: android camera 文章目录 Android O 的camera framewor ...

  7. ET 框架笔记【转载】

    ET框架笔记 (笑览世界写)(转) 客户端 1.使用unity2017.2.0版本及以上2018版以下,编译原始版ET客户端报没有引用错误时,删除hoxfit下引用的UnityEngine.dll和U ...

  8. 框架笔记:记录XLua的简单接入

    阅前提示 本系列为作者在学习框架与编写框架时的心得与笔记 适合人群:All 阅读方式:工具文章 本系列会不断更新,如果对你有所帮助,点赞收藏吧:) 文章目录 阅前提示 XLua 配置 [LuaCall ...

  9. 织梦轻量级mvc框架笔记

    为什么80%的码农都做不了架构师?>>>    织梦cms简单易用  是我们这些水货站长首选的cms  但随着功能的扩展,织梦好像渐渐的变得臃肿不堪,可扩展性不好,结构不是很清晰 , ...

最新文章

  1. kali 改root_Kali Linux 将默认以非 root 身份运行
  2. bugFree与zentao
  3. GeoHash -------寻找附近人
  4. js异步加载 defer和async 比较
  5. oracle驱动权限如何修改,详解如何实现Oracle修改用户权限和角色
  6. 国际运营版Java区块链交易所源码币币+法币+永续+期权+机器人
  7. e-006 matlab,基于MATLAB进行潮流计算
  8. js如何获取php中的变量的类型,js获取变量的类型
  9. 【数学建模】算法模型(三)|模糊综合和灰色关联分析 元胞自动机模型 图论 BP神经网络算法 马尔可夫链蒙特卡罗算法(MCMC)
  10. linux删除一个网口的ip地址,linux一个网口多个ip地址
  11. 机器学习算法——决策树3(CART决策树算法)
  12. 区块链各行业应用案例
  13. fspecial在matlab中什么意思,Matlab中fspecial的用法 | 学步园
  14. 扫描二维码进小程序指定页面
  15. C语言sizeof与strlen详解(附大量笔试题题解过程)
  16. 来曲唑十二烷基硫酸钠/苯丁酸氮芥/层状双金属氢氧化物纳米杂化物
  17. ADL100(2)-Wuwei-检索式对话
  18. 99%的游戏建模师不知道的ZBrush的15个建模技巧
  19. linux查看进程家族树,3.1.6 进程家族树
  20. 中国货币供应量全球第一的反思

热门文章

  1. 工业控制系统协议相关的安全问题
  2. 小米设备token获取HomeAssistant安装部署
  3. 混合云管平台排名您知道吗?看这里
  4. 腾讯区块链首次发声:将做深做透场景
  5. 遇见--CondaHTTPError: HTTP 000 CONNECTION FAILED for url 怎么解决
  6. 典型的计算机网络安全技术有哪些,网络安全(计算机网络安全技术有哪些)
  7. diskpart(diskpart转换GPT)
  8. lisp语言画地物符号_LISP语言在CAD工程制图中的应用_谢威
  9. Windows环境下Nginx配置本地虚拟域名和Nginx代理
  10. 用photoshop制作证件照片