概述

定义

  • Subsystems 是一套可以定义、自动实例化和释放的类的框架。可以将其理解为 GamePlay 级别的 Component
  • 不支持网络赋值
  • 4.22开始引入,4.24完善。(可以移植源码到更早之前的版本使用。源码在手,为所欲为)

五类 Subsystems 及其生命周期

  • UEngineSubsystem(继承自 UDynamicSubsystem,UDynamicSubsystem继承自 USubsystem)

    UEngine* GEngine

    代表引擎,数量1。 Editor或Runtime模式都是全局唯一,从进程启动开始创建,进程退出时销毁。

    UEngine::Init()

  • UEditorSubsystem(继承自 UDynamicSubsystem,UDynamicSubsystem继承自 USubsystem)

    UEditorEngine* GEditor

    代表编辑器,数量1。 顾名思义,只在编辑器下存在且全局唯一,从编辑器启动开始创建,到编辑器退出时销毁。

  • UGameInstanceSubsystem (继承自 USubsystem)

    UGameInstance* GameInstance

    代表一场游戏,数量1。 从游戏的启动开始创建,游戏退出时销毁。这里的一场游戏指的是Runtime或PIE模式的运行的都算,一场游戏里可能会创建多个World切换。

  • UWorldSubsystem (继承自 USubsystem)

    UWorld* World

    代表一个世界,数量可能>1。其生命周期,跟GameMode是一起的。(EWorldType:Game,Editor,PIE,EditorPreview,GamePreview等 )

  • ULocalPlayerSubsystem (继承自 USubsystem)

    ULocalPlayer* LocalPlayer:代表本地玩家,数量可能>1。

    UE支持本地分屏多玩家类型的游戏,但往往最常见的是就只有一个。LocalPlayer虽然往往跟PlayerController一起访问,但是其生命周期其实是跟UGameInstance一起的(默认一开始的时候就创建好一定数量的本地玩家),或者更准确的说是跟LocalPlayer的具体数量挂钩(当然你也可以运行时动态调用AddLocalPlayer)。

为什么要使用 Subsystems

  • 更适用的生命周期

    • 引擎只支持一个 GameInstance ,运行周期是整个引擎的生命周期
    • 自定义 ManagerActor,生命周期一般为当前 level 的生命周期
    • Subsystems 的生命周期可以依存于Engine,Editor,World,LocalPlayer
  • 更简
    • GameInstance 或者自定义 ManagerActor,需要手动维控制创建、释放
    • Subsystems 自动创建、释放,提供 Initialize()、Deinitialize(),并且可重载
  • 更模块化、更优雅、更封装、更易于维护、移植复用
    • GameInstance 中将任务系统,计分系统,经济系统、对话系统等多个Manager 写在一起,会变得臃肿

      • 模块间的数据访问封装不够良好,容易污染
      • 不利于业务逻辑模块的复用,特别是需要进行移植的时候,以及多个插件同时都有自己的 GameInstance
    • Subsystems 可以为不同的 Manager 创建对应的Subsystems
      • 如 Manager 划分,

        • 任务系统Subsystem : UGameInstanceSubsystem
        • 计分系统Subsystem : UGameInstanceSubsystem
        • 经济系统Subsystem : UGameInstanceSubsystem
        • 对话系统Subsystem : UGameInstanceSubsystem
      • 更模块化,代码显得优雅
      • 解耦性高,易于维护、分工协作;易于移植复用
      • 模块间的数据访问具有更好的封装性
  • 更友好的访问接口
    • Subsystem 更像以全局变量的形式来访问
    • 提供了 Python 脚本的访问,用于编写编辑器脚本或编写测试代码
  • Subsystem 无需覆盖引擎类。

