Skip to content

学习Rust的设计模式

实现观察者模式,且不使用dyn Trait的实现

一开始让AI来写,但是全是使用dyn Trait 以及 Box+Any等方法的实现。

本次是探讨是否可以用无虚表开销等方式的实现。

写这个灵感来自于看了·小彭大典·里面的CPP教程的实现 https://142857.red/book/design_gamedev/

rust
use std::collections::HashMap;

// 消息类型枚举
#[derive(Debug, Clone)]
pub enum Message {
    Move { velocity_change: Vec3 },
    Other { data: String },
}

// 简单的向量结构
#[derive(Debug, Clone, Copy, Default)]
pub struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}

impl Vec3 {
    fn new(x: f32, y: f32, z: f32) -> Self {
        Self { x, y, z }
    }

    fn add(&mut self, other: Vec3) {
        self.x += other.x;
        self.y += other.y;
        self.z += other.z;
    }
}

pub trait IsComponent {
    fn update(&mut self, _go: &GameObject) {}

    fn handle_message(&mut self, msg: Message);

    fn subscribe_messages<'a: 'b, 'b>(&'a mut self, _go: &mut GameObject<'b>) {}
}

#[derive(Debug, Clone, Copy)]
struct Movable {
    position: Vec3,
    velocity: Vec3,
}

impl IsComponent for Movable {
    fn handle_message(&mut self, msg: Message) {
        match msg {
            Message::Move { velocity_change } => {
                // self.velocity += velocity_change;
                println!("收到消息:{:#?}", velocity_change);
                self.velocity.add(velocity_change);
            }
            _ => {
                println!("not move msg")
            }
        }
    }
}

// 组件枚举
#[derive(Debug, Clone, Copy)]
enum Component {
    // Movable { position: Vec3, velocity: Vec3 },
    Movable(Movable),
}
impl IsComponent for Component {
    fn update(&mut self, go: &GameObject) {
        match self {
            Component::Movable(movable) => {
                movable.update(go);
            }
        }
    }

    fn handle_message(&mut self, msg: Message) {
        match self {
            Component::Movable(movable) => {
                movable.handle_message(msg);
            }
        }
    }

    fn subscribe_messages<'a: 'b, 'b>(&'a mut self, go: &mut GameObject<'b>) {
        go.subscribe(EventType::MoveMessage, self);
    }
}

// 事件类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum EventType {
    MoveMessage,
    // OtherMessage,
}

pub struct GameObject<'a> {
    // components: Vec<&'a Component>,
    subscribers: HashMap<EventType, Vec<&'a mut Component>>, // 存储组件在components中的索引
}

impl<'a> GameObject<'a> {
    fn new() -> Self {
        Self {
            // components: Vec::new(),
            subscribers: HashMap::new(),
        }
    }

    fn subscribe<'b: 'a>(&mut self, event_type: EventType, component: &'b mut Component) {
        self.subscribers
            .entry(event_type)
            .or_insert_with(Vec::new)
            .push(component);
    }

    // fn add(&mut self, component: &'a Component) {
    //     self.components.push(component);
    // }

    fn send(&mut self, event_type: EventType, message: Message) {
        if let Some(subscribers) = self.subscribers.get_mut(&event_type) {
            // for cp in subscribers {
            //     cp.handle_message(message.clone());
            // }
            let _ = subscribers
                .iter_mut()
                .for_each(|cp| cp.handle_message(message.clone()));
        }
    }

    fn update(&mut self) {
        println!("GameObject updated");
    }
}

struct PlayerController;

impl PlayerController {
    fn update(&mut self, go: &mut GameObject) {
        let message = Message::Move {
            velocity_change: Vec3::new(1.0, 2.0, 3.0),
        };
        go.send(EventType::MoveMessage, message);
    }
}

接下来就是main函数的效果

rust
    let mut game_object = GameObject::new();

    let mut movable_component = Component::Movable(Movable {
        position: Vec3 {
            x: 0.0,
            y: 0.0,
            z: 0.0,
        },
        velocity: Vec3 {
            x: 1.0,
            y: 1.0,
            z: 1.0,
        },
    });

    movable_component.subscribe_messages(&mut game_object);

    let mut palyer = PlayerController;
    palyer.update(&mut game_object);

打印:

收到消息:Vec3 {
    x: 1.0,
    y: 2.0,
    z: 3.0,
}