JavaFXGL框架笔记
实体类型
大多数游戏都有不同类型的实体,例如玩家、子弹、敌人等。
该类型可能是一个枚举,例如可能如下所示:
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
,暗示它可以移动。现在想象我们也有一个玩家角色,它也可以移动。我们可以加上moveSpeed
到Player
对象。然而,还有许多其他可移动的游戏对象。所以也许我们应该用继承来代替,有一个抽象类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 Component
有onUpdate()
可以实现以提供功能的方法。这就是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框架笔记相关推荐
- Java基础知识强化之集合框架笔记76:ConcurrentHashMap之 ConcurrentHashMap简介
1. ConcurrentHashMap简介: ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和Hashtable功能相同但是线程安全的方法.Conc ...
- ET框架笔记 (笑览世界写)(转)
客户端 1.使用unity2017.2.0版本及以上2018版以下,编译原始版ET客户端报没有引用错误时,删除hoxfit下引用的UnityEngine.dll和UnityEngine.UI.dll, ...
- msm8996平台的 camera 框架笔记
msm8996平台的 camera 框架笔记 标签(空格分隔): camera 文章目录 msm8996平台的 camera 框架笔记 @[toc] 0 前言 1 kernel中的camera结构 1 ...
- 解读基金—读书框架笔记①
解读基金-读书框架笔记① 基础概念 投资目标的确定 基金风险 投资金额比例 & 投资时间 复利计算"72"法则 & 基金选择的关键点 基金基础知识 & 品种 ...
- Android O Treble框架笔记(基于高通845平台)
Android O Treble框架笔记(基于高通845平台) tags: android 文章目录 Android O Treble框架笔记(基于高通845平台) @[toc] **0 前言** * ...
- Android O 的camera framework-hal层框架笔记(基于高通845平台)
Android O 的camera framework/hal层框架笔记(基于高通845平台) tags: android camera 文章目录 Android O 的camera framewor ...
- ET 框架笔记【转载】
ET框架笔记 (笑览世界写)(转) 客户端 1.使用unity2017.2.0版本及以上2018版以下,编译原始版ET客户端报没有引用错误时,删除hoxfit下引用的UnityEngine.dll和U ...
- 框架笔记:记录XLua的简单接入
阅前提示 本系列为作者在学习框架与编写框架时的心得与笔记 适合人群:All 阅读方式:工具文章 本系列会不断更新,如果对你有所帮助,点赞收藏吧:) 文章目录 阅前提示 XLua 配置 [LuaCall ...
- 织梦轻量级mvc框架笔记
为什么80%的码农都做不了架构师?>>> 织梦cms简单易用 是我们这些水货站长首选的cms 但随着功能的扩展,织梦好像渐渐的变得臃肿不堪,可扩展性不好,结构不是很清晰 , ...
最新文章
- kali 改root_Kali Linux 将默认以非 root 身份运行
- bugFree与zentao
- GeoHash -------寻找附近人
- js异步加载 defer和async 比较
- oracle驱动权限如何修改,详解如何实现Oracle修改用户权限和角色
- 国际运营版Java区块链交易所源码币币+法币+永续+期权+机器人
- e-006 matlab,基于MATLAB进行潮流计算
- js如何获取php中的变量的类型,js获取变量的类型
- 【数学建模】算法模型(三)|模糊综合和灰色关联分析 元胞自动机模型 图论 BP神经网络算法 马尔可夫链蒙特卡罗算法(MCMC)
- linux删除一个网口的ip地址,linux一个网口多个ip地址
- 机器学习算法——决策树3(CART决策树算法)
- 区块链各行业应用案例
- fspecial在matlab中什么意思,Matlab中fspecial的用法 | 学步园
- 扫描二维码进小程序指定页面
- C语言sizeof与strlen详解(附大量笔试题题解过程)
- 来曲唑十二烷基硫酸钠/苯丁酸氮芥/层状双金属氢氧化物纳米杂化物
- ADL100(2)-Wuwei-检索式对话
- 99%的游戏建模师不知道的ZBrush的15个建模技巧
- linux查看进程家族树,3.1.6 进程家族树
- 中国货币供应量全球第一的反思
热门文章
- 工业控制系统协议相关的安全问题
- 小米设备token获取HomeAssistant安装部署
- 混合云管平台排名您知道吗?看这里
- 腾讯区块链首次发声:将做深做透场景
- 遇见--CondaHTTPError: HTTP 000 CONNECTION FAILED for url 怎么解决
- 典型的计算机网络安全技术有哪些,网络安全(计算机网络安全技术有哪些)
- diskpart(diskpart转换GPT)
- lisp语言画地物符号_LISP语言在CAD工程制图中的应用_谢威
- Windows环境下Nginx配置本地虚拟域名和Nginx代理
- 用photoshop制作证件照片