昨天我们实现了交互,但是没有实现交互的具体逻辑
坏蛋Dan:rust基础学习--基于Bevy实现扫雷小游戏day5
今天我们继续
我们来拆分实现点击某个瓦片然后触发一系列其它瓦片的逻辑:
我们先来处理第一点,这个比较简单。
我们之前留了一个mod
没有动,那就是Uncover
,它将会被用来实现第一块逻辑。
不过在这之前,我们需要改一下我们之前的代码,现在我们把所有的状态都暴露出来了,我们需要隐藏它们,等到点击的时候再触发。
那要怎么覆盖呢?文档里搞了个很暴力的方案,用灰色的块盖住原来瓦片的位置,点击的时候再去掉。
回到我们的board.rs
中
这里没啥好说的,cover_tiles
就是用来覆盖原瓦片的,key
是瓦片的坐标,value
就是这个用来覆盖瓦片的瓦片。
safe_square_at
是我们之前写的方法,返回这个瓦片的周围瓦片的坐标。
之前忘了讲了,这个方法它里面将坐标move
了,一般情况下是会报错的,因为这里所有权已经跑到迭代的第一个闭包里了,然而我们给这个Coodinates
的struct
实现了Clone
和Copy
两个trait
,所以这里move
的时候会尝试Copy
。如果你移除这俩trait
的话就会报错。
另外记住这个adjacent_coverd_tiles
方法,它对于我们需要实现的逻辑二来说是非常重要的。
然后我们回到lib.rs
的spawn_lines
方法中,我们在这个方法里创建瓦片,那么瓦片的覆盖瓦片自然也在这里生成。
改动的点有些多,实际上只是copy
一份原瓦片,然后覆盖在它上面。
不过注意这里用的是cmd.with_children
,所以这个瓦片的cover
正常应该是原瓦片的child
。
现在我们重新执行下cargo run --features debug
另外你的窗口里的扫雷块应该又变回了灰色
ok
,那么覆盖瓦片就完成了,接下来我们来接入昨天写的pressed
监听,让它点击的时候去掉覆盖的瓦片。
我们来board_plugin/src
文件夹下创建events.rs
文件
我们在这里实现俩块代码的衔接,数据需要有一个中间变量作为载体,所以这里就有了TileTriggerEvent
。
然后我们导入到input.rs
中,别忘了先导入到lib.rs
中。
呃,前面好像没说过EventReader
[2]和EventWriter
[3] ,其实也没啥好说的,一个就是读这个类型的事件,也就是监听,另一个自然就是发送数据。
现在我们重新运行之后点击瓦片就会发送我们的这个载体给监听这个事件的对象。
注意,这个tileTriggerEvent
既是数据载体,又是一个event
。由于rust
特有的struct tuple
,这俩结合起来就有些抽象了。
接下来就该实现回调的具体内容了。
和之前一样,具体逻辑自然也是放到systems
里
我们在systems
下创建一个uncover.rs
文件。
这里是遍历事件,然后如果回调存在,那就拿出它里面的参数也就是coordinates
坐标,然后通过tile_to_uncover
这个我们在board.rs
中写的方法获取hashmap
中对应的实体。拿到实体后通过commands.entity
[4] 获取对应的实体,然后插入Uncover
这个components
,表示它将被揭开。
注意这里的Uncover
是components/uncover.rs
,是组件。
还需要注意这个*entity
实际上是返回这个实体的id
,然后commands
通过id
找到这个实体。
相信到这里你已经有些被绕晕了,不过先不要急,这里还有一部没有接上,接上后我们再画图分析执行流程。
另外别忘了从mod.rs
中导出。
Query
[5]<(Entity, &Parent), With
[6]>
:查询是Uncover
的实体以及它的parent
。Query<(&Coordinates, Option<&Bomb>, Option<&BombNeighbor>)>
:查询所有Coordinates
组件以及Bomb或者BombNeighbor
的组件如何通过query
获取数据具体可以看:https://bevy-cheatbook.github.io/programming/queries.html
这里做的事其实也挺好理解
uncover
的原瓦片,然后删除它们的覆盖瓦片。注意children
里迭代出来的entity
是覆盖瓦片,而不是原瓦片。parent
是tile
,也就是tile_cover
的parent
, get
方法返回这个实体自身。而parents
则是所有带有坐标组件或者炸弹(邻居)组件的实体mark
,但如果是炸弹邻居,就需要对这个炸弹邻居周围的坐标实体进行插入Uncover
组件。那么到这里就万事具备了。
最后就是将这些个system
和event
都注册到app
里。
回到src/lib.rs
中的build
方法
现在我们重新跑一次cargo run
现在我们的核心逻辑也处理完毕了,但你可能已经被绕晕了,所以这里会说一下整体的执行流程以及核心原理。
其实核心原理很简单
uncover
(通过插入Uncover
组件),然后监听事件中发现了这个瓦片是uncover
,于是将它的覆盖瓦片实体移除。Uncover
。Uncover
的(通过Query
),如果有就执行上面的流程,直到没有被标记为Uncover
的了。这种实现方式比起递归的方式(我之前用js
写的就是递归处理)来说性能强非常多。
这里直接以图的方式展示
图可能有些小。。。
这里就不贴修改的代码了,我把代码放到GitHub上了,可以直接拉
今天这块内容是这个游戏的核心,所以画了上面这张图。
到这里其实已经差不多了,后面的内容就是完善和部署了。
编辑于 2023-02-13 23:24・IP 属地广东