游戏开发核心架构指南

深入理解游戏开发中的核心架构模式:ECS、MVC、MVP、MVVM、OOP、DOP、FSM、HFSM、事件驱动、GOAP、Behavior Tree、Utility AI、Component、Observer、Ability System

目录

1. OOP(面向对象编程)

概述

OOP(Object-Oriented Programming)是最经典的游戏开发范式,以"对象"为核心,通过封装、继承、多态三大特性组织代码。

核心概念

类 Class

封装 Encapsulation

继承 Inheritance

多态 Polymorphism

数据隐藏

代码复用

动态分发

游戏中的典型应用

// 基类:游戏角色
class Character 
{
protected:
    std::string name;
    int hp;
    int maxHp;
    float speed;

public:
    Character(const std::string& n, int h, float s)
        : name(n), hp(h), maxHp(h), speed(s) {}

    virtual void Update(float deltaTime) 
    {
        // 基础更新逻辑
    }

    virtual void TakeDamage(int damage) 
    {
        hp -= damage;
        if (hp <= 0) Die();
    }

    virtual void Die() { /* 死亡逻辑 */ }
    virtual ~Character() = default;
};

// 派生类:玩家
class Player : public Character
{
private:
    int experience;
    int playerLevel;
public:
    Player(const std::string& name)
        : Character(name, 100, 5.0f), experience(0), playerLevel(1) {}

    void Update(float deltaTime) override 
    {
        HandleInput();
        Character::Update(deltaTime);
    }

    void GainExperience(int exp) 
    {
        experience += exp;
        if (experience >= playerLevel * 100) LevelUp();
    }

private:
    void HandleInput() { /* 键盘/手柄输入处理 */ }
    void LevelUp() {
        playerLevel++;
        maxHp += 20;
        hp = maxHp;
        speed += 0.5f;
    }
};

继承层次结构

Character

+string name

+int hp

+int maxHp

+float speed

+Update(float dt)

+TakeDamage(int dmg)

+Die()

Player

-int experience

-int playerLevel

+HandleInput()

+GainExperience(int)

Enemy

#AIState state

#float detectionRange

+UpdateAI(float dt)

Boss

+int phase

+int specialAttackCooldown

+PhaseTransition()

NPC

+string dialogue

+Interact()

现代游戏开发趋势:组合优于继承

传统OOP在游戏行业正在逐渐弱化深度继承,转而强化组合(Composition)

// ❌ 传统深度继承(已不推荐)
class Monster : public Character {};
class FireMonster : public Monster {};
class FireBossMonster : public FireMonster {};

// ✅ 推荐:OOP + Component(UE风格)
class ACharacter 
{
    UCameraComponent* Camera;
    UInventoryComponent* Inventory;
    UAbilitySystemComponent* Abilities;
    UEquipmentComponent* Equipment;
};

核心原则:现代游戏开发更推荐组合优于继承(Composition over Inheritance)。用组件拼装对象功能,而非用继承链定义对象类型。

优缺点

对比维度 传统深层继承(Inheritance) 现代组件组合(Composition)
核心逻辑 "是"关系 (Is-A)
通过父类是谁来决定自己拥有什么功能。
"有"关系 (Has-A)
通过自己挂载了什么组件来决定拥有什么功能。
解耦程度 强耦合(白盒复用)
基类的修改会波及整条继承链,引发"脆弱基类"问题。
强解耦(黑盒复用)
组件之间相互独立,行为内聚,接口清晰。
动态扩展性 编译期决定(死板)
无法在游戏运行时动态剥离或增加某个基类行为。
运行期决定(极度灵活)
支持在运行时通过代码或编辑器动态 AddComponent / DestroyComponent
类体量控制 类爆炸 / 菱形继承
为了组合不同功能(如:既能飞又能游泳的怪物),不得不陷入多重继承灾难。
线性增长(N个功能 = N个组件)
新功能只需抽象成独立组件,直接挂载到宿主即可,无视继承关系。
生产力协同 程序包揽(不利于策划)
新类型的定制必须由程序编写派生类,关卡设计师无法自由组装。
数据驱动(策划友好)
程序写好组件,策划可以在编辑器中通过蓝图或预制体自由拼装、调整参数。
主要代价/缺点 1. 继承链深了以后,代码极难重构与维护。
2. 虚函数虚表查找(V-Table Click)带来额外的 CPU 间接寻址开销。
1. 组件间通信(如物理组件调用动画组件)需要特定的解耦设计。
2. 挂载过多组件时,生命周期管理与依赖检查有额外开销。

2. Component(组件模式)

概述

Component模式是OOP的演进产物,将对象功能拆分为独立组件,通过组合方式构建复杂对象。UE的ActorComponent、Unity的MonoBehaviour都是这一模式的典型实现。

架构对比

组件组合

Entity

MovementComponent

AnimationComponent

ShootingComponent

✅ 自由组合
N个功能 = N个组件

传统继承树

Entity

MoveableEntity

AnimatedEntity

ShootableEntity

MoveableAnimatedEntity

MoveableShootableEntity

❌ 功能组合越多
类爆炸

Component C++ 实现(UE风格)

class Actor 
{
private:
    std::vector<std::unique_ptr<Component>> components;

public:
    ~Actor() = default;

    template<typename T>
    T* AddComponent() 
    {
        static_assert(std::is_base_of_v<Component, T>, "T must derive from Component");
        auto comp = std::make_unique<T>(this);
        T* ptr = comp.get();
        comp->Initialize();
        components.push_back(std::move(comp));
        return ptr;
    }

    template<typename T>
    T* GetComponent() 
    {
        for (auto& c : components) 
        {
            if (auto* casted = dynamic_cast<T*>(c.get()))
                return casted;
        }
        return nullptr;
    }

    void Update(float dt) 
    {
        for (auto& c : components) c->Tick(dt);
    }
};

// 组件基类
class Component 
{
protected:
    Actor* owner;
public:
    Component(Actor* o) : owner(o) {}
    virtual ~Component() = default;
    virtual void Initialize() {}
    virtual void Tick(float dt) {}
};

// 具体组件
class MovementComponent : public Component 
{
    Vector3 velocity;
public:
    void Tick(float dt) override 
    {
        owner->SetPosition(owner->GetPosition() + velocity * dt);
    }
};

class HealthComponent : public Component 
{
    int hp = 100;
public:
    void TakeDamage(int dmg) { hp -= dmg; }
    bool IsAlive() const { return hp > 0; }
};

// 使用
Actor player;
player.AddComponent<MovementComponent>();
player.AddComponent<HealthComponent>();
player.AddComponent<InventoryComponent>();

优缺点

优点 缺点
灵活组合功能 组件间通信复杂
避免继承爆炸 运行时类型检查开销
易于复用和测试 组件依赖管理
UE/Unity原生支持 过度拆分组件的风险

3. DOP(数据导向编程)

概述

DOP(Data-Oriented Programming)数据导向编程,强调以数据为中心组织程序,关注数据在内存中的布局和访问模式,最大化CPU缓存利用率。

核心思想

✅ DOP 内存布局(SoA)

流式访问

流式访问

HP数组: 1,2,3,4,...

PosX数组: 1,2,3,4,...

PosY数组: 1,2,3,4,...

❌ OOP 内存布局(AoS)

跳跃访问

对象1: HP|PosX|PosY

