Skip to content

LuaActor的使用

zjhongxian edited this page May 12, 2023 · 4 revisions

LuaActor

sluaunreal提供了c++层的LuaActor类,通过这个类你可以在lua里扩展c++的Actor或者蓝图的Actor,这样就可以使用lua来实现Actor的逻辑。

扩展c++ Actor

选择添加c++类,选择LuaActor为父类

1

给你的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)

扩展蓝图Actor

菜单选择新建蓝图类,选择LuaActor作为父类

2

给你的蓝图命名后,打开蓝图编辑器编辑该蓝图类,设置绑定的lua文件

3

含义与上面一致

你可以使用蓝图编写你的逻辑,对应事件消息也会传递到lua,也可以在蓝图里调用lua函数,使用Call Lua Member节点,例如

4

这样就可以在蓝图里调用对应lua的成员函数, 你同样可以使用self.Super来调用蓝图父类的蓝图逻辑,使用self来访问蓝图的方法和属性。

扩展现有UClass类

如果你已经存在一个已有的C++ UClass,你只希望部分代码用lua实现,保留部分C++ UFunction逻辑,你可以给添加一个继承类ILuaOverriderInterface,并完成它的GetLuaFilePath函数。如LuaActor。

修改现有蓝图继承自LuaOverrideInterface

如果你已经存在大量设计好的蓝图,你只希望部分代码用lua实现,不希望重新用lua实现所有蓝图逻辑,你可以把现有蓝图的的继承添加一个LuaOverrideInterface接口类,并完成它的GetLuaFilePath函数,就可以完成这个目标。

image image

其他常用Actor

slua 已经预先将常用的Actor,Charactor,Pawn,UserWidget,Controller, PlayerController, GameModeBase, HUD 都做了类似支持,方便使用,同时你也可以自行扩展更多Actor具备如此能力。