首页 > 解决方案 > 这是在 Lua 中为我的 FSM 图实现基本 FSM 的正确方法吗?

问题描述

你好 Stack Overflow 社区!Sooo 我正在尝试根据我制作的这个 FSM 图在我的 Roblox 游戏中为 AI 实现一个基本的 FSM。我将在图表之后展示模块,并举例说明我如何在我的 NPC 模块中使用它。我查看了很多其他语言的 FSM 实现,它们看起来更容易,因为我对 Lua 还很陌生。这看起来像是 FSM 实施的一个不错的开始吗?如果不是,我可以改进什么以及 FSM 中最常用的功能是什么?我正在尝试了解 FSM 模型,因为我将使用 AI 开发游戏,并且不想要大量的 if、else、booleans 等...感谢任何可以让我更深入地了解我的人可以多学这门课!


图表

AI FSM 图

状态模块:

local State = {}
State.__index = State

function State:New()
    local newState = {
            Init = function() print("Init ran") end,
            Update = function() print("Updating") end,
            Enter = function() print("Entering") end,
            Exit = function() print("Exiting") end,
    }
    setmetatable(newState, self)

    print("Created new state")
    return newState
end

return State

状态机模块:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local State = require(ReplicatedStorage:WaitForChild("State"))

local StateMachine = {}
StateMachine.__index = StateMachine

function StateMachine:Create()
    local machine = {}
    machine.initState = State:New()
    machine.currentState = machine.initState
    machine.currentState.Init()

    setmetatable(machine, self)
    return machine
end

function StateMachine:Update()
    if self.currentState ~= nil then
        self.currentState:Update()
    end
end

function StateMachine:SetState(state)
    assert(state ~= nil, "Cannot set a nil state.")
    self.currentState:Exit()
    self.currentState = state
    self.currentState.Init()
    self.currentState.Enter()
end

return StateMachine

这是我使用我的 FSM 版本的方式。

例子:

newZombie.stateMachine = StateMachine:Create()

newZombie.idleState = State:New()
newZombie.idleState.Init = function()
    print("idle state init")
end
newZombie.idleState.Enter = function()
    print("idle state enter!")
end
newZombie.idleState.Update = function()
    print("idle state updating!")
    if not newZombie.target then
        print("Getting target")
    newZombie.target = newZombie:GetNearestTarget()
    end

    if newZombie.zombieTarget then
        print("Found target")             
        newZombie.stateMachine:SetState(newZombie.chaseState)
    end
end

newZombie.chaseState = State:New()
newZombie.chaseState.Init = function()
    print("chaseState init")
end
newZombie.chaseState.Enter = function()
    print("chaseState enter")
end
newZombie.chaseState.Update = function()
    print("chaseState updating!")
    if newZombie.target then
        local direction = (newZombie.target.Position - newZombie.rootPart.Position).Unit * 0.5
        local distanceToTarget = (newZombie.target.Position - newZombie.rootPart.Position).magnitude
        local MAX_ATTACK_RADIUS = 4
        local ray = Ray.new(newZombie.rootPart.Position, (newZombie.target.Position - newZombie.rootPart.Position).Unit * 500)
        local ignoreList = {}
        for i, v in pairs(ZombiesServerFolder:GetChildren()) do
            table.insert(ignoreList, v)
        end
        local hit, position, normal = Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)

        if not hit.Parent:FindFirstChild("Humanoid") then
            print("Walk Path")
        end

        if distanceToTarget >= MAX_ATTACK_RADIUS then
            newZombie.rootPart.CFrame = newZombie.rootPart.CFrame + direction
        else
            newZombie.stateMachine:SetState(newZombie.attackState)
        end
    else
            newZombie.stateMachine:SetState(newZombie.idleState)
        end
end

newZombie.attackState = State:New()
newZombie.attackState.Init = function()
    print("attackState init")
end
newZombie.attackState.Enter = function()
    print("attackState enter")
end
newZombie.attackState.Update = function()
    print("attackState updating!")
    if newZombie.target then
        local distanceToTarget = (newZombie.target.Position - newZombie.rootPart.Position).magnitude
        local MAX_ATTACK_RADIUS = 4

        if distanceToTarget >= MAX_ATTACK_RADIUS then     
            newZombie.stateMachine:SetState(newZombie.chaseState)
        end
    end
end
----------------------------------------------------
----               STARTING STATE               ----
----------------------------------------------------
newZombie.stateMachine:SetState(newZombie.idleState)
----------------------------------------------------

同样在 NPC 更新功能中,我正在更新状态机当前状态更新功能。

if self.stateMachine then
    self.stateMachine:Update()
end

标签: luastate-machine

解决方案


推荐阅读