对象2: HP|PosX|PosY

对象3: HP|PosX|PosY

注意SoA(Struct of Arrays)是DOP的一种实现方式,而非DOP本身。DOP的核心思想是按访问模式组织数据,SoA只是最常见的实现手段。

C++ 实现

// DOP 风格:数据与逻辑分离,SoA布局

struct GameData 
{
    std::vector<float> posX;
    std::vector<float> posY;
    std::vector<float> velocityX;
    std::vector<float> velocityY;
    std::vector<int> hp;
    std::vector<uint8_t> isActive;
};

void UpdatePositions(GameData& data, float dt)
{
    const size_t count = std::min({
        data.posX.size(), data.posY.size(),
        data.velocityX.size(), data.velocityY.size()
    });
    float* px = data.posX.data();
    float* py = data.posY.data();
    float* vx = data.velocityX.data();
    float* vy = data.velocityY.data();

    for (size_t i = 0; i < count; ++i) 
    {
        px[i] += vx[i] * dt;
        py[i] += vy[i] * dt;
    }
}

struct HotData {
    std::vector<float> posX;
    std::vector<float> posY;
    std::vector<int> hp;
    std::vector<uint8_t> active;
};

struct ColdData 
{
    std::vector<std::string> name;
    std::vector<float> respawnTimer;
};

性能对比

constexpr size_t ENTITY_COUNT = 1000000;

// AoS (OOP 风格) - Cache不友好
struct EntityAoS { float x, y, vx, vy; int hp; };
std::vector<EntityAoS> entitiesAoS(ENTITY_COUNT);

// SoA (DOP 风格) - Cache友好
std::unique_ptr<float[]> x(new float[ENTITY_COUNT]);
std::unique_ptr<float[]> y(new float[ENTITY_COUNT]);
std::unique_ptr<float[]> vx(new float[ENTITY_COUNT]);
std::unique_ptr<float[]> vy(new float[ENTITY_COUNT]);

void UpdateAoS(float dt) 
{
    for (auto& e : entitiesAoS) {
        e.x += e.vx * dt;
        e.y += e.vy * dt;
    }
}

void UpdateSoA(float dt) 
{
    for (size_t i = 0; i < ENTITY_COUNT; ++i)
    {
        x[i] += vx[i] * dt;
        y[i] += vy[i] * dt;
    }
}

优缺点

优点 缺点
极高的缓存效率 代码可读性较差
易于并行化(SIMD/多线程) 开发效率较低
性能可预测 难以建模复杂关系
适合大规模数据处理 调试困难

4. ECS(实体组件系统)

概述

ECS(Entity Component System)是游戏开发中广泛使用的架构模式,将数据(Component)与行为(System)彻底分离,实体(Entity)只是一个ID。

架构图

ECS 架构

Entity
实体(仅ID)

Position

Velocity

Sprite

MovementSystem

RenderSystem

Component
组件(纯数据)

System
系统(纯逻辑)

核心概念

// ============ 1. Entity(实体):仅为ID ============
struct Entity 
{
    uint32_t index;
    uint32_t generation;

    bool operator==(const Entity& other) const 
    {
        return index == other.index && generation == other.generation;
    }
};

// ============ 2. Component(组件):纯数据 ============
struct Position { float x, y; };
struct Velocity { float vx, vy; };
struct Sprite { std::string textureName; int w, h; };
struct Health { int current, max; };

// ============ 3. System(系统):纯逻辑 ============
class MovementSystem 
{
public:
    void Update(ArchetypeManager& am, float dt) 
    {
        am.ForEach<Position, Velocity>([dt](Position& pos, Velocity& vel) 
        {
            pos.x += vel.vx * dt;
            pos.y += vel.vy * dt;
        });
    }
};

现代 ECS(Archetype/Chunk模型)

Archetype 模型

Chunk(固定大小内存块)

查询: Position + Velocity

查询: Position + Sprite

Archetype A
Position + Velocity

Archetype B
Position + Sprite

Entity[] (Index+Gen)

Position[] (连续数组)

Velocity[] (连续数组)

MovementSystem

RenderSystem

ArchetypeManager 简化实现

class ArchetypeManager 
{
private:
    struct Archetype 
    {
        std::bitset<32> mask;
        std::vector<uint8_t> buffer;
        size_t entityCount;
    };
    std::vector<Archetype> archetypes;

    template<typename T>
    static constexpr size_t GetComponentId() { return 0; }

public:
    template<typename... Comps>
    void ForEach(std::function<void(Comps&...)> callback) 
    {
        std::bitset<32> requiredMask;
        (requiredMask.set(GetComponentId<Comps>()), ...);

        for (auto& arch : archetypes)
        {
            if ((arch.mask & requiredMask) != requiredMask) continue;

            size_t offset = 0;
            for (size_t i = 0; i < arch.entityCount; ++i)
            {
                size_t localOff = offset;
                callback(*reinterpret_cast<Comps*>(arch.buffer.data() + localOff)...);
                ((localOff += sizeof(Comps)), ...);
            }
        }
    }
};

对比:OOP vs ECS

维度 OOP ECS
实体定义 对象实例 数字ID + Generation
数据存储 分散在对象中 连续数组(SoA)
逻辑组织 类方法 独立System
缓存效率 极高
扩展方式 继承/组合 新增Component/System

优缺点

优点 缺点
数据与逻辑完全分离 学习曲线陡峭
天然缓存友好 小项目过度设计
易于扩展新功能 组件通信复杂
适合大型复杂游戏 调试困难
便于多线程并行 某些模式难以表达

5. Ability System(能力系统)

概述

Ability System(技能能力系统)是现代商业游戏(尤其是UE项目)中管理技能、Buff、状态效果的核心架构,以数据驱动组装替代传统硬编码技能逻辑。

UE的 Gameplay Ability System(GAS) 是其代表实现,广泛应用于MMO、ARPG、MOBA等品类。

架构图

Ability System 核心概念

拥有/管理

应用

触发

消耗/条件

修改属性

产生

AbilitySystemComponent
能力系统组件
负责: 注册/激活/应用

GameplayAbility
技能/能力
生命周期: 激活→执行→结束

GameplayEffect
效果/Buff/伤害
Instant/Duration/Infinite

GameplayCue
视觉/音效反馈
轻量级通知

GameplayTag
标签系统
分类/条件/限制

AttributeSet
属性集
HP/MP/ATK等

技能生命周期

GC AttributeSet GameplayEffect GameplayAbility AbilitySystemComponent 玩家输入 GC AttributeSet GameplayEffect GameplayAbility AbilitySystemComponent 玩家输入 激活技能 CanActivate(检查Tag/消耗) 扣资源/CD 播放前摇动画 应用伤害/效果 修改属性 触发反馈 结束生命周期

C++ 简化实现

// ============ AttributeSet(属性集) ============
struct AttributeSet 
{
    float maxHp = 100;
    float currentHp = 100;
    float maxMana = 50;
    float currentMana = 50;
    float attackPower = 10;
    float defensePower = 5;
    float moveSpeed = 300;
};

// ============ GameplayTag(标签) ============
struct GameplayTag 
{
    std::string tag;

    bool Matches(const std::string& pattern) const 
    {
        return tag == pattern || tag.find(pattern) == 0;
    }
};

// ============ GameplayEffect = 效果/Buff系统 ============
enum class EffectPolicy 
{
    Instant,
    Duration,
    Infinite
};

