2026年4月20日月曜日

[Lua] イベント駆動型のサンプル

■EventBus.lua
local EventBus = {}
EventBus.__index = EventBus

function EventBus.new()
    return setmetatable({ listeners = {} }, EventBus)
end

-- 購読
function EventBus:on(event, callback)
    if not self.listeners[event] then
        self.listeners[event] = {}
    end
    table.insert(self.listeners[event], callback)

    -- 解除用関数を返す
    local removed = false
    return function()
        if removed then return end
        removed = true
        self:off(event, callback)
    end
end

-- 一度だけ実行
function EventBus:once(event, callback)
    local unsubscribe
    unsubscribe = self:on(event, function(...)
        unsubscribe()
        callback(...)
    end)
end

-- 購読解除
function EventBus:off(event, callback)
    local list = self.listeners[event]
    if not list then return end

    for i = #list, 1, -1 do
        if list[i] == callback then
            table.remove(list, i)
            break
        end
    end
end

-- 発行
function EventBus:emit(event, ...)
    local list = self.listeners[event]
    if not list then return end

    for _, callback in ipairs(list) do
        callback(...)
    end
end

return EventBus

--------------------------------------------------------------------
■使い方
local EventBus = require("EventBus")
local bus = EventBus.new()

-- 購読
local unsubscribe = bus:on("player_damage", function(amount)
    print("Player damaged:", amount)
end)

-- 発行
bus:emit("player_damage", 10)

-- 解除
unsubscribe()

--------------------------------------------------------------------
■一度だけ実行(once)が必要になる場面
1. ゲーム開始時に一度だけ処理したい
例:ロード完了イベントが来たら UI を表示する
→ 2回目以降は不要

2. 敵が死んだ瞬間に一度だけ演出を入れたい
例:ボスが死んだ瞬間にカットシーン再生
→ 何度も呼ばれたら困る

3. プレイヤーが初めてアイテムを拾った時だけチュートリアルを出す
→ 2回目以降はチュートリアル不要
■参考
https://github.com/ThomasKomarnicki/GameEventBus

0 件のコメント:

コメントを投稿