How would I use metatables to create entities and simulate their AIs? To my knowledge, other OOP languages usually have a .__process function to something similar which gets called each frame, and which I think would be the best way to program AI, unless there is a better way.
In a module script I have:
local EntityClass= {}
EntityClass.__index = EntityClass
function EntityClass.new()
local instance = setmetatable({},EntityClass)
return instance
end
-- creates a body for the entity
function EntityClass:CreateBody(entityId,pos)
local entity = game.ServerStorage.Entities[entityId]:Clone()
entity.Position = pos
entity.Parent = workspace
self.body = entity
return entity
end
-- How would I simulate entity Ai?
return EntityClass
Some additional question about OOP I have:
I plan to have dozens of different types of entities. Do I make a class and a module for each type of entity that inherits from the entity class? Or is there a more efficient way to do it?
If I want to, for example, hit an enemy and apply damage to an entity, how would I call a function that is inside the EntityClass that handles damage?
I’d probably say just make a function in the module to handle damage, etc. and call it with the appropriate entity, because the entity itself will inherit these functions so they can be called with the entity itself.
For the AI, you could even use a seperate script. Or, just use a procedure in your current module.
function EntityClass.TakeDamage(entity, damage)
--stuff goes here
end
function EntityClass.MoveEntity(entity)
--movement code could go here
end
function EntityClass.new()
local self = setmetatable({}, EntityClass)
task.spawn(self.MoveEntity, entity)
end
Also yes, you should probably make a new class for each entity. It’d help to simplify the overall system.
I hope this helps .
(I have later realised that parameters are not the best idea for this. It will inherit, so the function should be called with itself. for the sake of readability here, I made a quick type for the entity).
function EntityClass:TakeDamage()
--stuff goes here
end
function EntityClass:MoveEntity()
--stuff goes here
end
function EntityClass.new()
local self:Entity = setmetatable({}, EntityClass)
return self
end
export type Entity = {
["Damage"]: (Entity) -> (nil),
["MoveEntity"]: (Entity) -> (nil),
["new"]: (nil) -> (Entity)
}
Then, outside the script:
local Entity:module.Entity = EntityClass.new()
task.spawn(Entity.TakeDamage, Entity)
task.spawn(Entity.MoveEntity, Entity)
So I would have a script that handles where the entities are created, and give them a unique identifier so you know which entity is which.
--@@ Creating Entities
local Entities: { [string]: table } = {}
--> Creation:
local UniqueID = --> Can use a custom method or just HttpService:GenerateGUID()
local Entity = EntityClass.new()
Entity:CreateBody()
Entities[UniqueID] = Entity
Now whenever you want to access this specific entity you could just use the unique id to do so.
As for taking damage that is pretty straightforward, in the script that handles the creation of the entity you would add a damage function and also add a damage function to the entity class itself.
local function attackEntity(uniqueID: string, damage: number)
local Entity = Entities[uniqueID]
if not Entity then
return
end
Entity:Damage(damage)
end
How the class would look:
function EntityClass.new()
local self = setmetatable({}, EntityClass)
self.Health = 100
return self
end
--@@ Deals damage to entity
function EntityClass:Damage(damage: number)
self.Health -= damage
end
Finally the actual movement, there is so many ways to do this. One way is you could handle it all in the class itself, using pathfinding or any custom path you have and just have them move in a function, or you could use runservice to loop through all entities and move them accordingly. I haven’t really used entities in this way so I wouldn’t know the most optimised approach but you can always try them and see which one performs better.