enum class AttributeType 
{
    MaxHp, CurrentHp, MaxMana, CurrentMana,
    AttackPower, DefensePower, MoveSpeed
};

using TagContainer = std::vector<GameplayTag>;

enum class ModifierOp { Add, Multiply, Override };

struct ModifierOperation 
{
    AttributeType targetAttribute;
    float value;
    ModifierOp op;
};

class GameplayEffect 
{
public:
    std::string name;
    EffectPolicy policy;
    float duration;
    std::vector<ModifierOperation> modifiers;
    std::vector<GameplayTag> grantedTags;
    std::vector<GameplayTag> requiredTags;

    bool CanApplyTo(const TagContainer& tags) const 
    {
        for (auto& req : requiredTags)
        {
            bool found = false;
            for (auto& t : tags)
            {
                if (t.tag == req.tag) { found = true; break; }
            }
            if (!found) return false;
        }
        return true;
    }

    void Execute(AttributeSet& attributes) const 
    {
        for (auto& mod : modifiers)
        {
            float* attr = nullptr;
            switch (mod.targetAttribute)
            {
                case AttributeType::CurrentHp:  attr = &attributes.currentHp; break;
                case AttributeType::MaxHp:      attr = &attributes.maxHp; break;
                case AttributeType::CurrentMana: attr = &attributes.currentMana; break;
                case AttributeType::MaxMana:    attr = &attributes.maxMana; break;
                case AttributeType::AttackPower: attr = &attributes.attackPower; break;
                case AttributeType::DefensePower: attr = &attributes.defensePower; break;
                case AttributeType::MoveSpeed:  attr = &attributes.moveSpeed; break;
            }
            if (!attr) continue;

            if (mod.op == ModifierOp::Add) *attr += mod.value;
            else if (mod.op == ModifierOp::Multiply) *attr *= mod.value;
            else if (mod.op == ModifierOp::Override) *attr = mod.value;
        }
    }
};

// ============ GameplayAbility = 技能 ============
class GameplayAbility 
{
public:
    std::string name;
    float cooldown = 0;
    float manaCost = 0;
    float castTime = 0;
    GameplayTag abilityTag;
    std::vector<GameplayTag> requiredTags;
    std::vector<GameplayTag> blockedTags;
    std::vector<GameplayTag> cancelTags;
    std::vector<std::shared_ptr<GameplayEffect>> effects;

    virtual bool CanActivate(const TagContainer& ownerTags) const 
    {
        for (auto& req : requiredTags)
        {
            bool found = false;
            for (auto& t : ownerTags)
            {
                if (t.tag == req.tag) { found = true; break; }
            }
            if (!found) return false;
        }
        for (auto& blocked : blockedTags)
        {
            for (auto& t : ownerTags)
            {
                if (t.tag == blocked.tag) return false;
            }
        }
        return true;
    }

    virtual void OnActivate(AttributeSet& attributes) 
    {
        attributes.currentMana -= manaCost;
    }

    virtual void OnExecute(AttributeSet& attributes) 
    {
        for (auto& effect : effects)
            effect->Execute(attributes);
    }

    virtual void OnEnd() {}
    virtual void OnCancel() {}
};

// ============ AbilitySystemComponent = 核心管理器 ============
class AbilitySystemComponent 
{
private:
    std::vector<std::shared_ptr<GameplayAbility>> abilities;
    std::vector<std::shared_ptr<GameplayEffect>> activeEffects;
    AttributeSet attributes;
    TagContainer tags;

public:
    void GiveAbility(std::shared_ptr<GameplayAbility> ability) 
    {
        abilities.push_back(std::move(ability));
    }

    bool TryActivateAbility(const std::string& abilityName) 
    {
        for (auto& ability : abilities) 
        {
            if (ability->name == abilityName && ability->CanActivate(tags)) 
            {
                ability->OnActivate(attributes);
                return true;
            }
        }
        return false;
    }

    void ApplyEffect(std::shared_ptr<GameplayEffect> effect) 
    {
        effect->Execute(attributes);
        if (effect->policy == EffectPolicy::Duration) 
        {
            activeEffects.push_back(std::move(effect));
        }
    }

    void TickEffects(float dt) 
    {
        for (auto it = activeEffects.begin(); it != activeEffects.end(); )
        {
            auto& effect = *it;
            if (effect->policy == EffectPolicy::Duration)
            {
                effect->duration -= dt;
                if (effect->duration <= 0)
                {
                    it = activeEffects.erase(it);
                    continue;
                }
                effect->Execute(attributes);
            }
            ++it;
        }
    }
};

技能定义示例

auto fireball = std::make_shared<GameplayAbility>();
fireball->name = "Fireball";
fireball->manaCost = 20;
fireball->cooldown = 3.0f;
fireball->castTime = 1.5f;
fireball->abilityTag = GameplayTag{"Ability.Fireball"};
fireball->blockedTags = { GameplayTag{"State.Stun"}, GameplayTag{"State.Silence"} };

auto fireDamage = std::make_shared<GameplayEffect>();
fireDamage->name = "FireDamage";
fireDamage->policy = EffectPolicy::Instant;
fireDamage->modifiers = {
    { AttributeType::CurrentHp, -30, ModifierOp::Add }
};
fireball->effects.push_back(fireDamage);

auto burnEffect = std::make_shared<GameplayEffect>();
burnEffect->name = "Burn";
burnEffect->policy = EffectPolicy::Duration;
burnEffect->duration = 5.0f;
burnEffect->modifiers = 
{
    { AttributeType::CurrentHp, -5, ModifierOp::Add }
};
fireball->effects.push_back(burnEffect);

GAS 核心流程

Effect 生命周期

Instant
瞬间生效

GameplayEffect

Duration
周期性执行
到期移除

Infinite
手动移除

GAS 运行时流程

成功

失败

激活技能

检查条件
资源/Tag/CD

提交消耗
冷却/资源

结束/等待

执行技能
应用Effect

优缺点

优点 缺点
数据驱动,技能热更新 学习曲线陡峭
逻辑与数据分离 调试复杂(多层嵌套Effect)
Tag系统灵活组合 设计过度抽象的风险
优秀的Buff/状态管理 小项目过度工程化
社区已验证(Fortnite/帕鲁) Effect间交互逻辑复杂
UE原生支持(GAS Plugin) 仅在UE生态成熟

与ECS/Component的关系

影响

限制

修改

Actor
角色

AbilitySystemComponent
能力组件

HealthComponent
血量组件

MovementComponent
移动组件

技能库

活跃Buff列表

AttributeSet
属性集

GameplayTags
标签集

GAS可以看作 Component 模式的进阶应用,用标签(Tag)驱动数值变化和状态管理,适合MMO/ARPG等技能机制复杂的项目。


6. MVC(模型-视图-控制器)

概述

MVC 将应用程序分为三个核心部分:Model(数据与业务逻辑)、View(界面展示)、Controller(输入处理与协调)。

架构图

Model (模型) Controller (控制器) View (视图) 用户 Model (模型) Controller (控制器) View (视图) 用户 点击/按键 转发事件 更新数据 通知更新 刷新显示

C++ 实现

class Observer;

// ============ Model ============
class PlayerModel 
{
private:
    int hp;
    int maxHp;
    int mana;
    std::vector<Observer*> observers;

public:
    PlayerModel() : hp(100), maxHp(100), mana(50) {}

