.Touched works nicely, but using part.Position after lags behind part's actual position

I would like to make some ball projectiles able to hit the target and explode right in the exact position in which they hit the target. I have the damage part down fine, and even added some knock-back, but every time I throw one of the balls, it doesn’t explode where the part actually touched and explodes some place before.

Here’s a video of what I mean:
https://gyazo.com/77d6f5b784d140977a7984308beb57b5
As you can see, sometimes the positions are right, but most of the time it hits somewhere before where the ball touches.

Here’s the part in my script that throws the balls (roo = the ball btw):

elseif toDo == "Throw" then
		
	local ball = char.LocalRoo:FindFirstChild(player.Name.."'s Roo"..amount)
	ball.Parent = workspace
		
	if amount == 1 then
		rightHand:FindFirstChild("RooWeld"..amount):Destroy()
	elseif amount == 2 then
		leftHand:FindFirstChild("RooWeld"..amount):Destroy()
	else
		root:FindFirstChild("RooWeld"..amount):Destroy()
	end
		
	-- Removing Old Body Velocities
	for i,v in pairs(ball:GetChildren()) do
		if v.ClassName == "BodyVelocity" then
			v:Destroy()
		end
	end
		
	-- Creating New Body Velocity
	ball.Anchored = false
	local bv = Instance.new("BodyVelocity",ball)
	bv.Name = "Flying"
		
	bv.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
	bv.Velocity = (ball.Position-mouseHit).Unit*-150
		
	ball.Name = "RooAttack"
		
	local touch
	touch = ball.Touched:Connect(function(hit)
			
			if hit.Parent == char or hit.Parent.Parent == char then return end
			touch:Disconnect()
			
			-- Creating Explosion
			local explosions = {}
			local amount = 3
			
			for i = 1,amount do
				
				local explosion = ball:Clone()
				explosion.Name = "Explosion"
				explosion.Anchored = true
				explosion.Material = Enum.Material.Neon
				
				table.insert(explosions,explosion)
				
			end
			
			-- Adjusting Explosions
			local vary = ball.Size.X
			for i,v in pairs(explosions) do
				
			-- Positioning
			v.CFrame = CFrame.new(ball.Position + Vector3.new(math.random(-vary,vary),math.random(-vary,vary),math.random(-vary,vary)))
			v.Parent = workspace
				
			-- Getting Tweens
			local info = TweenInfo.new(
				0.5,
				Enum.EasingStyle.Sine,
				Enum.EasingDirection.InOut,
				0,false,0
			)
					
			local properties = {
				Size = v.Size*3;
				Transparency = 1
			}
				
			local tween = ts:Create(v,info,properties)
			tween:Play()
				
			-- Damage
			local density = ball.Density.Value
			local touched = {}
				
			v.Touched:Connect(function(hit) damage(player,hit,touched,density,false,ball) end)
				
		end
			
		-- Cleaning Up
		ball:Destroy()
			
		wait(0.5)
		for i,v in pairs(explosions) do
			v:Destroy()
		end
			
	end)
		
elseif toDo == "Burst" then

I looked around on the Developer Hub but I couldn’t find anything about the .Touched having positions lag behind, and also wasn’t sure what to search up to find anything like that. I also tried simply using ray-casting to find the position I’d need the ball to explode at, but it always seemed to go off in some random direction completely ignoring the mouse. I’d post about the ray-casting issue instead, but I feel like there should be some easy way to just used .Touched for this.

Edit: When I lower the strength of the body velocity, this doesn’t occur as much, so I think the issue might lie in the fast speeds of the projectile. Either way, I just need some workaround.

You can try to localize the touched event, as the script might think that the touched event is a global; and thus will result in the delay.
(On a side note, please just do a for i loop or gather all the explosion parts in a table instead of Explosion1, Explosion2, Explosion3, etcetera.)

