读《游戏设计模式》第二部分

本文深入探讨了观察者、单例和状态模式等设计模式在软件架构中的应用,特别是在Unity游戏开发中的实现。观察者模式促进了组件间的解耦,单例模式确保了资源的有效管理,而状态模式则清晰地定义了对象的状态转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

书籍在线阅读链接:https://gpp.tkchu.me/design-patterns-revisited.html

书籍设计模式的unity实现的链接:https://zhuanlan.zhihu.com/p/39707078 很好的文章,简单的实现了该书的大部分设计模式

 

1,观察者模式
随便打开电脑中的一个应用,很有可能它就使用了MVC架构, 而究其根本,是因为观察者模式。
观察者模式应用广泛,Java甚至将其放到了核心库之中(java.util.Observer),而C#直接将其嵌入了语法(event关键字)。 难以置信的是,如此直观的东西是无数程序和应用框架交流的主心骨。

观察者模式是同步的。 被观察者直接调用了观察者,这意味着直到所有观察者的通知方法返回后, 被观察者才会继续自己的工作。观察者会阻塞被观察者的运行。

如果观察者列表存在触发的顺序关系,这意味着这两个观察者有一些微妙的耦合,最终会害了你。

但是如果耦合发生在观察者列表中,想要知道哪个观察者被通知到了,唯一的办法是看看哪个观察者在列表中,而且处于运行中。 你得理清它的命令式,动态行为而非理清程序的静态交流结构。

如果为了理解程序的一部分,两个交流的模块都需要考虑, 那就不要使用观察者模式,使用其他更加显式的东西。

观察者模式是一个让这些不相关的代码块互相交流(比如:成就系统,任何完全不相干的事物都可以达成成就),而不必打包成更大的块的好方法。 这在专注于一个特性或层面的单一代码块内不会太有用。

如果设计今日的观察者模式,我会让它基于函数,而不是基于类。 哪怕是在C++中,我倾向于让你注册一个成员函数指针作为观察者,而不是Observer接口的实例。实现一整套接口只是为了接受一个通知不再符合今日的美学了。

 

个人感觉是:只要达到类似于观察者的通信形式,无论哪种写法都是可以的。

实现 https://blog.youkuaiyun.com/tran119/article/details/81166636 unity 消息发送机制

 

2,单例模式
就一句话:只提供一个实例。有2种情况:
1,做为其他类的实例的管理类。一般一个类比如:怪物类,会创建多个实例,这个时候使用一个单例类的一个实例来管理所有的怪物,完成创建,保存这些实例。常见的情况是,一个单例类里面有多个不同的单例类,多个不同的单例类各自管理某种类型的多个实例对象,而这些实例对象各自有自己的属性行为,自行管理自身,或者依靠公用的系统来管理行为,比如:计时器,事件管理等。所以当我们看到某个类是单例时,就应该想到该类是用来控制某一类型的多个实例。
2,异步操作,比如:一个实例删除了某个文件,但另一个实例又去取该文件。

3,状态模式
使用一个枚举列出所有的状态(比如:idle,jump,Attack,jumpAttack,down),使用另一个枚举列出所有的行为(比如:按下空格键,J键)。一个人物一个时间只能存在一种状态,从一种状态只能变成另一种状态,比如:人在jump时,他的下一个状态只能是down或者jumpAttck,不会同时down和jumpAttack。虽然看起来人物在跳跃攻击时也在下落,但是其实是跳跃攻击就包含了下落。
使用2个switch,一个处理状态切换,一个处理行为,并且同时执行相应的函数。这样做的好处是相互之间没有任何关系。如下:
假如按下J键和K键都是攻击
public void OnChangeState()
{
    //状态转换
    switch (当前行为)
    {
        case 按下空格键:
            当前状态 = jump;
            break;
        case 按下J键:
        case 按下K键:
            if(当前状态 = jump) 
                当前状态 = jumpAttack;
            else
                当前状态 = Attack;
            break;
        default:
            break;
    }
    //状态行为
    switch (当前状态)
    {
        case jump:
            执行jump相关函数
            break;
        case jumpAttack:
            执行jumpAttack相关函数
            break;
        default:
            break;
    }
 }

4,命令模式
个人感觉,不看到真正使用过得代码,根本不能理解为什么是这样的,尽管作者的例子都能看懂。
将函数作为数据,用来传递,存储,重复使用。
--inputHandler维护了所有继承了Command的子类的对象,通过handleInput拿到当前需要的子类对象,然后传递一个当前需要执行当前行为的人物作为参数。
Command* command = inputHandler.handleInput();
if (command)
{
  command->execute(actor);
}

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值