    void TakeDamage(int damage) 
    {
        hp = std::max(0, hp - damage);
        NotifyObservers();
    }

    void Heal(int amount) 
    {
        hp = std::min(maxHp, hp + amount);
        NotifyObservers();
    }

    int GetHp() const { return hp; }
    int GetMaxHp() const { return maxHp; }
    void Attach(Observer* obs) { observers.push_back(obs); }
    void NotifyObservers() 
    {
        for (auto* obs : observers) obs->Update();
    }
};

// ============ View ============
class HUDView : public Observer 
{
private:
    PlayerModel* model;
    UIWidget* hpBar;
public:
    HUDView(PlayerModel* m) : model(m) 
    {
        hpBar = new UIProgressBar(100, 20);
    }

    void Update() override 
    {
        float hpPercent = (float)model->GetHp() / (float)model->GetMaxHp();
        hpBar->SetProgress(hpPercent);
    }

    void Render() { hpBar->Draw(); }
};

// ============ Controller ============
class GameController 
{
private:
    PlayerModel* model;
    HUDView* view;

public:
    GameController(PlayerModel* m, HUDView* v) : model(m), view(v)
    {
        model->Attach(view);
    }

    void HandleInput(InputEvent event) 
    {
        switch (event.type) 
        {
            case InputType::KEY_F:
                model->Heal(25);
                break;
        }
    }
};

优缺点

优点 缺点
关注点分离清晰 视图与模型仍有耦合
可同时开发多部分 控制器可能变得臃肿
便于测试(Model独立) 简单UI过度设计
广泛应用,资料丰富 视图更新机制复杂

7. MVP(模型-视图-展示器)

概述

MVP 是 MVC 的变体,由 Presenter 完全接管视图逻辑,View 接口化,Model 与 View 完全解耦。

架构对比

MVP

通过接口

View Interface

Presenter

Model

MVC

View

Controller

Model

Presenter 核心实现

struct ItemData 
{
    std::string name;
    int slotIndex;
    int quantity;
    bool isValid = false;
    bool isUsable = false;

    std::string GetDescription() const 
    {
        return name + " x" + std::to_string(quantity);
    }
};

class InventoryModel 
{
private:
    std::vector<ItemData> items;

public:
    InventoryModel() : items(20) 
    {
        for (int i = 0; i < 20; ++i) items[i].slotIndex = i;
    }

    ItemData GetItem(int slot) const 
    {
        if (slot >= 0 && slot < (int)items.size() && items[slot].isValid)
            return items[slot];
        return ItemData{};
    }

    std::vector<ItemData> GetAllItems() const { return items; }

    void RemoveItem(int slot) 
    {
        if (slot >= 0 && slot < (int)items.size())
            items[slot] = ItemData{};
    }
};

// View 接口
class IInventoryView 
{
public:
    virtual ~IInventoryView() = default;
    virtual void ShowItem(int slot, const ItemData& item) = 0;
    virtual void ClearSlot(int slot) = 0;
    virtual void HighlightSlot(int slot) = 0;
    virtual void ShowTooltip(const std::string& text) = 0;
    virtual void RefreshAll(const std::vector<ItemData>& items) = 0;
};

// Presenter:完全控制视图逻辑
class InventoryPresenter 
{
private:
    IInventoryView* view;
    InventoryModel* model;

public:
    void OnItemClicked(int slot) 
    {
        ItemData item = model->GetItem(slot);
        if (item.isValid) {
            view->HighlightSlot(slot);
            view->ShowTooltip(item.GetDescription());
        }
    }

    void OnItemUsed(int slot) 
    {
        if (model->GetItem(slot).isUsable) 
        {
            model->RemoveItem(slot);
            RefreshView();
        }
    }

private:
    void RefreshView() 
    {
        view->RefreshAll(model->GetAllItems());
    }
};

优缺点

优点 缺点
View完全与Model解耦 Presenter变得庞大
View易于替换和测试 接口数量多
测试性极佳(Mock View) 简单场景过度抽象

8. MVVM(模型-视图-视图模型)

概述

MVVM 通过**数据绑定(Data Binding)**将 View 与 ViewModel 自动同步,ViewModel 暴露可观察的属性和命令。

架构图

MVVM

数据绑定

命令绑定

Model
业务数据+逻辑

ViewModel
可观察属性
命令

View
UI绑定

ViewModel 实现

class ObservableObject 
{
protected:
    void OnPropertyChanged(const std::string& propName) 
    {
        auto it = propertyListeners.find(propName);
        if (it != propertyListeners.end()) 
        {
            for (auto& callback : it->second) callback();
        }
    }

public:
    void BindProperty(const std::string& propName, std::function<void()> callback) 
    {
        propertyListeners[propName].push_back(std::move(callback));
    }

    std::unordered_map<std::string, std::vector<std::function<void()>>> propertyListeners;
};

struct PlayerStatsModel 
{
    int baseHp = 100;
    int currentHp = 100;
    int maxHp = 100;
    float mana = 50;
    struct Equipment { std::string name; int hpBonus; };
    std::vector<Equipment> equipment;
};

class PlayerViewModel : public ObservableObject 
{
private:
    PlayerStatsModel* model;

public:
    std::string DisplayedHp;
    std::string DisplayedMaxHp;
    std::string DisplayedMana;
    float HpPercent;
    bool IsAlive;

    std::function<void()> UseHealthPotion;
    std::function<void(int)> EquipItem;

    PlayerViewModel(PlayerStatsModel* m) : model(m) 
    {
        UseHealthPotion = [this]() 
        {
            model->currentHp = std::min(model->currentHp + 50, model->maxHp);
            RefreshStats();
        };
        RefreshStats();
    }

    void RefreshStats() 
    {
        int totalCurrentHp = model->currentHp;
        int totalMaxHp = model->maxHp;
        for (auto& eq : model->equipment) totalMaxHp += eq.hpBonus;

        DisplayedHp = std::to_string(totalCurrentHp);
        DisplayedMaxHp = std::to_string(totalMaxHp);
        HpPercent = totalMaxHp > 0 ? (float)totalCurrentHp / (float)totalMaxHp : 0.0f;
        IsAlive = totalCurrentHp > 0;

        OnPropertyChanged("DisplayedHp");
        OnPropertyChanged("DisplayedMaxHp");
        OnPropertyChanged("HpPercent");
    }
};

数据绑定

class UIBinder 
{
public:
    static void BindText(UILabel* label, PlayerViewModel* vm) 
    {
        vm->BindProperty("DisplayedHp", [label, vm]() {
            label->SetText(vm->DisplayedHp);
        });
    }

    static void BindProgress(UIProgressBar* bar, PlayerViewModel* vm) 
    {
        vm->BindProperty("HpPercent", [bar, vm]() 
        {
            bar->SetProgress(vm->HpPercent);
        });
    }
};

优缺点

优点 缺点
双向数据绑定自动同步 数据绑定调试困难
View与逻辑完全解耦 过度绑定导致性能问题
适合复杂UI交互 学习曲线较高
易于单元测试 内存泄漏风险(忘记解绑)

游戏UI架构的实际情况

很多新人会以为"游戏UI一定用MVVM",实际上并非如此。

