typora-root-url: ./images
title: UE - C++ (1)
categories: 技术
date: 2023-03-25 14:41:46
tags: C++

指针和引用

指针

AActor* 变量名;//硬指针,场景实际存在对象指针
TSubclassof< AActor> 变量名;//硬对象类指针,允许使用类型安全传递UClass模板
TSoftObjectPtr< AActor> 变量名;//软对象指针,在需要时才进行加载
TSoftClassPtr < AActor> 变量名;//软对象类指针,同上
TWeakObjectPtr< AActor> 变量名;//弱指针,可以访问一个对象又不对造成计数加1,一般用于局部变量,无需保存
TSharedPtr< T> 变量名;//共享智能指针,不用用于UObject对象,因为UObject有自己的一套GC垃圾回收规则,TSharedPtr用于自定义的结构体(不继承UObject)

引用

TSharfRef< T >变量名;//共享引用,和TSharfRef一样都用于非UObject,无法引用空的对象

UPROPERTY

常用属性

EditAnywhere

说明此属性可通过属性窗口在原型和实例上进行编辑。此说明符与所有"可见"说明符均不兼容。

BlueprintCallable

仅用于组播委托。应公开属性在蓝图代码中调用。

Replicated

属性复制

UMETA

meta = (DisplayName="Property Name")

此属性显示的命名,不显示代码生成的命名。

meta = (EditCondition = “BooleanPropertyName”)

对一个布尔属性进行命名,此属性用于说明此属性的编辑是否被禁用。将"!"放置在属性命名前可颠倒测试。

meta = (ClampMin/ClampMax="N")

用于浮点和整数属性。指定可在属性中输入的最小值/最大值 N。

meta = (ArrayClamp="ArrayProperty")

用于整数属性。将可在UI中输入的有效值锁定在0和命名数组属性的长度之间。

meta = (ExposeOnSpawn="true")

属性生成时公开

UFUNCTION

参数

值传递函数参数

变量类型 变量名; 
const 变量类型 变量名;//const 类型,无法修改。

引用传递函数参数

UPARAM(ref)变量类型& 变量名
const 变量类型& 变量名

指针传递函数参数

UObject*;
const UObject*;
const UObject*constUPARAM(Ref)UObject*&;

返回多个参数

变量类型& 变量名
UObject*&;
参数说明符描述
Out声明由引用传递的参数,使函数对其进行修改。
Optional通过任选关键词可使部分函数参数变为任选,便于调用。任选参数的数值(调用方未指定)取决于函数。例如,SpawnActor 函数使用任选位置和旋转,默认为生成的 Actor 根组件的位置和旋转。添加 = [value] 参数可指定任选参数的默认值。例如:function myFunc(optional int x = -1)。在多数情况下,如无数值被传递到任选参数,将使用变量类型的默认值或零(例如 0、false、""、none)。

委托

单播委托可以有返回值,多播没有

动态委托

可序列化且支持反射的委托。

声明动态委托

动态委托可序列化,其函数可按命名查找,但其执行速度比常规委托慢。

声明宏描述
DECLARE_DYNAMIC_DELEGATE[_RetVal, ...]( DelegateName )创建一个动态委托。
DECLARE_DYNAMIC_MULTICAST_DELEGATE[_RetVal, ...] ( DelegateName )创建一个动态组播委托。

动态委托绑定

辅助宏说明
BindDynamic( UserObject, FuncName )用于在动态委托上调用BindDynamic()的辅助宏。自动生成函数命名字符串。
AddDynamic( UserObject, FuncName )用于在动态组播委托上调用AddDynamic()的辅助宏。自动生成函数命名字符串。
RemoveDynamic( UserObject, FuncName )用于在动态组播委托上调用RemoveDynamic()的辅助宏。自动生成函数命名字符串。

执行动态委托

