Unable to add velocity to player?

I am working on custom explosions for my game. In the current version of my game they do no damage (intentional) and they are supposed to knock the player back. The explosion is represented by a ball part where CanCollide is set to false and the script is all executed server side. The explosion is also in its own collision group that cannot collide with default group. Here is my existing code for the explosion:

local body = script.Parent:WaitForChild("Body");
local physicsService = game:GetService("PhysicsService");


local settings = {
	damage = 100;
	radius = 6;
	owner = nil;
};

game:GetService("RunService").Heartbeat:Connect(function()
	body.Velocity = Vector3.new(0, 0, 0)
end)
	

body.Touched:Connect(function(hit)
	if not hit:IsDescendantOf(settings.owner) then
		local explosion = Instance.new("Part");
		explosion.Parent = script.Parent
		explosion.Shape = Enum.PartType.Ball;
		explosion.Size = Vector3.new(settings.radius, settings.radius, settings.radius);
		explosion.Transparency = 0;
		explosion.CanCollide = false;
		explosion.Anchored = true;
		explosion.CFrame = body.CFrame;
		explosion.BrickColor = BrickColor.new(Color3.new(255, 0, 0));
		explosion.Material = Enum.Material.Neon;
		game.Debris:AddItem(body, 10);
		body:remove();
		physicsService:SetPartCollisionGroup(explosion, "ExplosionGroup");
		
		local connection
		local active = true;
		local function onTouch(hit)
			if active == false then
				return
			end
			local debounce = {}
			for index, value in ipairs(explosion:GetTouchingParts()) do
			-- Each value is a part in the blast radius. This block is where I act on those values.
				local player = game.Players:GetPlayerFromCharacter(value.Parent)
				if player ~= nil and table.find(debounce, player) == nil then
					table.insert(debounce, player)
					local hrp = player.Character:WaitForChild("HumanoidRootPart")
					print(player)
					hrp.Velocity = hrp.Velocity - (explosion.Position - hrp.Position).Unit*250
				end
			end
			active = false
		end
		connection = explosion.Touched:Connect(onTouch);
		wait(1);
		explosion:remove();
	end
	
end)

Where body is the body of the rocket I shot.

But here is a video of what happens when I run it:

robloxapp-20200512-1215196.wmv (3.6 MB)

It seems to reset the player velocity to 0 most of the time. But for sometimes it will work and send me flying (the correct behavior) so I am wondering if anyone has had this sort of issue before? It only breaks with humanoids. If I take the same approach to launching loose parts it works as I would expect them to.

1 Like

I did some experimenting and found that by adding velocity on the client side using an event to tell the client to do it seems to work. Not sure if this is the best solution or if this will work well on networked play. If anyone has any suggestions I am all ears. Not gonna mark this as solved yet in case a better solution comes by within the next while.

Just as a test, can you try putting

game:GetService("RunService").Heartbeat:Wait()

before you set the velocity (on the server)?

4 Likes

Wow that made it work. It broke my debounce but I can fix that. Can you explain why that worked?

After some more testing, I think the issue has to deal with network ownership and physics syncing issues between the server/client.

Characters are physically simulated by the clients (not the server). That’s why setting the character’s velocity on the client works as it should. My guess is that when you’re setting the velocity on the server, it conflicts with the client (since the client is the one responsible for doing physics calculations). I can’t find any concrete evidence for this (aside from this post), but waiting a step (waiting for the next .Heartbeat) seems to sync the server?

As an alternative, you can also set the humanoidrootpart’s owner to the server before setting the velocity and setting it back to the client right after you change the velocity. In other words, run hrp:SetNetworkOwner(nil) before you set the velocity and hrp:SetNetworkOwner(player) after you set the velocity. This should work as well (no need to wait for the next .Heartbeat), because it gives the server control of physical simulations for a brief moment (so that you can apply the velocity).

4 Likes

Cool. Thanks for the info. I think this knowledge will be useful for my game in a number of places.

EDIT: After a little testing I found that the heartbeat method was less laggy for me however it was still an upgrade from trying to set it without ownership (which did nothing) but I am glad I learned a little bit about network ownership regardless.