elseif toDo == "Throw" then
		
	local ball = char.LocalRoo:FindFirstChild(player.Name.."'s Roo"..amount)
	ball.Parent = workspace
		
	if amount == 1 then
		rightHand:FindFirstChild("RooWeld"..amount):Destroy()
	elseif amount == 2 then
		leftHand:FindFirstChild("RooWeld"..amount):Destroy()
	else
		root:FindFirstChild("RooWeld"..amount):Destroy()
	end
		
	-- Removing Old Body Velocities
	for i,v in pairs(ball:GetChildren()) do
		if v.ClassName == "BodyVelocity" then
			v:Destroy()
		end
	end
		
	-- Creating New Body Velocity
	ball.Anchored = false
	local bv = Instance.new("BodyVelocity",ball)
	bv.Name = "Flying"
		
	bv.MaxForce = Vector3.new(50000000,50000000,50000000)
	bv.Velocity = (ball.Position-mouseHit).Unit*-150
		
	ball.Name = "RooAttack"
		
	local canTouch = true
		
local touched	
touched = ball.Touched:Connect(function(hit)
			
		if not canTouch or hit.Parent == char then return end
		canTouch = false
		
		-- Creating Explosion
		local explosion = ball:Clone()
		explosion.Name = "Explosion"
		explosion.Anchored = true
		explosion.Material = Enum.Material.Neon
			
		local explosion2 = explosion:Clone()
		local explosion3 = explosion:Clone()
		local explosion4 = explosion:Clone()
			
		-- Positioning
		local vary = ball.Size.X/2
		explosion.CFrame = CFrame.new(ball.Position + Vector3.new(math.random(-vary,vary),math.random(-vary,vary),math.random(-vary,vary)))
		explosion2.CFrame = CFrame.new(ball.Position + Vector3.new(math.random(-vary,vary),math.random(-vary,vary),math.random(-vary,vary)))
		explosion3.CFrame = CFrame.new(ball.Position + Vector3.new(math.random(-vary,vary),math.random(-vary,vary),math.random(-vary,vary)))
		explosion4.CFrame = CFrame.new(ball.Position + Vector3.new(math.random(-vary,vary),math.random(-vary,vary),math.random(-vary,vary)))
			
		-- Parenting
		explosion.Parent = workspace
		explosion2.Parent = workspace
		explosion3.Parent = workspace
		explosion4.Parent = workspace
			
		-- Getting Tweens
		local info = TweenInfo.new(0.5,Enum.EasingStyle.Sine,Enum.EasingDirection.InOut,0,false,0)
			
		local properties = {
			Size = explosion.Size*3;
			Transparency = 1
		}
			
		local tween1 = ts:Create(explosion,info,properties)
		local tween2 = ts:Create(explosion2,info,properties)
		local tween3 = ts:Create(explosion3,info,properties)
		local tween4 = ts:Create(explosion4,info,properties)
			
		-- Playing Tweens
		tween1:Play()
		tween2:Play()
		tween3:Play()
		tween4:Play()
			
		-- Damage
		local density = ball.Density.Value
		local touched = {}
			
		explosion.Touched:Connect(function(hit) damage(player,hit,touched,density,false,ball) end)
		explosion2.Touched:Connect(function(hit) damage(player,hit,touched,density,false,ball) end)
		explosion3.Touched:Connect(function(hit) damage(player,hit,touched,density,false,ball) end)
		explosion4.Touched:Connect(function(hit) damage(player,hit,touched,density,false,ball) end)
			
		-- Cleaning Up
		ball:Destroy()
			
		wait(0.5)
		explosion:Destroy()
		explosion2:Destroy()
		explosion3:Destroy()
		explosion4:Destroy()
			
	end)
		
elseif toDo == "Burst" then
1 Like

Localizing the .Touched event didn’t seem to work. It still lags behind. I did however take your advice and simplify the creating of the explosions with for loops. I’ll post the updated script in an edit, one sec.

Hard to explain, but simply put: A coroutine is like the equivalent of making a new script except you are still only using a single script. It could be used for cases where you want a function or anything to not stop the entire script until the function is fully executed. This page might explain it a little better:

Either way, if you still aren’t getting enough of an understanding, you can take a look at this modified version of your code and see what you can learn.

coroutine.resume(coroutine.create(function() -- will create a coroutine thread without us having to declare any variables
	local touched	
	touched = ball.Touched:Connect(function(hit)
		-- put your projectile function when the ball touches here or whatever
	end)
end))

With coroutines, you’ll be able to do many tasks at once all in one script.

I’ve heard the term coroutine before, but to be completely honest with you I’m not really sure what it is. Do you think you could explain it a little bit or maybe link a resource so I can have a look at it?

There you go. I forgot to post an entirely new message, a whoops on my part.

Hm, okay. I think I understand the concept now and I can see how coroutines could be useful, but I’m not quite sure how to use that to fix my issue. Based on your example, would I just put the coroutine around the touched event, or around the whole elseif?

I tried it around the touched event and it doesn’t seem to do anything different.

Hm, what’s the code? I’ll have to try to test it ingame and see the results. (Or you could just gif the results if you can.), and you have to put the coroutine around the touched event, by the way.

If anything, you could try this new modified version of the code I improvised. I’m afraid I might be filling up this thread, so I apologize for any inconvienences.

local TOUCHED_THREAD = coroutine.wrap(function() 
	local touched	
	touched = ball.Touched:Connect(function(hit)
		-- put your projectile function when the ball touches here or whatever
	end)
end))
TOUCHED_THREAD()

Sorry for taking a while, here’s a gif:
https://gyazo.com/047cda165c1e85b6d6a2106beba827db

It seems to do the same thing as before.

I’ll try using coroutine.wrap() and see if that works though.

Edit: Using the .wrap() still just does the same thing. I appreciate the help though.

By any chance that returning nothing for this touched event completely cancels the touched event? You could just try to replace this by removing “return end” and reverse the statement. (if canTouch or hit.Parent ~= char then)

local touched	
touched = ball.Touched:Connect(function(hit)
			
		if not canTouch or hit.Parent == char then return end
		canTouch = false

Because from what I’ve learned, returning a function will completely stop the function itself.
Or if there’s a long debounce in the touched function.

Returning the function does stop it, but the touched event is not being cancelled. The explosion is still being created, it’s just that the ball.Position is not accurate compared to where the ball actually collides.

I added that if return end to prevent it from creating an explosion if it hits the player who threw the ball and also to add a sort of debounce (hence “if not canTouch”) so it doesn’t add a million explosions, due to the nature of .Touched.

I’ll try to make some experiments, and tell you as soon as I find a solution to your problem.

Quick question, is the projectile’s network owner set to the server? (As in if you did any commands like :SetNetworkOwner() on the part.)

What exactly do you mean by network owner?

Also about that, a network owner is basically which sides are caculating physics (client and server.). By default, the network ownership of a part switches from client to server constantly, which could cause some delays and bad timing in the projectile.

Include this in your script after the “ball” has been parented to the workspace. This will make the script know that the ball’s physics are caculated at the server at all times.
ball:SetNetworkOwner(nil)

Hm, okay, I’ll try that. That seems like it could definitely work.

A reminder that inorder for a part to have its network owner set, it needs to follow the conditions:

  • It’s not anchored or welded to an anchored part. (Anchored parts don’t need to have physics caculated on them, so it makes sense in a way.)
  • It is a parent or descendant of the workspace.

Yeah I got an error for that at first. It works though, thanks! It does lag a little bit at the start, but I think that’s just because of where I’m switching the network owner. I really appreciate you taking your time to help me with it though. I’ll accept your answer.

Just one thing about that delay. I don’t think there is a way to get rid of that delay when you make the projectile entirely server-sided. You’ll have to make the projectile on the client and the damaging shenanigans managed the server and include it with proper security measures so it’s not too exploitable. But that might require a rework of your entire script if you have any concerns about that. Other than that, happy to help.

Hm okay, but how would I make specifically only the damaging on the server? Would I just set it to the client when I first create the ball and then change before throwing?