Subsystems 的使用

  • 创建过程,涉及 FSubsystemCollectionBase

    1. FSubsystemCollectionBase::Initialize()
    2. FSubsystemCollectionBase::AddAndInitializeSubsystem()
    3. FSubsystemCollectionBase::Deinitialize()

    参考附录源码 或者 EngineDir\Engine\Source\Runtime\Engine\Private\Subsystems\SubsystemCollection.cpp

  • 默认重载函数

    • ShouldCreateSubsystem
    • Initialize()
    • Deinitialize()
  • C++ 访问

    1. // UMyEngineSubsystem 获取
    2. UMyEngineSubsystem* MyEngineSubsystem = GEngine->GetEngineSubsystem<UMyEngineSubsystem>();
    3. // UMyEditorSubsystem 获取
    4. UMyEditorSubsystem* MyEditorSubsystem = GEditor->GetEditorSubsystem<UMyEditorSubsystem>();
    5. // UMyGameInstanceSubsystem 获取
    6. //UGameInstance* GameInstance = GetWorld()->GetGameInstance();
    7. UGameInstance* GameInstance = UGameplayStatics::GetGameInstance();
    8. UMyGameInstanceSubsystem* MyGameInstanceSubsystem = GameInstance->GetSubsystem<UMyGameInstanceSubsystem>();
    9. // UMyWorldSubsystem 获取
    10. UMyWorldSubsystem* MyWorldSubsystem = GetWorld()->GetSubsystem<UMyWorldSubsystem>();
    11. // UMyLocalPlayerSubsystem 获取
    12. ULocalPlayer* LocalPlayer = UGameplayStatics::GetPlayerController()->GetLocalPlayer();
    13. UMyLocalPlayerSubsystem* MyLocalPlayerSubsystem = LocalPlayer->GetSubsystem<UMyLocalPlayerSubsystem>();
    • 引擎自带 USubsystemBlueprintLibrary 访问方法
    1. UCLASS()
    2. class ENGINE_API USubsystemBlueprintLibrary : public UBlueprintFunctionLibrary
    3. {
    4. GENERATED_BODY()
    5. public:
    6. /** Get a Game Instance Subsystem from the Game Instance associated with the provided context */
    7. UFUNCTION(BlueprintPure, Category = "Engine Subsystems", meta = (BlueprintInternalUseOnly = "true"))
    8. static UEngineSubsystem* GetEngineSubsystem(TSubclassOf<UEngineSubsystem> Class);
    9. /** Get a Game Instance Subsystem from the Game Instance associated with the provided context */
    10. UFUNCTION(BlueprintPure, Category = "GameInstance Subsystems", meta = (WorldContext = "ContextObject", BlueprintInternalUseOnly = "true"))
    11. static UGameInstanceSubsystem* GetGameInstanceSubsystem(UObject* ContextObject, TSubclassOf<UGameInstanceSubsystem> Class);
    12. /** Get a Local Player Subsystem from the Local Player associated with the provided context */
    13. UFUNCTION(BlueprintPure, Category = "LocalPlayer Subsystems", meta = (WorldContext = "ContextObject", BlueprintInternalUseOnly = "true"))
    14. static ULocalPlayerSubsystem* GetLocalPlayerSubsystem(UObject* ContextObject, TSubclassOf<ULocalPlayerSubsystem> Class);
    15. /** Get a World Subsystem from the World associated with the provided context */
    16. UFUNCTION(BlueprintPure, Category = "GameInstance Subsystems", meta = (WorldContext = "ContextObject", BlueprintInternalUseOnly = "true"))
    17. static UWorldSubsystem* GetWorldSubsystem(UObject* ContextObject, TSubclassOf<UWorldSubsystem> Class);
    18. /**
    19. * Get a Local Player Subsystem from the LocalPlayer associated with the provided context
    20. * If the player controller isn't associated to a LocalPlayer nullptr is returned
    21. */
    22. UFUNCTION(BlueprintPure, Category = "LocalPlayer Subsystems", meta = (BlueprintInternalUseOnly = "true"))
    23. static ULocalPlayerSubsystem* GetLocalPlayerSubSystemFromPlayerController(APlayerController* PlayerController, TSubclassOf<ULocalPlayerSubsystem> Class);
    24. private:
    25. static UWorld* GetWorldFrom(UObject* ContextObject);
    26. };

UGameInstanceSubsystem

用法一

  • 支持委托

  • 支持普通变量和函数

  • 支持蓝图调用

    1. UCLASS()
    2. class DESIGNPATTERNS_API UScoreGameInsSubsystem : public UGameInstanceSubsystem
    3. {
    4. GENERATED_BODY()
    5. public:
    6. // 是否允许被创建
    7. virtual bool ShouldCreateSubsystem(UObject* Outer) const override { return true; }
    8. // 初始化
    9. virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    10. // 释放
    11. virtual void Deinitialize() override;
    12. // 声明委托
    13. DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FScoreChanged, int32, CurrentScore);
    14. UPROPERTY(BlueprintAssignable)
    15. FScoreChanged ScoreChange;
    16. UFUNCTION(BlueprintCallable, Category = "MySubsystem | ScoreGameInsSubsystem")
    17. int32 AddScore(int32 BaseScore);
    18. private:
    19. int32 Score;
    20. };
    1. void UScoreGameInsSubsystem::Initialize(FSubsystemCollectionBase& Collection)
    2. {
    3. Super::Initialize(Collection);
    4. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__""));
    5. }
    6. void UScoreGameInsSubsystem::Deinitialize()
    7. {
    8. Super::Deinitialize();
    9. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__""));
    10. }
    11. int32 UScoreGameInsSubsystem::AddScore(int32 BaseScore)
    12. {
    13. Score = UKismetMathLibrary::Max(0, Score + BaseScore);
    14. // 调用委托
    15. ScoreChange.Broadcast(Score);
    16. return Score;
    17. }

