-
Notifications
You must be signed in to change notification settings - Fork 428
LuaActor的使用
sluaunreal提供了c++层的LuaActor类,通过这个类你可以在lua里扩展c++的Actor或者蓝图的Actor,这样就可以使用lua来实现Actor的逻辑。
选择添加c++类,选择LuaActor为父类
给你的Actor起一个名字后,确定,Unreal将会为你生成对应的c++代码文件,类似如下:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "LuaActor.h"
#include "MyLuaActor.generated.h"
/**
*
*/
UCLASS()
class DEMOCPP_API AMyLuaActor : public ALuaActor
{
GENERATED_BODY()
};
设定Actor绑定的lua文件,为此我们需要添加构造函数,例如:
AMyLuaActor::AMyLuaActor() {
LuaFilePath = "MyLuaActor";
}
LuaFilePath 继承自 LuaActor的属性,LuaFilePath 表示绑定的lua文件地址,slua支持多state架构,每个state都对应一个名字,可以不填或者为空。你自己的loader需要保证能够通过LuaFilePath 加载到对应的lua文件,否则绑定失败。
这是你就可以去实现对应的lua文件了,通常如下:
local actor={}
function actor:ReceiveBeginPlay()
self.bCanEverTick = true
self.Super:ReceiveBeginPlay()
print("MyActor:ReceiveBeginPlay")
end
function actor:Tick(reason)
self.Super:Tick(reason)
end
function actor:ReceiveTick(dt)
end
return Class(nil, nil, actor)
注意需要返回actor表,lua通过这个表来映射c++ Actor和lua Actor之间的行为, 同时注意
self.bCanEverTick = true
这行代码的作用是告诉slua,这个lua actor需要Tick,如果没有或者设置为false,则表示这个lua actor不需要Tick函数,对应tick也不会调用到lua里,这么设计与c++一致,主要是为了优化掉不需要tick的actor调用tick函数导致的性能损失,bCanEverTick 的命名和c++的Actor属性没有关系,只是起了个和Actor一致的名字,便于理解。
此时你可以使用这个Actor了,例如:
local actorClass = import("MyLuaActor")
local p = FVector(math.random(-100,100),math.random(-100,100),0)
local actor = world:SpawnActor(actorClass,p,nil,nil)
当对应被构建后,lua代码就收到对应的回调事件,例如ReceiveBeginPlay用于处理BeginPlay事件,如果你需要在lua里调用c++父类的对应函数,类似Super::BeginPlay的调用,可以直接在lua里访问self.Super,并调用对应父类方法,例如self.Super:ReceiveBeginPlay()。
Slua: MyActor:ReceiveBeginPlay
的输出信息,self就是对应的c++类的lua封装,你可以使用self来调用c++的函数,访问属性等,同时self也是lua table,任何lua的数据也可以保存在self上,但要注意避免与c++对应的属性重名,否则会产生冲突,例如:
local actor={}
-- override event from blueprint
function actor:ReceiveBeginPlay()
self.bCanEverTick = true
-- set bCanBeDamaged property in parent
self.bCanBeDamaged = false
print("actor:ReceiveBeginPlay")
local world = self:GetWorld()
local bpClass = slua.loadClass("/Game/TestActor.TestActor")
self.balls={}
self.basepos={}
self.rot={}
for n=1,10 do
local p = FVector(math.random(-100,100),math.random(-100,100),0)
local actor = world:SpawnActor(bpClass,p,nil,nil)
self.balls[n]=actor
self.basepos[n]=p
self.rot[n]=math.random(-100,100)
actor.Name = 'ActorCreateFromLua_'..tostring(n)
end
local actorClass = import("MyLuaActor")
local p = FVector(math.random(-100,100),math.random(-100,100),0)
local actor = world:SpawnActor(actorClass,p,nil,nil)
self.Super:ReceiveBeginPlay()
end
-- override event from blueprint
function actor:ReceiveEndPlay(reason)
print("actor:ReceiveEndPlay")
self:Super()
end
local HitResult = import('HitResult');
local tt=0
function actor:Tick(dt)
print("map2actor:Tick")
tt=tt+dt
for i,actor in pairs(self.balls) do
local p = self.basepos[i]
local h = HitResult()
local rot = self.rot[i]
local v = FVector(math.sin(tt)*rot,0,0)
local offset = FVector(0,math.cos(tt)*rot,0)
local ok,h=actor:K2_SetActorLocation(p+v+offset,true,h,true)
end
self.Super:Tick(dt)
end
return Class(nil, nil, actor)
菜单选择新建蓝图类,选择LuaActor作为父类
给你的蓝图命名后,打开蓝图编辑器编辑该蓝图类,设置绑定的lua文件
含义与上面一致
你可以使用蓝图编写你的逻辑,对应事件消息也会传递到lua,也可以在蓝图里调用lua函数,使用Call Lua Member节点,例如
这样就可以在蓝图里调用对应lua的成员函数, 你同样可以使用self.Super来调用蓝图父类的蓝图逻辑,使用self来访问蓝图的方法和属性。
如果你已经存在一个已有的C++ UClass,你只希望部分代码用lua实现,保留部分C++ UFunction逻辑,你可以给添加一个继承类ILuaOverriderInterface,并完成它的GetLuaFilePath函数。如LuaActor。
如果你已经存在大量设计好的蓝图,你只希望部分代码用lua实现,不希望重新用lua实现所有蓝图逻辑,你可以把现有蓝图的的继承添加一个LuaOverrideInterface接口类,并完成它的GetLuaFilePath函数,就可以完成这个目标。
slua 已经预先将常用的Actor,Charactor,Pawn,UserWidget,Controller, PlayerController, GameModeBase, HUD 都做了类似支持,方便使用,同时你也可以自行扩展更多Actor具备如此能力。