大家好,我是阿赵。
使用UE引擎,大部分功能都可以使用蓝图的自带节点去完成。但有时候我们也需要扩展一些蓝图没有的功能。这一篇主要学习一下怎样用C++给蓝图新增自定义的函数节点。
一、 新建蓝图函数库
在添加C++类的时候,选择蓝图函数库:
填一个类名,比如我这里命名为AzhaoFunLib
会自动生成.h文件和cpp文件。这种函数库,是继承UBlueprintFunctionLibrary的。
接下来将会举一个非常简单的例子,我写一个函数,输入两个浮点参数a和b,然后得到他们相加的结果。所以我声明一个AddNumber函数:
AzhaoFunLib.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "AzhaoFunLib.generated.h"
/**
*
*/
UCLASS()
class UECPPTEST_API UAzhaoFunLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
static float AddNumber(float a, float b);
};
AzhaoFunLib.cpp
#include "AzhaoFunLib.h"
float UAzhaoFunLib::AddNumber(float a, float b)
{
return a + b;
}
二、 让蓝图函数显示在节点列表
函数已经写完了,编译一下,再回到UE的蓝图界面,会发现并不能搜索出我刚才写的函数节点:
接下来这一步非常的重要,加上去之后,就会出现奇迹:
在.h文件声明函数的地方,加上UFUNCTION宏,比如
UFUNCTION(BlueprintPure, Category = “Azhao”)
static float AddNumber(float a, float b);
这里用到2个参数
1. BlueprintPure
这里指定了函数在蓝图里面节点的类型
2. Category = “Azhao”
这里指定了函数的节点所在的分类
把宏加上后,编译,再回到蓝图界面,会发现可以搜索出自定义的函数了:
这时候创建出来的节点,是长这样的:
完整的.h文件现在是这样:
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "AzhaoFunLib.generated.h"
/**
*
*/
UCLASS()
class UECPPTEST_API UAzhaoFunLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
UFUNCTION(BlueprintPure, Category = "Azhao")
static float AddNumber(float a, float b);
};
可以看到,现在生成的节点是没有三角形调用进度,是一个纯的函数计算节点。
如果把UFUNCTION宏里面的BlueprintPure改为BlueprintCallable,比如这样:
UFUNCTION(BlueprintCallable, Category = "Azhao")
static float AddNumber(float a, float b);
那么节点会变成可以有调用进度的类型:
这里可以明显看出BlueprintPure和BlueprintCallable的区别了。
三、 WorldContext 参数的使用
接下来我要做一个通过路径来加载Actor蓝图的功能节点。
之前介绍过,在C++里面创建Actor,是这样写的:
UClass* bpVar = LoadClass<AActor>(nullptr, *path);
AActor* spawnActor = GetWorld()->SpawnActor<AActor>(bpVar)
但这里有一个问题,GetWorld是要继承Actor的类才能直接调用的,但作为纯代码库的蓝图库函数,是没有这个继承关系的,所以GetWorld也是不能直接调用。
所以这里要引入一个WorldContext 参数,让我们能通过它来调用GetWorld
在.h文件里面声明这个函数:
UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject"), Category = "Azhao")
static AActor* SpawnActorByPath(const UObject* WorldContextObject, FString path);
注意看UFUNCTION宏里面,多了一个meta = (WorldContext = “WorldContextObject”),然后在输入参数里面,声明了一个const UObject* WorldContextObject。
这样,在函数实现里面,我们就可以通过
AActor* spawnActor = WorldContextObject->GetWorld()->SpawnActor<AActor>(bpVar);
来调用GetWorld,并生成出Actor了
然后完整的代码是这样:
AzhaoFunLib.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "AzhaoFunLib.generated.h"
/**
*
*/
UCLASS()
class UECPPTEST_API UAzhaoFunLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
UFUNCTION(BlueprintCallable, Category = "Azhao")
static float AddNumber(float a, float b);
UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject"), Category = "Azhao")
static AActor* SpawnActorByPath(const UObject* WorldContextObject, FString path);
};
AzhaoFunLib.cpp
#include "AzhaoFunLib.h"
float UAzhaoFunLib::AddNumber(float a, float b)
{
return a + b;
}
AActor* UAzhaoFunLib::SpawnActorByPath(const UObject* WorldContextObject, FString path)
{
UClass* bpVar = LoadClass<AActor>(nullptr, *path);
if (bpVar != nullptr)
{
AActor* spawnActor = WorldContextObject->GetWorld()->SpawnActor<AActor>(bpVar);
return spawnActor;
}
else
{
return nullptr;
}
}
编译后回到了蓝图界面,发现自定义函数可以被搜索到了:
WorldContextObject参数并不会暴露出来,我们直接调用这个节点,然后填入Path参数,就可以在场景里面生成Actor了。