先贴链接:https://gpp.tkchu.me/prototype.html
轮到原型模式了,传统原型模式在现代游戏引擎中基本没用,但原型思想的应用还是较为广泛。
UE5中的"原型"实现
1. Class Default Object (CDO) - UE的核心原型系统
// UE的CDO就是原型模式的高级实现
class YOURGAME_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor()
{
// CDO构造时设置的值成为"原型"
PrimaryActorTick.bCanEverTick = true;
Health = 100.0f;
MaxHealth = 100.0f;
}
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Health;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MaxHealth;
};
// 每个类只有一个CDO(原型对象)
AMyActor* CDO = AMyActor::StaticClass()->GetDefaultObject<AMyActor>();
// 所有实例都从CDO"克隆"初始值
AMyActor* NewInstance = GetWorld()->SpawnActor<AMyActor>();
// NewInstance的Health和MaxHealth都是从CDO复制的100.0f
关键:UE的每个UClass都有一个CDO,这就是原型,所有实例创建时都从CDO复制初始状态。
2. Blueprint类系统 - 可视化原型设计
// Blueprint类就是运行时原型系统
class UBlueprintGeneratedClass : public UClass
{
// Blueprint编辑器中设置的所有默认值
// 都存储在这个类的CDO中
virtual void InitializeDefaultObject(UObject* DefaultObject) override
{
Super::InitializeDefaultObject(DefaultObject);
// 从Blueprint数据初始化CDO(原型)
for (const auto& PropertyValue : BlueprintDefaultValues)
{
PropertyValue.ApplyToObject(DefaultObject);
}
}
};
关键:Blueprint编辑器实际上就是一个可视化的原型设计工具
3. Data Table & Data Asset - 数据驱动的原型
// DataTable行就是数据原型
USTRUCT(BlueprintType)
struct FWeaponData : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString WeaponName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Damage;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Range;
};
// 使用原型数据创建武器实例
class AWeapon : public AActor
{
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FDataTableRowHandle WeaponDataHandle;
public:
void InitializeFromDataTable()
{
if (FWeaponData* Data = WeaponDataHandle.GetRow<FWeaponData>(""))
{
// 从原型数据"克隆"属性
WeaponName = Data->WeaponName;
Damage = Data->Damage;
Range = Data->Range;
}
}
};
这就是文章中提到的JSON原型委托思想在UE中的实现
4. UE5的实际原型应用场景
Spawner系统的现代实现:
// UE5中真正的Enemy Spawner实现
UCLASS(BlueprintType)
class YOURGAME_API UEnemySpawner : public UObject
{
GENERATED_BODY()
public:
// 使用SubClass而不是传统原型Clone
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AEnemy> EnemyClassToSpawn;
// 原型数据 - 可以覆盖CDO的默认值
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FEnemySpawnParameters SpawnParameters;
AActor* SpawnEnemy(UWorld* World, const FVector& Location)
{
if (!EnemyClassToSpawn) return nullptr;
// UE的"克隆"实际上是类实例化 + 参数覆盖
AEnemy* Enemy = World->SpawnActor<AEnemy>(EnemyClassToSpawn, Location, FRotator::ZeroRotator);
// 应用原型参数(覆盖CDO默认值)
if (Enemy)
{
Enemy->SetHealth(SpawnParameters.Health);
Enemy->SetSpeed(SpawnParameters.Speed);
Enemy->SetAIBehavior(SpawnParameters.AIBehaviorClass);
}
return Enemy;
}
};
为什么UE不用传统Clone模式?
- 性能考虑:UE的对象有复杂的组件层次和引用关系,深拷贝代价太高
- 内存管理:UE有垃圾回收系统,Clone会破坏对象生命周期管理
- 序列化复杂性:UE对象需要支持保存/加载,Clone会让序列化变得复杂
UE5中原型模式的应用
GameplayAbilitySystem中的原型思想
// Ability的Grant就是原型模式应用
class UGameplayAbility : public UObject
{
// Ability类本身就是"原型"
virtual UGameplayAbility* CreateAbilityInstance() const
{
// 没用简单的Clone,而是智能的实例化
UGameplayAbility* Instance = NewObject<UGameplayAbilityClass>();
Instance->SetAbilityLevel(this->AbilityLevel);
Instance->SetCooldownDuration(this->CooldownDuration);
return Instance;
}
};
World Partition的原型加载
// UE5的World Partition也使用了原型思想
class UWorldPartitionStreamingSource
{
// StreamingSource本身就是原型
// 每个加载的Cell都是从原型派生的变体
void LoadCell(const FWorldPartitionCellCoord& CellCoord)
{
// 从原型数据创建Cell实例
UWorldPartitionCell* Cell = CreateCellFromPrototype(CellCoord);
Cell->LoadActors(); // 加载Cell中的Actors(也是从原型实例化)
}
};
何时在UE项目中应用原型思想
1. 数据配置系统
// 使用DataAsset作为配置原型
UCLASS(BlueprintType)
class UEnemyConfigAsset : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float BaseHealth;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<UEnemyAIComponent> AIComponentClass;
// 这是原型的"克隆"方法
UFUNCTION(BlueprintCallable)
void ApplyToEnemy(AEnemy* Enemy) const
{
Enemy->SetMaxHealth(BaseHealth);
Enemy->CreateAIComponent(AIComponentClass);
}
};
2. 程序化生成系统
// 使用原型数据驱动程序化内容
USTRUCT(BlueprintType)
struct FBuildingPrototype
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FVector> WindowPositions;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> DoorClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UMaterialInterface* WallMaterial;
};
// 程序化建筑生成器
class UProceduralBuildingGenerator
{
TArray<FBuildingPrototype> BuildingPrototypes;
public:
AActor* GenerateBuilding(int32 PrototypeIndex, const FTransform& Transform)
{
const FBuildingPrototype& Prototype = BuildingPrototypes[PrototypeIndex];
// 从原型"克隆"建筑
ABuilding* Building = CreateBuilding(Transform);
Building->ApplyPrototype(Prototype); // 应用原型数据
return Building;
}
};
核心:UE5中的原型模式已经演化成了数据驱动的实例化系统,而非传统的对象克隆。这种设计更符合现代游戏引擎的需求。

1519

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



