【免费下载链接】amethystData-oriented and>项目地址https://gitcode.com/gh_mirrors/ame/amethyst点击查看免费下载本指南以 Amethyst 官方文档《Custom GameData》为骨架完整讲解如何用自定义GameData结构替换引擎默认的数据对象从而按State灵活开关系统调度——例如实现一个暂停状态按下空格暂停游戏逻辑仅保留渲染、输入、UI 等核心系统运行。读完本文你将掌握DataInit/DataDispose两个 trait 的实现要点、双Dispatcher构建器的设计方法以及如何在State的update中按需派发系统。为什么需要自定义GameDataAmethyst 引擎默认使用内置的GameData结构来装载并派发系统中的Dispatcher它的定义位于 src/game_data.rs/// Default game data. pub struct GameData { dispatcher: OptionDispatcher, } impl GameData { pub fn new(dispatcher: Dispatcher) - Self { ... } pub fn update(mut self, world: mut World, resources: mut Resources) { if let Some(dispatcher) mut self.dispatcher { dispatcher.execute(world, resources); } } }对小游戏和演示而言一个GameData、一个Dispatcher完全够用。但项目变大后你会很快遇到两类瓶颈按状态调度系统希望根据游戏State如菜单、主流程、暂停动态决定哪些System运行跨状态携带数据有些数据既不是Send Sync无法放进World作为资源又需要在状态之间传递。内置GameData无法满足这些需求。解决方案就是自己定义一个GameData结构把无法放入World的东西装进这个结构体并在不同State中控制它的派发行为。第一步定义自定义GameData结构我们从定义数据结构开始它持有两个可选的Dispatcher# extern crate amethyst; # use amethyst::ecs::Dispatcher; # pub struct CustomGameDataa, b { core_dispatcher: OptionDispatcher, running_dispatcher: OptionDispatcher, }这里的两条生命周期注解a、b与Dispatcher的泛型生命周期参数一一对应凡是在结构中持有Dispatcher的容器都需要声明它们。字段用Option包裹是为了支持后续take()取出并释放dispose这是DataDispose生命周期管理的关键。随后为它添加一个update工具方法用于在每帧执行派发。它接收一个running: bool开关为true时先派发running_dispatcher游戏逻辑系统随后无论如何都派发core_dispatcher渲染、输入、UI 等核心系统# extern crate amethyst; # use amethyst::ecs::{Dispatcher, World}; # impla, b CustomGameDataa, b { /// Update game data pub fn update(mut self, world: World, running: bool) { if running { if let Some(dispatcher) self.running_dispatcher.as_mut() { dispatcher.dispatch(world); } } if let Some(dispatcher) self.core_dispatcher.as_mut() { dispatcher.dispatch(world); } } }注意两点设计细节用as_mut()借用Option内部的Dispatcher避免在每次调用中移动所有权两个Dispatcher之间不存在依赖关系core_dispatcher始终执行running_dispatcher是否执行由调用方传入的布尔值决定。第二步实现DataInit与DataDispose要让自定义结构被 Amethyst 的Application接受必须满足两个 trait 约束这也是引擎对GameData的全部要求。两个 trait 的定义都在 src/game_data.rsDataInitTfn build(self, world: mut World, resources: mut Resources) - ResultT, Error负责从构建器构建出T类型的游戏数据DataDisposefn dispose(mut self, world: mut World, resources: mut Resources)负责在游戏关闭时释放资源。引擎的CoreApplication正是依赖这两个 trait 完成数据对象的创建与回收ApplicationBuilder::build中调用init.build(mut self.world, mut self.resources)?见 src/app.rs而在shutdown中调用self.data.dispose(mut self.world, mut self.resources)见 src/app.rs。自定义构建器CustomDispatcherBuilder为了构造两个独立的Dispatcher我们设计一个专门的构建器内部持有两个DispatcherBuilder# extern crate amethyst; # use amethyst::core::SystemBundle; # use amethyst::ecs::{Dispatcher, DispatcherBuilder, System, World}; # use amethyst::{DataDispose, DataInit, Error}; # use amethyst::core::ArcThreadPool; # pub struct CustomDispatcherBuilder { pub core: DispatcherBuilder, pub running: DispatcherBuilder, } impla, b Default for CustomDispatcherBuilder { fn default() - Self { CustomDispatcherBuilder::new() } } impla, b CustomDispatcherBuilder { pub fn new() - Self { CustomDispatcherBuilder { core: DispatcherBuilder::new(), running: DispatcherBuilder::new(), } } pub fn with_base_bundleB(mut self, world: mut World, bundle: B) - ResultSelf, Error where B: SystemBundlea, b, { bundle.build(world, mut self.core)?; Ok(self) } pub fn with_runningS(mut self, system: S, name: str, dependencies: [str]) - Self where forc S: System Send a, { self.running.add(system, name, dependencies); self } }接口分两类with_base_bundle把引擎的SystemBundle渲染、UI、输入、Transform 等装入core派发器——这些是任何状态下都必须运行的系统with_running把游戏逻辑系统装入running派发器——这些在暂停时会停止。实现DataInit接下来为构建器实现DataInitCustomGameData完成线程池获取、派发器构建与初始化impla, b DataInitCustomGameDataa, b for CustomDispatcherBuilder { fn build(self, world: mut World) - CustomGameDataa, b { // Get a handle to the ThreadPool. let pool (*resources.get::ArcThreadPool()).clone(); let mut core_dispatcher self.core.with_pool(pool.clone()).build(); let mut running_dispatcher self.running.with_pool(pool.clone()).build(); core_dispatcher.setup(world); running_dispatcher.setup(world); let core_dispatcher Some(core_dispatcher); let running_dispatcher Some(running_dispatcher); CustomGameData { core_dispatcher, running_dispatcher, } } }要点说明ArcThreadPool是ApplicationBuilder::new预先创建并注册进Resources的全局线程池见 src/app.rs。用with_pool让两个派发器复用同一线程池比各自新建线程池更高效setup(world)必须显式调用派发器需要为其中的每个System初始化所需的资源而这一步必须发生在可以访问World的时机构建完成后用Some(...)包裹存入CustomGameData。实现DataDisposeDataDispose只需把两个派发器逐一取出并释放impla, b DataDispose for CustomGameDataa, b { // We dispose each dispatcher owned by the CustomGameData structure. fn dispose(mut self, world: mut World) { if let Some(dispatcher) self.core_dispatcher.take() { dispatcher.dispose(world); } if let Some(dispatcher) self.running_dispatcher.take() { dispatcher.dispose(world); } } }take()会把Option中的值移出并留下None这样每个派发器只会被释放一次避免重复 drop。至此自定义GameData的核心骨架已经完成可以替代内置GameData使用。第三步编写使用自定义GameData的State下面创建两个状态Main主流程运行全部系统和Paused暂停只运行核心系统。# extern crate amethyst; # use amethyst::ecs::{Dispatcher, World}; # use amethyst::input::{is_close_requested, is_key_down, VirtualKeyCode}; # use amethyst::prelude::*; # struct Main; struct Paused; impla, b StateCustomGameDataa, b, StateEvent for Paused { fn on_start(mut self, data: StateDataCustomGameData) { create_paused_ui(data.world); } fn handle_event( mut self, data: StateDataCustomGameData, event: StateEvent, ) - TransCustomGameDataa, b, StateEvent { if let StateEvent::Window(event) event { if is_close_requested(event) || is_key_down(event, VirtualKeyCode::Escape) { Trans::Quit } else if is_key_down(event, VirtualKeyCode::Space) { delete_paused_ui(data.world); Trans::Pop } else { Trans::None } } else { Trans::None } } fn update( mut self, data: StateDataCustomGameData, ) - TransCustomGameDataa, b, StateEvent { data.data.update(data.world, false); // false to say we should not dispatch running Trans::None } } impla, b StateCustomGameDataa, b, StateEvent for Main { fn on_start(mut self, data: StateDataCustomGameData) { initialize(data.world); } fn handle_event( mut self, _: StateDataCustomGameData, event: StateEvent, ) - TransCustomGameDataa, b, StateEvent { if let StateEvent::Window(event) event { if is_close_requested(event) || is_key_down(event, VirtualKeyCode::Escape) { Trans::Quit } else if is_key_down(event, VirtualKeyCode::Space) { Trans::Push(Box::new(Paused)) } else { Trans::None } } else { Trans::None } } fn update( mut self, data: StateDataCustomGameData, ) - TransCustomGameDataa, b, StateEvent { data.data.update(data.world, true); // true to say we should dispatch running Trans::None } }这段代码揭示了核心机制State::update中调用data.data.update(data.world, running)running参数直接决定游戏逻辑派发器是否执行。Main传入truePaused传入false。事件处理上使用StateEvent::Window包装的窗口事件is_close_requested(event)窗口关闭按钮被点击 →Trans::Quitis_key_down(event, VirtualKeyCode::Escape)按 Esc → 退出Main中按空格 →Trans::Push(Box::new(Paused))压入暂停状态Main被自动暂停见 src/state.rs 的push实现会调用旧状态的on_pausePaused中按空格 → 删除暂停 UI 并Trans::Pop弹出恢复Mainpop会调用旧状态on_stop与下一个状态on_resume见 src/state.rs。第四步用CustomDispatcherBuilder构建Application最后一步是把构建器接入Application# extern crate amethyst; # use amethyst::{ # core::{transform::TransformBundle, SystemBundle}, # ecs::{Dispatcher, DispatcherBuilder, World}, # input::InputBundle, # prelude::*, # renderer::{ # plugins::{RenderFlat2D, RenderToWindow}, # types::DefaultBackend, # RenderingBundle, # }, # ui::{RenderUi, UiBundle}, # utils::application_root_dir, # DataDispose, DataInit, Error, # }; # use amethyst_rendy::bundle::Target::Main; # # fn main() - amethyst::Result() { let mut app_builder Application::build(assets_directory, Main)?; let game_data CustomDispatcherBuilder::default() .with_running(ExampleSystem, example_system, []) .with_base_bundle( mut app_builder.world, RenderingBundle::DefaultBackend::new() // The RenderToWindow plugin provides all the scaffolding for opening a window and // drawing on it .with_plugin( RenderToWindow::from_config_path(display_config_path) .with_clear([0.34, 0.36, 0.52, 1.0]), ) .with_plugin(RenderFlat2D::default()) .with_plugin(RenderUi::default()), )? .with_base_bundle(mut app_builder.world, TransformBundle::new())? .with_base_bundle(mut app_builder.world, UiBundle::new())? .with_base_bundle( mut app_builder.world, InputBundle::new().with_bindings_from_file(key_bindings_path)?, )?; let mut game app_builder.build(game_data)?; //game.run(); # }组装要点Application::build(assets_directory, Main)?返回的ApplicationBuilder暴露了world字段见 src/app.rs供with_base_bundle注册实体/资源with_running(ExampleSystem, example_system, [])把游戏逻辑系统放进 running 派发器渲染、Transform、UI、输入等Bundle全部通过with_base_bundle进入 core 派发器调用链以with_running开头、with_base_bundle结尾的顺序没有硬性要求但注意with_base_bundle返回ResultSelf, Error需要用?传播错误最后app_builder.build(game_data)?要求game_data实现DataInit构建出CoreApplication后调用run()启动主循环主循环中每个 tick 会依次驱动状态的update、handle_event等回调见 src/app.rs。仓库中的完整可运行示例本指南的完整可运行版本位于 examples/custom_game_dataexamples/custom_game_data/main.rs定义了Loading/Main/Paused三个状态。Loading异步加载场景与 UI 预制体加载完成后Trans::Switch进入MainMain按下空格Trans::Push(Paused)Paused按下空格删除暂停 UI 后Trans::Pop返回Mainexamples/custom_game_data/game_data.rs示例对本书的方案做了一点升级——把延迟执行的构建器操作DispatcherOperationtrait收集到VecBoxdyn DispatcherOperation中从而让with_base_bundle不需要提前拿到mut World还额外提供了with_base方法注册单个SystemDesc。示例中main的组装main.rs与上文完全同构with_base(PrefabLoaderSystemDesc, ...)与with_running(ExampleSystem, ...)注册自定义系统TransformBundle、FpsCounterBundle、InputBundle、UiBundle、RenderingBundle依次装入 core 派发器最后Application::build(assets_dir, Loading::default())?.build(game_data)?启动。运行方式在examples/custom_game_data目录下执行cargo run --features sdl_controller之类的特性组合即可具体特性依赖见该示例的 Cargo.toml。示例默认读取assets/与config/display.ron可通过application_root_dir定位按空格进入/退出暂停按 Esc 退出游戏。三种控制系统执行的方案对比自定义GameData只是控制系统的三种官方方案之一见 book/src/controlling_system_execution.md方案适用场景实现要点自定义GameData需要按State整体开关一组系统且要跨状态携带非Send Sync数据实现DataInitDataDispose持有多个DispatcherState::update决定派发哪些State 专属Dispatcher某组系统只在该State内运行在State字段中持有OptionDispatcheron_start中构建并setupupdate中派发见 book/src/controlling_system_execution/state-specific_dispatcher.md可暂停系统Pausable System精细到单个系统、且跨多个派发器/状态复用给系统包裹pausable(value)包装器只有Resource值匹配时才执行见 book/src/controlling_system_execution/pausable_systems.md其中pausable的实现位于 amethyst_core/src/system_ext.rs它返回一个PausableS, V包装器在run_unsafe中先以ReadV读取控制资源当资源值与预设值不相等时直接返回、不执行内部系统见 system_ext.rs。该模块附带了完整的单元测试验证了资源匹配时执行、不匹配时暂停的行为见 system_ext.rs。从源码注释还可以了解到一个值得注意的约束暂停期间run不会被执行因此不要用可暂停系统去读取EventChannel否则 reader 侧消息会无限累积造成内存泄漏见 system_ext.rs。小结与建议自定义GameData的本质是把调度决策权从引擎内部上移到你的State代码中。整个方案的完整流程可概括为四步定义持有多个OptionDispatcher的数据结构为其实现DataInit/DataDispose通过配套的构建器完成线程池复用与setup在State::update中调用data.data.update(data.world, running)按状态控制派发用自定义构建器替换默认的DispatcherBuilder传入ApplicationBuilder::build。在此基础上可以继续演进把构建操作抽象成可延迟执行的DispatcherOperation如 examples/custom_game_data/game_data.rs 所示让构建器接口更接近引擎原生的DispatcherBuilder或者组合使用 State 专属Dispatcher与可暂停系统实现更细粒度的调度控制。现在去构建你的游戏吧赞分享【免费下载链接】amethystData-oriented and>项目地址https://gitcode.com/gh_mirrors/ame/amethyst点击查看免费下载相关推荐CounterFab在电商应用中的实战应用购物车计数最佳实践CounterFab在电商应用中的实战应用购物车计数最佳实践 CounterFab是一款专为Android应用设计的浮动操作按钮FAB子类它能够在按钮右Amethyst 自定义 GameData 与多 Dispatcher 实战custom_game_data 示例深度解析Amethyst 自定义 GameData 与多 Dispatcher 实战custom_game_data 示例深度解析 导读 在 Amethyst 游戏引NtyTcp安全机制分析用户态协议栈的安全防护策略NtyTcp安全机制分析用户态协议栈的安全防护策略 NtyTcp作为一款单线程用户态TCP/IP协议栈采用epoll实现提供了服务器案例和并发测试案例。在网络通信上一篇如何高效部署与使用ESET-KeyGen自动化密钥生成系统下一篇视频号下载终极指南如何快速保存微信视频号、抖音、小红书等平台资源创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
装了这 6 个 CLI,Claude Code 配合 TaoToken 全自动建站上线 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 21:39:09
Cursor 2.0 重磅更新!Multi-Agents 与 Composer 编程体验要起飞了!!! /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:07:00
3步搞定winxp个人网站免费建设,新手入门避坑指南 3步搞定winxp个人网站免费建设,新手入门避坑指南 还在为那些千篇一律、配色刺眼的模板网站头疼吗?说实话,那种一键生成的站点,除了“能打开”,几乎没有任何灵魂,甚至显得廉价。很多刚接触建站的新手入门者,往往陷入一个误区:以为免费就等于随便… · 2026/9/27 22:07:00
移动端适配难点:ClaudeCode+Figma-MCP 前端代码 1:1 还原 UI 的对策 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:07:00
Android Cursor 类完全指南:从概念到实战配置 TaoToken 统一 API 通道 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:07:00
IDEA debug 断点失效解决办法:TaoToken 统一 Key 通道下的排查清单 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:07:00
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01