引擎/框架 官方推荐 项目实际
UE UMG MVVM(UE5.2 Experimental,UE5.3+逐渐成熟) 大量项目用Widget+Controller,更接近MVP
Unity uGUI 无限制 MVC / MVP / MVVM 皆有
Cocos Creator 无限制 多为MVC变体
自研引擎 视团队而定 通常是混合体

实际工程中,MVC、MVP、MVVM往往是混合使用的,没有绝对界限。大型3A项目中自研UI框架的比例也不低(如GTA、巫师、战神等),并不一定使用MVVM。


9. Observer(观察者模式)与Event(事件系统)

概述

Observer模式和事件驱动架构是游戏系统间解耦通信的基础。UE中的Delegate/MulticastDelegate、各种事件总线都基于此。

观察者模式

Observer 模式

Subject 主题
状态变化通知

Observer A
更新

Observer B
更新

Observer C
更新

C++ 实现

// ============ Observer 基类 ============
class Observer 
{
public:
    virtual ~Observer() = default;
    virtual void OnNotify(const std::string& event, void* data) = 0;
    virtual void Update() {}
};

// ============ Subject / 事件源 ============
class Subject 
{
private:
    std::vector<Observer*> observers;

public:
    void Attach(Observer* obs) { observers.push_back(obs); }
    void Detach(Observer* obs) 
    {
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end());
    }

    void Notify(const std::string& event, void* data = nullptr) 
    {
        for (auto* obs : observers) 
        {
            obs->OnNotify(event, data);
        }
    }
};

// ============ UE风格:Delegate ============
template<typename... Args>
class MulticastDelegate 
{
private:
    std::vector<std::function<void(Args...)>> listeners;

public:
    void AddListener(std::function<void(Args...)> callback) 
    {
        listeners.push_back(std::move(callback));
    }

    void Broadcast(Args... args) 
    {
        for (auto& listener : listeners) 
        {
            listener(args...);
        }
    }
};

// 使用
MulticastDelegate<int> OnDamageTaken;
OnDamageTaken.AddListener([](int dmg) 
{
    // 更新血条
});
OnDamageTaken.AddListener([](int dmg) 
{
    // 播放受伤音效
});
OnDamageTaken.Broadcast(50);

事件驱动架构(Event Bus)

enum class EventType 
{
    DamageTaken, HealthChanged, PlayerDied,
    EnemyKilled, ItemCollected, QuestUpdated,
    GamePaused, GameSaved, LevelLoaded
};

struct Event 
{
    EventType type;
    void* data = nullptr;
};

// ============ 事件总线(双缓冲队列) ============
class EventBus 
{
private:
    using EventHandler = std::function<void(const Event&)>;
    std::unordered_map<EventType, std::vector<EventHandler>> listeners;
    std::queue<Event> eventQueue;
    std::queue<Event> pendingQueue;

public:
    void Subscribe(EventType type, EventHandler handler) 
    {
        listeners[type].push_back(handler);
    }

    void Publish(const Event& event) 
    {
        auto it = listeners.find(event.type);
        if (it != listeners.end()) 
        {
            for (auto& handler : it->second) handler(event);
        }
    }

    void QueueEvent(const Event& event) 
    {
        pendingQueue.push(event);
    }

    void DispatchQueuedEvents() 
    {
        std::swap(eventQueue, pendingQueue);
        while (!pendingQueue.empty())
        {
            pendingQueue.pop();
        }

        while (!eventQueue.empty()) 
        {
            Publish(eventQueue.front());
            eventQueue.pop();
        }
    }
};

事件风暴(Event Storm)问题

DamageEvent
伤害事件

-> UIEvent
更新血条

-> SoundEvent
播放音效

-> QuestEvent
任务追踪

-> AchievementEvent
成就解锁

-> AnalyticsEvent
数据统计

... 更多级联...

⚠️ 事件链路过长
导致不可追踪
性能问题

事件驱动的主要性能问题不在于虚函数调用,而是:

  1. Cache Miss - 事件处理函数分散在不同内存位置
  2. 间接调用 - 函数指针/虚函数影响分支预测
  3. 事件风暴 - 单个事件触发链式反应,事件链路无限增长

优缺点

优点 缺点
系统间完全解耦 事件追踪困难
易于扩展新系统 循环事件风险
代码组织清晰 事件风暴(级联爆炸)
支持异步处理 全局状态管理复杂
灵活的事件组合 调试困难(调用链不透明)

10. FSM(有限状态机)

概述

FSM(Finite State Machine)是游戏AI和行为控制最经典的模式,实体在有限个状态间切换,每个状态有进入、更新、退出逻辑。

⚠️ 重要:FSM更适合作为局部行为控制器,而非整个AI系统。实际商业游戏中,FSM常与Behavior Tree、HFSM、Utility AI等组合使用。

状态图

发现敌人

受伤

进入范围

丢失目标

敌人死亡

血量过低

远离危险

Start

Idle 待机

Chase 追击

Flee 逃跑

Attack 攻击

状态转换表

enum class State 
{
    Idle, Chase, Attack, Flee, Patrol, Search, Alert
};

enum class Condition 
{
    EnemyDetected, InAttackRange, TargetLost,
    TargetDead, LowHealth, SafeDistance
};

struct Transition 
{
    State from;
    State to;
    Condition condition;
};

std::vector<Transition> enemyTransitions = 
{
    {State::Idle,   State::Chase,  Condition::EnemyDetected},
    {State::Chase,  State::Attack, Condition::InAttackRange},
    {State::Chase,  State::Idle,   Condition::TargetLost},
    {State::Attack, State::Idle,   Condition::TargetDead},
    {State::Attack, State::Flee,   Condition::LowHealth},
    {State::Flee,   State::Idle,   Condition::SafeDistance},
};

优缺点

优点 缺点
简单直观,易于实现 状态爆炸(复杂行为)
行为可预测 状态间耦合
调试容易(可见状态) 难以表达并行状态
性能开销小 代码重复(共享逻辑)

11. HFSM(分层有限状态机)

概述

HFSM(Hierarchical FSM)通过将状态组织成层次结构解决FSM的状态爆炸问题,子状态继承父状态的行为,支持嵌套和事件穿透。

层次结构

发现敌人

敌人消灭

Patrol 巡逻(父状态)

Walk 行走

Wait 等待

Investigate 调查

Combat 战斗(父状态)

Defend 子状态

Attack 子状态

Block 格挡

战斗状态

Attack 攻击

Defend 防御

Reload 换弹

MeleeAttack 近战

RangedAttack 远程

SpecialAttack 特殊

Dodge 闪避

Counter 反击

Patrol

Combat

分层状态实现

class HierarchicalState 
{
protected:
    HierarchicalState* parentState = nullptr;
    std::vector<std::unique_ptr<HierarchicalState>> children;
    HierarchicalState* currentChild = nullptr;
    std::string stateName;

public:
    virtual ~HierarchicalState() = default;

    virtual void Enter() 
    {
        if (!children.empty() && !currentChild) 
        {
            SetChildState(children[0].get());
        }
    }

    virtual void Update(float dt) 
    {
        if (currentChild) currentChild->Update(dt);
    }

    virtual void Exit() 
    {
        if (currentChild) 
        {
            currentChild->Exit();
            currentChild = nullptr;
        }
    }

    virtual bool HandleEvent(const GameEvent& event) 
    {
        if (currentChild && currentChild->HandleEvent(event))
            return true;
        return false;
    }