用法二

  • 支持创建抽象类,多个派生类,支持蓝图继承,支持遍历访问

    • 注意UCLASS(Type)

      • 父类为 Abstract抽象类,防止实例化
      • Blueprintable 蓝图继承
      • BlueprintType 可定义蓝图变量
    • 注意每种类型的 Subsystem 只能创建一个实例
  • C++访问

    • 访问单个 GetWorld()->GetGameInstance()->GetSubsystem<T>()
    • 访问多个 GetWorld()->GetGameInstance()->GetSubsystemArray<T>()
  • 使用示例

    1. /**
    2. * 抽象类 USourceControlSubsystem
    3. */
    4. UCLASS(Abstract, Blueprintable, BlueprintType)
    5. class DESIGNPATTERNS_API USourceControlSubsystem : public UGameInstanceSubsystem
    6. {
    7. GENERATED_BODY()
    8. public:
    9. // ShouldCreateSubsystem 默认返回 True
    10. // 可重载
    11. UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "MySubsystem | SourceControl")
    12. FString GetPlatformName();
    13. };
    14. /**
    15. * 派生类 UGitSubsystem
    16. */
    17. UCLASS()
    18. class DESIGNPATTERNS_API UGitSubsystem : public USourceControlSubsystem
    19. {
    20. GENERATED_BODY()
    21. public:
    22. // 初始化
    23. virtual void Initialize(FSubsystemCollectionBase& Collection) override {
    24. Super::Initialize(Collection);
    25. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"【%s】"),*GetName());
    26. }
    27. // 释放
    28. virtual void Deinitialize() override {
    29. Super::Deinitialize();
    30. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"【%s】"),*GetName());
    31. }
    32. virtual FString GetPlatformName_Implementation()override {
    33. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"【Git】"));
    34. return TEXT("Git");
    35. }
    36. void Help() {
    37. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Help Command"));
    38. }
    39. };
    40. /**
    41. * 派生类 USVNSubsystem
    42. */
    43. UCLASS()
    44. class DESIGNPATTERNS_API USVNSubsystem : public USourceControlSubsystem
    45. {
    46. GENERATED_BODY()
    47. public:
    48. // 初始化
    49. virtual void Initialize(FSubsystemCollectionBase& Collection) override {
    50. Super::Initialize(Collection);
    51. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"【%s】"), *GetName());
    52. }
    53. // 释放
    54. virtual void Deinitialize() override {
    55. Super::Deinitialize();
    56. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"【%s】"), *GetName());
    57. }
    58. virtual FString GetPlatformName_Implementation()override {
    59. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"【SVN】"));
    60. return TEXT("SVN");
    61. }
    62. };
    63. /**
    64. * 用于测试 ASourceControlActor
    65. */
    66. UCLASS(Blueprintable, BlueprintType)
    67. class DESIGNPATTERNS_API ASourceControlActor : public AActor
    68. {
    69. GENERATED_BODY()
    70. public:
    71. ASourceControlActor(){}
    72. virtual void BeginPlay()override {
    73. Super::BeginPlay();
    74. // 获取单个 Subsystem
    75. UGitSubsystem* GitSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UGitSubsystem>();
    76. GitSubsystem->Help();
    77. // 获取多个 Subsystem,继承自同个抽象类
    78. const TArray<USourceControlSubsystem*> SourceControlSubsystems = GetWorld()->GetGameInstance()->GetSubsystemArray<USourceControlSubsystem>();
    79. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"********************* 遍历USourceControlSubsystem ********************"));
    80. for (USourceControlSubsystem* ItemSubSystem : SourceControlSubsystems)
    81. {
    82. ItemSubSystem->GetPlatformName();
    83. }
    84. }
    85. };
  • 蓝图调用

