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
posted @ 2020-04-09 12:40  剑过不留痕残月分断魂  阅读(1293)  评论(0编辑  收藏  举报