UE_C++ —— Gameplay 架构

【Unreal】虚幻GAS系统快速入门 Remove Gameplay Effect Query:上面一条的高级版,可以匹配GE的类(Effect Definition),匹配来源(Effect Source)以及匹配GE修改的属性(Modifying Attribute),移除成功匹配的GE。每层Effect如果是Modifiers来计算,则为直接叠加的效果,比如用Modifiers来增加3攻击力,则第一层为增加3攻击力,第二层为增加6攻击力,第三层为增加9攻击力,而如果需要根据层数不同而改变增加的值,则需要使用Executions。 阅读详情

目录

一,Asserts

Check

Verify

Ensure

二,Gameplay Classes

Class Specifiers

Metadata Specifiers

Class Constructor

三,Gameplay Modules

模块创建

INI文件设置

四,Gameplay Tags

Defining Gameplay Tags

Using Defined Gameplay Tags

Advanced Topics

五,Logging in Unreal

UE_LOG

UE_LOGFMT

On-screen debug messages


        使用 C++ 代码进行游戏元素编程时,每个模块都会包含许多 C++ 类;每个类定义了新Actor或对象的模板,类头文件里声明了类和类的函数和属性;类还可包括结构体这种有助于进行相关属性组织和操作的数据结构;结构体也可被自行定义; 通过Interfaces可以使不同的类应用额外的游戏行为;

        在UE中编程时,可使用标准 C++ 类、函数和变量,及使用标准 C++ 语法对它们进行定义; 然而,UCLASS()、UFUNCTION() 和 UPROPERTY() 宏可使引擎识别新的类、函数和变量;如,以 UPROPERTY() 宏声明的变量可被引擎垃圾回收, 也可在编辑器中显示和编辑;此外还有 UINTERFACE() 和 USTRUCT() 宏, 以及其他宏关键词(用于指定 类、函数、属性、接口或结构体在引擎和编辑器中的行为);

    一,Asserts

            在C和C++编程中,assert 可在开发期间帮助检测和诊断不正常或无效的运行时条件;这些条件通常检查是否指针为非空、除数为非零、函数并非递归运行,或代码要求的其他重要假设;但每次检查会使得效率十分低下;某些情况下,assert 会在延迟崩溃发生之前发现导致该崩溃的bug,如删除未来tick所需的对象,协助开发人员发现引起崩溃的根本原因;assert 的关键特性之一是不存在于发布代码中,这意味着不但不会影响发布产品的性能,也没有任何副作用;对 assert 最简单的理解就是:"断言"必须一律为true,否则程序会停止运行;

            引擎提供 assert 等同项的三个不同族系:check、verify 和 ensure;若要检查这些功能背后的代码,可在 Engine/Source/Runtime/Core/Public/Misc/AssertionMacros.h 中找到相关的宏;各个功能的行为略有不同,但它们都是开发期间使用的诊断工具,目标大致相同;

    Check

            Check族系最接近基础 assert,因为当第一个参数得出的值为false时,此族系的成员会停止执行,且默认不会在发布版本中运行;

    MacroParametersBehavior
    check or checkSlowExpression若 Expression 为false,停止执行;
    checkf 或 checkfSlowExpression、FormattedText、...若 Expression 为false,则停止执行并将 FormattedText 输出到日志;
    checkCodeCode

    在运行一次的do-while循环结构中执行 Code;主要用于准备另一个Check所需的信息;

    checkNoEntry/若此行被hit,则停止执行,类似于 check(false),但主要用于不可到达的代码路径;
    checkNoReentry/若此行被hit超过一次,则停止执行;
    checkNoRecursion/若此行被hit超过一次而未离开作用域,则停止执行;
    unimplemented/若此行被hit,则停止执行,类似于 check(false),但主要用于应被覆盖而不会被调用的虚拟函数;

            检查宏仅在调试(Debug)、开发(Development)版本中运行;"Slow"宏仅在调试(Debug)版本中运行;设置 USE_CHECKS_IN_SHIPPING=1 ,可使Check宏在测试(Test)和发布编辑器(Shipping Editor)版本中运行;此法在以下情况中十分实用:怀疑Check宏中的代码正在修改值,发现了仅存在于在发布版本中且难以追踪的bug,但认为现有Check宏能找到这些bug;项目发布时应将 USE_CHECKS_IN_SHIPPING 设为默认值 0;

    Verify

            在大部分版本中,Verify族系的行为与Check族系相同;但即便在禁用Check宏的版本中,Verify宏也会计算其表达式的值,意味着仅当该表达式需要独立于诊断检查之外运行时,才应使用Verify宏;如,若某个函数执行操作,然后返回 bool 来说明该操作是否成功,则应使用Verify而非Check来确保该操作成功;因为在发布版本中Verify将忽略返回值,但仍将执行操作;而Check在发布版本中根本不调用该函数,所以行为才会有所不同;

    MacroParametersBehavior
    verify or verifySlowExpression若 Expression 为false,停止执行;
    verifyf or verifyfSlowExpression、FormattedText、...若 Expression 为false,则停止执行并将 FormattedText 输出到日志;

            验证宏在调试(Debug)、开发(Development)、测试(Test)和发布编辑器(Shipping Editor)版本中完整运行(以"Slow"结尾的宏除外,其仅在调试(Debug)版本中运行);定义 USE_CHECKS_IN_SHIPPING 来保留一个true值(通常为 1),从而覆盖此行为;在所有其他情况下,Verify宏将计算其表达式,但不会停止执行或将文本输出到日志;

    Ensure

            Ensure族系类似于Verify族系,但可在出现非致命错误时使用;这意味着,若Ensure宏的表达式计算得出的值为false,引擎将通知崩溃报告器,但仍会继续运行;为避免崩溃报告器收到太多通知,Ensure宏在每次引擎或编辑器会话中仅报告一次;若实际情况需要Ensure宏在每次表达式计算得值为false时都报告一次,则使用"Always"版本的宏;

    MacroParametersBehavior
    ensureExpressionExpression 首次为false时通知崩溃报告器;
    ensureMsgfExpression、FormattedText、...Expression 首次为false时通知崩溃报告器并将 FormattedText 输出到日志;
    ensureAlwaysExpressionExpression 为false时通知崩溃报告器;
    ensureAlwaysMsgfExpression、FormattedText、...Expression 为false时通知崩溃报告器并将 FormattedText 输出到日志;

            Ensure宏在所有版本中计算其表达式的值,但仅在调试(Debug)、开发(Development)、测试(Test)和发布编辑器(Shipping Editor)版本中联系崩溃报告器;

    Usage Examples

            以下假设情况展示了一些用例,其中Check、Verify和Ensure可帮助理清代码或协助调试;

    // 决不可使用空JumpTarget调用此函数;若发生此情况,须停止程序;
    void AMyActor::CalculateJumpVelocity(AActor* JumpTarget, FVector& JumpVelocity)
    {
        check(JumpTarget != nullptr);
        // 计算在JumpTarget上着陆所需的速度,现在可确定JumpTarget为非空;
    }
    // 这行代码捕获了在产品发布版本中可能出现的小错误;
    // 此错误较小,无需停止执行便可解决;
    // 虽然该bug已修复,但开发者仍然希望了解之前是否曾经出现过此bug;
    void AMyActor::Tick(float DeltaSeconds)
    {
    	Super::Tick(DeltaSeconds);
    	// 确保bWasInitialized为true,然后再继续;若为false,则在日志中记录该bug尚未修复;
    	if (ensureMsgf(bWasInitialized, TEXT("%s ran Tick() with bWasInitialized == false"), *GetActorLabel()))
    	{
    		// 执行一些需要已正确初始化AMyActor的操作
    	}
    }
    // 若添加新形状类型,但忘记在此切换块中处理,则此代码将停止;
    switch (MyShape) {
    case EShapes::S_Circle:
    	//(处理圆圈。)
    	break;
    case EShapes::S_Square:
    	//(处理方块。)
    	break;
    default:
    	// 每种形状类型都应有相应情况,因此这种情况不应该发生;
    	checkNoEntry();
    	break;
    }
    // 此UObject拥有测试函数IsEverythingOK,没有副作用,若出现问题则返回false;
    // 若发生这种情况,将出现致命错误并终止;
    // 因为代码无副作用,仅作诊断之用,因此无需在发布版本中运行;
    checkCode(
        if (!IsEverythingOK())
            UE_LOG(LogUObjectGlobals, Fatal, TEXT("Something is wrong with %s!Terminating."), *GetFullName());
    );
    // 此列表中不应有圆圈,若有,程序将停转;但检查圆圈耗时较长,因此建议在调试版本中操作;
    checkSlowf(!MyLinkedList.HasCycle(), TEXT("Found a cycle in the list!"));
    // 遍历列表,在各个元素上运行一些代码;

    二,Gameplay Classes

            引擎中每个游戏类都由一个头文件(.h)和一个源文件(.cpp)构成;头文件包含类和类成员(如变量和函数)的声明,而源文件是类功能的实现;类通过首字母或前缀来标准化的命名;

    Adding Classes

            主菜单Tools > New C++ Class...是一种快速简便的方法,将本机C++代码类添加到项目中,以便根据需要扩展功能;这会将纯内容项目转换为代码项目;确保安装了兼容版本的Visual Studio;

    Class Headers

            引擎中的类通常拥有单独且唯一的头文件,通常这些文件的命名与其中定义的类相匹配(减去 A 或 U 前缀,并使用 .h 文件扩展名),如AActor 类的类头文件命名为 Actor.h;

            头文件使用标准 C++ 语法,并结合专门的宏,以简化类、变量和函数的声明过程;

    // 在每个头文件都包含自动生成的头文件;
    #include "ClassName.generated.h"

    Class Declaration

            类声明定义类的名称、其继承的类,以及继承的函数和变量;通过类说明符和元数据,类声明还定义了引擎和编辑器的特定行为;

    UCLASS([specifier, specifier, ...], [meta(key = value, key = value, ...)])
    class ClassName : public ParentName
    {
        GENERATED_BODY() // 必须被放置在类体的最前方
    }
    Class Specifiers

    在类声明中添加类说明符,以控制类在引擎和编辑器的各个方面如何表现;

    Class SpecifierEffect
    Abstract

    声明为"抽象基类",不可向关卡中添加此类的Actor,如ATriggerBase基类是抽象类,而ATriggerBox子类不是抽象类,可以放置在关卡;

    Blueprintable将此类公开为用于创建蓝图的可接受基类,默认为NotBlueprintable,子类会继承;
    NotBlueprintable指定此类不是可用于创建蓝图的可接受基类,此为默认说明符,子类会继承;
    BlueprintType将此类公开为可用于蓝图变量的类型;
    Const此类中的所有属性和函数都是const并且导出为const,子类会继承;
    ConversionRoot根转换,将子类限制为仅可沿层级向上转换为第一个根类的子类;
    CustomConstructor阻止构造函数声明自动生成;
    DefaultToInstanced此类的所有实例都被认为是"实例化的",实例化的类(组件)将在构造时被复制,子类会继承;
    EditInlineNew可从编辑器"Property"窗口创建此类的对象,而非从现有资源引用;默认仅可通过"Property"窗口指定对现有对象的引用;
    此说明符会传播到所有子类,子类可通过 NotEditInlineNew 说明符覆盖它;
    Intrinsic此类直接在C++中声明,无 Unreal Header Tool 生成的样板,请勿在新类上使用此说明符;
    MinimalAPI

     仅导出类的类型信息供其他模块使用;

    该类可被类型转换,但无法调用该类的函数(内联方法除外);

    对于不需要在其他模块中访问所有函数的类,通过不导出所有内容,可以缩短编译时间;

    NoExport

    此类的声明不应包含在自动生成的C++头文件中,必须在单独的头文件中手动定义该C++类声明;

    仅对本地类有效,请勿对新类使用此说明符;

    Transient

    属于此类别的对象将永远不会保存到磁盘,与非持久性的本地类别(如玩家或窗口)结合使用时非常有用;

    此说明符会传递给子类,但可以被非瞬态说明符覆盖;

    NonTransient使继承自基类的Transient说明符无效;
    Placeable可在编辑器中创建此类,且可将此类放置到关卡、UI场景或蓝图(取决于类类型)中;
    此标志会传播到所有子类,子类可使用NotPlaceable说明符覆盖此标志;
    NotPlaceable使继承自基类的Placeable说明符无效,不可以在编辑器中将此类的对象放置到关卡、UI场景或蓝图中;
    PerObjectConfig此类别的配置信息将按对象存储,其中每个对象在.ini文件中都有一个以对象名称命名的部分,格式为[ObjectName ClassName],此说明符会传递给子类;
    Within=OuterClassName

    此类的对象无法在OuterClassName对象的实例之外存在;

    要创建此类的对象,需要提供OuterClassName的一个实例作为其Outer对象;

    AdvancedClassDisplay强制类的所有属性仅显示在细节面板的"Advanced"部分;
    ClassGroup=GroupName在编辑器的Actor浏览器中启用 Group View 时,Actor Browser 应在指定的 GroupName 中包含此类及其所有子类;
    Config=ConfigName

    此类可在配置文件(.ini)中存储数据,如存在使用config或globalconfig说明符的类属性,将使这些属性存储在指定的配置文件中;

    会传播到所有子类并且无法使其无效,但是子类可通过重新声明config说明符并提供不同的ConfigName来更改配置文件;常见的ConfigName值是"Engine"、"Editor"、"Input"和"Game";

    DependsOn=(ClassName1, ClassName2, ...)

    所列的类将先于此类被编译,提供的类名必须指明是同一(或之前)包中的类;

    可使用单个DependsOn行(以逗号分隔)来标识多个依赖类,或通过为每个类使用单独的DependsOn行来指定多个依赖类;

    当某个类使用在另一个类中声明的结构体或枚举时,这非常重要,因为编译器只知道它已经编译过的类中的内容;

    Deprecated此类已弃用,序列化时将不保存此类的对象,子类会继承;
    AutoCollapseCategories=(Category1, Category2, ...)使父类上的 AutoExpandCategories 说明符列出类别的效果无效;
    AutoExpandCategories=(Category1, Category2, ...)指定应自动在编辑器属性窗口中展开的一个或多个类别;
    要自动展开未使用类别变量,使用类名称;
    DontAutoCollapseCategories=(Category, Category, ...)使列出类别的继承自父类的AutoCollapseCategories说明符无效;
    CollapseCategories此类的属性不应划分到编辑器属性窗口的类别中,会传播到子类,可由DontCollapseCategories说明符覆盖;
    DontCollapseCategories使继承自基类的CollapseCatogories说明符无效;
    ShowCategories=(Category1, Category2, ...)使继承自基类的HideCategories说明符无效;
    HideCategories=(Category1, Category2, ...)

    列出对用户完全隐藏的类别,此说明符会传播到子类;

    要隐藏未使用类别声明的属性,使用声明变量的类的名称;

    ShowFunctions=(Category1, Category2, ...)在属性查看器中显示列出的类别中的所有函数;
    HideFunctions=(Category1, Category2, ...)让指定分类中的所有函数都对用户完全隐藏;
    ShowFunctions=FunctionName在属性查看器中显示指定的函数;
    HideFunctions=FunctionName将提到的函数对用户完全隐藏;
    HideDropdown阻止此类在属性窗口组合框中显示;
    Metadata Specifiers

            在声明类、接口、结构体、枚举/枚举值、函数或属性时,可以添加元数据说明符来控制其与引擎和编辑器各个方面的交互方式;每种数据结构或成员类型都有其自己的元数据说明符列表,元数据仅存在于编辑器中,请勿编写访问元数据的游戏逻辑;

    Class Meta TagEffect
    BlueprintSpawnableComponent如其存在,组件类可由蓝图生成;
    BlueprintThreadSafe只在蓝图函数库上有效,将把此类中的函数在动画蓝图中的非游戏线程上标记为可调用;
    ChildCannotTick用于Actor和组件类,如本地类无法tick,那么基于此Actor或组件的蓝图生成类则无法tick,即使 bCanBlueprintsTickByDefault 为true也同样如此;
    ChildCanTick用于Actor和组件类,如本地类无法tick,那么可以覆盖基于此Actor或组件的蓝图生成类的 bCanEverTick 标签,即使 bCanBlueprintsTickByDefault 为false也同样如此;
    DeprecatedNode用于行为树节点,说明类已废弃,编译时将显示一条警告;
    DeprecationMessage="Message Text"如类已废弃,尝试编译使用此类的蓝图时,会添加到标准废弃警告;
    DisplayName="Blueprint Node Name"

    此节点在蓝图中的命名将被此处提供的值所取代,而非代码生成的命名;

    DontUseGenericSpawnObject不使用蓝图中的通用Create Object节点来生成类的一个对象,此说明符只有在用于既非Actor又非ActorComponent的BluprintType类时才有意义;
    ExposedAsyncProxy在 Async Task 节点中公开此类的一个代理对象;
    IgnoreCategoryKeywordsInSubclasses用于让一个类的首个子类忽略所有继承的 ShowCategories 和 HideCategories 说明符;
    IsBlueprintBase="true/false"说明此类是否为创建蓝图的一个可接受基类,与 UCLASS 说明符Blueprintable 或 NotBlueprintable 相似;
    KismetHideOverrides="Event1, Event2, .."不允许被覆盖的蓝图事件的列表;
    ProhibitedInterfaces="Interface1, Interface2, .."列出与类不兼容的接口;
    RestrictedToClasses="Class1, Class2, .."由蓝图函数库类使用,用于限制列表中命名类的用法;
    ToolTip="Hand-written tooltip"覆盖从代码注释自动生成的提示文本;
    ShortToolTip="Short tooltip"完整提示文本过长时使用的简短提示文本,如父类选取器对话;
    ShowWorldContextPin说明放置在此类拥有的图表中的蓝图节点必须显式其World情景引脚(即使其通常状态下为隐藏也同样如此),因为此类的对象无法被用作World情景;
    UsesHierarchy表示类使用层级数据,用于实例化"细节"面板中的层级编辑功能;
    ScriptName="DisplayName"在将此类、属性或函数导出到脚本语言时使用的名称,可以包含已弃用的名称,作为额外的以分号分隔的条目;

    Class Implementation

            所有类必须使用 GENERATED_BODY 宏才能正确实现,这是在定义类及其所有变量和函数的类头(.h)文件中完成的;最佳方法是使类源和头文件的命名与实现的类相匹配,减去 A 或 U 前缀,如AActor 类的源文件命名为 Actor.cpp,其头文件命名为 Actor.h(对编辑器中"Add C++ Class"而言是自动进行);

            源文件(.cpp)必须包含 C++ 类声明的头文件(.h),通常为自动生成,但也可手动生成(如有必要),如 AActor 类的 C++ 声明在 EngineClasses.h 头文件中自动生成,Actor.cpp 文件必须包括 EngineClasses.h 文件或包含它的另一个文件;

    Class Constructor

            UObjects 使用 Constructors 设置属性和其他变量的默认值,并执行其他必要的初值设定;类构造函数通常放置在类实现文件中,如 AActor::AActor 构造函数位于 Actor.cpp 中;

    注,部分构造函数可能以每个模块为基础放置在一个特殊的"constructors"文件中;

            也可以将构造函数内联放置在类头文件中;然而,如果构造函数在类头文件中,UClass 必须结合 CustomConstructor 说明符进行声明,因为这阻止了自动代码生成器在标头中创建构造函数声明;

    Constructor Format

    // UObject 构建函数最基本的形式
    UMyObject::UMyObject()
    {
        // 在此处初始化 Class Default Object 属性
    }
    // 另一个辅助构造函数,支持一个特殊的属性调整结构;
    UMyObject::UMyObject(const FObjectInitializer& ObjectInitializer)
        : Super(ObjectInitializer)
    {
        // Initialize CDO properties here.
    }

            尽管上述两个构造函数实际上都没有执行任何初始化操作,但引擎已经将所有字段初始化为零、NULL或其默认构造函数所实现的任何值;任何在构造函数中的初始化代码都将应用于CDO,也会被复制到正确创建的对象的任何新实例中,就像使用CreateNewObject或SpawnActor一样;

            传递给构造函数的FObjectInitializer参数,尽管被标记为const,但可以通过内置的可变函数进行配置,以覆盖属性和子对象;正在创建的UObject将受到这些更改的影响,并且这可用于更改已注册属性或组件的值;

    AUDKEmitterPool::AUDKEmitterPool(const FObjectInitializer& ObjectInitializer)
        : Super(ObjectInitializer.DoNotCreateDefaultSubobject(TEXT("SomeComponent")).DoNotCreateDefaultSubobject(TEXT("SomeOtherComponent")))
    {
        // Initialize CDO properties here.
    }

            在上例中,超类将在其构建函数中创建名为"SomeComponent"和"SomeOtherComponent"的子对象,但由于 FObjectInitializer 的原因,该操作将不会执行;在下例中,SomeProperty 在 CDO 中将默认为 26,因此在 AUTDemoHUD 的每个新实例中均为如此;

    AUTDemoHUD::AUTDemoHUD()
    {
        // 在此处初始化 CDO 属性
        SomeProperty = 26;
    }

    Constructor Statics and Helpers

            为更复杂的数据类型(尤其是类引用、名称和资源引用)设置值时,需在构造函数中定义并实例化一个 ConstructorStatics 结构体来保存所需的各种属性值;该结构体仅在构造函数首次运行时创建,在后续运行中,它只需复制一个指针,因此速度极快;在创建 ConstructorStatics 结构体时,会将值分配给该结构体的成员,以便在构造函数后续将值分配给实际属性时进行访问;
            ContructorHelpers 是在ObjectBase.h中定义的一个特殊命名空间,其中包含用于执行特定于构造函数的常见操作的辅助模板,如存在用于查找资源或类引用以及创建和查找组件的辅助模板;

    Asset References

            理想情况下,类中不应存在资源引用,硬编码的资源引用易出错,更推荐使用蓝图来配置资源属性;不希望每次构造对象时都搜索资源,通过一个静态结构体,确保只进行一次资源搜索;
            ConstructorHelpers::FObjectFinder 通过 StaticLoadObject 函数查找指定 UObject 的引用,通常用于引用存储在内容包中的资源,如未找到对象,则会报告失败;

    ATimelineTestActor::ATimelineTestActor()
    {
    	// Structure to hold one-time initialization
    	struct FConstructorStatics
    	{
    		ConstructorHelpers::FObjectFinder<UStaticMesh> Object0;
    		FConstructorStatics()
    			: Object0(TEXT("StaticMesh'/Game/UT3/Pickups/Pickups/Health_Large/Mesh/S_Pickups_Base_Health_Large.S_Pickups_Base_Health_Large'"))
    		{
    		}
    	};
    	static FConstructorStatics ConstructorStatics;
    
    	// Property initialization
    
    	StaticMesh = ConstructorStatics.Object0.Object;
    }

    Class References

            ConstructorHelpers::FClassFinder 为特定的 UClass 寻找引用,如类未找到,则报告失败;

    APylon::APylon(const class FObjectInitializer& ObjectInitializer)
    	:Super(ObjectInitializer)
    {
    	// 进行一次性初始化的结构
    	static FClassFinder<UNavigationMeshBase> ClassFinder(TEXT("class'Engine.NavigationMeshBase'"));
    	if (ClassFinder.Succeeded())
    		NavMeshClass = ClassFinder.Class;
    	else
    		NavMeshClass = nullptr;
    }

            在许多情况下,可只使用 USomeClass::StaticClass(),绕开复杂的全部 ClassFinder;对跨模块的引用而言,使用 ClassFinder 法较好;

    NavMeshClass = UNavigationMeshBase::StaticClass();

    Components and Sub-Objects

            在构造函数中也可创建组件子对象并将其附加到角色的层次结构中;当生成一个角色时,其组件将从CDO中克隆;为了确保组件始终被创建、销毁并正确地进行垃圾回收,应在所属类的UPROPERTY中存储一个指向构造函数中创建的每个组件的指针;

    UCLASS()
    class AWindPointSource : public AActor {
    	GENERATED_BODY()
    public:
    	UPROPERTY()
    	UWindPointSourceComponent* WindPointSource;
    
    	UPROPERTY()
    	UDrawSphereComponent* DisplaySphere;
    };
    
    AWindPointSource::AWindPointSource()
    {
    	// Create a new component and give it a name.
    	WindPointSource = CreateDefaultSubobject<UWindPointSourceComponent>(TEXT("WindPointSourceComponent0"));
    
    	// Set our new component as the RootComponent of this actor, or attach it to the root if one already exists.
    	if (RootComponent == nullptr)
    		RootComponent = WindPointSource;
    	else
    		WindPointSource->AttachTo(RootComponent);
    
    	// Create a second component. This will be attached to the component we just created.
    	DisplaySphere = CreateDefaultSubobject<UDrawSphereComponent>(TEXT("DrawSphereComponent0"));
    	DisplaySphere->AttachTo(RootComponent);
    
    	// Set some properties on the new component.
    	DisplaySphere->ShapeColor.R = 173;
    	DisplaySphere->ShapeColor.G = 239;
    	DisplaySphere->ShapeColor.B = 231;
    	DisplaySphere->ShapeColor.A = 255;
    	DisplaySphere->AlwaysLoadOnClient = false;
    	DisplaySphere->AlwaysLoadOnServer = false;
    	DisplaySphere->bAbsoluteScale = true;
    }

    三,Gameplay Modules

            将属于同一游戏项目的游戏类集合编译成动态链接库(DLL);

            与引擎本身由一系列模块组成的方式相同,每个游戏也由一个或多个游戏模块组成;这些模块类似于引擎早期版本中的包,因为它们是相关类集合的容器;由于Gameplay都是用C++编写的,模块实际上是动态链接库(DLL),而不是专有的包文件;

    模块创建

            游戏模块至少必须包含一个头文件(.h)、一个C++文件(.cpp)和一个构建文件(*.Build.cs);

    • 头文件,必须位于模块目录的Public文件夹中,即[GameName] \Source\ [ModuleName]\Public;此文件包含类所需的任何头文件,包括模块自动生成的头文件;
    • C++文件,位于模块目录的Private文件夹中,即[GameName] \Source\ [ModuleName]\Private,用于注册和实现该模块;
    • 构建文件,位于游戏模块的根目录,即 [GameName] \Source\ [ModuleName],定义构建工具(UnrealBuildTool)编译模块时使用的某些信息;
    #include "Engine.h"
    #include "EnginePrivate.h"
    #include "<ModuleName>Classes.h"
    
    // Include our game's header file
    #include "<ModuleName>.h"
     
    // 游戏中至少有一个模块必须使用 IMPLEMENT_PRIMARY_GAME_MODULE 进行注册;其他模块可以使用备选的 IMPLEMENT_GAME_MODULE 方法;
    IMPLEMENT_PRIMARY_GAME_MODULE( <ModuleName>, "<GameName>" );
    
    using UnrealBuildTool;
    
    public class <ModuleName> : ModuleRules
    {
    	public <ModuleName>(TargetInfo Target)
    	{
    		PublicDependencyModuleNames.AddRange(new string[]{ "Core", "Engine" });
    		PrivateDependencyModuleNames.AddRange(new string[]{ "RenderCore" });
    	}
    }
    INI文件设置

    由于新的游戏模组将包含UObject代码,因此需要进行一些必要的配置;

    [UnrealEd.EditorEngine]
    + EditPackages = <ModuleName>
    [Launch]
    Module = <ModuleName>
    [/Script/Engine.UObjectPackages]
    +NativePackages=<ModuleName>

    多个游戏模块

            每个团队根据自身需求和原则可将游戏拆分为多个DLL文件,但可能会带来更多麻烦;使用多个游戏模块将带来更短的链接时间和更快的代码迭代,但随着模块数量的增加,将需要更频繁地处理DLL导出和/或接口类;
            可创建一个主游戏模块,然后创建任意数量的特定游戏模块;可为这些新模块创建 *.Build.cs 文件,然后将这些模块的引用添加到 Target.cs 文件中(OutExtraModuleNames数组);在C++代码中,一定要为游戏模块使用适当的宏;至少有一个模块必须使用 IMPLEMENT_PRIMARY_GAME_MODULE 宏,而所有其他模块则应使用 IMPLEMENT_GAME_MODULE 宏;然后,UBT 应自动发现这些模块并编译额外的游戏DLL文件;

            确实支持创建相互依赖的模块(即彼此之间有导出和导入函数和数据,例如Engine和UnrealEd模块),但这对编译时间来说并不理想,有时可能会导致变量静态初始化的问题。不相互依赖的游戏模块更难设计和维护,但代码可能会因此更清晰;

    四,Gameplay Tags

            Gameplay Tags 是用户定义的分层标签系统,可应用于项目中的对象,并对其求值以驱动Gameplay实现;可使用它们传达许多不同的概念,包括:

    • 对象的属性,如 Character.Enemy.Zombie;
    • 对象在执行或能够执行的事情,如 Movement.Mode.Swimming;
    • 游戏事件和触发器,如 GameplayEvent.RequestReset;

            Gameplay Tags 有任意数量的分层级别,以 . 字符分隔表示,如标签 Event.Movement.Dash 有三个级别,其中 Event 是层级中最宽泛的标识符,而 Dash 是最具体的;

    Defining Gameplay Tags

            必须将Gameplay Tags添加到标签字典,以便虚幻引擎识别它们;可以使用以下某种方法添加(或删除)标签:

    • 直接在 Project Settings 中添加或删除;
    • 从 Data Table 资产导入;
    • 使用 C++ 定义;

            以上所有方法都在 Project Settings 的 GameplayTags 分段中下设置;

    Adding Tags in Project Settings

    定义新Gameplay Tags的最简单方式是,直接在 Project Settings 中添加;

    • 启用 Import Tags From Config;
      • 会导入 .ini 文件中的所有Gameplay Tags,包括 Config/DefaultGameplayTags.ini 以及 Config/Tags 中的所有标签;
    • 点击 Add new Gameplay Tag source 按钮;
      • 在 Config/Tags 中创建新的源 .ini 文件来存储Gameplay Tags;为项目的各个方面创建单独的源文件,可能对于大型项目的组织和协作很有用;
    • 点击 Gameplay Tag List 条目旁边的 Manage Gameplay Tags 按钮,会打开 GameplayGameplay Tag Manager 窗口;
      • 在 Gameplay Tag Manager 窗口中,点击左上角的 添加(Add (+)) 按钮;
      • 输入所需的 Name 、 Comment 和 Source;Comment显示在标签的提示文本上,Source 是存储标签的 .ini 文件;
      • 点击 Add New Tag 按钮;

            可重命名、删除、复制标签或向其添加新的子标签;若标签的来源不是 .ini 文件,则不能在 Gameplay Tag Manager 窗口中重命名或删除;

    注,可使用文本编辑器编辑标签 .ini 源文件,但必须重启编辑器才能加载更改;

    Importing Tags from Data Table Assets

            可使用行类型 GameplayTagTableRow 从 Data Table 资产导入Gameplay Tags;使用此方法可以:

    • 在 Data Table Editor 中管理标签;
    • 在编辑器运行期间更改数据表;
    • 通过将 .csv 或 .json 文件作为数据表导入来添加标签;

    要从数据表导入标签,在 Project Settings 中执行以下操作:

    • 点击 Gameplay Tag Table List 旁边的 Add Element (+) 按钮;
    • 点击新索引的下拉菜单并选择数据表;

    Defining Tags with C++

    可使用 NativeGameplayTags.h 中定义的宏,在C++定义Gameplay Tags:

    • UE_DECLARE_GAMEPLAY_TAG_EXTERN :在 .h 文件中用于声明 .cpp 文件中定义的标签;
    • UE_DEFINE_GAMEPLAY_TAG :在 .cpp 文件中用于定义 .h 文件中声明的标签,不带提示文本注释;
    • UE_DEFINE_GAMEPLAY_TAG_COMMENT :在 .cpp 文件中用于定义 .h 文件中声明的标签,带有提示文本注释;
    • UE_DEFINE_GAMEPLAY_TAG_STATIC :在 .cpp 文件中用于定义仅对定义文件可用的标签;不同于其他 DEFINE 宏,这不应该与 DECLARE 宏调用配对;
    //Example Implementation
    // In .h file
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Movement_Mode_Walking);
    
    // In .cpp file
    UE_DEFINE_GAMEPLAY_TAG_COMMENT(Movement_Mode_Walking, "Movement.Mode.Walking", "Default Character movement tag");

    注,必须将 GameplayTags 模块添加到项目的 Build.cs 文件,才能在C++中访问Gameplay Tags功能;

    Using Defined Gameplay Tags

            定义后,可将标签应用于对象并对标签求值,以在项目中驱动Gameplay;

    Applying Tags to Objects

    • 将 Gameplay Tag Container (FGameplayTagContainer) 类型变量添加到对象,此变量存储多个Gameplay Tags;
    • AddTag 函数添加标签到容器;
    • RemoveTag 函数从容器移除标签;
    • AppendTags 函数将多个标签拼接到一起;

    注,FGameplayTag 表示单个标签,FGameplayTagContainer 表示一组标签容器(通常使用它);

    Evaluating Tags with Conditional Functions

            可基于对象的标签来驱动Gameplay实现;要对存储在对象的Gameplay Tag Containers中的标签求值,可以使用各种条件函数,如:

    • 有标签(HasTag)
    • 有任何标签(HasAny)
    • 有所有标签(HasAll);

            除了 HasAll 之类的之外(因为容器中的所有标签在源集内都没有缺失;),使用空的Gameplay Tag Containers作为输入参数时会返回false;

    Gameplay Tag Queries

            Gameplay Tag Query(FGameplayTagQuery)类型变量组合了条件函数,以更直白精简的方式建立复杂逻辑;

    Gameplay Tag 查询支持以下表达式:

    • Any Tags Match :测试是否能在容器中发现查询中的至少一个标签;
    • All Tags Match :测试查询中的所有标签是否都在容器中;如查询为空,这会返回true;
    • No Tags Match :测试查询中的所有标签是否都不在容器中;如果查询为空,这会返回true;

    此外,查询支持基于子表达式求值的以下根表达式:

    • Any Expressions Match :测试是否有任何子表达式返回true;
    • All Expressions Match :测试是否所有子表达式都返回true;如没有子表达式,这会返回true;
    • No Expressions Match :测试是否没有子表达式返回true;如没有子表达式,这会返回true;
    Advanced Topics

    Setting Tag Editing Restrictions

            可限制用户对Gameplay Tag进行编辑(在任意层级级别);在 Project Settings 的 Advanced Gameplay Tags > Advanced 下进行以下设置:

    • Restricted Config Files:用于存储受限制标签的 .ini 文件列表,这些标签与具有编辑权限的 所有者(Owners) 列表配对;
    • Restricted Tag List :显示 Gameplay Tag Manager 窗口,可在该窗口中修改受限制标签;

            如有用户(非列表中的所有者)尝试编辑受限制的标签,将弹出警告消息,要求用户确认自己已获得所有者的编辑授权;如用户无法确认,则不会做出编辑;

    注,受限制的标签在创建之后,不能在编辑器中删除;要删除受限制的标签,必须直接编辑 .ini 文件;

    Streamlining Tag Access in C++

    可使用 IGameplayTagAssetInterface 来改进Gameplay标签实现;该接口提供了以下优势:

    • 不用显式将对象进行转换,就可以获取对象的标签;
    • 可以为每种可能的类型编写自定义代码;

            实现该接口并重载 GetOwnedGameplayTags ​函数,就能创建一种能够被蓝图访问的方法,来为 Gameplay Tag Container 填充与该对象关联的标签;大部分情况下,意味着将基类中的标签复制到新容器中,但实现可以从多个容器收集标签,或调用蓝图函数以访问蓝图声明的标签或对象需要的任意内容;

    五,Logging in Unreal

            日志是一种非常实用的调试工具,可以详细说明代码当前的执行逻辑;可以检查函数之间传递的数据值,并报告潜在的问题;

    • 日志提供多种方法来有序记录运行时特定时刻的事件、函数调用和变量值;

    • 在 Window > Output Log 查看编辑器中的日志;

    • 日志保存在项目 Saved/Logs 目录的 .txt 文件中;

    // 在.h头文件中定义一个跨文件/全局的自定义日志类别;
    // 声明(.h 中)必须和定义(.cpp 中)配对使用,否则链接会报错;
    // .cpp文件定义 DEFINE_LOG_CATEGORY(MyCategory);
    DECLARE_LOG_CATEGORY_EXTERN(MyCategory, Log, All);
    
    // 在.cpp源文件中定义一个仅当前文件可见(静态)的自定义日志类别;
    DEFINE_LOG_CATEGORY_STATIC(LogBlankProgram, Log, All);
    
    // MyCategory,自定义类别名;
    // Log,运行时的默认详细级别,枚举ELogVerbosity;
    //    Fatal,致命错误(触发断点/崩溃);
    //    Error,严重问题(程序能跑,打印红色文字);
    //    Warning,警告/潜在问题(不影响主流程,打印黄色文字);
    //    Display,显示;
    //    Log,普通日志;
    //    Verbose,详解日志;
    //    VeryVerbose,极详细日志;
    // All,编译时过滤级别(相当于总开关),与上一参数一样;
    //    NoLogging,不输出日志;
    //    All,保留所有日志;等同于 VeryVerbose;
    UE_LOG
    // 临时日志类别,通常不应使用它去检测,位于 Runtime/Core/CoreGlobals.h ;
    CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogTemp, Log, All);
    // UE_LOG 是一个将格式化信息记录到日志文件中的宏;
    UE_LOG(LogTemp, Warning, TEXT("Hello World"));
    // LogTemp 是类别名称(提供给 DEFINE_LOG_CATEGORY 宏)
    // Warning 是一个日志详细级别,用于将警告打印到控制台和日志文件中;可设置不同的日志详细程度,调整日志的换行模式,或者设置日志的文本颜色;
    // Text 是C语言库函数printf样式中字符串文字的格式;

    Log Verbosity

            ELogVerbosity 是一个定义日志详细级别的枚举,还定义了额外的非日志详细级别(允许给定日志换行或设置日志文本的颜色);

    EnumDescription
    Fatal始终将致命错误打印到控制台和日志文件,然后出现崩溃(即使禁用日志);
    Error将错误打印到控制台和日志文件;Commandlet和编辑器会收集并报告错误;错误消息导致commandlet故障;
    Warning将警告打印到控制台和日志文件;Commandlet和编辑器会收集并报告警告;
    Display将消息打印到控制台和日志文件;
    Log将消息打印到日志文件,但不打印到控制台;
    Verbose如为给定类别启用了冗长日志记录,则将冗长消息打印到日志文件;这通常用于详细日志记录;
    VeryVerbose将冗长消息打印到日志文件;如启用了极其冗长日志记录,则用于详细日志记录,否则将产生垃圾信息输出;
    // 从命令行更改不同日志类别的冗长度
    // 此命令允许指定多个类别(包括一个all类别)
    -LogCmds="LogDerivedDataCache Verbose"  
    // 使用控制台命令更改冗长度
    log LogDerivedDataCache verbose

    Logging Fundamental Data Types

    Data TypeExample
    Fstring
    UE_LOG(LogTemp, Warning, TEXT("An Actor's name is %s"), *Actor->GetName());
    Bool
    UE_LOG(LogTemp, Warning, TEXT("The boolean value is %s"), ( bBool ? TEXT("true"): TEXT("false") ));
    Integer
    UE_LOG(LogTemp, Warning, TEXT("The integer value is: %d"), ExampleInteger);
    Float
    UE_LOG(LogTemp, Warning, TEXT("The float value is: %f"), ExampleFloat);
    FVector
    UE_LOG(LogTemp, Warning, TEXT("The vector value is: %s"), *Vector.ToString());
    Multiple Specifiers
    UE_LOG(LogTemp, Warning, TEXT("Current values are: vector %s, float %f, and integer %d"), *Vector.ToString(), Float, Integer);

    Define Your Own Log Category

            可自行定义日志类别,并将其用在C++代码示例中;如有多个文件或框架需要进行不同的分类,此功能可能会很有用;

    // 在相关的头文件中,在include指令下方添加以下内容:
    DECLARE_LOG_CATEGORY_EXTERN(<LOG_CATEGORY>, <VERBOSITY_LEVEL>, All);
    // 在相关的C++文件中,在include指令下方添加以下内容:
    DEFINE_LOG_CATEGORY(<LOG_CATEGORY>);
    Log verbosity levels
    Verbosity LevelPrinted in ConsolePrinted in Editor LogText ColorAdditional Information
    FatalYNANA会话崩溃;
    ErrorYYRedNA
    WarningYYYellowNA
    DisplayYYGreyNA
    LogNYGreyNA
    VerboseNNNANA
    VeryVerboseNNNA可使用日志掩码和特殊枚举值设置文本颜色;

    注,可以继承 FOutputDevice,以实现自己的输出;

    UE_LOGFMT

            UE_LOGFMT 会记录结构化日志事件,支持 Positional 或 Named 参数,但不能混合使用两种类型;要使用此宏,需要包含 Logging/StructeredLog.h 库声明;

    注,UE5.2中引入了 UE_LOGFMT 宏,检查当前的引擎是否为最新版本;

    Parameter NameDescription
    Positional

    字段值必须与格式引用的字段完全匹配;

    UE_LOGFMT(LogCore, Warning, "Loading `{Name}` failed with error {Error}", Package->GetName(),  ErrorCode);
    Named

    字段值必须包含格式引用的每个字段;顺序无关紧要,而且允许使用额外字段;

    UE_LOGFMT(LogCore, Warning, "Loading `{Name}` failed with error {Error}",("Name", Package->GetName()), ("Error", ErrorCode),("Flags", LoadFlags));

    字段名称必须匹配"[A-Za-z0-9_]+"格式,并且在此日志事件中必须唯一;字段值使用SerializeForLog或运算符 <<(FCbWriter&, FieldType) 进行序列化;

    • CategoryName :DECLARE_LOG_CATEGORY_* 声明的日志类别的名称;

    • Verbosity :ELogVerbosity 中的日志详细级别的名称;

    • Format :以 FLogTemplate 样式格式化字符串;

    • Fields[0-16] : 0到16字段或字段值;

    On-screen debug messages
    // 添加屏幕调试消息
    if (GEngine)
    {
    	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, TEXT("This is an Example on-screen debug message."));
    }
    // key 会获取一个唯一的整型值,用于防止同一消息被多次添加;
    // TimeToDisplay 会获取一个浮点值,用于表示消息在显示多少秒后消失;
    // DisplayColor 用于指定文本显示的颜色;
    // DebugMessage 是要显示的消息;可以像使用日志那样,在屏幕调试消息中使用格式说明符和变量;

    Unity可扩展、灵活的战斗框架实现3 GameplayTag 由于我们希望实现的目的是所有的技能、效果等都是可配置的,而目前来说一个效果的配置是存储在scriptableObject中,多个效果实例在代码层面上,都是一个类型,仅仅是读取的配置项不同。而为了区分它们,以便对同一效果进行区分对待,比如有的技能多次命中会刷新时间eg:大部分的眩晕技能),有的技能多次命中则叠加层数(eg:小鱼人的敏捷偷取),这里就抽象出一层标签。一个parent,可以为空,用于配置自己的父级,一个方法用于确认标签是否是另一个标签的衍生层级。我们来看代码,代码非常简单。 阅读详情

    相关推荐

    UE架构实战:UObject、蓝图与C++协作及Gameplay框架解析

    游戏引擎架构决定了项目的可扩展性与开发效率。在虚幻引擎(UE)中,UObject的反射机制支撑起蓝图与C++的深度协作,使得编辑器封装、状态管理和网络同步得以高效运行。理解Actor与Component的“组合优于继承”原则,能有效避免继承链过深带来的维护灾难。从玩家登录到计分模块,Gameplay框架将GameMode、GameState、PlayerState等职责清晰划分,帮助开发者从“做画面”升级到“做规则”。进一步引入GameplayTag与GAS,以标记系统替代枚举,可轻松应对复杂技能与状态组合

    weixin_29001655的博客 253

    【UE5 C++课程系列笔记】12——Gameplay标签的基本使用

    目录: 概念 主要作用 1 组织与分类游戏元素 1.1 驱动游戏逻辑 1.2 便于查询与筛选 2 助力网络同步与优化 定义Gameplay标签 1在项目设置中添加标签 2从数据表资产导入标签 3 使用C++定义标签 3.1UE_DECLARE_GAMEPLAY_TAG_EXTERN 3.2UE_DEFINE_GAMEPLAY_TAG 3.3UE_DEFINE_GAMEPLAY_TAG_COMMENT 3.4UE_DEFINE_GAMEPLAY_TAG_STATIC...

    ChaoChao66666的博客 4094

    UE实战架构拆解:C++与Gameplay框架及渲染管线优化

    游戏引擎架构是大型项目开发的基石,其核心在于对象模型、内存管理与渲染管线的协同。Unreal Engine通过Gameplay框架、反射系统和垃圾回收机制,为多人在线、状态同步等复杂场景提供标准化解决方案。深入理解C++与蓝图的边界划分、渲染管线的分层设计以及性能分析工具链,能够帮助开发者构建可维护、高性能的游戏系统。在性能敏感的核心逻辑、网络复制规则和渲染管线扩展中,C++层承担着骨架作用,而蓝图则聚焦于表现层与快速迭代。掌握这些底层原理与工程实践,是UE项目从Demo走向上线的关键。

    weixin_29474431的博客 251

    15. UE5 GAS RPG使用GameplayTag

    GameplayTag本来是应用在GAS游戏技能系统里面的,后来UE直接将其抽离出来,作为一个模块,现在可以不在GAS里也可以使用这个模块。比如,我需要判断一个射线拾取的物体,首先我需要判断这个actor是否存在,然后判断是否是对应类型,如果我们使用GameplayTag,直接判断拾取的actor上面是否含有对应标签,可以提高性能。GameplayTag也不是单纯的字符串标签,而是一种具有层级结构的对象,可以用作识别、匹配、分类或过滤易于管理,而且完全支持自定义结构。

    现在学习也不晚 3345

    虚幻引擎调试黑科技:用Gameplay Debugger自定义你的专属数据监控面板

    本文深入解析了虚幻引擎内置的Gameplay Debugger调试器,指导开发者如何通过C++深度扩展,构建自定义的实时数据监控面板。文章从核心架构入手,提供了创建战斗状态、网络同步验证等专属调试类别的完整实战代码,帮助开发者在复杂AI、网络同步等场景中实现高效调试,提升问题定位效率。

    weixin_29018815的博客 260

    深入理解虚幻引擎源码:从编译到核心架构的完整指南

    游戏引擎是现代游戏开发的核心框架,它封装了底层硬件交互、资源管理和实时渲染等复杂功能,为开发者提供了高效的创作工具。其核心原理在于通过模块化架构组织代码,并利用反射系统实现运行时类型信息与动态交互,这极大地提升了开发效率和灵活性。理解引擎源码的技术价值在于,它使开发者能从“使用者”转变为“构建者”,不仅能快速定位和修复深层次Bug,还能根据项目需求定制引擎功能、优化性能瓶颈。在大型3A游戏开发、高保真模拟、实时可视化等应用场景中,这种底层掌控能力至关重要。本文以虚幻引擎为例,详细解析了其源码的获取、编译、核

    weixin_31032799的博客 315

    虚幻引擎C++组件拆分与Gameplay框架实战:从上帝类到清晰架构

    在游戏开发中,C++代码的组织方式直接影响项目的可维护性和扩展性。许多开发者初学虚幻引擎时,习惯将移动、开火、换弹等逻辑全部堆叠在Character类中,最终形成难以维护的“上帝类”。本文从基础的组件化思想切入,介绍如何利用ActorComponent拆分职责,并深入解析GameMode、PlayerController、Pawn、PlayerState四件套的协作关系,帮助理解对象生命周期和Spawn流程。同时结合第三人称射击Demo,讲解射线检测、Projectile移动与伤害接口的工程实现。通过合理的

    weixin_29015127的博客 279

    虚幻引擎5 Gameplay框架(二):从3C到网络复制的实战构建

    本文是虚幻引擎5 Gameplay框架系列的第二篇,聚焦于将单机3C角色改造为支持多人联机的实战过程。核心在于理解并应用网络复制机制,详细讲解了如何为Character、PlayerController和GameState配置网络属性与RPC调用,以构建一个稳定的客户端-服务器架构原型,确保游戏状态在多客户端间的权威同步。

    weixin_29018815的博客 237

    【我用虚幻引擎做了一款,我目前最满意的数字孪生角色Pawn!(升级版)】

    嘿,各位做数字孪生、智慧城市的兄弟们,我是王胖胖。两个月前,我免费分享过一个角色控制插件。当时评论区炸了——很多人说好用,但也有人反馈:“胖胖,你这个插件有bug啊,如果下面是树木植被的话,镜头会疯狂抖动!对,我看到了。那个版本确实有坑。所以这两个月,除去日常工作,还有项目外包,剩下的时间就埋头修bug、做优化。现在,修复版 + 全面升级版,它来了!不过这次,我把它上架到了Epic Fab商城,正式成为一个付费插件。这个插件我还会持续更新,后面会加入一些其他功能。

    BigGuanRen的博客 450

    UE引擎架构实战:从反射到性能优化的核心机制解析

    从游戏引擎的底层抽象谈起,理解运行时类型信息与对象生命周期管理是架构设计的基石。虚幻引擎通过UObject反射体系实现属性序列化、蓝图交互与网络复制,并以多线程流水线协同渲染与逻辑计算。在实际项目中,这套机制不仅支撑起Gameplay框架的组件化组合,也是网络同步与性能优化的基础。本文结合UE实战,深入讲解反射选型、线程调度、GAS落地及性能剖析等关键技术,帮助开发者理顺架构脉络,真正实现从“会用UE”到“能改UE”的跨越。

    weixin_31306591的博客 212

    UE架构实战:拆解Gameplay框架、蓝图分层与GAS技能系统

    游戏引擎的架构设计决定了项目的可扩展性与维护成本,尤其在虚幻引擎中,理解运行时骨架是搭建稳定玩法系统的前提。UE采用平台层、引擎层、游戏层的模块化分层,依赖方向自下而上,游戏逻辑应独立成模块,避免与引擎层耦合。Gameplay框架明确了Pawn、Character、Controller的职责边界,Actor与Component的粒度选择需依据生命周期与依附关系。蓝图并非玩具,数据、逻辑、表现三层分离,借助事件和委托解耦通信,可显著提升协作效率。C++与蓝图混合开发遵循“性能下沉、配置上浮”原则,常用接口类打

    weixin_29016833的博客 247

    UE_C++ —— Gameplay Architecture111111

    游戏架构!

    NapoleonCoder的博客 290

    UE5 GAS 学习笔记 1.2游戏标签

    这一节对GameplayTag游戏标签进行详细的讲解。

    CloudHuCoding 1927

    程序员UE5入门指南:从C++架构到蓝图协作的思维转换与实践

    在游戏开发与实时交互应用领域,理解引擎核心架构是程序员高效工作的基础。现代游戏引擎普遍采用基于组件的实体系统与反射机制,这为可视化编程和动态运行时功能提供了支撑。其技术价值在于实现了逻辑与表现的分离,提升了开发效率与协作流畅度。在应用场景上,这种架构特别适合需要快速迭代的复杂交互系统,如游戏、数字孪生和仿真训练。本文聚焦于虚幻引擎5(UE5),深入解析其以UObject为基石的反射系统、Actor-Component组合模式以及Gameplay框架。通过剖析C++与蓝图的无缝协作工作流,并结合委托通信、资源

    weixin_33721427的博客 356

    告别抓瞎!手把手教你用Keysight Infinium和M8040完成PCIe 5.0 TX/RX信号完整性测试

    本文详细介绍了如何使用Keysight Infinium示波器和M8040误码仪完成PCIe 5.0 TX/RX信号完整性测试。从测试前的设备准备、软件配置到具体的TX信号测试步骤和RX容限测试实战,提供了全面的操作指南和常见问题解决方案,帮助工程师高效完成PCIe 5.0合规性测试。

    weixin_30911451的博客 503

    AI Agent生产环境压测指南:并发、延迟与资源瓶颈定位

    去年我接触了不下10个AI Agent上线翻车的案例:有做智能客服的公司双11当天流量暴涨3倍,Agent服务直接雪崩,用户咨询全超时,损失超200万订单;有做企业内部RAG Agent的团队,上线后100人同时查询就把向量库打挂,还连累了其他业务的数据库;还有做多智能体系统的团队,压测只测了单请求性能,上线后多Agent交互超时率高达30%。

    软件工程实践的博客 170

    虚幻引擎调试黑科技:用Gameplay Debugger自定义你的专属调试面板(C++版)

    本文深入探讨了虚幻引擎中Gameplay Debugger的C++扩展方法,指导开发者创建自定义调试面板以提升复杂项目的调试效率。通过构建自定义数据收集组件和HUD显示组件,实现实时监控技能系统、库存状态等游戏运行时数据,并涵盖网络同步与性能优化等高级实践。

    f6g7h8i9j的博客 181

    实现单芯片2400bps 音频编解码方案

    终于按照朋友的需求,帮他开发了一款单芯片集音频采集编码传送并接收解码播放功能的方案,交给快递公司邮寄给远在郑州的他.整个项目从开始定型到现在一个样机送出,整整花了1个月时间,感觉挺有成就感,挺有意思,工作是辛苦的,结果却是快乐的.现在将这中快乐的的心情与大家分享一下.    真个项目用一颗主控搞定,ARM9的,200KBytes 片内RAM,主频可以跑到300MHz,64pin LQFP封装

    nyxsec的专栏 3682

    UE引擎架构深度解析:模块、GAS与网络同步

    游戏引擎架构设计是决定大型项目长期稳定与可扩展性的基石。理解模块化工程中的依赖隔离原则、以及从启动流程到模块调度的整体运作机制,是开发者从功能使用者升级为引擎级工程师的关键一步。针对多人联机与复杂战斗场景,多线程任务调度与网络同步架构更是绕不开的核心技术挑战,如何在服务器权威与客户端预测之间取得体验与性能的平衡,考验着每个团队的架构功力。同时,借助Gameplay框架与GAS能力,将状态与效果数据化、配置化,能够极大提升玩法迭代效率。以UE引擎为例,深入剖析模块依赖、Gameplay框架、GAS、多线程与网

    weixin_30368981的博客 204

    国内用户稳定开通ChatGPT Plus会员的完整实操指南

    ChatGPT作为当前最先进的AI对话模型,其技术原理基于Transformer架构和大规模语言预训练。通过深度学习技术,ChatGPT能够理解自然语言并生成高质量回复,在代码生成、文档分析等场景具有重要技术价值。对于需要高频使用的开发者而言,ChatGPT Plus会员提供了优先访问权和GPT-4模型支持等核心功能。在实际应用场景中,国内用户常面临支付限制和区域封锁等挑战。本文针对这些痛点,重点解析如何通过可靠的支付渠道和账号安全策略,实现稳定的会员开通方案,帮助用户突破技术使用壁垒,充分发挥AI助手在开

    weixin_29042035的博客 952

    ABAP - Function ALV 11 - 单元格下拉框

    【代码】ABAP - Function ALV 11 - 单元格下拉框。

    HeathlX的博客 1812

    虚幻引擎实战指南:模块化、蓝图C++边界与GAS落地

    游戏引擎的架构设计最终要落到具体工程实践中。在虚幻引擎中,模块化通过Build.cs定义依赖边界,UObject、Actor与Component的分工则决定了对象模型的清晰度。蓝图与C++的双轨开发,需要围绕性能、复杂度、迭代频率找到合理边界,用UFUNCTION与UPROPERTY将C++封装成蓝图可用的积木。高级玩法系统可借助GAS完成技能、Buff与属性的统一抽象,配合DataAsset或DataTable实现数据驱动设计。性能优化需依托Unreal Insights与stat命令定位瓶颈,并从架构层

    weixin_32479897的博客 216

    FPGA原型验证(八):SoC设计适配与实现策略

    时钟门控在SoC设计中很常见,应该小心处理门控时钟,以成功地在FPGA上原型化SoC设计。现代FPGA综合工具在正确约束的情况下,可以自动处理大多数门控时钟。通过遵循本章中的指导方针,即使具有复杂时钟门控的SoC设计也可以得到处理,并成功地在FPGA上进行原型化。

    叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 477

    InsideLyra - 致UE“初学者”

    剖析Lyra项目,尽快掌握“工业级”入门游戏,提高自己的游戏开发水平。

    UWA—简单优化,优化简单! 162

    玩转企业微信群机器人:一份超详细的Webhook消息推送指南

    企业微信群机器人是一个连接内部协作与外部服务的强大桥梁。通过灵活运用本文介绍的各种消息类型,你可以构建出丰富多彩、信息明确的自动化通知系统,极大地提升团队沟通效率。现在,就动手试试,将你的第一个机器人集成到工作流中吧!如果你有任何有趣的机器人应用场景或遇到了问题,欢迎在评论区留言交流!

    代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 3975

    OpenRubrics:自动化评估工具优化LLM对齐效果

    在大型语言模型(LLM)应用中,模型输出与人类价值观的对齐是关键挑战。传统人工反馈强化学习(RLHF)依赖大量标注,成本高且一致性差。OpenRubrics作为开源工具,通过结构化准则实现自动化评估,显著提升效率。其核心原理是将主观评价转化为可量化的多维指标,支持动态权重调整,并兼容主流RLHF框架。该工具在降低标注成本的同时,通过Fleiss' Kappa系数等指标保证评估一致性,适用于多语言和医疗等专业领域,为LLM对齐提供了工程化解决方案。

    weixin_28730037的博客 357
    上一篇: UE_C++ —— Gameplay Modules
    下一篇: UE_C++ —— Logging in Unreal
    挨代码
    博客等级 码龄5年 1696粉丝 384原创
    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    个

    红包个数最小为10个

    元

    红包金额最低5元

    当前余额3.43元 前往充值 >
    需支付:10.00元
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付元
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值