通过调用委托的 Execute() 函数执行绑定到委托的函数。执行前须检查委托是否已绑定。 此操作是为了使代码更安全,因为有时委托可能含有未初始化且被后续访问的返回值和输出参数。 执行未绑定的委托在某些情况下确实可能导致内存混乱。可调用 IsBound() 检查是否可安全执行委托。 同时,对于无返回值的委托,可调用 ExecuteIfBound(),但需注意输出参数可能未初始化。

执行函数描述
Execute不检查其绑定情况即执行一个委托
ExecuteIfBound检查一个委托是否已绑定,如是,则调用Execute
IsBound检查一个委托是否已绑定,经常出现在包含 Execute 调用的代码前

多播委托

可以绑定到多个函数并一次性同时执行它们的委托
多播委托拥有大部分与单播委托相同的功能。它们只拥有对对象的弱引用,可以与结构体一起使用,可以四处轻松复制等等。
就像常规委托一样,多播委托可以远程加载/保存和触发;但多播委托函数不能使用返回值。它们最适合用来 四处轻松传递一组委托。

声明多播委托

声明宏说明
DECLARE_MULTICAST_DELEGATE[_RetVal, ...]( DelegateName )创建一个多播委托。
DECLARE_DYNAMIC_MULTICAST_DELEGATE[_RetVal, ...]( DelegateName )创建一个动态多播委托。

示例

\\在class类外声明委托
DECLARE_MULTICAST_DELEGATE_OneParam(DelegateName,float,Param1)
//在class类内声明一个委托属性
UPROPERTY(BlueprintAssignable)//注意,只有多播委托适用于蓝图
DelegateName DelegateName1;
//.cpp
DelegateName1.Broadcast(63.0);//调用委托

事件

事件是一种特殊类型的多播委托,它在访问 Broadcast() 、IsBound() 和 Clear() 函数时会受到限制。
事件与 组播委托 十分相似。虽然任意类均可绑定事件,但只有声明事件的类可以调用事件 的 Broadcast、IsBound 和 Clear函数。这意味着事件对象可在公共接口中公开,而无需让外部类访问这些敏感度函数。事件使用情况有:在纯抽象类中包含回调、限制外部类调用 Broadcast、IsBound 和 Clear 函数。

声明事件

声明宏描述
DECLARE_EVENT( OwningType, EventName )创建一个事件。
DECLARE_EVENT_OneParam( OwningType, EventName, Param1Type )创建带一个参数的事件。
DECLARE_EVENT_TwoParams( OwningType, EventName, Param1Type, Param2Type )创建带两个参数的事件。
DECLARE_EVENT_Params( OwningType, EventName, Param1Type, Param2Type, ...)创建带 N 个参数的事件。

DECLARE_EVENT 宏的首个参数OwningType是"拥有"此事件的类,因此可调用 Broadcast() 函数。

绑定事件

事件绑定与 组播委托绑定 方式相同。

事件执行

事件允许附带多个函数委托,然后调用事件的 Broadcast() 函数将它们一次性全部执行。事件签名不允许使用返回值。对于事件而言,只有定义事件的类才能调用 Broadcast() 函数。

即使不存在绑定,在事件上调用 Broadcast() 也是安全操作。唯一需要注意的情况是使用事件初始化输出变量,通常不建议执行此操作。

调用 Broadcast() 函数时,被绑定函数的执行顺序尚未定义。有可能不按照函数的添加顺序执行。

static

static

  1. 在修饰变量的时候,static 修饰的静态局部变量只执行初始化一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放。
  2. static 修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是 extern 外部声明也不可以。
  3. static 修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。static 修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为 0。
  4. 不想被释放的时候,可以使用static修饰。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用 static 修饰。
  5. 考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用 static)。

蓝图函数库需要使用static修饰函数的原因:

静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问。使得函数库与特定游戏对象解耦。

反射

获取到程序里定义的所有的class

TArray<UObject*> result;
GetObjectsOfClass(UClass::StaticClass(), result);   //获取所有的class和interface
GetObjectsOfClass(UEnum::StaticClass(), result);   //获取所有的enum
GetObjectsOfClass(UScriptStruct::StaticClass(), result);   //获取所有的struct

根据一个名字来查找某个类型对象

