Essentially, It will have a base routine of either wandering around when it is day time, or sleeping when it is night. This routine can be interrupted, however. In this case for this tree, the base routine can be interrupted by the presence of a player or prey creature. I will need to somehow “pause” this routine and supplant it with an aggression routine, which will be cancelled once the target is either destroyed or is too far away. At this point, the creature will then return to the base routine of wandering or sleeping.
The issue is, this tree has many components. It has wandering, Sleeping, Aggression, Fighting with its own kind over a corpse, pouncing an enemy, and finding foliage before choosing to sleep. The issue is, I want this to be in modular pieces. These are behaviors I want for this creature, but maybe I want to make a creature that doesnt sleep? or one that simply doesnt look for foliage before sleeping? or maybe one that doesnt pounce as its opening attack? maybe one that isnt aggressive at all? Every single piece here needs to somehow be a modular component that can be inserted or removed. Im confident I could build a spaghetti script that bunches this all together, but im at a loss as to how i would make these components individually.
-- MODULE SCRIPT <-- Modular.
local AIStateHandler = {}
AIStateHandler.__index = AIStateHandler
-- Basically here you can input your "AI" and states then it will return functions you can use.
function AIStateHandler.new(States : any)
return setmetatable({
States = States, -- All of our states
State = "None" -- The current active State
}, AIStateHandler)
end
-- You can set your state and do the rest on your side
function AIStateHandler:ChangeState(NewState : string)
if getmetatable(self) == nil then
return
end
if table.find(self.States, NewState) and self.State ~= NewState then
self.State = NewState
else
return "State Already Active"
end
end
-- Self explanatory but it gets the current state.
function AIStateHandler:GetCurrentState()
if getmetatable(self) == nil then
return
end
return self.State
end
-- if you couldnt add any states before now you can!
function AIStateHandler:AddState(State : string)
if getmetatable(self) == nil then
return
end
-- Make sure we dont add multiple of the same state
if not table.find(self.States, State) then
-- We insert it into our states so it can be set and removed.
table.insert(self.States, State)
else
return "State Already Defined"
end
end
function AIStateHandler:RemoveState(State : string)
if getmetatable(self) == nil then
return
end
-- Make sure we dont add multiple of the same state
if table.find(self.States, State) then
local Index = table.find(self.States, State) -- We get the state index or the position of the state in the table
table.remove(self.States, Index) -- then we remove it
else
return "State Not Found"
end
end
-- Use this when your done with the AI. It frees memory and cleans everything up
function AIStateHandler:Destroy()
if getmetatable(self) == nil then
return
end
setmetatable(self, nil)
end
return AIStateHandler
Let me know if this helps
basically you have some states you can input and you can set these states and as you do you can make it so that the AI or character transitions into that state
for example if the state was “Idle” and the script running the AI found the player then you can
change the state and move on
Alright, so each state in the tree above would be kind of like an interruptible loop, each time the state is changed, the last loop is destroyed and a new one begins. Each consecutive loop would have different behaviors depending on which loop was inserted. This makes sense, thank you
Well ideally, i would want to be able to have states with complex behaviors. Wandering is relatively simple, but maybe a creature has an aggro routine where it tries to stay hidden until it is within a certain distance, then attacks the target? That might be a bit more complex. Having a broad number of creatures with different types of behaviors in the game would also increase the number of possible states that would exist in the game. In this script, it looks like the state is a string? if thats the case, then maybe the string itself is used to reference a state somewhere else in the script, or perhaps in other modules?
I was thinking maybe instead of the state being a string, the state would instead be another object created from another module, which would be executed. When the state changes, that module will close and a new object from another module will be instantiated and its contained behaviors executed
Oh no i think ive got it. The only thing that concerns me though is the memory efficiency you mentioned earlier, would this method be costly in terms of memory?
by the way I also wrote you a simple Idle state and this should give you something to work off of with
if your having issues with the oop make sure to do
local Module = require(PATH)
Module = Module.new(Parameters)
local IdleState = {}
IdleState.__index = IdleState
function IdleState.new()
return setmetatable({
Active = false,
}, IdleState)
end
function IdleState:Start()
if getmetatable(self) == nil then
return
end
-- your code here
end
function IdleState:SetState(Active : boolean)
if getmetatable(self) == nil then
return
end
-- your code here
end
function IdleState:Destroy()
if getmetatable(self) == nil then
return
end
setmetatable(self, nil)
end
return IdleState