个人在游戏开发(cocos)使用的常见的设计模式(一)

386 阅读7分钟

在游戏开发中,设计模式起着至关重要的作用

设计模式为游戏开发带来了诸多好处。首先,它们提高了代码的可维护性。通过将复杂的逻辑分解为清晰的结构和模式,开发者能够更轻松地理解和修改代码。例如,当游戏中的某个功能需要更新或优化时,如果采用了合适的设计模式,就可以快速定位到相关代码并进行修改,而不会影响到其他不相关的部分。

以下介绍自己在游戏开发使用的3个常用的开发的模式

一、单例模式

单例模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。

class Singleton {
    private static instance: Singleton | null = null;

    private constructor() {}

    public static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
}

以上是这个TS的单例模式的例子

单例模式在游戏开发中的使用场景:

  1. 游戏功能管理器
    音频控制通常需要一个全局的管理器来统一处理声音的播放、暂停、音量调节等操作。使用单例模式创建音频管理器可以确保整个游戏只有一个音频控制实例。
  2. 资源加载器
    游戏中的资源(如图片、音频文件等)加载通常是一个耗时的操作。使用单例模式创建一个资源加载器,可以避免重复加载相同的资源,提高资源加载的效率。同时,在游戏的不同模块中都可以方便地获取已加载的资源。

单例模式就像是游戏世界中的一个 “超级管家”。想象一下,在一个庞大的游戏中,有很多地方都需要管理音频播放、读取配置文件或者记录游戏的全局状态。如果没有单例模式,可能会出现多个 “管家” 同时管理这些事情,导致混乱和资源浪费。而单例模式确保只有一个 “超级管家” 存在,无论你在游戏的哪个角落需要这些服务,都可以方便地找到这个唯一的 “管家”,让它来为你服务。这个 “管家” 只在第一次被需要的时候创建,之后就一直为整个游戏服务,既高效又方便。

二、观察者模式

**观察者模式,用于实现对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。
**

// 定义观察者接口
interface Observer {
    update(data: any): void;
}

// 被观察的主题类
class Subject {
    private observers: Observer[] = [];

    addObserver(observer: Observer): void {
        this.observers.push(observer);
    }

    removeObserver(observer: Observer): void {
        const index = this.observers.indexOf(observer);
        if (index!== -1) {
            this.observers.splice(index, 1);
        }
    }

    notifyObservers(data: any): void {
        for (const observer of this.observers) {
            observer.update(data);
        }
    }
}

// 具体的观察者类
class ConcreteObserver implements Observer {
    update(data: any): void {
        console.log(`Received data: ${data}`);
    }
}

以上是这个TS的观察者模式的例子

观察者模式在游戏开发中的使用场景

1.游戏中的事件系统

  • 当游戏中发生特定事件时,如玩家攻击、怪物死亡、任务完成等,不同的模块可能需要做出不同的反应。通过观察者模式,可以将这些模块注册为观察者,当事件发生时,作为被观察对象的事件系统通知所有相关的观察者进行相应的处理。
  • 例如,当玩家完成一个任务时,任务系统作为被观察对象通知界面显示模块更新任务进度条、通知成就系统检查是否解锁新成就、通知奖励系统发放奖励等。

2.角色属性变化通知

  • 游戏角色的属性(如生命值、魔法值、经验值等)发生变化时,多个模块可能需要做出响应。比如血条显示模块需要根据生命值的变化更新血条的显示,经验条显示模块需要根据经验值的变化更新经验条,战斗日志模块需要记录属性变化的信息。

  • 角色作为被观察对象,各个显示模块和日志模块作为观察者,当角色属性变化时,自动触发这些模块的更新。

观察者模式就像是一个消息中心。想象一下,在一个游戏世界里,有很多不同的角色和模块,它们都在关注着某些特定的事情。比如,一些模块在关注玩家的生命值变化,一些在关注怪物的出现,还有一些在关注任务的完成。这些被关注的事情就像是消息中心发布的不同消息。当某个事情发生时,比如玩家的生命值降低了,这个消息就会被发送到所有关注生命值变化的模块那里。这些模块就会根据这个消息做出相应的反应,比如更新血条显示、记录战斗日志等。就像一个广播电台在广播消息,而听众们根据听到的消息做出不同的行动。这样,各个模块之间不需要直接相互了解和调用,而是通过这个消息中心来进行间接的通信和协作,使得游戏的开发更加灵活和易于维护。

三、状态模式

状态模式允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。在游戏开发中,状态模式常用于管理游戏对象的不同状态以及状态之间的转换。

// 状态接口
interface State {
    handle(context: Context): void;
}

// 具体状态类 A
class ConcreteStateA implements State {
    handle(context: Context): void {
        console.log('当前处于状态 A');
        context.setState(new ConcreteStateB());
    }
}

// 具体状态类 B
class ConcreteStateB implements State {
    handle(context: Context): void {
        console.log('当前处于状态 B');
        context.setState(new ConcreteStateA());
    }
}

// 上下文类
class Context {
    private state: State;

    constructor() {
        this.state = new ConcreteStateA();
    }

    setState(state: State): void {
        this.state = state;
    }

    request(): void {
        this.state.handle(this);
    }
}

以上是这个TS的状态模式的例子

状态模式在游戏开发中的使用场景:

1.游戏角色状态管理

  • 在游戏中,角色可能有不同的状态,如行走、攻击、受伤、死亡等。每种状态下角色的行为和外观都不同。状态模式可以让角色根据当前状态做出相应的动作,而无需在一个庞大的条件判断中处理所有状态。
  • 例如,当角色处于攻击状态时,它会执行攻击动画和伤害计算逻辑;当处于受伤状态时,播放受伤动画并减少生命值。

2.游戏场景切换

  • 不同的游戏场景可能有不同的规则和行为。可以将每个场景视为一种状态,当场景切换时,游戏系统进入相应的状态,执行特定的逻辑。
  • 比如从战斗场景切换到探索场景时,游戏的音乐、敌人行为、玩家操作等都会发生变化,可以用状态模式来管理这些不同的场景状态。

状态模式就像一个会变身的 “游戏角色”。这个角色有多种不同的形态,比如行走形态、攻击形态、防御形态等。每种形态都有自己独特的行为和表现。当角色处于行走形态时,它会按照行走的方式移动;当切换到攻击形态时,它就会执行攻击动作。

在游戏代码中,我们把这些不同的形态看作是不同的状态类,而角色本身就是一个上下文对象。通过状态模式,我们可以轻松地管理角色在不同状态下的行为变化,而不需要在一个大的方法中写很多复杂的条件判断来确定角色应该做什么。这样使得代码更加清晰、易于理解和维护。就好像我们给这个角色准备了不同的 “服装”(状态),它可以根据情况随时换上不同的 “服装”,展现出不同的行为。