• UE4 C++设计模式:享元模式(Flyweight Pattern)


    目录

    描述

    套路

    使用场景

    优缺点

    UE4实践

    创建享元抽象类

    创建具体享元类-地鼠

    创建具体享元类-炸弹

    创建享元工厂类


    描述

    • 运用共享技术有效的支持大量细粒度对象的复用,系统只使用少量对象,而这些对象都很相似,状态变换很小,可以实现对象的多次复用
    • 由于向元模式要求能够共享的对象必须是细粒度对象,因此又称为轻量级模式,是一种对象结构型模式
    • 享元类可以有多个对象,单例类只有一个对象
    • 内部状态
      • 享元对象内部不随外界环境改变而改变的公共部分
    • 外部状态
      • 享元对象内部随环境的改变而改变,不能够共享的状态就是外部状态

    套路

    • Flyweight(抽象享元类)
      • 一个接口或者抽象类,声明了具体享元类的公共方法
    • ConcreteFlyweight(具体享元类)
      • 实现了抽象享元类,实例为享元对象
    • UnsharedConcreteFlyweight(非共享具体享元类)
      • 并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类
    • FlyweightFactory(享元工厂类)
      • 用于创建并管理享元对象,一般设计为一个存储“Key-Value”键值对的组合,可结合工厂模式设计
      • 提供一个用于存储享元对象的享元池,当用户需要对象时,首先从享元池中获取,如果享元池中不存在,那么创建一个新的享元对象返回给用户,并在享元池中保存该新增对象

    使用场景

    • 程序需要生成数量巨大的相似对象
    • 对象中包含可抽取且能在多个对象之间共享的重复状态
    • 示例:
      • 游戏场景中的植被、子弹等重复物体,贴图或者是网格,Transform不一样
      • 与对象池的区别:对象池是可以重复使用,每次使用都是独占使用;享元是共享使用,对象池可以看做享元模式的升级版

    优缺点

    • 优点
      • 可以极大地减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份
      • 外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以再不同的环境中被共享
    • 缺点
      • 使系统变得复杂,需要分离出内部状态和外部状态,使得程序逻辑复杂化
      • 为了使对象可以共享,享元模式需要将对线的状态外部化,而读取外部状态使得运行时间变长

    UE4实践

    打地鼠,中间有炸弹。

    避免高频率创建和销毁,提高重复利用,此时可以使用享元模式

    创建享元抽象类

    1. // 抽象享元类
    2. UCLASS(Abstract)
    3. class DESIGNPATTERNS_API AFlyweightObject : public AActor
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 用于每次重新调用时,初始化各个参数
    8. virtual void Init(FVector pInitPos) {
    9. bIsLiving = true;
    10. UE_LOG(LogTemp, Warning, TEXT("----------------------------------"));
    11. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s is used. Pos%s"),
    12. *this->GetName(),
    13. *UKismetStringLibrary::Conv_VectorToString(pInitPos));
    14. GetWorld()->GetTimerManager().SetTimer(m_pTimerHandle, this, &AFlyweightObject::End, 1.0f, false);
    15. }
    16. // 自定义销毁程序,不是真正的销毁实例
    17. virtual void End() {
    18. if (bIsLiving)
    19. {
    20. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" ******** %s is destroyed."), *this->GetName());
    21. bIsLiving = false;
    22. GetWorld()->GetTimerManager().ClearTimer(m_pTimerHandle);
    23. }
    24. }
    25. // 击中
    26. virtual void Hit() PURE_VIRTUAL(AFlyweightObject::IsValid, );
    27. // 用于判断是否已经在使用、空闲状态
    28. bool IsLiving() {
    29. return bIsLiving;
    30. }
    31. protected:
    32. bool bIsLiving;
    33. FTimerHandle m_pTimerHandle;
    34. };

    创建具体享元类-地鼠

    1. // 具体享元类 —— 地鼠
    2. UCLASS()
    3. class DESIGNPATTERNS_API AMole : public AFlyweightObject
    4. {
    5. GENERATED_BODY()
    6. public:
    7. virtual void Hit() {
    8. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s is hit. Add 10 score"), *this->GetName());
    9. End();
    10. }
    11. };

    创建具体享元类-炸弹

    1. // 具体享元类 —— 炸弹
    2. UCLASS()
    3. class DESIGNPATTERNS_API ABomb : public AFlyweightObject
    4. {
    5. GENERATED_BODY()
    6. public:
    7. virtual void Hit() {
    8. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s is hit. Add -5 score"), *this->GetName());
    9. End();
    10. }
    11. };

    创建享元工厂类

    内部可使用享元池存储多个享元对象

    1. // 享元工厂类
    2. UCLASS()
    3. class DESIGNPATTERNS_API AFlyweightFactory : public AActor
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 初始化多个地鼠和炸弹实例以供使用
    8. void Init() {
    9. for (int32 i = 0; i < 3; i++)
    10. {
    11. AMole* mole = GetWorld()->SpawnActor(FVector::ZeroVector, FRotator::ZeroRotator);
    12. m_pMolePool.Add(mole->GetName(), mole);
    13. ABomb* bomb = GetWorld()->SpawnActor(FVector::ZeroVector, FRotator::ZeroRotator);
    14. m_pBombPool.Add(bomb->GetName(), bomb);
    15. }
    16. }
    17. // 获取未被使用的地鼠实例
    18. AMole* GetMole() {
    19. for (TTuple& item : m_pMolePool)
    20. {
    21. if (!(item.Value)->IsLiving())
    22. {
    23. return item.Value;
    24. }
    25. }
    26. return nullptr;
    27. }
    28. // 获取未被使用的炸弹实例
    29. ABomb* GetBomb() {
    30. for (TTuple& item : m_pBombPool)
    31. {
    32. if (!(item.Value)->IsLiving())
    33. {
    34. return item.Value;
    35. }
    36. }
    37. return nullptr;
    38. }
    39. // 随机获取未被使用的地鼠或炸弹实例
    40. AFlyweightObject* GetRandomElement() {
    41. if (UKismetMathLibrary::RandomFloat() < 0.7f)
    42. {
    43. return GetMole();
    44. }
    45. else
    46. {
    47. return GetBomb();
    48. }
    49. }
    50. private:
    51. // 存储的地鼠对象池
    52. UPROPERTY()
    53. TMap m_pMolePool;
    54. // 存储的炸弹对象池
    55. UPROPERTY()
    56. TMap m_pBombPool;
    57. };

     

    测试

    1. UCLASS()
    2. class DESIGNPATTERNS_API AFlyweightTestActor : public AActor
    3. {
    4. GENERATED_BODY()
    5. public:
    6. // Sets default values for this actor's properties
    7. AFlyweightTestActor();
    8. // Called every frame
    9. virtual void Tick(float DeltaTime) override;
    10. protected:
    11. // Called when the game starts or when spawned
    12. virtual void BeginPlay() override;
    13. public:
    14. UPROPERTY()
    15. AFlyweightFactory* m_pFlyweightFactory;
    16. UPROPERTY()
    17. FTimerHandle TimerHandle;
    18. // 定时生成地鼠或炸弹
    19. UFUNCTION()
    20. void AutoSpawner();
    21. };

     

    1. // Called when the game starts or when spawned
    2. void AFlyweightTestActor::BeginPlay()
    3. {
    4. Super::BeginPlay();
    5. // 享元工厂实例
    6. m_pFlyweightFactory = GetWorld()->SpawnActor(AFlyweightFactory::StaticClass());
    7. m_pFlyweightFactory->Init();
    8. // 定时器
    9. GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AFlyweightTestActor::AutoSpawner, 0.4f, true);
    10. }
    11. void AFlyweightTestActor::AutoSpawner()
    12. {
    13. // 随机位置
    14. int32 RandomInt = UKismetMathLibrary::RandomInteger(16);
    15. FVector RandomPos = FVector::ZeroVector + FVector(1, 0, 0) * (RandomInt / 4) + FVector(0, 1, 0) * (RandomInt % 4);
    16. // 获取随机空闲的地鼠或炸弹
    17. AFlyweightObject* FlyweightObj = m_pFlyweightFactory->GetRandomElement();
    18. if (FlyweightObj)
    19. {
    20. // 初始化享元对象
    21. FlyweightObj->Init(RandomPos);
    22. // 假定一定概率击中
    23. if (UKismetMathLibrary::RandomFloat()<0.2)
    24. {
    25. FLatentActionInfo LatentActionInfo;
    26. UKismetSystemLibrary::Delay(GetWorld(), 0.1, LatentActionInfo);
    27. FlyweightObj->Hit();
    28. }
    29. }
    30. }

     

    1. LogTemp: Warning: ----------------------------------
    2. LogTemp: Warning: AFlyweightObject::Init Mole_0 is used. PosX=3.000 Y=1.000 Z=0.000
    3. LogTemp: Warning: ----------------------------------
    4. LogTemp: Warning: AFlyweightObject::Init Mole_1 is used. PosX=2.000 Y=3.000 Z=0.000
    5. LogTemp: Warning: ----------------------------------
    6. LogTemp: Warning: AFlyweightObject::Init Mole_2 is used. PosX=1.000 Y=1.000 Z=0.000
    7. LogTemp: Warning: AMole::Hit Mole_2 is hit. Add 10 score
    8. LogTemp: Warning: AFlyweightObject::End ******** Mole_2 is destroyed.
    9. LogTemp: Warning: AFlyweightObject::End ******** Mole_0 is destroyed.
    10. LogTemp: Warning: ----------------------------------
    11. LogTemp: Warning: AFlyweightObject::Init Mole_0 is used. PosX=3.000 Y=3.000 Z=0.000
    12. LogTemp: Warning: AFlyweightObject::End ******** Mole_1 is destroyed.
    13. LogTemp: Warning: ----------------------------------
    14. LogTemp: Warning: AFlyweightObject::Init Mole_1 is used. PosX=3.000 Y=2.000 Z=0.000
    15. LogTemp: Warning: ----------------------------------
    16. LogTemp: Warning: AFlyweightObject::Init Bomb_0 is used. PosX=3.000 Y=1.000 Z=0.000
    17. LogTemp: Warning: AFlyweightObject::End ******** Mole_0 is destroyed.
    18. LogTemp: Warning: ----------------------------------
    19. LogTemp: Warning: AFlyweightObject::Init Bomb_1 is used. PosX=3.000 Y=2.000 Z=0.000
    20. LogTemp: Warning: ABomb::Hit Bomb_1 is hit. Add -5 score
    21. LogTemp: Warning: AFlyweightObject::End ******** Bomb_1 is destroyed.
    22. LogTemp: Warning: AFlyweightObject::End ******** Mole_1 is destroyed.
    23. LogTemp: Warning: ----------------------------------
    24. LogTemp: Warning: AFlyweightObject::Init Bomb_1 is used. PosX=2.000 Y=2.000 Z=0.000
    25. LogTemp: Warning: AFlyweightObject::End ******** Bomb_0 is destroyed.
    26. LogTemp: Warning: ----------------------------------
    27. LogTemp: Warning: AFlyweightObject::Init Bomb_0 is used. PosX=2.000 Y=2.000 Z=0.000
    28. LogTemp: Warning: ----------------------------------
    29. LogTemp: Warning: AFlyweightObject::Init Bomb_2 is used. PosX=0.000 Y=3.000 Z=0.000
    30. LogTemp: Warning: AFlyweightObject::End ******** Bomb_1 is destroyed.
    31. LogTemp: Warning: ----------------------------------
    32. LogTemp: Warning: AFlyweightObject::Init Bomb_1 is used. PosX=1.000 Y=2.000 Z=0.000
    33. LogTemp: Warning: ABomb::Hit Bomb_1 is hit. Add -5 score
    34. LogTemp: Warning: AFlyweightObject::End ******** Bomb_1 is destroyed.
    35. LogTemp: Warning: AFlyweightObject::End ******** Bomb_0 is destroyed.
    36. LogTemp: Warning: ----------------------------------
    37. LogTemp: Warning: AFlyweightObject::Init Mole_0 is used. PosX=1.000 Y=2.000 Z=0.000
    38. LogTemp: Warning: AMole::Hit Mole_0 is hit. Add 10 score
    39. LogTemp: Warning: AFlyweightObject::End ******** Mole_0 is destroyed.
    40. LogTemp: Warning: AFlyweightObject::End ******** Bomb_2 is destroyed.
    41. LogTemp: Warning: ----------------------------------
    42. LogTemp: Warning: AFlyweightObject::Init Mole_0 is used. PosX=2.000 Y=3.000 Z=0.000
    43. LogTemp: Warning: ----------------------------------
    44. LogTemp: Warning: AFlyweightObject::Init Mole_1 is used. PosX=1.000 Y=0.000 Z=0.000
    45. LogTemp: Warning: ----------------------------------
    46. LogTemp: Warning: AFlyweightObject::Init Bomb_0 is used. PosX=3.000 Y=3.000 Z=0.000
    47. LogTemp: Warning: ABomb::Hit Bomb_0 is hit. Add -5 score
    48. LogTemp: Warning: AFlyweightObject::End ******** Bomb_0 is destroyed.
    49. LogTemp: Warning: AFlyweightObject::End ******** Mole_0 is destroyed.
  • 相关阅读:
    C/C++后端学习秘籍
    JDBC批处理
    【Vue3+Ts】—— webpack打包其他资源学习笔记(二)
    30.在springboot中使用thymeleaf的模板(include,insert)
    K3S 系列文章-5G IoT 网关设备 POD 访问报错 DNS 'i/o timeout'分析与解决
    深度剖析Linux磁盘分区 | LVM逻辑卷 | VDO卷 | AutoFS存储自动挂载
    使用vue实现简易的音乐播放器
    Tomcat总体架构,启动流程与处理请求流程
    CodeTON Round 3 (Div. 1 + Div. 2, Rated, Prizes) 「D. Count GCD gcd+容斥」
    msvcr110.dll丢失的解决方法介绍,教你如何快速修复问题
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/126541409