如何在Rust的Specs ECS crate中实现实体间交互并跨系统传递数据
用Specs实现实体交互及系统间传值的惯用方案
一、实体两两交互的遍历实现
你可以先通过Join接口收集所有参与交互的实体及其关联组件,再通过双层循环完成两两校验:
- 首先通过
join拉取所有符合条件的实体信息,存入临时向量:// 以碰撞检测为例,拉取所有带Position组件的实体 let entities: Vec<(Entity, &Position)> = (&entities, &positions).join().collect(); - 双层循环遍历避免重复比对(i从0开始,j从i+1开始,无需重复计算A-B和B-A两组相同交互):
for i in 0..entities.len() { let (entity_a, pos_a) = entities[i]; for j in (i + 1)..entities.len() { let (entity_b, pos_b) = entities[j]; // 执行交互判定:比如检测坐标是否重叠、是否符合交易匹配规则 if pos_a.distance(pos_b) < COLLISION_THRESHOLD { // 存储交互结果 } } }
二、交互结果向下游系统传递的两种常用方案
方案1:通过临时组件传递
适合单实体关联的结果传递,比如你提到的经济模拟场景:
- 先定义临时结果组件:
#[derive(Component, Debug)] #[storage(VecStorage)] pub struct PendingPurchase { pub seller_entity: Entity, pub goods_id: u32, pub price: u32, } InventoryCheck系统完成交易匹配后,给有购买意向的买方实体插入对应的PendingPurchase组件。PurchasingSystem直接通过join拉取同时带有Buyer、Inventory、PendingPurchase组件的实体,完成资金扣减、库存转移逻辑后,删除对应实体的PendingPurchase组件即可。
方案2:通过Resource全局资源传递
适合批量交互结果的传递,比如碰撞检测的批量事件:
- 定义全局资源存储交互结果:
#[derive(Resource, Default)] pub struct CollisionEvents { pub list: Vec<(Entity, Entity, CollisionInfo)>, } - 碰撞检测系统完成两两判定后,将所有碰撞对写入
CollisionEvents资源的list中。 - 下游的碰撞处理系统读取
CollisionEvents资源,处理完所有碰撞逻辑后,清空list列表等待下一帧写入即可。
两种方案可以结合使用,你可以根据自己的业务场景选择更合适的传递方式,不需要额外做复杂度优化的情况下,以上写法完全符合Specs的惯用规范。
内容的提问来源于stack exchange,提问作者finnkauski
相关产品推荐
相关产品推荐

