サバイバルホラーの追跡者、RPGのボス、戦術的な兵士AIなど、魅力的なNPCを作る最大の障壁はスパゲッティコードです。単一スクリプトに膨大なif文を重ねると、索敵の破綻や極度のサーバーラグを引き起こします。
商用ゲーム開発では、全体状態を制御する有限ステートマシン(FSM)と、細かな戦術を切り替えるビヘイビアツリー(BT)を組み合わせます。本稿ではブラックボード設計、視界コーンのレイキャスト、AI LODによる負荷分散を徹底解説します。
敵の複雑さに応じた適切な設計パターンの選択:
サーバー負荷を抑えつつ自然な索敵を実現する仕組み:
-- Modular Finite State Machine for Roblox NPC
local StateMachine = {}
StateMachine.__index = StateMachine
function StateMachine.new(npcInstance, states, initialState)
local self = setmetatable({}, StateMachine)
self.NPC = npcInstance
self.States = states
self.CurrentState = nil
self.Blackboard = {
Target = nil,
LastSeenPos = nil,
Health = 100,
PatrolPoints = {}
}
self:ChangeState(initialState)
return self
end
function StateMachine:ChangeState(newStateName)
if self.CurrentState and self.States[self.CurrentState].Exit then
self.States[self.CurrentState]:Exit(self.NPC, self.Blackboard)
end
self.CurrentState = newStateName
if self.States[newStateName] and self.States[newStateName].Enter then
self.States[newStateName]:Enter(self.NPC, self.Blackboard)
end
end
function StateMachine:Update(dt)
if self.CurrentState and self.States[self.CurrentState].Update then
local nextState = self.States[self.CurrentState]:Update(self.NPC, self.Blackboard, dt)
if nextState and nextState ~= self.CurrentState then
self:ChangeState(nextState)
end
end
end
return StateMachine
優先順位の階層的組み立て:
100体以上の敵がいてもサーバー60 FPSを維持するテクニック:
全NPCが毎フレーム全速力でレイキャストやパス計算を行っているためです。AI LODで遠方の計算頻度を下げてください。
新アクションを追加する際に既存の遷移網を書き換える必要がなく、ツリーにノードを挿入するだけで済む拡張性の高さです。
NPCの頭部からプレイヤーへレイキャストを飛ばし、障害物がヒットしないか確認する遮蔽判定を実装します。
チートや同期崩れを防ぐため、標的決定やダメージ計算は必ずサーバーで実行し、クライアントは見た目の補間のみを担当させます。