ComponentsIntermediate

Enemy AI with Components

Tag-based enemy instances, each with independent health state, damaged individually or all at once via GetAll().

ComponentExtensionsGetAll
ServerScriptService/Services/EnemyService.lua
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Owl = require(ReplicatedStorage.OwlKnit.Owl)
local Component = Owl.Util.Component
 
local EnemyComponent = Component.new({
    Tag = "Enemy",
    Type = "Server",
    Ancestors = { workspace },
})
 
function EnemyComponent:Construct()
    self.MaxHealth = self.Instance:GetAttribute("MaxHealth") or 100
    self.Health = self.MaxHealth
    self.IsDead = false
end
 
function EnemyComponent:Start()
    self._trove:Add(
        self.Instance:GetAttributeChangedSignal("MaxHealth"):Connect(function()
            self.MaxHealth = self.Instance:GetAttribute("MaxHealth")
        end)
    )
end
 
function EnemyComponent:TakeDamage(amount: number)
    if self.IsDead then return end
 
    self.Health = math.max(0, self.Health - amount)
 
    if self.Health == 0 then
        self.IsDead = true
        self.Instance:Destroy()
    end
end
 
local EnemyService = Owl.CreateService({ Name = "EnemyService" })
 
function EnemyService:OwlInit()
end
 
function EnemyService:OwlStart()
    EnemyComponent.Added:Connect(function(instance, comp)
        print("Enemy spawned:", instance.Name, "| HP:", comp.MaxHealth)
    end)
 
    EnemyComponent.Removed:Connect(function(instance)
        print("Enemy defeated:", instance.Name)
    end)
 
    -- > // Watch() is also called automatically by Owl on startup
end
 
function EnemyService:DamageAllEnemies(amount: number)
    for _, enemy in ipairs(EnemyComponent:GetAll()) do
        enemy:TakeDamage(amount)
    end
end
 
return EnemyService
What's happening here
  • Tagging any Model "Enemy" in CollectionService instantiates this Component automatically, no manual Component.new() per instance.
  • MaxHealth reads from an Attribute, so a designer can tune enemy stats in Studio without touching a line of code.
  • EnemyComponent:GetAll() returns every currently alive enemy, the basis for an AOE ability like DamageAllEnemies.
  • Destroying the instance is enough to clean it up: the Component's Trove and Destroy() run automatically on Instance.Destroying.