I’m messing with a combat system right now, and I’m just not satisfied with the way BodyVelocity is working with my vision. I have an animation that’s about 2 seconds long, and I want knockback afterwards. Here’s just the base animation:
I’m sure seeing the video it’s clear where the knockback is meant to be. The problem is that because animations don’t actually move the HumanoidRootPart, right as the animation ends and the knockback is applied, the character jarringly teleports back into the position they were in before the attack, as shown here:
Here’s the script that runs the animation and triggers the knockback.
hitbox.Touched:Connect(function(part)
if part.Parent:FindFirstChild("Humanoid") then --if detected part is a humanoid
hitbox:Destroy() --Prevent multiple touched events
local victim = Players:GetPlayerFromCharacter(part.Parent) --easy access to hit player (If it was a player)
--Initialize animations
local atk = player.Character.Humanoid.Animator:LoadAnimation(RS.Animations.Combat.HumanUnpowered.ShoCharAtk)
local vic = part.Parent.Humanoid.Animator:LoadAnimation(RS.Animations.Combat.HumanUnpowered.ShoCharVic)
--Attaches another sound the the victim and adds it to garbage collection queue
wind.Parent = part.Parent.Head
game.Debris:AddItem(wind, 5)
--Take damage, play first sound
part.Parent.Humanoid:TakeDamage(10)
impact:Play()
--Prevents characters from moving during animation
player.Character.Humanoid.WalkSpeed = 0
player.Character.Humanoid.JumpHeight = 0
player.Character.Humanoid.AutoRotate = false
part.Parent.Humanoid.WalkSpeed = 0
part.Parent.Humanoid.JumpHeight = 0
part.Parent.Humanoid.AutoRotate = false
for _,v in pairs(player.Character:GetChildren()) do
if v:IsA("BasePart") then
v.CanTouch = false
end
end
--adjust both characters to be facing each other and close together
part.Parent.HumanoidRootPart.CFrame = CFrame.new(part.Parent.HumanoidRootPart.Position, player.Character.HumanoidRootPart.Position)
player.Character.HumanoidRootPart.CFrame = part.Parent.HumanoidRootPart.CFrame * CFrame.new(0,0,-3) * CFrame.Angles(0,math.pi,0)
--Play animations
atk:Play()
vic:Play()
vic.Stopped:Connect(function()
if victim then
hitFunc(victim, true, 1.5, 0)
isDown[victim] = false
end
part.Parent.Humanoid:TakeDamage(20)
local velocity = Instance.new("BodyVelocity", part.Parent.HumanoidRootPart)
velocity.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
velocity.P = 1000
local angle = ((part.Parent.HumanoidRootPart.Position - player.Character.HumanoidRootPart.Position) * Vector3.new(10,0,10)).Unit * 70 + Vector3.new(0,5,0)
velocity.Velocity = angle
game:GetService("Debris"):AddItem(velocity,.3)
part.Parent.Humanoid.WalkSpeed = 16
part.Parent.Humanoid.JumpHeight = 7.2
part.Parent.Humanoid.AutoRotate = true
end)
Moving the HRP during animation would just change how the character is animated, as animations play relative to the HRP. So, my question is, are there any ways to get around this issue? Are there other methods of animation that don’t have this problem? Is there something wrong with the way I’m applying BodyVelocity? Is this animation specifically just not feasible because of the position it puts the character in? What should I do to get a result as close to my vision as possible?
I believe the problem with the jarring is caused by latency with server-client communication, if everything here is happening on the server, then if one player has a higher ping than the other, the animations will be off sync per client, thus resulting in a jitter – the player being knocked back is slightly behind in time than the attacking player creating a not-so-subtle rubber-band effect. One possible way I would think to handle this better is to handle the animations on the client side in local scripts using remote events, but have the actual attack and knocking back done entirely on the server, I think in theory this would eliminate the major rubber-banding happening (assuming the knockback isn’t an animation as well).
Yes the animations are both run by the server, and I did that because I was under the impression that latency would be worse if I fired an event to the two clients to run the separate animations. Would that not cause more latency then running them both at the same time from the same server script?
It would not really cause as much latency since it would use ping of each client to smooth it in out. What I would suggest is just do a FireAllClients.
Thank you for your response, is it necessary to use FireAllClients? Could I not just fire to the clients who are running the animations and have them replicate from there? Or is there a reason other than replication that you are suggesting FireAllClients
I’ve taken your responses into consideration because it is a good point, and it solves an issue that I didn’t even realize I had. However, it still does not fix the issue from my original post. As you can see, I’ve switched to running the animations themselves client-sided and the abrupt transition is still there.
Video of issue:
New Script:
hitbox.Touched:Connect(function(part)
if part.Parent:FindFirstChild("Humanoid") then --if detected part is a humanoid
hitbox:Destroy() --Prevent multiple touched events
local victim = Players:GetPlayerFromCharacter(part.Parent) --easy access to hit player (If it was a player)
--Attaches another sound the the victim and adds it to garbage collection queue
wind.Parent = part.Parent.Head
game.Debris:AddItem(wind, 5)
--Take damage, play first sound
part.Parent.Humanoid:TakeDamage(10)
impact:Play()
--Prevents characters from moving during animation
player.Character.Humanoid.WalkSpeed = 0
player.Character.Humanoid.JumpHeight = 0
player.Character.Humanoid.AutoRotate = false
part.Parent.Humanoid.WalkSpeed = 0
part.Parent.Humanoid.JumpHeight = 0
part.Parent.Humanoid.AutoRotate = false
for _,v in pairs(player.Character:GetChildren()) do
if v:IsA("BasePart") then
v.CanTouch = false
end
end
--adjust both characters to be facing each other and close together
part.Parent.HumanoidRootPart.CFrame = CFrame.new(part.Parent.HumanoidRootPart.Position, player.Character.HumanoidRootPart.Position)
player.Character.HumanoidRootPart.CFrame = part.Parent.HumanoidRootPart.CFrame * CFrame.new(0,0,-3.5) * CFrame.Angles(0,math.pi,0)
animEvent:FireAllClients(player, victim, RS:FindFirstChild("ShoCharAtk", true), RS:FindFirstChild("ShoCharVic", true))
local vic = part.Parent.Humanoid.Animator:LoadAnimation(RS:FindFirstChild("ShoCharVic", true))
if vic.Length == 0 then
while vic.Length == 0 do
task.wait()
end
end
task.delay(vic.Length, function()
if victim then
hitFunc(victim, true, 1.5, 0)
isDown[victim] = false
end
part.Parent.Humanoid:TakeDamage(20)
local velocity = Instance.new("BodyVelocity", part.Parent.HumanoidRootPart)
velocity.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
velocity.P = 1000
local angle = ((part.Parent.HumanoidRootPart.Position - player.Character.HumanoidRootPart.Position) * Vector3.new(10,0,10)).Unit * 70 + Vector3.new(0,5,0)
velocity.Velocity = angle
game:GetService("Debris"):AddItem(velocity,.3)
part.Parent.Humanoid.WalkSpeed = 16
part.Parent.Humanoid.JumpHeight = 7.2
part.Parent.Humanoid.AutoRotate = true
for _,v in pairs(player.Character:GetChildren()) do
if v:IsA("BasePart") then
v.CanTouch = true
end
end
player.Character.Humanoid.WalkSpeed = 16
player.Character.Humanoid.JumpHeight = 7.2
player.Character.Humanoid.AutoRotate = true
canPunch[player] = true
canBlock[player] = true
chargeEvent:FireClient(player)
return
end)
end
end)
Like I said in the original post, I’m almost certain the problem is caused by the HumanoidRootPart being stationary for the duration of the animation. Changing or messing with network ownership is not going to solve the problem of the player’s body orientation switching when the animation ends.
The only way I can think of solving this issue, assuming you are correct that it’s caused by the animation setting positional/orientational data is by not animating the movement of the target. That probably doesn’t make sense, so I’ll elaborate.
What I mean is, animate the target being launched into the air the way Roblox does player jumping animations - don’t actually make them get launched up in the animation, just make it look like they’re being launched. Then, after setting network ownership to the player throwing the other, have that player control the launching aspect so that they are actually being launched (physically, meaning the HRP is being moved with the rest of the rig), and then after they land again, launch them in whichever direction you desire. Theoretically, at least the way I imagine it playing out, this would solve the strange issue you face.
I realise you don’t want to use BodyVelocity, but it’s the only solution I can think of at the moment.
Edit: I didn’t take a look at your code, so if there’s something I’ve missed, please let me know.
I’m sorry if it was unclear, but I’m absolutely fine with using BodyVelocity as long as it turns out the way I want it to. If there is no way to get the result I want using BodyVelocity I’ll look for another way. I’ll try this method and see what I can get out of it, thanks!
This seems more like a BodyVelocity issue, not an animation issue. It can happen when a BodyVelocity is made on the server. Can you try changing the MaxForce from math.huge to 100000? Also I recommend setting its parent after you set it up.
This was easy, I had actually already made the second change cause I remembered reading somewhere that setting the parent too early could cause jitters. Here’s the code and the results:
I think you’re misunderstanding that part. The ragdoll is supposed to happen, it’s just the weird pause right before that is causing me trouble. Maybe it isn’t perfectly clear in this one clip, but the person being thrown flips upside down while they are in the air. At the end of the animation when they are hit, they are being hit in the back. The problem comes afterwards, when the animation ends and their body resets to the position it was in at the start of the animation, so they are facing each other again. It’s jarring and it’s like the player is T-Posing right before they get launched away