精選文章

SmallBurger Asset Home

  SmallBurger

2011年3月1日 星期二

在LUA裡實作state machine…

  為了之後AI的彈性與好維護,加入狀態機管理機制是有必要的,但如何在LUA實作狀態機
呢?在此以一個隨機行走、攻擊的AI來介紹一下有狀態機的差別及相關實作:
  沒有狀態機的情況:
math.randomseed(os.time())
gScrollMoveTime = 0.0
gResponseTime = 0.0
function Stroll(fDeltaTime)
  if gResponseTime > 0.0 then
    gResponseTime = gResponseTime-fDeltaTime
  end
  if gScrollMoveTime > 0.0 then
   gScrollMoveTime = gScrollMoveTime-fDeltaTime
  end
  if gResponseTime <= 0.0 then
    gResponseTime = 2.0
    local iRandomValue = math.random(7)
    if iRandomValue >= 7 then
      Brain:ChangeBehavior("CMDJumpBehavior")
    end
    if iRandomValue >= 5 then
      Brain:InsertAction("ATTACK")
    end
    if gScrollMoveTime <= 0.0 then
      if iRandomValue <= 4 then
        local fRandomValue = math.random()
        gScrollMoveTime = fRandomValue*3.0
        Brain:ReplaceYaw(fRandomValue*360.0)
        Brain:ChangeBehavior("CMDMoveBehavior")
      else
        Brain:ChangeBehavior("CMDIdleBehavior")
      end
    end
  end
end
  由上得知,為了區分是不是要走或不走,會導致複雜的巢狀判斷式,就算走處理的部份移
到不同的函式中,仍然避免不了複雜的if、elase。
  有狀態機的情況:
math.randomseed(os.time())
gScrollMoveTime = 0.0
gResponseTime = 0.0
function RandomAttackAndJump(iRandomValue)
  if iRandomValue >= 7 then
    Brain:ChangeBehavior("CMDJumpBehavior")
  end
  if iRandomValue >= 5 then
    Brain:InsertAction("ATTACK")
  end
end
function IsCanProcess(fDeltaTime)
  local Result = 1
  if gResponseTime > 0.0 then
    gResponseTime = gResponseTime-fDeltaTime
  end
  if gResponseTime > 0.0 then
    Result = 0
    return 0
  else
    gResponseTime = 2.0
  end
  return 1
end
function MoveByTimeLength_Enter(FromStateName)
  fRandomValue = math.random()
  gScrollMoveTime = fRandomValue*3.0
  Brain:ReplaceYaw(fRandomValue*360.0)
  if FromStateName == "Stroll" then
    Brain:ChangeBehavior("CMDMoveBehavior")
  end
end
function MoveByTimeLength_Update(fDeltaTime)
  if IsCanProcess(fDeltaTime) == 1 then
    RandomAttackAndJump(math.random(7))
  end
  gScrollMoveTime = gScrollMoveTime-fDeltaTime
  if gScrollMoveTime <= 0.0 then
    Brain:ChangeState("Stroll")
  end
end
function MoveByTimeLength_Exit()
  Brain:ChangeBehavior("CMDIdleBehavior")
end
function Stroll_Enter(FromStateName)
end
function Stroll_Update(fDeltaTime)
  if IsCanProcess(fDeltaTime) == 1 then
    local iRandomValue = math.random(7)
    RandomAttackAndJump(iRandomValue)
    if iRandomValue <= 4 then
      Brain:ChangeState("MoveByTimeLength")
    end
  end
end
function Stroll_Exit()
end
  Script的進入點是Stroll_Enter。由上得知,狀態機可以讓我們專心做此狀態下該做的事,而
不用去判斷目前是什麼狀態或flag(上述的例子還好,但AI的狀態會越來越多,之後就很難維護
了)。另外值得一提的是此框架提供進入點和離開點,且在進入點會帶舊的狀態參數,以上例
而言,如果是是Stroll狀態進入的,才需要切換走路行為。為什麼要這麼做呢?原因是如果從其
他已經切換成走路行為的狀態進入,可能會多切一次走路行為而造成走路動作重新撥放而產生
動作抖動的問題。
  此框架提供在Scirpt裡自訂任何的狀態,新狀態的產生及切換不用修改任何的native c++
source code,在此提供給大家參考。

4 則留言:

  1. 夢癡大~
    感謝分享

    回覆刪除
  2. 夢癡大有想過使用QT4來取代MFC開發工具嗎@@?
    最近在研究QT4@@!

    回覆刪除
  3. 目前沒這個打算,要的話也會轉向wxWidgets。

    回覆刪除