我正在尝试编写一个简单的游戏引擎,但是由于严格的借用规则,我在此处无法使用的常规模式无法正常工作。
有一个World
结构,它拥有Object
特征对象的集合。我们可以将它们视为游戏引擎中移动的物理对象。 World
负责在每个游戏滴答声中分别调用update
和draw
。World
结构还拥有Event
特征对象的集合。这些事件中的每一个都封装了一段任意的代码,这些代码在游戏滴答声期间会修改objects
。
理想情况下,Event
特性对象具有单个方法do_event
,该方法不接受World
在每个游戏滴答期间都可以调用的参数。问题是特定的Event
需要对其修改的对象具有可变的引用,但是这些对象归World
拥有。当它为每个事件调用World
时,我们可以让do_event
将可变引用传递给必要的对象,但是它如何知道将哪些对象传递给每个事件对象呢?
如果这是C++或Python,则在构造特定的Event
时,我会将事件的目标传递给构造函数,在该构造函数中保存该事件,然后在do_event
期间使用它。显然,这在Rust中不起作用,因为目标都由World
拥有,因此我们无法在其他地方存储可变引用。
我可以添加一些ID系统和一个查找表,以便World
知道将哪些对象传递给Event
,但这很麻烦且代价高昂。我怀疑我解决此类问题的全部方式都需要改变,但是,我很沮丧。
我的代码的草图:
trait Object {
fn update(&mut self);
fn draw(&self);
}
trait Event {
fn do_event(&mut self);
}
struct World {
objects: Vec<Box<dyn Object + 'a>>,
events: Vec<Box<dyn Event + 'a>>,
}
impl World {
fn update(&mut self) {
for obj in self.objects.iter_mut() {
obj.update();
}
for evt in self.events.iter_mut() {
evt.do_event();
}
}
fn draw(&mut self) { /*...*/ }
}
struct SomeEvent<'a, T: Object> {
target: &'a mut T,
}
impl<'a, T: Object> Event for SomeEvent<'a, T> {
fn update(&self) {
self.target.do_shrink(); // need mutable reference here
}
}
我知道
RefCell
允许多个对象获取可变引用。也许那是我应该走的方向。但是,基于what I learned from the Rust book,我感觉到RefCell
通过引入不安全的代码和循环引用破坏了Rust的一些主要思想。我想我只是想知道是否还有其他一些明显的设计模式更符合惯用的做事方式。
最佳答案
在阅读了问题注释并观看了kyren's RustConf 2018 talk(感谢trentcl)之后,我意识到面向我的游戏引擎的面向对象方法从根本上与Rust不兼容(或者至少相当困难)。
在一天左右的工作之后,我强烈建议您观看发布的视频,然后使用specs库(user2722968提到的ECS系统的实现)。
希望这对以后的人有所帮助。
关于rust - 遇到正确的设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59617276/