UClass* classObj=FindObject<UClass>(ANY_PACKAGE,”MyClass“);   //获得表示MyClass的UClass*

根据名称查找属性

UProperty* UStruct::FindPropertyByName(FName InName) const
{
    for (UProperty* Property = PropertyLink; Property != NULL; Property = Property->PropertyLinkNext)
    {
        if (Property->GetFName() == InName)
        {
            return Property;
        }
    }
    return NULL;
}

根据名称查找函数

UFunction* UClass::FindFunctionByName(FName InName, EIncludeSuperFlag::Type IncludeSuper) const;

运行一个函数

UFUNCTION(BlueprintCallable)
int32 InvokeFunction(UObject* obj, FName functionName,float param1)
{
    struct Func_Parms   //定义一个结构用来包装参数和返回值,就像在gen.cpp里那样
    {
        float param1;
        int32 ReturnValue;
    };
    UFunction* func = obj->FindFunctionChecked(functionName);
    MyClass_Func_Parms params;
    params.param1=param1;
    obj->ProcessEvent(func, &params);
    return params.ReturnValue;
}
template<typename... TReturns, typename... TArgs>
void InvokeFunction(UClass* objClass, UObject* obj, UFunction* func, TTuple<TReturns...>& outParams, TArgs&&... args)
{
    objClass = obj != nullptr ? obj->GetClass() : objClass;
    UObject* context = obj != nullptr ? obj : objClass;
    uint8* outPramsBuffer = (uint8*)&outParams;

    if (func->HasAnyFunctionFlags(FUNC_Native)) //quick path for c++ functions
    {
        TTuple<TArgs..., TReturns...> params(Forward<TArgs>(args)..., TReturns()...);
        context->ProcessEvent(func, &params);
        //copy back out params
        for (TFieldIterator<UProperty> i(func); i; ++i)
        {
            UProperty* prop = *i;
            if (prop->PropertyFlags & CPF_OutParm)
            {
                void* propBuffer = prop->ContainerPtrToValuePtr<void*>(&params);
                prop->CopyCompleteValue(outPramsBuffer, propBuffer);
                outPramsBuffer += prop->GetSize();
            }
        }
        return;
    }

    TTuple<TArgs...> inParams(Forward<TArgs>(args)...);
    void* funcPramsBuffer = (uint8*)FMemory_Alloca(func->ParmsSize);
    uint8* inPramsBuffer = (uint8*)&inParams;

    for (TFieldIterator<UProperty> i(func); i; ++i)
    {
        UProperty* prop = *i;
        if (prop->GetFName().ToString().StartsWith("__"))
        {
#####             //忽略私有参数例如蓝图函数库的函数的__WolrdContext ignore private param like __WolrdContext of function in blueprint funcion library
            continue;
        }
        void* propBuffer = prop->ContainerPtrToValuePtr<void*>(funcPramsBuffer);
        if (prop->PropertyFlags & CPF_OutParm)
        {
            prop->CopyCompleteValue(propBuffer, outPramsBuffer);
            outPramsBuffer += prop->GetSize();
        }
        else if (prop->PropertyFlags & CPF_Parm)
        {
            prop->CopyCompleteValue(propBuffer, inPramsBuffer);
            inPramsBuffer += prop->GetSize();
        }
    }

    context->ProcessEvent(func, funcPramsBuffer);   //call function
    outPramsBuffer = (uint8*)&outParams;    //reset to begin

    //copy back out params
    for (TFieldIterator<UProperty> i(func); i; ++i)
    {
        UProperty* prop = *i;
        if (prop->PropertyFlags & CPF_OutParm)
        {
            void* propBuffer = prop->ContainerPtrToValuePtr<void*>(funcPramsBuffer);
            prop->CopyCompleteValue(outPramsBuffer, propBuffer);
            outPramsBuffer += prop->GetSize();
        }
    }
}

获得一个类下面的所有子类

const UClass* classObj; //结构和类
TArray<UClass*> result;
GetDerivedClasses(classObj, result, false);