其他用法

  • 支持Tick, 需继承 FTickableGameObject,参考 UAutoDestroySubsystem 写法

    1. UCLASS()
    2. class DESIGNPATTERNS_API UScoreGameInsSubsystem : public UGameInstanceSubsystem, public FTickableGameObject
    3. {
    4. GENERATED_BODY()
    5. public:
    6. virtual void Tick(float DeltaTime)override{ UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"Ping Pong"));}
    7. virtual bool IsTickable()const override { return !IsTemplate(); } //判断是否是 CDO,避免执行两次 Tick
    8. virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(UMyScoreSubsystem, STATGROUP_Tickables); }
    9. };
  • 支持 Subsystem 之间的访问

  • 支持多个Subsystem定义依赖顺序,再初始化时调用 Collection.InitializeDependency(UScoreGameInsSubsystem::StaticClass());


UWorldSubsystem

  • 可以再编辑器 和 运行时 使用

    1. UCLASS()
    2. class DESIGNPATTERNS_API UMyWorldSubsystem : public UWorldSubsystem
    3. {
    4. GENERATED_BODY()
    5. public:
    6. // 是否允许被创建
    7. virtual bool ShouldCreateSubsystem(UObject* Outer) const override { return true; }
    8. // 初始化
    9. virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    10. // 释放
    11. virtual void Deinitialize() override;
    12. FString GetWorldType();
    13. };
    1. void UMyWorldSubsystem::Initialize(FSubsystemCollectionBase& Collection)
    2. {
    3. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" [World] %-20s\t[Type] %s\t"), *GetWorld()->GetName(), *GetWorldType());
    4. }
    5. void UMyWorldSubsystem::Deinitialize()
    6. {
    7. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" [World] %-20s\t[Type] %s\t"), *GetWorld()->GetName(), *GetWorldType());
    8. }
    9. FString UMyWorldSubsystem::GetWorldType()
    10. {
    11. FString WorldTypeName;
    12. switch (GetWorld()->WorldType) {
    13. case EWorldType::None: {WorldTypeName = TEXT("None"); }break;
    14. case EWorldType::Game: {WorldTypeName = TEXT("Game"); }break;
    15. case EWorldType::Editor: {WorldTypeName = TEXT("Editor"); }break;
    16. case EWorldType::PIE: {WorldTypeName = TEXT("PIE"); }break;
    17. case EWorldType::EditorPreview: {WorldTypeName = TEXT("EditorPreview"); }break;
    18. case EWorldType::GamePreview: {WorldTypeName = TEXT("GamePreview"); }break;
    19. case EWorldType::GameRPC: {WorldTypeName = TEXT("GameRPC"); }break;
    20. case EWorldType::Inactive: {WorldTypeName = TEXT("Inactive"); }break;
    21. default: WorldTypeName = TEXT("default");
    22. };
    23. return WorldTypeName;
    24. }

UEditorSubsystem

  • 需要再 .build.cs 添加 EditorSubsystem 模块

    1. if (Target.bBuildEditor)
    2. {
    3. PublicDependencyModuleNames.AddRange(new string[] { "EditorSubsystem" });
    4. }
  • 需要添加头文件

  • 编译模式记得选Editor 模式

    1. #include "EditorSubsystem.h"
    2. UCLASS()
    3. class DESIGNPATTERNS_API UMyEditorSubsystem : public UEditorSubsystem
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 是否允许被创建
    8. virtual bool ShouldCreateSubsystem(UObject* Outer) const override { return true; }
    9. // 初始化
    10. virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    11. // 释放
    12. virtual void Deinitialize() override;
    13. };

系统自带 Subsystem 使用蓝图示例

Asset Editor Subsystem

Asset Tag Subsystem

Editor Utility Subsystem

Layer Subsystem

Import Subsystem

Editor Validator Subsystem

  • 检查、验证资产

  • 从 EditorUtilityBlueprint 中选择父类 EditorValidatorBase


参考


