【Defold】階層的なステートマシンを作成しました
階層的なステートマシンが欲しくなったので、作ってみました。
目次
クラス
local M = {}
function M.new()
local instance = {}
instance.on_enter = bit.lshift(1, 0)
instance.loop = bit.lshift(1, 1)
instance.flag = instance.on_enter
instance.time = 0
function instance.has_flag(flag)
return (bit.band(instance.flag, flag) ~= 0)
end
function instance.add_flag(flag)
instance.flag = bit.bor(instance.flag, flag)
end
function instance.del_flag(flag)
instance.flag = bit.band(instance.flag, bit.bnot(flag))
end
function instance.del_flag_all()
instance.flag = 0
end
function instance.set_flag(flag, b)
if b then
instance.add_flag(flag)
else
instance.del_flag(flag)
end
end
function instance.one_time_flag(flag)
local temp = instance.has_flag(flag)
instance.del_flag(flag)
return temp
end
function instance.update(dt)
--instance.del_flag_all()
instance.time = instance.time + dt
end
function instance.reset()
instance.add_flag(instance.on_enter)
instance.time = 0
end
return instance
end
return M
local M = {}
local state_info = require("mira.state_info")
local unused_state = 99999
function M.new()
local instance = {}
instance.info = state_info.new()
instance.child_state = nil
instance.prev_state = 0
instance.state = 0
function instance.set_state(state, loop)
instance.prev_state = state
instance.state = state
instance.info.reset()
if loop then
instance.info.add_flag(instance.info.loop)
end
if instance.child_state ~= nil then
instance.child_state.reset()
end
end
function instance.term()
instance.set_state(unusedState, falsefactory)
end
function instance.is_term()
return instance.is_state(unusedState)
end
function instance.get_state()
return instance.state;
end
function instance.is_state(state)
return (instance.state == state)
end
function instance.prev(loop)
instance.set_state(instance.get_state() - 1, loop)
end
function instance.next(loop)
instance.set_state(instance.get_state() + 1, loop)
end
function instance.get_child()
if instance.child_state == nil then
instance.child_state = M.new()
end
return instance.child_state
end
function instance.update(dt)
if instance.prev_state == instance.state then
instance.info.update(dt)
if instance.child_state ~= nil then
instance.child_state.update(dt)
end
end
instance.prev_state = instance.state
end
function instance.is_on_enter()
return instance.info.one_time_flag(instance.info.on_enter)
end
function instance.is_loop()
if instance.child_state ~= nil then
return instance.child_state.is_loop() or instance.info.one_time_flag(instance.info.loop)
end
return instance.info.one_time_flag(instance.info.loop)
end
function instance.reset()
instance.prev_state = 0
instance.state = 0
if instance.child_state ~= nil then
instance.child_state.reset()
end
end
function instance.wait_time(duration)
return (instance.info.time > duration)
end
return instance
end
return M
これら二つのluaファイルを以下に配置して使います。
- [project]/mira/state.lua
- [project]/mira/state_info.lua
使用例
local mira_state = require("mira.state")
-- state for update()
local state_idle = 0
local state_main = 1
local state_sub = 2
-- state for sub_update()
local sub_state_idle = 0
local sub_state_wait = 1
function init(self)
self.state = mira_state.new()
end
function update(self, dt)
local state = self.state.get_state()
if state == state_idle then
if self.state.is_on_enter() then
print("update state_idle is_on_enter")
self.state.next(true)
end
elseif state == state_main then
if self.state.is_on_enter() then
print("update state_main is_on_enter")
self.state.next(true)
end
elseif state == state_sub then
if sub_update(self.state.get_child()) then
self.state.set_state(state_idle, true)
end
end
self.state.update(dt)
end
function sub_update(sub_state)
local state = sub_state.get_state()
if state == sub_state_idle then
if sub_state.is_on_enter() then
print("sub_update sub_state_idle is_on_enter")
sub_state.next(true)
end
elseif state == sub_state_wait then
print("sub_update sub_state_wait waiting...")
if sub_state.wait_time(2.0) then
print("sub_update sub_state_wait end")
-- exit sub_update
return true
end
end
-- loop sub_update
return false
end
特徴的な機能は以下の2点です。
関数名 | 機能 |
is_on_enter | ステート進入時1度だけtrueを返します |
wait_time | 指定した時間が経過するとtrueを返します |
動作確認
ステートマシンは、ひとつ有ると便利です。