i think i made a post similar to this long ago, but that was back when my enemy animate scripts were like 50 lines in length
every enemy shares the same signals, there’s no difference in that
the only difference that the enemies have are animations and sounds (and quantity)
because my current animate script looks like this:
local scriptsFolder = script.Parent
local enemy = scriptsFolder.Parent
local rootPart = enemy:WaitForChild("HumanoidRootPart")
-- Path to modules;
local enemyStorage = enemy.Parent.Parent
local modulesFolder = enemyStorage:WaitForChild("Modules")
-- Modules;
local utility = require(modulesFolder:WaitForChild("Utility"))
-- Animator content;
local animationController = enemy:WaitForChild("AnimationController")
local animator = animationController:WaitForChild("Animator")
-- Animation folders;
local animationsFolder = enemy:WaitForChild("Animations")
local idleAnimationsFolder = animationsFolder:WaitForChild("Idles")
local wanderAnimationsFolder = animationsFolder:WaitForChild("Wanders")
local chaseAnimationsFolder = animationsFolder:WaitForChild("Chases")
local attackAnimationsFolder = animationsFolder:WaitForChild("Attacks")
-- Sound folders;
local soundsFolder = enemy:WaitForChild("Sounds")
-- Reparent sounds to root part;
soundsFolder.Parent = rootPart
local idleSounds = soundsFolder:WaitForChild("Idle")
local eventSounds = soundsFolder:WaitForChild("Event")
local combatSounds = soundsFolder:WaitForChild("Combat")
-- Gameplay values;
local valuesFolder: Folder = enemy:WaitForChild("Values")
local signalsFolder: Folder = valuesFolder:WaitForChild("signals")
-- Signals;
local wandering: BoolValue = signalsFolder:WaitForChild("wandering")
local startled: BoolValue = signalsFolder:WaitForChild("startled")
local idling: BoolValue = signalsFolder:WaitForChild("idling")
local chasing: BoolValue = signalsFolder:WaitForChild("chasing")
local fleeing: BoolValue = signalsFolder:WaitForChild("fleeing")
-- Helper functions;
local function PlayRandomAnim(animations: {Animation}, forcePrimary: boolean)
animations = utility.ApplyEqualChances(animations)
local randomAnimation: Animation = utility.RNG(animations)
local isPrimary = false
if forcePrimary then
isPrimary = true
for _, animation in animations do
if animation:GetAttribute("primary") then
randomAnimation = animation
break
end
end
end
local loadedAnimation = animator:LoadAnimation(randomAnimation)
if randomAnimation:GetAttribute("primary") then
isPrimary = true
end
loadedAnimation:Play()
return loadedAnimation, isPrimary
end
local function PlayRandomSound(sounds: {Sound})
sounds = utility.ApplyEqualChances(sounds)
local randomSound: Sound = utility.RNG(sounds)
if not randomSound.IsPlaying then
randomSound:Play()
end
return randomSound
end
-- Main functions;
local function OnWanderingSignaled(active: boolean)
if active then
local wanderAnimations = {}
for _, animation: Animation in ipairs(wanderAnimationsFolder:GetChildren()) do
table.insert(wanderAnimations, animation)
end
local forcePrimary = false
while active do
local chosenAnimation, isPrimary = PlayRandomAnim(wanderAnimations, forcePrimary)
if not isPrimary then
chosenAnimation.Stopped:Wait()
forcePrimary = true
else
if not chosenAnimation.Looped then
chosenAnimation.Looped = true
end
-- since the animation played is primary, it loops until the enemy stops wandering
-- to prevent the loop from... looping, we yield the loop until the enemy no longer wanders
-- and then both loops break
while active do
task.wait()
end
end
end
else
utility.StopAllAnimations(animator)
end
end
local function OnStartledSignaled(active: boolean)
if active then
local startledSounds = {}
for _, sound: Sound in eventSounds:GetChildren() do
if string.find(sound.Name, "Startled") then
table.insert(startledSounds, sound)
end
end
local soundPlayed = PlayRandomSound(startledSounds)
end
end
local function OnIdlingSignaled(active: boolean)
if active then
local sounds = idleSounds:GetChildren()
while active do
local soundPlayed = PlayRandomSound(sounds)
local cooldown = math.random(5, 10)
task.wait(cooldown)
if soundPlayed.IsPlaying then
while soundPlayed.IsPlaying do
task.wait()
end
end
end
end
end
DO note that this is 150 lines of code, and it’s not finished yet (this is one enemy, where my game will have amounts as high as 23 at a time)
i had a bit of a revelation, and thought if it wouldn’t be better to just have a single script that sets up connections for each enemy, and then handles the logic based on that
NOTE:
please don’t suggest using metatables (assuming they’re even a good-use case scenario here)