I’m developing a battleground game and currently play all animations on the server using an OOP animation handler. I haven’t experienced delays, but I’ve learned that server-played animations aren’t ideal. I want to move animations to the client, but my combat system relies on animation events (e.g., hit markers for damage). I’ve been advised not to use animation events and to use delays instead, and I’m looking for guidance on the best approach.
i would do client anims with delays
![]()
dam why is the oop style animation handler looked at so bad?
Thanks
why don’t you just do animationtrack::play, there is no need for any oop animation handler
I would play it server sided char
Yeah you should always do client-sided animations because they also allow you optimization improvements. For example, you could decide to not play an animation when the NPC is hidden or not in range.
You shouldn’t rely on animation events because exploiters can abuse animations in whatever way they want. So it would be exploited.
Problem is with server-sided animations is its gonna be kinda choppy. The server has to continously replicate all the cframe changes that the animation will create to all clients. So it will not only look choppy as latency fluctuates but also will create unnecessary network traffic.
Ok idk if this is a stupid question but when playing the animations client sided, do i just fire the client who m1s and play the animation on them, or fire all clients and play the animation on them
Hello, I would usually go for a server sided approach, I know what most people say about it being choppy but in reality its like this only the first time its played after that its good. This is what I usually do, you can do what you want its your game in the end. If you want an easier approach use client animations.
Ye, I also preload the animations and send those animation tracks to the client and preload them again
You shouldn’t have to preload an animation where its not used or played. If you preload an animation on server that just creates a new instance there, when sending to the client you don’t send a preloaded animation you just send a normal one that the client has to preload, making the server one useless and unoptimized. I strongly advise the use of the same animation track when multiple requests of the same animation have to be played more then once. And when you are done with it don’t forget to clean up ( :Destroy() )
Ok Thank you
this is the function I have setup to load these animations on the server, is this fine?
function M:PreloadAnimations(Animations)
local function DeepSearch(original: any)
-- Recursively copy each key-value pair
for key, value in pairs(original) do
if typeof(value) == "Instance" then
CP:PreloadAsync({value})
value:Play()
task.delay(1, function()
value:Stop()
end)
elseif typeof(value) == "table" then
DeepSearch(value)
end
end
end
DeepSearch(self)
end
holy ai slop
No you usually save the track into a variable
First point
First point
First point
??
It’s as scalable and manageable as an OOP animation handler, if not more
First point
You have to make a new AnimLibrary for the same animators in every new script
Completely subjective. I find tables with loaded anims to be more scalable personally
You don’t even need OOP for this tho:
animRegister:LoadAnimation(<Animation>, name)
animRegister:Play(name, <Animator>, options)
I thought that was only with tweens? Is it the same for animations?
When a client plays an animation on its own character, that animation gets replicated to all other clients
Use generalized iteration, and define the deepclone outside the function. Also you should probably use type(value) == "table" as table is a primitive type but that one really doesn’t matter
And you should probably name ContentProvider differently…
Should be, since NPCs/player characters utilize motor6d joints. Things like their C0/C1 will be updated. And i’m pretty sure the character part’s cframe themselves are also updated. Then again I don’t have any benchamrks on me.
their C0/C1 aren’t updated during animations, their Transform property is
Yeah transform property I forgot about the exact syntax and stuff for motor6ds lol.
to be fair roblox’s animation system is one of the most poorly thought out systems in the entire engine so another layer of abstraction isn’t the worst thing in the world
I wasn’t specifically referring to oop (assuming that’s why you think I put
) but true
It’s pretty dumb, in fact the default character system itself is very brain dead. If we don’t consider the fact that clients literally have network ownership of their own characters so they can exploit, characters are moving very frequently and so there’s very frequent replication. You essentially get the same problems server side tweens have. I’m pretty sure the basis of Roblox default characters haven’t been changed since 2006 so that’s why it’s such a terrible system.
Still learning, correct me if I’m wrong.
If you are animating unanchored humanoids, then yeah, should have similar bandwidth? The server would have to replicate:
.Transformchanges on the character when animations play / property changes that are being tweened- Physics simulations on the character
No idea about how frequent replication is for tweens or property changes, all I know is that physics replication is 20Hz. For serversided tweens, they either:
- Replicate every property change
- Replicate “snapshots” and lets the client interpolate between those snapshots
Not sure on how rendering information is replicated either. It may be the case that all necessary information for creating an instance is sent to the client (for the first time), but once the instance already exists, property changes are the only information sent.
So, lets say a player can do an attack, lets say Punch.
Now, this is how you are going to achieve this. I’d assume that it is just going to be a normal battlegrounds game and nothing too competitive, because then you will have to implement a lot of complicated mechanisms so that the gameplay is very precise to each frame.
So, first when someone punches, there are going to be 2 stages.
First, the windup, aka the time it will take for the punch attack to hit someone from the time the user initiated the attack.
For example, i use m1 button to make a punch, then the characters pulls their hand back and then punches, thats called windup. In this case, the windup should be the time it takes from the start of the punch animation, to the part in the animation where the character pushes their hand forward to make, A Punch.
Ideally it would be around 0.2 to 0.3 seconds.
Now, there is the second stage, where the punch will make the hits.
For this, do not use the Touched event for this, because then there will be lag + the hit area will be very small and we dont want this.
Instead, utilize hitboxes, hitboxes are basically 3D primitive shapes (cuboids, spheres, cylinders, wedges) as it is computationally faster to compute the things that are in the hitbox.
Now when using hitboxes, for a simple punch, make a hitbox that has its height equal to the player’s height and width equal to the player’s width, and the length being how far you want the punch to hit players. And also place the hitbox in front of the player.
Now, a single hitbox for a single frame wont cut it, instead, generate hitboxes every frame until the punch stops from the windup end stage. this would generally last 0.4 to 0.5 seconds or maybe less or more according to your animations.
This was just the idea, now here’s how to implement it.
When the client wants to initiate a punch, send this information to the server.
And generate hitboxes on both the client initiating the attack as well as the server.
Now, if something it hit on the server, take that as the hit player, because server is the actual game world.
And if something is hit on the client initiating the attack, but not the server, send the information such as the person being hit and at what point they were exactly hit.
This condition may come up because of the latency between the server and the client, because it is possible that the target being hit on the client has moved away on the server.
Now, if the server can just take the client’s word as is, then the exploiters would exploit this by sending the information that they hit everybody even when they didnt. So to counter this, have a toleration distance, meaning the distance a player can be away from another player and call the other player being hit and the server would accept it.
This toleration distance would be generally around 10 studs.
So, to judge weather the attacker hit their target or not, take the target’s position, subtract the attacker’s position, take is magnitude and see if its under toleration distance on not.
example code:
if (target.HumanoidRootPart.CFrame.Position - attacker.HumanoidRootPart.CFrame.Position).magnitude < tolerationDistance then
--Register hit
end
Now, lastly onto the animations, play the animation on the client when they initiate their attack, then play the animation on the server when they are aware that the client has initiated an attack aswell.
Therefore it will make the animations responsive for the person attacking, and the server in case the attack information is relayed before the humanoid’s animator sends the animation information and then the client lags.