接上一回处理多个LUA状态机问题,暂时无法解决单个虚幻状态机对应多个LUA状态机问题,故先搁置,转而看看UnLua的设计,本文记录对其改进过程。
UnLua里面有个非常便捷的功能,就是在蓝图编辑器界面可以直接生成LUA代码模板,开始以为是基于反射生成的,看了下发现其实是从内置的LUA文件复制的,
非常不灵活,其内置了Actor,UserWidget等几种常用类型,但是对于有些自己项目中的C++反射类没法准确支持,只能机械地去复制Actor或UserWidget等父类LUA表。
改动很简单,根据上一回的UClass相关描述,直接迭代UClass获取UFunction及其参数即可。代码如下:
// Tencent is pleased to support the open source community by making UnLua available.
//
// Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
#include "UnLuaPrivate.h"
#include "Misc/FileHelper.h"
#include "Engine/Blueprint.h"
#include "Blueprint/UserWidget.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/Actor.h"
#include "Interfaces/IPluginManager.h"
#include "PlatformFilemanager.h"
void OpenLuaFileInTextEditor(FString InFileName);
bool CreateLuaTemplateFileEx(UBlueprint* Blueprint);
// create Lua template file for the selected blueprint
bool CreateLuaTemplateFile(UBlueprint *Blueprint)
{
return CreateLuaTemplateFileEx(Blueprint);
if (Blueprint)
{
UClass *Class = Blueprint->GeneratedClass;
FString ClassName = Class->GetName();
FString OuterPath = Class->GetPathName();
int32 LastIndex;
if (OuterPath.FindLastChar('/', LastIndex))
{
OuterPath = OuterPath.Left(LastIndex + 1);
}
OuterPath = OuterPath.RightChop(6); // ignore "/Game/"
FString FileName = FString::Printf(TEXT("%s%s%s.lua"), *GLuaSrcFullPath, *OuterPath, *ClassName);
if (FPaths::FileExists(FileName))
{
UE_LOG(LogUnLua, Warning, TEXT("Lua file (%s) is already existed!"), *ClassName);
return false;
}
static FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("UnLua"))->GetContentDir();
FString TemplateName;
if (Class->IsChildOf(AActor::StaticClass()))
{
// default BlueprintEvents for Actor
TemplateName = ContentDir + TEXT("/ActorTemplate.lua");
}
else if (Class->IsChildOf(UUserWidget::StaticClass()))
{
// default BlueprintEvents for UserWidget (UMG)
TemplateName = ContentDir + TEXT("/UserWidgetTemplate.lua");
}
else if (Class->IsChildOf(UAnimInstance::StaticClass()))
{
// default BlueprintEvents for AnimInstance (animation blueprint)
TemplateName = ContentDir + TEXT("/AnimInstanceTemplate.lua");
}
else if (Class->IsChildOf(UActorComponent::StaticClass()))
{
// default BlueprintEvents for ActorComponent
TemplateName = ContentDir + TEXT("/ActorComponentTemplate.lua");
}
FString Content;
FFileHelper::LoadFileToString(Content, *TemplateName);
Content = Content.Replace(TEXT("TemplateName"), *ClassName);
return FFileHelper::SaveStringToFile(Content, *FileName);
}
return false;
}
// create Lua template file for the selected blueprint
bool CreateLuaTemplateFileEx(UBlueprint* Blueprint)
{
if (Blueprint)
{
UClass* Class = Blueprint->GeneratedClass;
FString ClassName = Class->GetName();
FString OuterPath = Class->GetPathName();
int32 LastIndex;
if (OuterPath.FindLastChar('/', LastIndex))
{
OuterPath = OuterPath.Left(LastIndex + 1);
}
OuterPath = OuterPath.RightChop(6); // ignore "/Game/"
FString FileName = FString::Printf(TEXT("%s%s%s.lua"), *GLuaSrcFullPath, *OuterPath, *ClassName);
if (FPlatformFileManager::Get().GetPlatformFile().FileSize(*FileName) > 1)
{
UE_LOG(LogUnLua, Warning, TEXT("Lua file (%s) is already existed!"), *ClassName);
OpenLuaFileInTextEditor(FileName);
return false;
}
static FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("UnLua"))->GetContentDir();
FString Content;
FString CurrentTime = FDateTime::Now().ToString();
FString ComputerUserName = FPlatformProcess::UserName(true);
FString HeaderStr = FString::Printf(TEXT("
--Author:%s
--Date:%s
require('UnLua')
local %s = Class()
"), *ComputerUserName, *CurrentTime, *ClassName);
Content += HeaderStr;
for (TFieldIterator<UFunction> Func(Class); Func; ++Func)
{
if (!Func->HasAnyFunctionFlags(FUNC_BlueprintEvent))
{
continue;
}
FString ReturnType = FString("void");
if (Func->GetReturnProperty())
{
ReturnType = Func->GetReturnProperty()->GetCPPType();
}
FString FuncName = Func->GetName();
FString ParamTypeList;
FString ParamList;
#if ENGINE_MINOR_VERSION < 25
for (TFieldIterator<UProperty> Prop(*Func); Prop; ++Prop)
#else
for (TFieldIterator<FProperty> Prop(*Func); Prop; ++Prop)
#endif
{
if (!Prop->HasAnyPropertyFlags(CPF_OutParm))
{
if (ParamList.Len() > 0)
{
ParamList = ParamList + FString(", ") + Prop->GetName();
ParamTypeList = ParamTypeList + FString(", ") + Prop->GetCPPType();
}
else
{
ParamList = ParamList + Prop->GetName();
ParamTypeList = ParamTypeList + Prop->GetCPPType();
}
}
}
FString FuncStr = FString::Printf(TEXT("--%s(%s)
--function %s:%s(%s)
--end"),*ReturnType, *ParamTypeList, *ClassName, *FuncName, *ParamList);
Content = Content + FString("
") + FuncStr + FString("
");
}
FString EndStr = FString::Printf(TEXT("
return %s"), *ClassName);
Content = Content + EndStr;
bool bValidFile = FFileHelper::SaveStringToFile(Content, *FileName);
if (bValidFile)
{
OpenLuaFileInTextEditor(FileName);
}
return bValidFile;
}
return false;
}
void OpenLuaFileInTextEditor(FString InFileName)
{
#if PLATFORM_WINDOWS
TArray<FString> TextEditorPathList;
TextEditorPathList.Add(FString("C:\Program Files\Microsoft VS Code\Code.exe"));
TextEditorPathList.Add(FString("C:\Program Files\Notepad++\notepad++.exe"));
//fallback to windows notepad
TextEditorPathList.Add(FString("C:\Windows\notepad.exe"));
for (int32 Idx = 0; Idx < TextEditorPathList.Num(); ++Idx)
{
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*TextEditorPathList[Idx]))
{
uint32 OutPID = 0;
FPlatformProcess::CreateProc(*TextEditorPathList[Idx], *InFileName, true, false, false, &OutPID, 0, nullptr, nullptr);
break;
}
}
#endif
}
修改点:
1.新增了bool CreateLuaTemplateFileEx(UBlueprint* Blueprint)函数自动生成LUA文件,而不是机械复制几个固定的模板
2.在判断目标文件已经存在时,改用判断文件大小,方便在VSCode清空文本并重新生成LUA文件
3.目前只支持了BlueprintEvent标记的函数,包括BlueprintImplementation/BlueprintNativeEvent;可根据需要去掉限制
4.为LUA函数生成对应c++函数签名,一眼便知函数返回值和各参数类型
5.若在Windows系统,生成成功后会调用文本编辑器打开LUA文件
效果如下:
--Author:UserNameOnComputer
--Date:2020.04.09-13.20.15
require('UnLua')
local BP_LuaCharacter_C = Class()
--void(float)
--function BP_LuaCharacter_C:OnWalkingOffLedge(TimeDelta)
--end
--void(FVector, bool, bool)
--function BP_LuaCharacter_C:OnLaunched(LaunchVelocity, bXYOverride, bZOverride)
--end
--void()
--function BP_LuaCharacter_C:OnLanded()
--end
--void()
--function BP_LuaCharacter_C:OnJumped()
--end
--void(float)
--function BP_LuaCharacter_C:K2_UpdateCustomMovement(DeltaTime)
--end
--void(float, float)
--function BP_LuaCharacter_C:K2_OnStartCrouch(HalfHeightAdjust, ScaledHalfHeightAdjust)
--end
--void(TEnumAsByte<EMovementMode>, TEnumAsByte<EMovementMode>, uint8, uint8)
--function BP_LuaCharacter_C:K2_OnMovementModeChanged(PrevMovementMode, NewMovementMode, PrevCustomMode, NewCustomMode)
--end
--void(float, float)
--function BP_LuaCharacter_C:K2_OnEndCrouch(HalfHeightAdjust, ScaledHalfHeightAdjust)
--end
--bool()
--function BP_LuaCharacter_C:CanJumpInternal()
--end
--void(AController*)
--function BP_LuaCharacter_C:ReceiveUnpossessed(OldController)
--end
--void(AController*)
--function BP_LuaCharacter_C:ReceivePossessed(NewController)
--end
--void()
--function BP_LuaCharacter_C:UserConstructionScript()
--end
--void(float)
--function BP_LuaCharacter_C:ReceiveTick(DeltaSeconds)
--end
--void(float, UDamageType*, FVector, AController*, AActor*)
--function BP_LuaCharacter_C:ReceiveRadialDamage(DamageReceived, DamageType, Origin, InstigatedBy, DamageCauser)
--end
--void(float, UDamageType*, FVector, FVector, UPrimitiveComponent*, FName, FVector, AController*, AActor*)
--function BP_LuaCharacter_C:ReceivePointDamage(Damage, DamageType, HitLocation, HitNormal, HitComponent, BoneName, ShotFromDirection, InstigatedBy, DamageCauser)
--end
--void(UPrimitiveComponent*, AActor*, UPrimitiveComponent*, bool, FVector, FVector, FVector)
--function BP_LuaCharacter_C:ReceiveHit(MyComp, Other, OtherComp, bSelfMoved, HitLocation, HitNormal, NormalImpulse)
--end
--void(TEnumAsByte<EEndPlayReason::Type>)
--function BP_LuaCharacter_C:ReceiveEndPlay(EndPlayReason)
--end
--void()
--function BP_LuaCharacter_C:ReceiveDestroyed()
--end
--void()
--function BP_LuaCharacter_C:ReceiveBeginPlay()
--end
--void(float, UDamageType*, AController*, AActor*)
--function BP_LuaCharacter_C:ReceiveAnyDamage(Damage, DamageType, InstigatedBy, DamageCauser)
--end
--void(FKey)
--function BP_LuaCharacter_C:ReceiveActorOnReleased(ButtonReleased)
--end
--void(TEnumAsByte<ETouchIndex::Type>)
--function BP_LuaCharacter_C:ReceiveActorOnInputTouchLeave(FingerIndex)
--end
--void(TEnumAsByte<ETouchIndex::Type>)
--function BP_LuaCharacter_C:ReceiveActorOnInputTouchEnter(FingerIndex)
--end
--void(TEnumAsByte<ETouchIndex::Type>)
--function BP_LuaCharacter_C:ReceiveActorOnInputTouchEnd(FingerIndex)
--end
--void(TEnumAsByte<ETouchIndex::Type>)
--function BP_LuaCharacter_C:ReceiveActorOnInputTouchBegin(FingerIndex)
--end
--void(FKey)
--function BP_LuaCharacter_C:ReceiveActorOnClicked(ButtonPressed)
--end
--void(AActor*)
--function BP_LuaCharacter_C:ReceiveActorEndOverlap(OtherActor)
--end
--void()
--function BP_LuaCharacter_C:ReceiveActorEndCursorOver()
--end
--void(AActor*)
--function BP_LuaCharacter_C:ReceiveActorBeginOverlap(OtherActor)
--end
--void()
--function BP_LuaCharacter_C:ReceiveActorBeginCursorOver()
--end
--void()
--function BP_LuaCharacter_C:K2_OnReset()
--end
--void(APlayerController*)
--function BP_LuaCharacter_C:K2_OnEndViewTarget(PC)
--end
--void(APlayerController*)
--function BP_LuaCharacter_C:K2_OnBecomeViewTarget(PC)
--end
--void(int32)
--function BP_LuaCharacter_C:ExecuteUbergraph(EntryPoint)
--end
return BP_LuaCharacter_C