    void SetChildState(HierarchicalState* child) 
    {
        assert(std::find_if(children.begin(), children.end(),
            [child](const auto& p) { return p.get() == child; })
            != children.end());

        if (currentChild)
        {
            currentChild->Exit();
        }
        
        currentChild = child;
        
        if (currentChild)
        {
            currentChild->Enter();
        }
    }
};

FSM vs HFSM 对比

HFSM: 分层状态(优雅组织)

Base_AI

Idle_Group

Idle

Patrol

Combat_Group

Chase

Attack

Flee

FSM: 平面状态(状态爆炸)

Idle

Chase

Attack

Flee

Patrol

Search

Alert

优缺点

优点 缺点
减少状态数量 实现复杂度高
代码复用(父子共享) 调试困难(嵌套层次)
行为组织清晰 层次设计需要经验
易于扩展 状态穿透逻辑复杂

12. Behavior Tree(行为树)

概述

Behavior Tree(行为树)是现代商业游戏中最常见的高层AI决策架构之一,尤其常见于角色驱动型游戏(ACT、ARPG、TPS、FPS)。通常与FSM、HFSM等状态机结合使用。UE、Unity、Cocos等引擎均原生支持。

架构对比

游戏AI架构概览

Behavior Tree
行为树

UE EQS + BT
Unity Behavior Tree
Cocos Behavior Tree

FSM
有限状态机

局部AI控制
动画状态机
UI状态机

GOAP
目标导向规划

开放世界NPC
策略游戏

Utility AI
效用AI

模拟/策略游戏

一般商业项目中,Behavior Tree 与 FSM 最为常见,HFSM、Utility AI、GOAP通常作为补充方案。两者通常是结合使用的。

核心节点类型

Behavior Tree 节点

Root 根节点

Selector 选择器
OR: 依次执行子节点
第一个成功则返回

Sequence 顺序器
AND: 依次执行子节点
全部成功才返回

Condition 条件节点
检查条件

Action 动作节点
执行行为

Action 动作节点
执行行为

Decorator 装饰器
取反/循环/超时

Action 动作节点
执行行为

行为树结构

Root: AI Controller

Selector
选择策略

Sequence
攻击流程

Has Target?

In Range?

Attack

Sequence
追击流程

Has Target?

Chase

Sequence
巡逻流程

Patrol

Wait

C++ 实现

class Blackboard 
{
private:
    std::unordered_map<std::string, std::any> data;
public:
    template<typename T>
    void Set(const std::string& key, T value) { data[key] = value; }

    template<typename T>
    T Get(const std::string& key) const 
    {
        return std::any_cast<T>(data.at(key));
    }
};

// ============ 节点基类 ============
enum class Status { Success, Failure, Running };

class BTNode 
{
public:
    virtual ~BTNode() = default;
    virtual Status Tick(Blackboard& bb, float dt) = 0;
};

// ============ 叶子节点 ============
class ConditionNode : public BTNode 
{
private:
    std::function<bool(Blackboard&)> condition;
public:
    ConditionNode(std::function<bool(Blackboard&)> fn) : condition(std::move(fn)) {}
    Status Tick(Blackboard& bb, float dt) override 
    {
        return condition(bb) ? Status::Success : Status::Failure;
    }
};

class ActionNode : public BTNode 
{
private:
    std::function<Status(Blackboard&, float)> action;
public:
    ActionNode(std::function<Status(Blackboard&, float)> fn) : action(std::move(fn)) {}
    Status Tick(Blackboard& bb, float dt) override 
    {
        return action(bb, dt);
    }
};

class Selector : public BTNode 
{
private:
    std::vector<std::unique_ptr<BTNode>> children;
public:
    void AddChild(std::unique_ptr<BTNode> child) 
    {
        children.push_back(std::move(child));
    }
    Status Tick(Blackboard& bb, float dt) override 
    {
        for (size_t i = 0; i < children.size(); ++i) {
            Status s = children[i]->Tick(bb, dt);
            if (s != Status::Failure) return s;
        }
        return Status::Failure;
    }
};

class Sequence : public BTNode 
{
private:
    std::vector<std::unique_ptr<BTNode>> children;
public:
    void AddChild(std::unique_ptr<BTNode> child) 
    {
        children.push_back(std::move(child));
    }
    Status Tick(Blackboard& bb, float dt) override 
    {
        for (size_t i = 0; i < children.size(); ++i) 
        {
            Status s = children[i]->Tick(bb, dt);
            if (s != Status::Success) return s;
        }
        return Status::Success;
    }
};

// ============ 装饰器 ============
class Inverter : public BTNode 
{
private:
    std::unique_ptr<BTNode> child;
public:
    Status Tick(Blackboard& bb, float dt) override 
    {
        Status s = child->Tick(bb, dt);
        if (s == Status::Success) return Status::Failure;
        if (s == Status::Failure) return Status::Success;
        return Status::Running;
    }
};

// ============ 行为树执行器 ============
class BehaviorTreeRunner 
{
private:
    std::unique_ptr<BTNode> root;

public:
    BehaviorTreeRunner(std::unique_ptr<BTNode> r) : root(std::move(r)) {}

    void Tick(Blackboard& bb, float dt) 
    {
        if (root) root->Tick(bb, dt);
    }
};

// ============ 构建行为树 ============
BehaviorTreeRunner BuildEnemyBT(Blackboard& bb) 
{
    auto root = std::make_unique<Selector>();

    // 攻击分支
    auto attackSeq = std::make_unique<Sequence>();
    attackSeq->AddChild(std::make_unique<ConditionNode>(
        [](Blackboard& b) { return b.Get<bool>("has_target"); }
    ));
    attackSeq->AddChild(std::make_unique<ConditionNode>(
        [](Blackboard& b) { return b.Get<float>("distance") < 5.0f; }
    ));
    attackSeq->AddChild(std::make_unique<ActionNode>(
        [](Blackboard& b, float) -> Status 
        {
            // 执行攻击
            return Status::Success;
        }
    ));

    // 追击分支
    auto chaseSeq = std::make_unique<Sequence>();
    chaseSeq->AddChild(std::make_unique<ConditionNode>(
        [](Blackboard& b) { return b.Get<bool>("has_target"); }
    ));
    chaseSeq->AddChild(std::make_unique<ActionNode>(
        [](Blackboard& b, float dt) -> Status 
        {
            // 追击目标
            return Status::Running;
        }
    ));

    // 巡逻分支
    auto patrolSeq = std::make_unique<Sequence>();
    patrolSeq->AddChild(std::make_unique<ActionNode>(
        [](Blackboard& b, float dt) -> Status 
        {
            // 巡逻
            return Status::Running;
        }
    ));

    root->AddChild(std::move(attackSeq));
    root->AddChild(std::move(chaseSeq));
    root->AddChild(std::move(patrolSeq));

    return BehaviorTreeRunner(std::move(root));
}

Behavior Tree vs FSM

维度 FSM Behavior Tree
状态管理 显式状态 隐式(节点状态)
扩展性 状态爆炸 轻松添加分支
可读性 中等 可视化极佳
复用性 高(子树复用)
调试 容易 可视化调试
UE支持 动画状态机 Behavior Tree + EQS

优缺点

