下面给出一套“系统性、全面”的设计模式实现方案,基于 O3DE(Open 3D Engine)脚本/组件化开发思路。由于 O3DE 的脚本语言主要是 Lua(BehaviorContext 可绑定)与 Python(编辑器与构建流水线、工具、自动化),而运行时游戏逻辑常用 C++/ScriptCanvas/Lua,下面我将:
- 用 C++(反映到 BehaviorContext,可被 ScriptCanvas/Lua 使用)实现核心模式,贴合 O3DE 组件化、事件总线(EBus)、反射、Prefab、资产系统、TickBus 等生态。
- 为部分模式给出 Lua 脚本/ScriptCanvas 思路片段,方便脚本侧调用。
- 每个模式给出:场景、核心接口与关键点、O3DE 相关点(如 EBus、Components、Reflection)、完整示例代码(C++),并在可能处给出 Lua 调用示例。
- 代码组织:一个 Gem(DesignPatternsGem),在其 Source/ 下为每个模式给出独立文件。所有类通过 AZ::Component/EBus/Interface/Reflection 暴露。
说明:
- 以下代码面向 O3DE 23.x/24.x 风格。CMake/Gem.json 略述结构,示例为可编译片段,但你需要按工程整合。
- 为避免篇幅过巨,这里覆盖 23 种常见 GoF 设计模式,全代码均为“可用最小实现 + O3DE 风格”。你可以直接复制到你的 Gem 中。若你需要 ScriptCanvas 节点注册,我为若干模式示例了 BehaviorContext 方法暴露,其他模式可依葫芦画瓢。
目录(GoF 23 模式)
- 创建型
- 抽象工厂(Abstract Factory)
- 建造者(Builder)
- 工厂方法(Factory Method)
- 原型(Prototype)
- 单例(Singleton)
- 结构型
- 适配器(Adapter)
- 桥接(Bridge)
- 组合(Composite)
- 装饰(Decorator)
- 外观(Facade)
- 享元(Flyweight)
- 代理(Proxy)
- 行为型
- 责任链(Chain of Responsibility)
- 命令(Command)
- 解释器(Interpreter)
- 迭代器(Iterator)
- 中介者(Mediator)
- 备忘录(Memento)
- 观察者(Observer)
- 状态(State)
- 策略(Strategy)
- 模板方法(Template Method)
- 访问者(Visitor)
工程结构建议
- Gems/DesignPatternsGem/Gem.json
- Gems/DesignPatternsGem/Code/CMakeLists.txt
- Gems/DesignPatternsGem/Code/Include/DesignPatternsGem/… 头文件
- Gems/DesignPatternsGem/Code/Source/… 源文件
- 将每个模式放在 Code/Source/Patterns/ 下
- 一个 SystemComponent 负责反射所有类到 BehaviorContext
示例 Gem.json(简化)
- name: DesignPatternsGem
- type: Code
- summary: GoF patterns for O3DE
- dependencies: AZCore, AzFramework, AzToolsFramework (仅编辑器工具部分)
下面给出完整代码。你可将所有代码放入一个 Gem 内按文件拆分,或先合并为单一编译单元验证。
注意:
- 以下代码较长。为确保“完整自包含”,我集中在一个头/源合集示例中。你在落地时请按文件拆分。
- 为了演示,每种模式包含最小功能与 O3DE 反射要点;生产中请加上更严谨的错误处理与线程安全保护。
========================
CMake 与系统组件
CMakeLists.txt(简化示例)
cmake_minimum_required(VERSION 3.20)
project(DesignPatternsGem)
set(CMAKE_CXX_STANDARD 17)
find_package(o3de REQUIRED CONFIG)
ly_add_target(
NAME DesignPatternsGem.Static STATIC
NAMESPACE Gem
FILES_CMAKE
designpatterns_files.cmake
BUILD_DEPENDENCIES
AZ::AzCore
AZ::AzFramework
AZ::AzGameFramework
)
ly_add_target(
NAME DesignPatternsGem MODULE
NAMESPACE Gem
FILES_CMAKE
designpatterns_files.cmake
BUILD_DEPENDENCIES
Gem::DesignPatternsGem.Static
)
designpatterns_files.cmake(列出所有源码)
set(FILES
Source/DesignPatternsSystemComponent.cpp
Source/Patterns/AbstractFactory.cpp
Source/Patterns/Builder.cpp
Source/Patterns/FactoryMethod.cpp
Source/Patterns/Prototype.cpp
Source/Patterns/Singleton.cpp
Source/Patterns/Adapter.cpp
Source/Patterns/Bridge.cpp
Source/Patterns/Composite.cpp
Source/Patterns/Decorator.cpp
Source/Patterns/Facade.cpp
Source/Patterns/Flyweight.cpp
Source/Patterns/Proxy.cpp
Source/Patterns/ChainOfResponsibility.cpp
Source/Patterns/Command.cpp
Source/Patterns/Interpreter.cpp
Source/Patterns/Iterator.cpp
Source/Patterns/Mediator.cpp
Source/Patterns/Memento.cpp
Source/Patterns/Observer.cpp
Source/Patterns/State.cpp
Source/Patterns/Strategy.cpp
Source/Patterns/TemplateMethod.cpp
Source/Patterns/Visitor.cpp
)
========================
统一头部与命名空间
以下代码片段以同一命名空间 dp(design patterns),并通过 SystemComponent 统一反射到 BehaviorContext,供 ScriptCanvas/Lua 调用。
请将以下“合并源文件”拆分为对应文件。为了便于复制,这里一次性给出所有实现。
文件:Source/DesignPatternsSystemComponent.cpp
#include <AzCore/Component/Component.h>
#include <AzCore/Module/Module.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Console/ConsoleBus.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
namespace dp
{
// 统一日志 EBus(演示)
class LogRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual void Info(const AZStd::string& msg) = 0;
};
using LogBus = AZ::EBus;
class Logger
: public LogBus::Handler
{
public:
void Activate() { LogBus::Handler::BusConnect(); }
void Deactivate() { LogBus::Handler::BusDisconnect(); }
void Info(const AZStd::string& msg) override
{
AZ_Printf("DesignPatterns", "%s\n", msg.c_str());
}
};
// ============= 抽象工厂 =============
struct IEnemy
{
AZ_RTTI(IEnemy, "{BC2E7BE6-0D5E-4E7A-8E71-7F3FD0A5B8F2}");
virtual ~IEnemy() = default;
virtual AZStd::string Attack() = 0;
};
struct Orc : IEnemy
{
AZ_RTTI(Orc, "{DB3DB22D-2E4B-4E8C-9F8C-F2E02CF0183D}", IEnemy);
AZStd::string Attack() override { return "Orc swings axe"; }
};
struct Troll : IEnemy
{
AZ_RTTI(Troll, "{C0B41D82-0F4D-4FBE-8E38-12E1D656B859}", IEnemy);
AZStd::string Attack() override { return "Troll smashes ground"; }
};
struct IEnemyFactory
{
AZ_RTTI(IEnemyFactory, "{9AE2E0D6-FB8A-4C45-A70E-85C6C367A84F}");
virtual ~IEnemyFactory() = default;
virtual AZStd::shared_ptr<IEnemy> CreateMelee() = 0;
virtual AZStd::shared_ptr<IEnemy> CreateTank() = 0;
};
struct OrcFactory : IEnemyFactory
{
AZ_RTTI(OrcFactory, "{2A3BFE53-2B1E-4E1E-B5BB-F0CA709C8C1F}", IEnemyFactory);
AZStd::shared_ptr<IEnemy> CreateMelee() override { return AZStd::make_shared<Orc>(); }
AZStd::shared_ptr<IEnemy> CreateTank() override { return AZStd::make_shared<Orc>(); }
};
struct TrollFactory : IEnemyFactory
{
AZ_RTTI(TrollFactory, "{B9F73C7A-8D70-4C10-8870-BA0F1E7F3C7E}", IEnemyFactory);
AZStd::shared_ptr<IEnemy> CreateMelee() override { return AZStd::make_shared<Troll>(); }
AZStd::shared_ptr<IEnemy> CreateTank() override { return AZStd::make_shared<Troll>(); }
};
// ============= 建造者 =============
struct Character
{
AZ_TYPE_INFO(Character, "{A3E5C7A6-35E3-4F39-8C9B-5A5BD4B846E0}");
AZStd::string m_name;
int m_hp = 100;
int m_attack = 10;
};
struct ICharacterBuilder
{
AZ_RTTI(ICharacterBuilder, "{E871D82E-6B80-4F19-8BFA-0DDF60F5457B}");
virtual ~ICharacterBuilder() = default;
virtual void Reset() = 0;
virtual void SetName(const AZStd::string&) = 0;
virtual void SetHP(int) = 0;
virtual void SetAttack(int) = 0;
virtual Character GetResult() = 0;
};
struct WarriorBuilder : ICharacterBuilder
{
AZ_RTTI(WarriorBuilder, "{2DE29D4A-2C13-4AF1-9FBB-2C0B1BFA2E9D}", ICharacterBuilder);
Character m_char;
void Reset() override { m_char = Character{}; }
void SetName(const AZStd::string& n) override { m_char.m_name = n; }
void SetHP(int hp) override { m_char.m_hp = hp; }
void SetAttack(int atk) override { m_char.m_attack = atk; }
Character GetResult() override { return m_char; }
};
struct CharacterDirector
{
AZ_TYPE_INFO(CharacterDirector, "{D1F1DABC-AD02-4C52-A50D-4A5A9D0DFAE7}");
void MakeTank(ICharacterBuilder& b)
{
b.Reset();
b.SetName("Tank");
b.SetHP(300);
b.SetAttack(20);
}
};
// ============= 工厂方法 =============
struct IProjectile
{
AZ_RTTI(IProjectile, "{A9CDE2C5-16C5-49C9-9A4D-5F6C0E0D4C54}");
virtual ~IProjectile() = default;
virtual AZStd::string Fire() = 0;
};
struct Arrow : IProjectile
{
AZ_RTTI(Arrow, "{C0C5723C-3D7B-4D8E-8F1E-93C8B79F5F3F}", IProjectile);
AZStd::string Fire() override { return "Arrow fired"; }
};
struct Fireball : IProjectile
{
AZ_RTTI(Fireball, "{A2EEA7A9-2AB3-4A5D-99D7-3D54C83F30A0}", IProjectile);
AZStd::string Fire() override { return "Fireball cast"; }
};
struct ProjectileSpawner
{
AZ_RTTI(ProjectileSpawner, "{6A68279B-0973-4AC2-BD8B-0A3F3A9C9E2C}");
virtual ~ProjectileSpawner() = default;
virtual AZStd::shared_ptr<IProjectile> Make() = 0;
};
struct ArrowSpawner : ProjectileSpawner
{
AZ_RTTI(ArrowSpawner, "{5B2F2D3A-7F5F-4A08-9B95-3A0E808E2F60}", ProjectileSpawner);
AZStd::shared_ptr<IProjectile> Make() override { return AZStd::make_shared<Arrow>(); }
};
struct FireballSpawner : ProjectileSpawner
{
AZ_RTTI(FireballSpawner, "{8F2A79F5-5C2A-4D9F-8D7E-5C5D1E1A558C}", ProjectileSpawner);
AZStd::shared_ptr<IProjectile> Make() override { return AZStd::make_shared<Fireball>(); }
};
// ============= 原型 =============
struct ICloneable
{
AZ_RTTI(ICloneable, "{8B8CE02E-31B7-4C09-8468-2B6C7E7B2E64}");
virtual ~ICloneable() = default;
virtual AZStd::shared_ptr<ICloneable> Clone() const = 0;
};
struct Spell : ICloneable
{
AZ_RTTI(Spell, "{9B8D0F05-67C7-4C3D-9E5A-7B9F5B3C812A}", ICloneable);
AZStd::string m_name = "Heal";
int m_power = 10;
AZStd::shared_ptr<ICloneable> Clone() const override
{
auto s = AZStd::make_shared<Spell>();
s->m_name = m_name;
s->m_power = m_power;
return s;
}
};
// ============= 单例(基于 Interface) =============
class ConfigInterface
{
public:
AZ_RTTI(ConfigInterface, "{C5F7F3B1-9A90-4B5A-87E0-0D0D35A1DE98}");
virtual ~ConfigInterface() = default;
virtual void Set(const AZStd::string& key, const AZStd::string& val) = 0;
virtual AZStd::string Get(const AZStd::string& key) const = 0;
static ConfigInterface* Get();
static void Register(ConfigInterface* impl);
static void Unregister(ConfigInterface* impl);
};
class ConfigImpl final : public ConfigInterface
{
public:
AZ_CLASS_ALLOCATOR(ConfigImpl, AZ::SystemAllocator);
AZ_RTTI(ConfigImpl, "{F9F8C7B3-17F5-4ED5-8D2C-1B6C73F18D64}", ConfigInterface);
void Set(const AZStd::string& key, const AZStd::string& val) override { m_map[key] = val; }
AZStd::string Get(const AZStd::string& key) const override
{
auto it = m_map.find(key);
return it==m_map.end()? "" : it->second;
}
private:
AZStd::unordered_map<AZStd::string, AZStd::string> m_map;
};
static ConfigInterface* g_configSingleton = nullptr;
ConfigInterface* ConfigInterface::Get() { return g_configSingleton; }
void ConfigInterface::Register(ConfigInterface* impl) { g_configSingleton = impl; }
void ConfigInterface::Unregister(ConfigInterface* impl) { if (g_configSingleton == impl) g_configSingleton = nullptr; }
// ============= 适配器 =============
struct LegacyAudioSystem
{
AZ_TYPE_INFO(LegacyAudioSystem, "{0E31D5E4-8737-4A06-BB77-4F4A4C43574E}");
AZStd::string PlayWav(const AZStd::string& p) { return "Legacy play wav: " + p; }
};
struct IAudio
{
AZ_RTTI(IAudio, "{F41F4AD3-99F5-4E6B-9E0A-2B0DE7A3E6F2}");
virtual ~IAudio() = default;
virtual AZStd::string Play(const AZStd::string& path) = 0;
};
struct LegacyAudioAdapter : IAudio
{
AZ_RTTI(LegacyAudioAdapter, "{1B6B3C08-0B35-4D66-9D7F-113A4F1E3476}", IAudio);
LegacyAudioSystem m_legacy;
AZStd::string Play(const AZStd::string& path) override { return m_legacy.PlayWav(path); }
};
// ============= 桥接 =============
struct IRenderImpl
{
AZ_RTTI(IRenderImpl, "{5E9C2D9C-16D8-4A02-9E1E-5F9F43D9E651}");
virtual ~IRenderImpl() = default;
virtual AZStd::string Draw(const AZStd::string& what) = 0;
};
struct VulkanImpl : IRenderImpl
{
AZ_RTTI(VulkanImpl, "{5F6B2B7E-1F6D-44B0-9A3E-3D3ECA4A22B6}", IRenderImpl);
AZStd::string Draw(const AZStd::string& what) override { return "Vulkan draws " + what; }
};
struct DX12Impl : IRenderImpl
{
AZ_RTTI(DX12Impl, "{EECF9A88-6A14-4BE8-8B3F-AE0E34F6D6B3}", IRenderImpl);
AZStd::string Draw(const AZStd::string& what) override { return "DX12 draws " + what; }
};
struct RenderAbstraction
{
AZ_TYPE_INFO(RenderAbstraction, "{48F4D7B1-40D6-4A65-A1FA-265C9F2B6D6C}");
AZStd::shared_ptr<IRenderImpl> m_impl;
explicit RenderAbstraction(AZStd::shared_ptr<IRenderImpl> impl) : m_impl(impl) {}
AZStd::string RenderMesh(const AZStd::string& mesh) { return m_impl ? m_impl->Draw(mesh) : "No impl"; }
};
// ============= 组合 =============
struct INode
{
AZ_RTTI(INode, "{1E92F2E0-1B2A-4D1B-9E6E-5A3E5C3E0A19}");
virtual ~INode() = default;
virtual int GetCost() const = 0;
};
struct Leaf : INode
{
AZ_RTTI(Leaf, "{17E7C0F7-A1B9-41C1-8EBD-5F1AD0EE52F2}", INode);
int m_cost = 1;
int GetCost() const override { return m_cost; }
};
struct CompositeNode : INode
{
AZ_RTTI(CompositeNode, "{1CF0F93F-3F63-42D0-9970-3C84F9F9E6C9}", INode);
AZStd::vector<AZStd::shared_ptr<INode>> m_children;
void Add(const AZStd::shared_ptr<INode>& n) { m_children.push_back(n); }
int GetCost() const override
{
int sum = 0; for (auto& c : m_children) sum += c->GetCost(); return sum;
}
};
// ============= 装饰 =============
struct IWeapon
{
AZ_RTTI(IWeapon, "{FB55D3E1-6D2E-4A6C-B6C5-6B2F8A8121E5}");
virtual ~IWeapon() = default;
virtual int Damage() const = 0;
virtual AZStd::string Desc() const = 0;
};
struct Sword : IWeapon
{
AZ_RTTI(Sword, "{5D8AD4F9-1F31-4CE5-BD88-1E56D5BCF5B2}", IWeapon);
int Damage() const override { return 10; }
AZStd::string Desc() const override { return "Sword"; }
};
struct WeaponDecorator : IWeapon
{
AZ_RTTI(WeaponDecorator, "{7C2C7E30-19E9-4B3B-8F94-5F0E0F5E2458}", IWeapon);
AZStd::shared_ptr<IWeapon> m_wrap;
explicit WeaponDecorator(AZStd::shared_ptr<IWeapon> w) : m_wrap(w) {}
int Damage() const override { return m_wrap->Damage(); }
AZStd::string Desc() const override { return m_wrap->Desc(); }
};
struct FireEnchant : WeaponDecorator
{
AZ_RTTI(FireEnchant, "{B4E4E2F1-F2E7-4C79-AD5D-0C10E570A1E1}", WeaponDecorator);
using WeaponDecorator::WeaponDecorator;
int Damage() const override { return WeaponDecorator::Damage() + 5; }
AZStd::string Desc() const override { return WeaponDecorator::Desc() + " + Fire"; }
};
// ============= 外观 =============
struct PhysicsSubsys { AZStd::string Init(){ return "PhysicsReady"; } };
struct AudioSubsys { AZStd::string Init(){ return "AudioReady"; } };
struct RenderSubsys { AZStd::string Init(){ return "RenderReady"; } };
struct EngineFacade
{
AZ_TYPE_INFO(EngineFacade, "{48F1A6C0-1D81-4F8D-8FBC-0D5B4EAFB3FD}");
PhysicsSubsys m_p; AudioSubsys m_a; RenderSubsys m_r;
AZStd::string Boot() { return m_p.Init() + ", " + m_a.Init() + ", " + m_r.Init(); }
};
// ============= 享元 =============
struct BulletIntrinsic
{
AZ_TYPE_INFO(BulletIntrinsic, "{62E8866E-3C8E-4A49-9A73-5B28A2EABF5F}");
int meshId; int materialId;
};
struct BulletExtrinsic
{
AZ_TYPE_INFO(BulletExtrinsic, "{85A1B4B2-A53E-44B5-8C0F-4B2C3E6442C9}");
float x; float y; float z; float speed;
};
struct BulletFlyweightFactory
{
AZ_TYPE_INFO(BulletFlyweightFactory, "{C01B6D4E-3A6F-44D8-9E5D-2E7B119D0C5B}");
AZStd::unordered_map<int, BulletIntrinsic> m_pool;
const BulletIntrinsic& Get(int key)
{
auto it = m_pool.find(key);
if (it == m_pool.end())
{
m_pool[key] = BulletIntrinsic{key, key+100};
}
return m_pool[key];
}
};
// ============= 代理 =============
struct IAssetLoader
{
AZ_RTTI(IAssetLoader, "{7C8A1F7A-3D3A-4A87-9E33-3F2FA0C77493}");
virtual ~IAssetLoader() = default;
virtual AZStd::string Load(const AZStd::string& path) = 0;
};
struct RealAssetLoader : IAssetLoader
{
AZ_RTTI(RealAssetLoader, "{B1E9C7D1-9E29-4B52-9A1E-1C7E8A0A4B13}", IAssetLoader);
AZStd::string Load(const AZStd::string& path) override { return "Loaded:" + path; }
};
struct CachingAssetLoaderProxy : IAssetLoader
{
AZ_RTTI(CachingAssetLoaderProxy, "{F6272D75-7B7C-44D0-98A4-2B8E0E3E3D8A}", IAssetLoader);
AZStd::shared_ptr<RealAssetLoader> m_real = AZStd::make_shared<RealAssetLoader>();
AZStd::unordered_map<AZStd::string, AZStd::string> m_cache;
AZStd::string Load(const AZStd::string& path) override
{
auto it = m_cache.find(path);
if (it != m_cache.end()) return it->second;
auto data = m_real->Load(path);
m_cache[path] = data;
return data;
}
};
// ============= 责任链 =============
struct IRequest
{
AZ_RTTI(IRequest, "{D1C3A7B1-8C46-4C6E-98D7-5A819DDFE5C3}");
virtual ~IRequest() = default;
virtual int GetLevel() const = 0;
};
struct DamageRequest : IRequest
{
AZ_RTTI(DamageRequest, "{CA5C873C-71AF-4D4E-8E3E-2E6F23A7B2D0}", IRequest);
int m_level = 1;
int GetLevel() const override { return m_level; }
};
struct Handler
{
AZ_RTTI(Handler, "{F1F9E3B3-7FD9-4C23-8481-2A8B2CB4C64E}");
AZStd::shared_ptr<Handler> m_next;
virtual ~Handler() = default;
void SetNext(AZStd::shared_ptr<Handler> n) { m_next = n; }
virtual bool Handle(IRequest& r)
{
if (m_next) return m_next->Handle(r);
return false;
}
};
struct LowLevelHandler : Handler
{
AZ_RTTI(LowLevelHandler, "{D7B02E21-1320-4D61-BD48-1C9B9A2DD2D8}", Handler);
bool Handle(IRequest& r) override
{
if (r.GetLevel() <= 1) return true;
return Handler::Handle(r);
}
};
struct HighLevelHandler : Handler
{
AZ_RTTI(HighLevelHandler, "{B9A42F0F-15A4-45E0-BB6C-38C1A0C0F8B1}", Handler);
bool Handle(IRequest& r) override
{
if (r.GetLevel() > 1) return true;
return Handler::Handle(r);
}
};
// ============= 命令 =============
struct ICommand
{
AZ_RTTI(ICommand, "{BFB6E486-8B9C-4E3F-8D9B-3B6E4D8B9D6F}");
virtual ~ICommand() = default;
virtual void Execute() = 0;
virtual void Undo() = 0;
};
struct MoveReceiver
{
AZ_TYPE_INFO(MoveReceiver, "{F2A93F12-8A1A-4B29-8C2E-2B1FA0F0F11A}");
float x=0,y=0;
void Move(float dx,float dy){x+=dx;y+=dy;}
};
struct MoveCommand : ICommand
{
AZ_RTTI(MoveCommand, "{5B7B3D44-BC49-4F2B-8B5E-09E7F3A5A2F8}", ICommand);
MoveReceiver* m_r=nullptr; float dx=0,dy=0;
MoveCommand(MoveReceiver* r,float dx_,float dy_):m_r(r),dx(dx_),dy(dy_){}
void Execute() override { if(m_r) m_r->Move(dx,dy); }
void Undo() override { if(m_r) m_r->Move(-dx,-dy); }
};
struct Invoker
{
AZ_TYPE_INFO(Invoker, "{2E7F5C45-7F5E-4A0C-9FCA-168B59A9C9D7}");
AZStd::vector<AZStd::shared_ptr<ICommand>> history;
void Do(const AZStd::shared_ptr<ICommand>& c){ c->Execute(); history.push_back(c); }
void UndoLast(){ if(!history.empty()){ history.back()->Undo(); history.pop_back(); } }
};
// ============= 解释器 =============
struct IExpression
{
AZ_RTTI(IExpression, "{8F1E32D2-3B5A-4C6C-8C2A-3E7E223B5A11}");
virtual ~IExpression() = default;
virtual int Eval() const = 0;
};
struct NumberExpr : IExpression
{
AZ_RTTI(NumberExpr, "{1E0C3CB3-43B2-4A5A-9A7C-EE6D6B2E2B44}", IExpression);
int v; explicit NumberExpr(int vv):v(vv){}
int Eval() const override { return v; }
};
struct AddExpr : IExpression
{
AZ_RTTI(AddExpr, "{E1E66B63-6F1A-4D5F-93E7-6A9370C3C69D}", IExpression);
AZStd::shared_ptr<IExpression> a,b;
int Eval() const override { return a->Eval()+b->Eval(); }
};
// ============= 迭代器 =============
template<class T>
struct SimpleCollection
{
AZ_TYPE_INFO_TEMPLATE(SimpleCollection, "{B0B0A4E5-3D66-4B95-8E29-39E2D6B2D3C3}", T);
AZStd::vector<T> data;
struct Iterator
{
size_t i; SimpleCollection* c;
bool HasNext() const { return i < c->data.size(); }
T& Next() { return c->data[i++]; }
};
Iterator GetIterator() { return Iterator{0,this}; }
};
// ============= 中介者 =============
struct Mediator;
struct Colleague
{
AZ_RTTI(Colleague, "{E9B7C9D3-3B6D-4C6E-9E2A-4E3B7D2F5B6C}");
Mediator* m = nullptr;
virtual ~Colleague() = default;
virtual void Receive(const AZStd::string& msg) = 0;
void Send(const AZStd::string& msg);
};
struct Mediator
{
AZ_RTTI(Mediator, "{6B7E4C9D-5F6B-4E8A-9B2C-7E6F5A9B3C2D}");
AZStd::vector<Colleague*> cs;
void Register(Colleague* c){ cs.push_back(c); c->m=this; }
void Broadcast(const AZStd::string& msg)
{ for(auto* c:cs) c->Receive(msg); }
};
inline void Colleague::Send(const AZStd::string& msg){ if(m) m->Broadcast(msg); }
struct ConcreteColleague : Colleague
{
AZ_RTTI(ConcreteColleague, "{D6B5E4F2-1F4B-43A6-8B6E-6E2D4F3A2B1C}", Colleague);
AZStd::string last;
void Receive(const AZStd::string& msg) override { last = msg; }
};
// ============= 备忘录 =============
struct Snapshot
{
AZ_TYPE_INFO(Snapshot, "{0D7B2BDE-3B45-4E4C-8B0D-6F0B5B8E6C2F}");
int hp; int mp;
};
struct Player
{
AZ_TYPE_INFO(Player, "{E1A6B2B9-8C31-4A3E-9D7F-7E3C2B1A9F0C}");
int hp=100, mp=50;
Snapshot Save() const { return Snapshot{hp,mp}; }
void Load(const Snapshot& s){ hp=s.hp; mp=s.mp; }
};
// ============= 观察者(用 EBus/BehaviorContext) =============
class HealthNotifications
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
virtual void OnHealthChanged(int newHp) = 0;
};
using HealthNotificationBus = AZ::EBus<HealthNotifications>;
class HealthComponent
: public AZ::Component
{
public:
AZ_COMPONENT(HealthComponent, "{4B1F8E8B-8B17-49D3-8E7A-4B8D7688A7C5}");
static void Reflect(AZ::ReflectContext* context)
{
if (auto bc = azrtti_cast<AZ::BehaviorContext*>(context))
{
bc->Class<HealthComponent>("dp.HealthComponent")
->Method("SetHp", &HealthComponent::SetHp)
->Method("GetHp", &HealthComponent::GetHp);
bc->EBus<HealthNotificationBus>("dp.HealthNotificationBus")
->Handler<HealthNotifications>();
}
}
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType&){}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType&){}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType&){}
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType&){}
void Activate() override {}
void Deactivate() override {}
void SetHp(int hp)
{
m_hp = hp;
HealthNotificationBus::Broadcast(&HealthNotifications::OnHealthChanged, m_hp);
}
int GetHp() const { return m_hp; }
private:
int m_hp = 100;
};
// ============= 状态 =============
struct IAIState
{
AZ_RTTI(IAIState, "{2B8E4F01-0EAF-4430-8B8D-2A9B1BFE9C81}");
virtual ~IAIState() = default;
virtual AZStd::string Update() = 0;
};
struct PatrolState : IAIState
{
AZ_RTTI(PatrolState, "{E1C3F51C-2F8D-4C2A-8F6A-1B3E2D4C5F6A}", IAIState);
AZStd::string Update() override { return "Patrolling"; }
};
struct ChaseState : IAIState
{
AZ_RTTI(ChaseState, "{B7E8A2D4-3B1C-4D5E-8A3F-7E2D1C4B5A6C}", IAIState);
AZStd::string Update() override { return "Chasing"; }
};
struct AIContext
{
AZ_TYPE_INFO(AIContext, "{7E2F1B3C-5D4A-4A88-8D9C-1F2E3D4C5B6A}");
AZStd::shared_ptr<IAIState> cur;
AZStd::string Tick(){ return cur?cur->Update():"Idle"; }
void Set(AZStd::shared_ptr<IAIState> s){ cur = s; }
};
// ============= 策略 =============
struct IPathStrategy
{
AZ_RTTI(IPathStrategy, "{A1B2C3D4-E5F6-47A8-90AB-1C2D3E4F5A6B}");
virtual ~IPathStrategy() = default;
virtual AZStd::string Calc() = 0;
};
struct AStarStrategy : IPathStrategy
{
AZ_RTTI(AStarStrategy, "{B2C3D4E5-F6A7-48B9-80AB-2C3D4E5F6A7B}", IPathStrategy);
AZStd::string Calc() override { return "A* path"; }
};
struct DijkstraStrategy : IPathStrategy
{
AZ_RTTI(DijkstraStrategy, "{C3D4E5F6-A7B8-49CA-70AB-3C4D5E6F7A8B}", IPathStrategy);
AZStd::string Calc() override { return "Dijkstra path"; }
};
struct PathContext
{
AZ_TYPE_INFO(PathContext, "{D4E5F6A7-B8C9-4ADB-60AB-4D5E6F7A8B9C}");
AZStd::shared_ptr<IPathStrategy> s;
void Set(AZStd::shared_ptr<IPathStrategy> ss){ s = ss; }
AZStd::string Run(){ return s? s->Calc() : ""; }
};
// ============= 模板方法 =============
struct LevelLoader
{
AZ_RTTI(LevelLoader, "{E5F6A7B8-C9DA-4BEC-50AB-5D6E7F8A9B0C}");
virtual ~LevelLoader() = default;
AZStd::string Load()
{
return Read() + "->" + Parse() + "->" + CreateEntities();
}
virtual AZStd::string Read(){ return "Read file"; }
virtual AZStd::string Parse(){ return "Parse data"; }
virtual AZStd::string CreateEntities(){ return "Create entities"; }
};
struct JsonLevelLoader : LevelLoader
{
AZ_RTTI(JsonLevelLoader, "{F6A7B8C9-DAB0-4CED-40AB-6D7E8F9A0B1C}", LevelLoader);
AZStd::string Read() override { return "Read JSON"; }
AZStd::string Parse() override { return "Parse JSON"; }
};
// ============= 访问者 =============
struct IVisitable;
struct IVisitor
{
AZ_RTTI(IVisitor, "{07E76194-2C28-4D2B-8C8F-559F43B2CCB6}");
virtual ~IVisitor() = default;
virtual void Visit(class VisitableA&) = 0;
virtual void Visit(class VisitableB&) = 0;
};
struct IVisitable
{
AZ_RTTI(IVisitable, "{C50E7D0D-3D36-4F42-9B6F-B67F8F9E62F3}");
virtual ~IVisitable() = default;
virtual void Accept(IVisitor&) = 0;
};
struct VisitableA : IVisitable
{
AZ_RTTI(VisitableA, "{6B6F1E38-1ED5-4918-BA71-7F2A0EFA3F4A}", IVisitable);
void Accept(IVisitor& v) override { v.Visit(*this); }
};
struct VisitableB : IVisitable
{
AZ_RTTI(VisitableB, "{0F3A0C5E-57B2-42C1-8B81-0D8E610C716F}", IVisitable);
void Accept(IVisitor& v) override { v.Visit(*this); }
};
struct ConcreteVisitor : IVisitor
{
AZ_RTTI(ConcreteVisitor, "{E3E9B8C1-1DA4-41E7-8B9E-6F2C1E4B3A5C}", IVisitor);
AZStd::string log;
void Visit(VisitableA&) override { log += "A;"; }
void Visit(VisitableB&) override { log += "B;"; }
};
// ============= SystemComponent 反射所有类型 =============
class DesignPatternsSystemComponent
: public AZ::Component
, public LogRequests
{
public:
AZ_COMPONENT(DesignPatternsSystemComponent, "{0C2E7E65-5C7A-4C63-A07D-7C0C2E0F83C7}");
static void Reflect(AZ::ReflectContext* context)
{
if (auto sc = azrtti_cast<AZ::SerializeContext*>(context))
{
sc->Class<DesignPatternsSystemComponent, AZ::Component>();
sc->Class<HealthComponent, AZ::Component>();
}
if (auto bc = azrtti_cast<AZ::BehaviorContext*>(context))
{
// 日志 EBus
bc->EBus<LogBus>("dp.LogBus")
->Event("Info", &LogRequests::Info);
// 抽象工厂
bc->Class<IEnemy>("dp.IEnemy")->Method("Attack", &IEnemy::Attack);
bc->Class<Orc>("dp.Orc")->Constructor<>();
bc->Class<Troll>("dp.Troll")->Constructor<>();
bc->Class<IEnemyFactory>("dp.IEnemyFactory");
bc->Class<OrcFactory>("dp.OrcFactory")->Constructor<>();
bc->Class<TrollFactory>("dp.TrollFactory")->Constructor<>();
// 建造者
bc->Class<Character>("dp.Character")
->Property("name", BehaviorValueProperty(&Character::m_name))
->Property("hp", BehaviorValueProperty(&Character::m_hp))
->Property("attack", BehaviorValueProperty(&Character::m_attack));
bc->Class<WarriorBuilder>("dp.WarriorBuilder")->Constructor<>()
->Method("Reset", &WarriorBuilder::Reset)
->Method("SetName", &WarriorBuilder::SetName)
->Method("SetHP", &WarriorBuilder::SetHP)
->Method("SetAttack", &WarriorBuilder::SetAttack)
->Method("GetResult", &WarriorBuilder::GetResult);
bc->Class<CharacterDirector>("dp.CharacterDirector")->Constructor<>()
->Method("MakeTank", &CharacterDirector::MakeTank);
// 工厂方法
bc->Class<IProjectile>("dp.IProjectile")->Method("Fire", &IProjectile::Fire);
bc->Class<Arrow>("dp.Arrow")->Constructor<>();
bc->Class<Fireball>("dp.Fireball")->Constructor<>();
bc->Class<ArrowSpawner>("dp.ArrowSpawner")->Constructor<>()
->Method("Make", &ArrowSpawner::Make);
bc->Class<FireballSpawner>("dp.FireballSpawner")->Constructor<>()
->Method("Make", &FireballSpawner::Make);
// 原型
bc->Class<ICloneable>("dp.ICloneable");
bc->Class<Spell>("dp.Spell")->Constructor<>()
->Property("name", BehaviorValueProperty(&Spell::m_name))
->Property("power", BehaviorValueProperty(&Spell::m_power))
->Method("Clone", &Spell::Clone);
// 单例接口(通过静态访问)
bc->Class<ConfigInterface>("dp.Config")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Method("Get", &ConfigInterface::Get)
->Method("Register", &ConfigInterface::Register)
->Method("Unregister", &ConfigInterface::Unregister);
bc->Class<ConfigImpl>("dp.ConfigImpl")->Constructor<>()
->Method("Set", &ConfigImpl::Set)
->Method("GetKV", &ConfigImpl::Get);
// 适配器
bc->Class<LegacyAudioAdapter>("dp.LegacyAudioAdapter")->Constructor<>()
->Method("Play", &LegacyAudioAdapter::Play);
// 桥接
bc->Class<VulkanImpl>("dp.VulkanImpl")->Constructor<>();
bc->Class<DX12Impl>("dp.DX12Impl")->Constructor<>();
bc->Class<RenderAbstraction>("dp.RenderAbstraction")->Constructor<AZStd::shared_ptr<IRenderImpl>>()
->Method("RenderMesh", &RenderAbstraction::RenderMesh);
// 组合
bc->Class<Leaf>("dp.Leaf")->Constructor<>()
->Property("cost", BehaviorValueProperty(&Leaf::m_cost))
->Method("GetCost", &Leaf::GetCost);
bc->Class<CompositeNode>("dp.CompositeNode")->Constructor<>()
->Method("Add", &CompositeNode::Add)
->Method("GetCost", &CompositeNode::GetCost);
// 装饰
bc->Class<Sword>("dp.Sword")->Constructor<>()
->Method("Damage", &Sword::Damage)
->Method("Desc", &Sword::Desc);
bc->Class<FireEnchant>("dp.FireEnchant")->Constructor<AZStd::shared_ptr<IWeapon>>()
->Method("Damage", &FireEnchant::Damage)
->Method("Desc", &FireEnchant::Desc);
// 外观
bc->Class<EngineFacade>("dp.EngineFacade")->Constructor<>()
->Method("Boot", &EngineFacade::Boot);
// 享元
bc->Class<BulletFlyweightFactory>("dp.BulletFlyweightFactory")->Constructor<>()
->Method("Get", &BulletFlyweightFactory::Get);
// 代理
bc->Class<CachingAssetLoaderProxy>("dp.CachingAssetLoaderProxy")->Constructor<>()
->Method("Load", &CachingAssetLoaderProxy::Load);
// 责任链
bc->Class<DamageRequest>("dp.DamageRequest")->Constructor<>()
->Property("level", BehaviorValueProperty(&DamageRequest::m_level));
bc->Class<LowLevelHandler>("dp.LowLevelHandler")->Constructor<>()
->Method("SetNext", &LowLevelHandler::SetNext)
->Method("Handle", &LowLevelHandler::Handle);
bc->Class<HighLevelHandler>("dp.HighLevelHandler")->Constructor<>()
->Method("SetNext", &HighLevelHandler::SetNext)
->Method("Handle", &HighLevelHandler::Handle);
// 命令
bc->Class<MoveReceiver>("dp.MoveReceiver")->Constructor<>()
->Property("x", BehaviorValueProperty(&MoveReceiver::x))
->Property("y", BehaviorValueProperty(&MoveReceiver::y))
->Method("Move", &MoveReceiver::Move);
bc->Class<MoveCommand>("dp.MoveCommand")->Constructor<MoveReceiver*,float,float>()
->Method("Execute", &MoveCommand::Execute)
->Method("Undo", &MoveCommand::Undo);
bc->Class<Invoker>("dp.Invoker")->Constructor<>()
->Method("Do", &Invoker::Do)
->Method("UndoLast", &Invoker::UndoLast);
// 解释器
bc->Class<NumberExpr>("dp.NumberExpr")->Constructor<int>()
->Method("Eval", &NumberExpr::Eval);
bc->Class<AddExpr>("dp.AddExpr")->Constructor<>()
->Property("a", BehaviorValueProperty(&AddExpr::a))
->Property("b", BehaviorValueProperty(&AddExpr::b))
->Method("Eval", &AddExpr::Eval);
// 迭代器(以简单容器暴露仅作示意,脚本侧更适配数组)
// 这里不做 BehaviorContext 暴露,避免模板复杂性;游戏里建议用 Lua 表或 ScriptCanvas 数组。
// 中介者
bc->Class<Mediator>("dp.Mediator")->Constructor<>()
->Method("Register", &Mediator::Register)
->Method("Broadcast", &Mediator::Broadcast);
bc->Class<ConcreteColleague>("dp.ConcreteColleague")->Constructor<>()
->Property("last", BehaviorValueProperty(&ConcreteColleague::last))
->Method("Send", &ConcreteColleague::Send);
// 备忘录
bc->Class<Player>("dp.Player")->Constructor<>()
->Property("hp", BehaviorValueProperty(&Player::hp))
->Property("mp", BehaviorValueProperty(&Player::mp))
->Method("Save", &Player::Save)
->Method("Load", &Player::Load);
// 观察者(HealthComponent 已反射,EBus 也暴露)
HealthComponent::Reflect(context);
// 状态
bc->Class<PatrolState>("dp.PatrolState")->Constructor<>()
->Method("Update", &PatrolState::Update);
bc->Class<ChaseState>("dp.ChaseState")->Constructor<>()
->Method("Update", &ChaseState::Update);
bc->Class<AIContext>("dp.AIContext")->Constructor<>()
->Method("Set", &AIContext::Set)
->Method("Tick", &AIContext::Tick);
// 策略
bc->Class<AStarStrategy>("dp.AStarStrategy")->Constructor<>()
->Method("Calc", &AStarStrategy::Calc);
bc->Class<DijkstraStrategy>("dp.DijkstraStrategy")->Constructor<>()
->Method("Calc", &DijkstraStrategy::Calc);
bc->Class<PathContext>("dp.PathContext")->Constructor<>()
->Method("Set", &PathContext::Set)
->Method("Run", &PathContext::Run);
// 模板方法
bc->Class<JsonLevelLoader>("dp.JsonLevelLoader")->Constructor<>()
->Method("Load", &JsonLevelLoader::Load);
// 访问者
bc->Class<VisitableA>("dp.VisitableA")->Constructor<>()
->Method("Accept", &VisitableA::Accept);
bc->Class<VisitableB>("dp.VisitableB")->Constructor<>()
->Method("Accept", &VisitableB::Accept);
bc->Class<ConcreteVisitor>("dp.ConcreteVisitor")->Constructor<>()
->Property("log", BehaviorValueProperty(&ConcreteVisitor::log));
}
}
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& s) { s.push_back(AZ_CRC_CE("DesignPatternsService")); }
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& s) { s.push_back(AZ_CRC_CE("DesignPatternsService")); }
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArray::type&) {}
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArray::type&) {}
void Activate() override
{
m_logger.Activate();
LogBus::Handler::BusConnect();
ConfigInterface::Register(&m_config);
}
void Deactivate() override
{
ConfigInterface::Unregister(&m_config);
LogBus::Handler::BusDisconnect();
m_logger.Deactivate();
}
// LogRequests
void Info(const AZStd::string& msg) override
{
AZ_Printf("DesignPatterns", "[Gem] %s\n", msg.c_str());
}
private:
Logger m_logger;
ConfigImpl m_config;
};
class DesignPatternsModule
: public AZ::Module
{
public:
AZ_RTTI(DesignPatternsModule, "{E3AB2E2B-8B5C-4E6F-9DB1-3C9C2CB9317E}", AZ::Module);
AZ_CLASS_ALLOCATOR(DesignPatternsModule, AZ::SystemAllocator);
DesignPatternsModule()
{
m_descriptors.insert(m_descriptors.end(), {
DesignPatternsSystemComponent::CreateDescriptor(),
HealthComponent::CreateDescriptor()
});
}
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return AZ::ComponentTypeList{
azrtti_typeid<DesignPatternsSystemComponent>()
};
}
};
}
AZ_DECLARE_MODULE_CLASS(Gem_DesignPatternsGem, dp::DesignPatternsModule)
========================
Lua 使用示例(脚本侧)
以下是一些 Lua 侧调用示例(在 O3DE Lua 环境,BehaviorContext 暴露名称以 dp.* 为前缀):
-- 抽象工厂
local orcFactory = dp.OrcFactory()
local melee = orcFactory:MakeMelee() -- 注意:上面示例暴露了 Make() 的工厂方法在工厂方法模式里,这里示例抽象工厂你可扩展暴露
-- 由于 BehaviorContext 未直接暴露 IEnemyFactory 的 CreateMelee/CreateTank,你可添加:
-- bc->Class("dp.OrcFactory")->Method("CreateMelee", &OrcFactory::CreateMelee) 等
-- 这里仅演示其它模式:
-- 工厂方法
local sp = dp.FireballSpawner()
local proj = sp:Make()
print(proj:Fire())
-- 建造者
local b = dp.WarriorBuilder()
local dir = dp.CharacterDirector()
dir:MakeTank(b)
local c = b:GetResult()
print(c.name, c.hp, c.attack)
-- 单例
local cfg = dp.Config.Get()
-- 注意:Get 返回的是 C++ 指针语义;可通过 SystemComponent 在激活时已注册。
local impl = dp.ConfigImpl()
impl:Set("difficulty", "hard")
dp.Config.Register(impl)
print(dp.Config.Get():GetKV("difficulty"))
-- 装饰
local sword = dp.Sword()
local fireSword = dp.FireEnchant(sword)
print(fireSword:Desc(), fireSword:Damage())
-- 命令
local r = dp.MoveReceiver()
local inv = dp.Invoker()
local cmd = dp.MoveCommand(r, 1, 2)
inv:Do(cmd)
print(r.x, r.y)
inv:UndoLast()
print(r.x, r.y)
-- 状态/策略
local ctx = dp.AIContext()
ctx:Set(dp.PatrolState())
print(ctx:Tick())
ctx:Set(dp.ChaseState())
print(ctx:Tick())
-- 访问者
local v = dp.ConcreteVisitor()
local a = dp.VisitableA()
local b2 = dp.VisitableB()
a:Accept(v); b2:Accept(v)
print(v.log)
========================
ScriptCanvas 使用
- 因所有相关类已通过 BehaviorContext 暴露,你可以在 ScriptCanvas 中看到 dp.* 类型和方法。
- 对于 EBus(dp.LogBus、dp.HealthNotificationBus),可用 ScriptCanvas 的 EBus 节点进行广播/侦听。
- 若需要节点更友好命名,可用 AZ::Script::Attributes 指定。
========================
在 O3DE 场景中的结合点
- 观察者:HealthComponent 通过 HealthNotificationBus 广播,其他组件或 ScriptCanvas 图接入 OnHealthChanged 事件。
- 命令:Undo/Redo 可在编辑器工具或玩法中管理玩家操作历史。
- 代理:资源加载可接 AZ::Data::AssetManager 封装,示例为最小实现。
- 享元:将 mesh/material 等内在状态放到工厂池,实例外在状态运行时传入,用于弹药、粒子等高频对象。
- 单例:通过 Interface/Register 在 SystemComponent Activate/Deactivate 生命周期注册与释放。
- 桥接/适配器:和渲染/音频子系统对接时保持上层抽象解耦。
需要我把上述代码拆分为标准 Gem 目录的“完整工程模板”(含 Gem.json、实际 CMake 文件、每个 .h/.cpp 分拆)吗?我可以直接生成按文件划分的全套代码与清单。
367

被折叠的 条评论
为什么被折叠?



