我正在尝试在Rust中编写一个回合制游戏,而且我正在用语言碰壁(除非我不理解某些东西 - 我是语言新手).基本上,我想改变游戏中每个州有不同行为的状态.例如,我有类似的东西:
struct Game {
state: [ Some GameState implementer ],
}
impl Game {
fn handle(&mut self, event: Event) {
let new_state = self.handle(event);
self.state = new_state;
}
}
struct ChooseAttackerPhase {
// ...
}
struct ResolveAttacks {
// ...
}
impl ResolveAttacks {
fn resolve(&self) {
// does some stuff
}
}
trait GameState {
fn handle(&self, event: Event) -> [ A New GateState implementer ]
}
impl GameState for ChooseAttackerPhase {
fn handle(&self, event: Event) -> [ A New GameState implementer ] {
// ...
}
}
impl GameState for ResolveAttacks {
fn handle(&self, event: Event) -> [ A New GameState implementer ] {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
这是我最初的计划.我想handle成为一个返回新GameState实例的纯函数.但据我所知,Rust目前无法实现.所以我尝试使用enums元组,每个元组都有各自的处理程序,最终成为一个死胡同,因为我必须匹配每个状态.
无论如何,代码不是来自我的原始项目.这只是一个例子.我的问题是:在Rust中有这样的模式,我错过了吗?我希望能够在每个状态中分离我需要做的事情的逻辑,这些逻辑对于每个状态是唯一的,并且避免编写冗长的模式匹配语句.
如果我需要更多地澄清我的问题,请告诉我.
She*_*ter 14
有限状态机(FSM)可以使用两个枚举直接建模,一个表示所有状态,另一个表示所有转换:
#[derive(Debug)]
enum Event {
Coin,
Push,
}
#[derive(Debug)]
enum Turnstyle {
Locked,
Unlocked,
}
impl Turnstyle {
fn next(self, event: Event) -> Turnstyle {
use Event::*;
use Turnstyle::*;
match self {
Locked => {
match event {
Coin => Unlocked,
_ => self,
}
},
Unlocked => {
match event {
Push => Locked,
_ => self,
}
}
}
}
}
fn main() {
let t = Turnstyle::Locked;
let t = t.next(Event::Push);
println!("{:?}", t);
let t = t.next(Event::Coin);
println!("{:?}", t);
let t = t.next(Event::Coin);
println!("{:?}", t);
let t = t.next(Event::Push);
println!("{:?}", t);
}
Run Code Online (Sandbox Code Playgroud)
最大的缺点是一种方法最终会变得非常混乱所有状态/转换对.你有时可以match通过匹配对来消除一点:
match (self, event) {
(Locked, Coin) => Unlocked,
(Unlocked, Push) => Locked,
(prev, _) => prev,
}
Run Code Online (Sandbox Code Playgroud)
避免编写冗长的模式匹配语句.
每个匹配臂都可以是您要为您执行的每个独特操作调用的功能.在上面,Unlocked可以用一个叫做的函数代替unlocked它做任何它需要的东西.
使用枚举[...]最终成为一个死胡同,因为我必须匹配每个州.
请注意,您可以使用它_来匹配任何模式.
枚举的一个缺点是它不能让其他人加入它.也许你想为你的游戏建立一个可扩展的系统,mods可以添加新的概念.在这种情况下,您可以使用特征:
#[derive(Debug)]
enum Event {
Damage,
Healing,
Poison,
Esuna,
}
#[derive(Debug)]
struct Player {
state: Box<PlayerState>,
}
impl Player {
fn handle(&mut self, event: Event) {
let new_state = self.state.handle(event);
self.state = new_state;
}
}
trait PlayerState: std::fmt::Debug {
fn handle(&self, event: Event) -> Box<PlayerState>;
}
#[derive(Debug)]
struct Healthy;
#[derive(Debug)]
struct Poisoned;
impl PlayerState for Healthy {
fn handle(&self, event: Event) -> Box<PlayerState> {
match event {
Event::Poison => Box::new(Poisoned),
_ => Box::new(Healthy),
}
}
}
impl PlayerState for Poisoned {
fn handle(&self, event: Event) -> Box<PlayerState> {
match event {
Event::Esuna => Box::new(Healthy),
_ => Box::new(Poisoned),
}
}
}
fn main() {
let mut player = Player { state: Box::new(Healthy) };
println!("{:?}", player);
player.handle(Event::Damage);
println!("{:?}", player);
player.handle(Event::Healing);
println!("{:?}", player);
player.handle(Event::Poison);
println!("{:?}", player);
player.handle(Event::Esuna);
println!("{:?}", player);
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以实现您想要的任何状态.
我想
handle成为一个返回新GameState实例的纯函数.
您无法返回GameState实例,因为编译器需要知道每个值需要多少空间.如果你可以返回一个结构,它在一次调用中占用4个字节,或者从另一个调用中占用8个字节,编译器就不会知道你实际需要多少空间.
您必须做出的权衡是始终返回一个新分配的特征对象.需要进行此分配,以便为PlayerState可能出现的每种可能变体提供同质大小.
在将来,可能会支持说函数返回特征(fn things() -> impl Iterator例如).这基本上是隐藏的事实,有是与已知大小的程序员不/不能写一个值.如果我理解正确的话,它会不会在这种情况下帮助,因为规模的不确定性不会在编译时确定.
在极少数情况下,您的状态没有任何实际状态,您可以创建每个状态的共享,不可变的全局实例:
trait PlayerState: std::fmt::Debug {
fn handle(&self, event: Event) -> &'static PlayerState;
}
static HEALTHY: Healthy = Healthy;
static POISONED: Poisoned = Poisoned;
impl PlayerState for Healthy {
fn handle(&self, event: Event) -> &'static PlayerState {
match event {
Event::Poison => &POISONED,
_ => &HEALTHY,
}
}
}
impl PlayerState for Poisoned {
fn handle(&self, event: Event) -> &'static PlayerState {
match event {
Event::Esuna => &HEALTHY,
_ => &POISONED,
}
}
}
Run Code Online (Sandbox Code Playgroud)
这将避免分配的开销(无论可能是什么).我不会尝试这个,直到你知道没有状态,并且在分配中花费了很多时间.
| 归档时间: |
|
| 查看次数: |
1718 次 |
| 最近记录: |