优点 缺点
可视化编辑,直觉性强 树结构可能过于复杂
模块化,子树可复用 Running状态管理复杂
条件与动作分离 不适合高度动态规划
UE/Unity原生支持 调试父子节点关系
适合复杂AI行为 内存占用相对较高

13. GOAP(目标导向行为规划)

概述

GOAP(Goal-Oriented Action Planning)是一种基于规划的AI架构,AI Agent 根据当前世界状态和目标,动态规划一系列动作来达成目标。

经典代表作:《FEAR》(F.E.A.R.)的AI系统。

架构图

规划过程

Start

Step 1: 寻路

Step 2: 移动

Step 3: 拾取武器

Step 4: 攻击

Goal: 消灭敌人

GOAP 架构

输入

输入

输入

输出: 动作序列

WorldState
世界状态

Planner
规划器

Goals
目标列表

Actions
动作集合

Plan
行动计划

核心数据结构

struct WorldStateBool 
{
    std::unordered_map<std::string, bool> bools;
};

struct WorldStateNumeric 
{
    std::unordered_map<std::string, float> values;
};

class GoapAction 
{
public:
    std::string name;
    float cost;
    WorldStateBool preconditions;
    WorldStateBool effects;
    bool inProgress = false;
    float elapsedTime = 0;

    virtual ~GoapAction() = default;

    bool IsUsable(const WorldStateBool& state) const 
    {
        for (auto& [key, value] : preconditions.bools) 
        {
            auto it = state.bools.find(key);
            if (it == state.bools.end() || it->second != value)
                return false;
        }
        return true;
    }

    void ApplyEffects(WorldStateBool& state) const 
    {
        for (auto& [key, value] : effects.bools) 
        {
            state.bools[key] = value;
        }
    }

    virtual bool Perform() = 0;
    virtual float GetDuration() const { return 1.0f; }
};

规划器(A*)

class GoapPlanner 
{
public:
    std::vector<std::shared_ptr<GoapAction>> Plan(
        const WorldStateBool& startState,
        const Goal& goal,
        const std::vector<std::shared_ptr<GoapAction>>& availableActions)
    {
        // A* 搜索最优动作序列
        return {};
    }
};

FSM vs GOAP 对比

GOAP: 自动规划

方案A

方案B

方案C

目标: 消灭敌人

规划器

拾取武器→攻击

使用魔法→攻击

呼叫支援→围剿

✅ 动态选择最优方案

FSM: 人工设计

敌人可见

无敌人

血量低

可攻击

角色状态

追击

巡逻

逃跑

攻击

❌ 所有逻辑硬编码

优缺点

优点 缺点
高度灵活的动态行为 规划计算开销大
无需手动设计状态转换 复杂目标设计困难
适应性强(应对意外情况) 调试困难
可复用动作组件 实时性要求高时可能卡顿

14. Utility AI(效用AI)

概述

Utility AI(效用AI)基于评分系统而非状态转换。AI Agent为每个行为计算一个效用值(Utility Score),选择最高分的行动执行。

公开资料中常见案例包括:《模拟人生》系列、《文明》系列等。注意《全面战争》系列实际上是Utility AI、Rule-Based与FSM的混合体。

核心思想

Utility AI 评分系统

输入: 距离敌人 5m

攻击效用
= 距离因子 × 血量因子

输入: HP 30/100

逃跑效用
= 1/血量因子 × 安全距离

输入: 弹药 10

换弹效用
= 1/弹药因子

比较最高分

CompareCompare

选择: 逃跑

C++ 实现

class Curve 
{
private:
    std::function<float(float)> function;

public:
    static Curve Linear() 
    {
        return Curve{[](float x) { return std::clamp(x, 0.0f, 1.0f); }};
    }

    static Curve Logistic(float steepness = 4.0f) 
    {
        return Curve{[steepness](float x) 
        {
            return 1.0f / (1.0f + std::exp(-steepness * (x - 0.5f)));
        }};
    }

    static Curve Exponential(float power = 2.0f) 
    {
        return Curve{[power](float x) 
        {
            return std::pow(std::clamp(x, 0.0f, 1.0f), power);
        }};
    }

    float Evaluate(float input) const { return function(input); }
};

struct Consideration 
{
    std::function<float(Blackboard&)> inputGetter;
    Curve curve;float weight;

    float Score(Blackboard& bb) const 
    {
        float rawInput = inputGetter(bb);
        float mapped = curve.Evaluate(rawInput);
        return mapped * weight;
    }
};

struct UtilityAction 
{
    std::string name;
    std::vector<Consideration> considerations;

    float CalculateScore(Blackboard& bb) const 
    {
        float score = 1.0f;
        for (auto& c : considerations) 
        {
            score *= c.Score(bb);
        }
        return score;
    }
};

class UtilityAIAgent 
{
private:
    std::vector<UtilityAction> actions;
    Blackboard& blackboard;
    float evaluationInterval = 0.5f;
    float timer = 0;

public:
    void AddAction(UtilityAction action) 
    {
        actions.push_back(std::move(action));
    }
    
    UtilityAction* SelectBestAction(float dt) 
    {
        timer += dt;
        if (timer < evaluationInterval) return nullptr;
        timer = 0;

        UtilityAction* bestAction = nullptr;
        float bestScore = 0;

        for (auto& action : actions) 
        {
            float score = action.CalculateScore(blackboard);
            if (score > bestScore) 
            {
                bestScore = score;
                bestAction = &action;
            }
        }
        return bestAction;
    }
};

void SetupAI(UtilityAIAgent& agent, Blackboard& bb) {UtilityAction attack;
    attack.name = "Attack";
    attack.considerations = 
    {
        Consideration
        {
            [](Blackboard& b) { return 1.0f - b.Get<float>("distance") / 100.0f; },
            Curve::Logistic(), 1.0f
        },
        Consideration
        {
            [](Blackboard& b) { return b.Get<float>("hp") / 100.0f; },
            Curve::Linear(), 1.0f
        },
        Consideration
        {
            [](Blackboard& b) { return b.Get<int>("ammo") / 30.0f; },
            Curve::Exponential(2.0f), 1.5f
        }
    };

    UtilityAction flee;
    flee.name = "Flee";
    flee.considerations = 
    {
        Consideration
        {
            [](Blackboard& b) { return 1.0f - b.Get<float>("hp") / 100.0f; },
            Curve::Exponential(3.0f), 2.0f
        }
    };

    agent.AddAction(std::move(attack));
    agent.AddAction(std::move(flee));
}

FSM vs Utility AI

Utility AI: 连续评分

HP=80

HP=50

HP=20

评分曲线

攻击 0.9

攻击 0.5 / 逃跑 0.3

逃跑 0.8

✅ 平滑过渡
自然行为表现

FSM: 非黑即白

HP > 30

HP <= 30

状态

战斗

逃跑

❌ 边界问题
29HP逃跑 31HP战斗

优缺点

优点 缺点
自然平滑的行为 调参困难(曲线调试)
无硬编码状态转换 评分公式复杂
易于微调(修改曲线) 难以预测行为
适合复杂决策 大量行为频繁评估时会产生额外CPU开销
《模拟人生》验证 极端情况难控制

15. 架构对比总结

分层架构体系

游戏架构分层

基础层
OOP + Component + Event

性能层
DOP + ECS (Mass)

能力系统层
Ability System / GAS

UI层
MVC + MVP + MVVM

