Improving Enemy Logic

I have this very basic Enemy code that simply walks towards the player and plays an animation. Problem is i want to add Animation events to the script. problem is its a server side script which animation events don’t work for some reason so i either need a way around it or a way to convert this to a local script. Thanks

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local Model = script.Parent.Parent
local humanoid = script.Parent
local root = humanoid.Parent.PrimaryPart
root:SetNetworkOwner(nil)

local TargetDistance = script.Parent.Parent:GetAttribute("TargetDistance")
local StopDistance = script.Parent.Parent:GetAttribute("StopDistance")
local Damage = script.Parent.Parent:GetAttribute("Damage")
local AttackDistance = script.Parent.Parent:GetAttribute("AttackDistance")
local AttackCooldown = script.Parent.Parent:GetAttribute("AttackCooldown")

local LastAttack = tick()

local Animator = humanoid:WaitForChild("Animator")
local IdleAnimation = script:WaitForChild("Idle")
local WalkingAnimation = script:WaitForChild("Walk")
local AttackAnimation = script:WaitForChild("Attack")

local IdleTrack = Animator:LoadAnimation(IdleAnimation)
local WalkingTrack = Animator:LoadAnimation(WalkingAnimation)
local AttackTrack = Animator:LoadAnimation(AttackAnimation)



if WalkingTrack.IsPlaying or AttackTrack.IsPlaying then
	IdleTrack:Stop()
else
	IdleTrack:Play()
end



function FindNearestPlayer()
	local playerList = Players:GetPlayers()
	
	local nearestPlayer = nil
	local distance = nil
	local direction = nil
	
	for _, player in pairs(playerList) do
		local character = player.Character
		local distanceVector = (player.Character.HumanoidRootPart.Position - root.Position)
		if character then	
			if not nearestPlayer then 
				nearestPlayer = player
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit

			elseif distanceVector.Magnitude < distance then
				nearestPlayer = player
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit
			end
		end
	end
	return nearestPlayer, distance, direction
end
	
	
RunService.Heartbeat:Connect(function() 
	local nearestPlayer, distance, direction = FindNearestPlayer()
	
	if nearestPlayer then

		if distance <= TargetDistance and distance >= StopDistance then
			--Runs when walking
			if not WalkingTrack.IsPlaying then
				WalkingTrack:Play()
			end
			humanoid:Move(direction)
		else
			--Runs when standing still
			if WalkingTrack.IsPlaying then
				WalkingTrack:Stop()
			end
			humanoid:Move(Vector3.new())
		end
		
		if distance <= AttackDistance and tick() - LastAttack >= AttackCooldown and Model:GetAttribute("Stunned") == false then
			--runs when attacking
			
			
			AttackTrack:Play()
			LastAttack = tick()
			wait(2)
			
			
			
			
			
		end
	end 
end)

-- what the walkspeed and jump height will revert to
local BaseWalkSpeed = 12
local BaseJumpHeight = 50

coroutine.resume(coroutine.create(function()
	while true do
		wait()
		if script.Parent.Parent:GetAttribute("Stunned") == true then
			script.Parent.Parent:FindFirstChild("Humanoid").WalkSpeed = 0
			script.Parent.Parent:FindFirstChild("Humanoid").JumpHeight = 0
		else
			script.Parent.Parent:FindFirstChild("Humanoid").WalkSpeed = BaseWalkSpeed
			script.Parent.Parent:FindFirstChild("Humanoid").JumpHeight = BaseJumpHeight
		end
	end
end))


Ok, so I am going to say there is a ton of crap going on with your code that I don’t have time to fix. But I CAN help you with the core answer quickly.

  1. Use Timers

  2. I also advise your gonna need to spend some time writing some stuff to help you manage this in an easy way:

  3. With the animation ref, grab the key frame information from roblox ( its built in read it up you can do it)

  4. Organize it in order by time and keep the key names lined up

  5. Instead of using Animation events you will Name Keys the unique name of the thing you want to happen in that animation.

  6. You will collapse the dictionary of key/values from the animation down to only being the unique keys and times, and then store that somewhere likely use the animation ID as your UUID for a dictionary to that table.

  7. When it comes time to do that animation, you pull that table and set up timers with the approate actions happening at the set times that comes with that animation. And then just have cancel conditions built in so that they can fail out. You can use “playing states” or things like that for that.

