UE 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*const;
UPARAM(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_ | 创建带 N 个参数的事件。 |
DECLARE_EVENT 宏的首个参数OwningType是"拥有"此事件的类,因此可调用 Broadcast() 函数。
绑定事件
事件绑定与 组播委托绑定 方式相同。
事件执行
事件允许附带多个函数委托,然后调用事件的 Broadcast() 函数将它们一次性全部执行。事件签名不允许使用返回值。对于事件而言,只有定义事件的类才能调用 Broadcast() 函数。
即使不存在绑定,在事件上调用 Broadcast() 也是安全操作。唯一需要注意的情况是使用事件初始化输出变量,通常不建议执行此操作。
调用 Broadcast() 函数时,被绑定函数的执行顺序尚未定义。有可能不按照函数的添加顺序执行。
static
static
- 在修饰变量的时候,static 修饰的静态局部变量只执行初始化一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放。
- static 修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是 extern 外部声明也不可以。
- static 修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。static 修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为 0。
- 不想被释放的时候,可以使用static修饰。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用 static 修饰。
- 考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用 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, ¶ms);
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, ¶ms);
//copy back out params
for (TFieldIterator<UProperty> i(func); i; ++i)
{
UProperty* prop = *i;
if (prop->PropertyFlags & CPF_OutParm)
{
void* propBuffer = prop->ContainerPtrToValuePtr<void*>(¶ms);
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);