AI层
FSM + HFSM + BehaviorTree + GOAP + UtilityAI

选择指南

项目类型

小型/原型

OOP + Component + FSM

中型项目

Component + BehaviorTree + Event

大型3A

Behavior Tree 主要AI

ECS/Mass 性能层

Event Driven 通信

GAS/Ability System
技能系统

MVVM / MVP / 自研UI框架

DOP 热点优化

策略/模拟游戏

Utility AI + GOAP

综合对比表

架构 核心思想 适用场景 性能 复杂度 扩展性
OOP 继承+封装+多态 中小型、原型 ⭐⭐⭐ ⭐⭐ ⭐⭐
Component 组合功能 UE/Unity开发 ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐
DOP 数据布局优化 大规模物理/粒子 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐
ECS 数据-逻辑分离 大型游戏、万人同屏 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐
Ability System 数据驱动技能 MMO/ARPG/MOBA ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
MVC 三层分离 UI、编辑器 ⭐⭐ ⭐⭐ ⭐⭐⭐
MVP 接口解耦 可测试UI ⭐⭐ ⭐⭐⭐ ⭐⭐⭐
MVVM 数据绑定 复杂UI ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
Observer/Event 发布-订阅 跨系统通信 ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐
FSM 状态+转换 局部控制、动画 ⭐⭐⭐⭐⭐
HFSM 层次化状态 复杂状态切换 ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
Behavior Tree 树节点组合 主流AI ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐
GOAP 动态规划 开放世界NPC ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
Utility AI 评分决策 策略/模拟游戏 ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐

商业游戏AI使用案例

⚠️ 不同品类游戏AI架构差异极大,不存在统一排名。以下仅列出有公开技术资料确认的案例。

游戏 AI架构 来源
FEAR GOAP GDC演讲(经典案例)
模拟人生 Utility AI 官方文档
最后生还者 HFSM + Behavior Tree GDC演讲

实际项目推荐

// 1. 独立游戏 / 原型
class Enemy 
{
    HealthComponent* health;
    MovementComponent* movement;
    FiniteStateMachine ai;
};

// 2. 中大型商业项目
class GameWorld 
{
    std::vector<Actor*> actors;
    BehaviorTreeRunner ai;
    EventBus eventBus;
    AbilitySystemComponent abilitySystem;
    UIManager ui;
};

// 3. 3A级大型游戏
class TripleAGame 
{
    EntityComponentSystem ecs;
    DataOrientedPipeline dop;
    GameplayAbilitySystem gas;
    BehaviorTreeSystem behaviorAI;
    GoapSystem goap;
    UtilitySystem utility;
    EventBus eventBus;
    std::unique_ptr<IUISystem> ui;
};

16. Unreal Engine 架构映射

概述

以下将前文介绍的架构模式映射到Unreal Engine的对应系统,帮助UE开发者快速定位和使用。

架构映射表

概念 UE对应 说明
OOP UObject 体系 UE的根基,所有对象继承UObject,支持GC、序列化、反射
Component ActorComponent / UActorComponent 每个Actor可挂载多个Component实现功能组合
Observer Delegate / MulticastDelegate 单播/多播委托,支持动态绑定
Event GameplayMessageSubsystem UE5的Gameplay消息系统,模块间解耦通信
FSM Animation Blueprint(状态机) 动画蓝图本质上是FSM,每个状态对应一个动画混合
HFSM StateTree(UE5.1+) UE5引入的层次化状态机,面向AI行为编排
Behavior Tree Behavior Tree + Blackboard + EQS UE原生BT系统,搭配EQS做环境查询,是官方主力AI方案
GOAP 无官方实现 UE社区有GOAP插件,或需自研(基于A*规划)
Utility AI 无官方实现 UE社区有Utility AI插件,或需自研(评分曲线系统)
ECS Mass Entity UE5的Mass框架,面向超大规模实体(万人同屏)
DOP Mass + Niagara Mass的数据布局(Archetype/Chunk)、Niagara的粒子计算
Ability System GameplayAbilitySystem(GAS) UE官方技能系统插件,支持技能/Buff/Tag/属性
MVVM MVVM Plugin(UE5.2 Experimental,UE5.3+逐渐成熟) UE官方MVVM框架,此前大量项目自建MVP风格
MVC/MVP Widget + Controller 大量UE4/UE5项目的UI架构风格,非官方强制

Mass ECS 架构简图

适用于超大规模Entity

传统Actor

Actor

SceneComponent

CharacterMovementComponent

Unreal Mass ECS

遍历Chunk

Chunk

连续内存
Position Array
Velocity Array

Archetype

Fragment: Position

Fragment: Velocity

Fragment: Health

Processor

MovementProcessor

DamageProcessor

Mass

Mass: 10000+ Entity
Actor: 数百~数千上限

GAS 架构简图

UE GameplayAbilitySystem

拥有

追踪

管理

持有

应用

触发

修改

条件检查

Grant

AbilitySystemComponent
挂载在Character/Pawn上

GameplayAbility
技能蓝图/C++类

GameplayTag
标签层级系统

GameplayEffect
数据资产/Buff定义

AttributeSet
属性集定义

GameplayCue
通知类反馈

StateTree vs Behavior Tree 对比

维度 Behavior Tree StateTree(UE5.1+)
本质 树节点组合(类似决策树) 层次化状态机(HFSM)
状态管理 隐式(节点Running/Success/Failure) 显式(状态间转换)
适用 复杂AI行为决策 角色行为切换、战斗状态管理
事件 每帧Tick驱动 事件驱动+Tick混用
并行能力 需Parallel节点 支持同状态下多Task协同执行
UE版本 UE4+/UE5全支持 UE5.1+

在UE5项目中,Behavior Tree + StateTree 可组合使用:BT负责高层决策,StateTree负责底层状态切换。两者并非替代关系。

UE项目架构推荐

// 1. 常规UE5项目(中小型)
class AMyCharacter : public ACharacter 
{
    UHealthComponent* Health;
    UInventoryComponent* Inventory;
    UBehaviorTree* BehaviorTree;
    UBlackboardComponent* Blackboard;
    UAbilitySystemComponent* AbilitySystem;
    UAnimInstance* AnimInstance;
};

// 2. UE5大型项目(万人同屏场景)
class AMassGameMode : public AGameModeBase 
{
    UMassEntitySubsystem* MassEntity;
    UBehaviorTreeComponent* BehaviorTree;
    UAbilitySystemComponent* AbilitySystem;
    UMVVMViewModelCollectionObject* ViewModels;
    UGameplayMessageSubsystem* MessageSystem;
};

结语

游戏架构设计没有银弹,每个模式都有其适用场景:

核心原则

  1. 简单优先:能用FSM解决的问题不要上GOAP
  2. 数据驱动:性能问题需要数据说话,过早优化是万恶之源
  3. 为扩展留空间:架构要有弹性,但不要过度设计
  4. 团队匹配:团队熟悉度 > 架构先进性
  5. 混合为王:实际项目往往是多种模式的组合

分层思维

基础层:    OOP + Component + Event    (地基)
性能层:    DOP + ECS/Mass              (加速)
能力系统层:  Ability System / GAS       (技能)
UI层:      MVC + MVP + MVVM           (交互)
AI层:      BT + FSM + Utility          (智能)

“架构不是一步到位的,而是在迭代中演化的。”

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