UnLua改进记录
接上一回处理多个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("\r\n--Author:%s\r\n--Date:%s\r\n\nrequire('UnLua')\r\n\nlocal %s = Class()\r\n"), *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)\r\n--function %s:%s(%s)\r\n--end"),*ReturnType, *ParamTypeList, *ClassName, *FuncName, *ParamList); Content = Content + FString("\r\n") + FuncStr + FString("\r\n"); } FString EndStr = FString::Printf(TEXT("\r\nreturn %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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· 程序员常用高效实用工具推荐,办公效率提升利器!
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)