附录

  • SubsystemCollection.cpp

    • FSubsystemCollectionBase::Initialize()
    • FSubsystemCollectionBase::AddAndInitializeSubsystem()
    • FSubsystemCollectionBase::Deinitialize()
    1. void FSubsystemCollectionBase::Initialize(UObject* NewOuter)
    2. {
    3. if (Outer != nullptr)
    4. {
    5. // already initialized
    6. return;
    7. }
    8. Outer = NewOuter;
    9. check(Outer);
    10. if (ensure(BaseType) && ensureMsgf(SubsystemMap.Num() == 0, TEXT("Currently don't support repopulation of Subsystem Collections.")))
    11. {
    12. check(!bPopulating); //Populating collections on multiple threads?
    13. if (SubsystemCollections.Num() == 0)
    14. {
    15. FSubsystemModuleWatcher::InitializeModuleWatcher();
    16. }
    17. TGuardValue<bool> PopulatingGuard(bPopulating, true);
    18. if (BaseType->IsChildOf(UDynamicSubsystem::StaticClass())) // 判断是否是 UDynamicSubsystem 的子类
    19. {
    20. for (const TPair<FName, TArray<TSubclassOf<UDynamicSubsystem>>>& SubsystemClasses : DynamicSystemModuleMap)
    21. {
    22. for (const TSubclassOf<UDynamicSubsystem>& SubsystemClass : SubsystemClasses.Value)
    23. {
    24. if (SubsystemClass->IsChildOf(BaseType))
    25. {
    26. AddAndInitializeSubsystem(SubsystemClass);
    27. }
    28. }
    29. }
    30. }
    31. else // 不是 UDynamicSubsystem 的子类
    32. {
    33. TArray<UClass*> SubsystemClasses;
    34. GetDerivedClasses(BaseType, SubsystemClasses, true);
    35. for (UClass* SubsystemClass : SubsystemClasses)
    36. {
    37. AddAndInitializeSubsystem(SubsystemClass);
    38. }
    39. }
    40. // Statically track collections
    41. SubsystemCollections.Add(this);
    42. }
    43. }
    44. void FSubsystemCollectionBase::Deinitialize()
    45. {
    46. // Remove static tracking
    47. SubsystemCollections.Remove(this);
    48. if (SubsystemCollections.Num() == 0)
    49. {
    50. FSubsystemModuleWatcher::DeinitializeModuleWatcher();
    51. }
    52. // Deinit and clean up existing systems
    53. SubsystemArrayMap.Empty();
    54. for (auto Iter = SubsystemMap.CreateIterator(); Iter; ++Iter) // 遍历 SubsystemMap
    55. {
    56. UClass* KeyClass = Iter.Key();
    57. USubsystem* Subsystem = Iter.Value();
    58. if (Subsystem->GetClass() == KeyClass)
    59. {
    60. Subsystem->Deinitialize(); // 清理、释放
    61. Subsystem->InternalOwningSubsystem = nullptr;
    62. }
    63. }
    64. SubsystemMap.Empty();
    65. Outer = nullptr;
    66. }
    67. USubsystem* FSubsystemCollectionBase::AddAndInitializeSubsystem(UClass* SubsystemClass)
    68. {
    69. if (!SubsystemMap.Contains(SubsystemClass))
    70. {
    71. // Only add instances for non abstract Subsystems
    72. if (SubsystemClass && !SubsystemClass->HasAllClassFlags(CLASS_Abstract))
    73. {
    74. // Catch any attempt to add a subsystem of the wrong type
    75. checkf(SubsystemClass->IsChildOf(BaseType), TEXT("ClassType (%s) must be a subclass of BaseType(%s)."), *SubsystemClass->GetName(), *BaseType->GetName());
    76. // Do not create instances of classes aren't authoritative
    77. if (SubsystemClass->GetAuthoritativeClass() != SubsystemClass)
    78. {
    79. return nullptr;
    80. }
    81. const USubsystem* CDO = SubsystemClass->GetDefaultObject<USubsystem>();
    82. if (CDO->ShouldCreateSubsystem(Outer)) // 从CDO调用ShouldCreateSubsystem来判断是否要创建
    83. {
    84. USubsystem* Subsystem = NewObject<USubsystem>(Outer, SubsystemClass); //创建
    85. SubsystemMap.Add(SubsystemClass,Subsystem); // 添加到 SubsystemMap
    86. Subsystem->InternalOwningSubsystem = this;
    87. Subsystem->Initialize(*this); //调用Initialize
    88. return Subsystem;
    89. }
    90. }
    91. return nullptr;
    92. }
    93. return SubsystemMap.FindRef(SubsystemClass);
    94. }