So basically, in short, instead of catching and hooking a function to animation events , you will construct your own animation events using unique keyframe names.

  1. Since I can tell you are still kind of new to this, don’t take this as a diss I am just stating the obvious, because there are many ways you can do this but due to the in experience I am going to make a suggestion to help you with this.
  • Create a Set of Animation Events that are always used.
  • Do not create a ton of different ones standardize there names and like the bass ones an animation needs etc. This will help you out big time.
  • Then have your animator or you inside of roblox Rename the keyframes where you want the event to happen,

BONUS:
→ This is still the simpler implementation of this, but you can take the animation, and since you know it’s total time, you could create a module with a table referencing that animation and the name of those events on the server matched up with the time in that animation for them to happen.

So either way, when the animation starts by the server, you start a timer for each of the events in that animation to fire off at the amount of time. Since we can safely assume that the animation is replicating to everyone now the animation itself is just a visual representation of what is happening, while the server has full awareness of the real timing of events occurring. So now you have zero reliance for the clients events or trusting the client for anything.

This can be done, and I highly suggest you try to keep it as simple as possible to manage. Because it gets out of hand quickly, and you may have to stop and write a widget tool to manage all the data and such. But this will do what you want.

functionally by doing this, you are abstracting the same thing roblox does with events, but you are not just waiting for them to manage the firing of the event itself.

I remember this problem a long time ago and how annoying it is, so I Can tell you with great certitanty that this works very very well, especially when you trigger the animation from the server to the client. In those cases you don’t really need to worry about lag compensation, but generally speaking you will want to place your events well. Also, you can compensate for large for any sort of detection on events by having a few checks over the course of the animation and you can write logic on determining validity that will give you a fairly accurate setup within about 200 - 270ms, but there is just only so much lag compensation you can do without complete circomventing the animation system as a whole and doing it all manually.

2 Likes

thanks so much this is very useful

1 Like

No worries, I worked on solving this problem for a stupidly long amount of time.

The final stage for me to ultra get it right, is to do all the work to completely break the animation chain so that not only can NPCs be server authoritative but I can client simulate server verify before replicating animations to other players.

That being said, looks like you only need NPC stuff so that makes it at least some level of easier lol.

1 Like

U mind if i try it out and when i have a somewhat working script you can check it out?

i also wanted to know if i should have a module script with the animations for each enemy or one for every enemy. Thanks

That’s pretty preference based. It’s more how you want to organize it, and how globalized do you need the data.

Likely, it will be best to centralize it. I highly suggest try to keep modules in general under 600 lines, if you cross that, make a child module and just reference pass through that way you don’t get a MONOLITH of information and you can kind of get some easy explorer navigation organization, but really this can go either way.

I personally tend to centralize data.

1 Like

How would i use an animator to load animation on a module script also or should that be done server side?

I mean your module can be server side, There are lots of ways to do that. It’s really preferential. You won’t need to load the animation in order to grab the keyframe data from it, go look up keyframes and do the research on their main site. That will lead you to how to gather that info and such and what to do.

From what i understand you either need a animator or a animator controller to load animations. Problem is for the animator controller you need to replace the humanoid and for the animator you need to identify the humanoid which i don’t know how to do in a local script.

Sounds like research and dev time my friend =D, I dunno exactly what your trying to do. But I have faith if you break it into small steps and just create a simple environment to experiment small things with it to get it, you will figure out what you need to know to get it to do what you want. The documentation on that stuff is actually pretty good by roblox.

well thanks anyways you’ve been a massive help!

All good, no worries =) And I have faith in it, just remember to bob and weave balance implementation ease with what you need and remember, to always stop and ask yourself what features are ABSOLUTLY necessary.

And remember above all else, we are magicians, not scientist, how you make it works matters far less then how the player perceives it works =D

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.