【UE4 C++】编程子系统 Subsystem的更多相关文章

  1. Windows - 子系统(subsystem)错误

    Windows - 子系统(subsystem)错误 本文地址: http://blog.csdn.net/caroline_wendy VS2012生成错误: "error LNK2019 ...

  2. UE4蓝图编程的第一步

    认识UE4蓝图中颜色与变量类型: UE4中各个颜色对应着不同的变量,连接点和连线的颜色都在表示此处是什么类型的变量.对于初学者来说一开始看到那么多连接点, 可能会很茫然,搞不清还怎么连,如果知道了颜色 ...

  3. UE4的编程C++创建一个FPSproject(两)角色网格、动画、HUD、子弹类

    立即归还,本文将总结所有这些整理UE4有关角色的网络格.动画.子弹类HUD一个简单的实现. (五)角色加入网格 Character类为我们默认创建了一个SkeletaMeshComponent组件,所 ...

  4. UE4新手编程之创建C++项目

    虚幻4中常用的按键和快捷键 虚幻4中有一些按键和快捷键很常用,牢记它们并运动到实际的项目开发中,将会大大地提高你的工作效率和使得工作更简便快捷.下面将列举它们出来: 按键   动作  鼠标左键   选 ...

  5. UE4新手编程之创建空白关卡和添加碰撞体

    让我们接着上次继续学习UE4引擎,今天我们学习下怎样创建空白的关卡以及添加碰撞物体. 一. 创建空白关卡 1) 点击文件 -> 新建关卡(或者按快捷键Ctrl+N). 2) 你可以选择Defau ...

  6. ue4 C++ 编程 通过三个点的位置算出夹角

    const FVector2D& Pt1 = 第一个点的位置; const FVector2D& Pt2 = 第二个点的位置; float EdgeRadians1 = FMath:: ...

  7. 【UE4 C++】学习笔记汇总

    UE4 概念知识 基础概念--文件结构.类型.反射.编译.接口.垃圾回收.序列化[导图] GamePlay架构[导图] 类的继承层级关系[导图] 反射机制 垃圾回收机制/算法 序列化 Actor 的生 ...

  8. 【UE4】GamePlay架构

    新标签打开或者下载看大图 更新: 增加 编程子系统 Subsystem 思维导图 Character pipeline

  9. UE4编程之C++创建一个FPS工程(一)创建模式&角色&处理输入

    转自:http://blog.csdn.net/u011707076/article/details/44180951 从今天开始,我们一起来学习一下,如何使用C++将一个不带有任何初学者内容的空模板 ...

随机推荐

  1. 20210713考试-2021noip13

    这位巨佬的博客还是比我好多了 T1 工业题 考场: 暴力挺香的,不想正解了. 题解: $f(i,j)$ 只会得到 $f(i-1,j)$ 和 $f(i,j-1)$ 的贡献.每向右一步乘 $a$ ,向下一 ...

  2. MFGTool2 的使用

    环境 宿主机平台:Ubuntu 16.04.6 目标机:iMX6ULL开发板 MFGTool 2.7 参考:https://www.cnblogs.com/helloworldtoyou/p/6053 ...

  3. 痞子衡嵌入式:在MDK开发环境下将关键函数重定向到RAM中执行的几种方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在MDK开发环境下将关键函数重定向到RAM中执行的几种方法. 这个关键函数重定向到 RAM 中执行系列文章,痞子衡已经写过 <IA ...

  4. 取消input默认提示框

    input输入框有自动保存记忆功能,点击的时候之前输入的内容会在下拉框自动提示 autocomplete="off",这是H5的一个属性. <input type=" ...

  5. [第十二篇]——Docker Dockerfile之Spring Cloud直播商城 b2b2c电子商务技术总结

    Docker Dockerfile 什么是 Dockerfile? Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明. 使用 Dockerfile 定制 ...

  6. JDK源码阅读:Object类阅读笔记

    Object 1. @HotSpotIntrinsicCandidate @HotSpotIntrinsicCandidate public final native Class<?> g ...

  7. HDU1213How Many Tables(基础并查集)

    HDU1213How Many Tables Problem Description Today is Ignatius' birthday. He invites a lot of friends. ...

  8. 【简单数据结构】并查集--洛谷 P1111

    题目背景 AA地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车.政府派人修复这些公路. 题目描述 给出A地区的村庄数NN,和公路数MM,公路是双向的.并告诉你每条公路的连着哪两个村庄,并告诉你 ...

  9. php处理url的rawurlencode:可处理空格加号

    (PHP 4, PHP 5, PHP 7) rawurlencode - 按照 RFC 3986 对 URL 进行编码 返回字符串,此字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号( ...

  10. 《如何进行接口mock测试》

    前言: Mock通常是指:在测试一个对象时,我们构造一些假的对象来模拟与其交互.而这些Mock对象的行为是我们事先设定且符合预期.通过这些Mock对象来测试对象在正常逻辑,异常逻辑或压力情况下工作是否 ...