Projectiles and workspace:FindPartOnRay()

I’m using FindPartOnRay for a projectile, and the ray is of course the same as the trajectory of the projectile, but how do I check when the projectile is touching the part from FindPartOnRay (if it’s not nil)? Do I just check the projectile’s position every heartbeat or instead take the unit-ray (of the trajectory) instead and then if there’s a part using FindPartOnRay that means it touched since it’s just one?

1 Like

I’m gonna list some possible approaches from the top of my head, the choice is yours.

  • Use :GetTouchingParts() on the projectile every heartbeat (wouldn’t recommend)

  • Use the .Touched event on the projectile and check if the BasePart that the projectile hit is the same BasePart from :FindPartOnRay(). The variable passed through the .Touched event is the BasePart that the projectile hit, by the way.

That is a horrible idea. The projectile can just fly through a part in case of lag, meaning no collision for the gameplay.

If your projectile’s physics are scripted then:

local ignpart
local whlist = {ignpart}

local pos0 = Vector3.new()
local pos1 = Vector3.new()
local acc = Vector3.new(0,-9.81*2.5,0)

local con;con = game:GetService("RunService").Heartbeat:Connect(function(dt)
  pos1,pos0 = 2*pos1 - pos0 + acc * dt*dt, pos1

  local ray = Ray.new(pos0,pos1-pos0)
  if not workspace:FindPartOnRay(ray,ignpart) then
    local P,p,n = workspace:FindPartOnRayWithWhitelist(ray,whlist)
    if P then
      --stuff
    end
  end
end)

Otherwise, you could replace pos0 with the old position of the projectile part and pos1 with its current.

1 Like

Run it on the client to minimise that :stuck_out_tongue:

Still a horrible practice. Also, clients are more prone to unstable frame rate.

I’m using BodyVelocity so how do I know what the acceleration is?

Idk if there’s an inbuilt function, but:

Acceleration = Change in velocity / Change in time


  • Record the position of the projectile.
  • Record the position again, lets say after 0.25 seconds.
  • Work out the magnitude between the two vectors (Vector1 - Vector2).magnitude
  • Divide the magnitude by the time elapsed (0.25 seconds) to work out the acceleration (studs per second squared)

1 Like

Then we are talking about the second mentioned case. You are moving the projectile with BodyMovers, not a function.

local ignlist = {ignpart,projectile}
local whlist = {ignpart}

local pos0 = projectile.Position

local con;con = game:GetService("RunService").Heartbeat:Connect(function(dt)
  if not projectile then con:Disconnect() return end
  local pos1 = projectile.Position

  local ray = Ray.new(pos0,pos1-pos0)
  if not workspace:FindPartOnRayWithIgnoreList(ray,ignlist) then
    local P,p,n = workspace:FindPartOnRayWithWhitelist(ray,whlist)
    if P then
      --stuff
    end
  end
  pos0 = pos1
end)
2 Likes

Why would you whitelist “ignpart”?

What I do in this case is every heartbeat fire a ray from the bullet’s last position to its current position, and handle it on the client for smoother and less delayed shots.

Because that’s the piece that detects if the projectile is inside the part you wanted ignored.

1 Like

Oh I was over complicating that lol. Also, do you know if just CFraming the projectile would cause less lag than using a BodyMover?

Not really less lag, but it will make it more stable as BodyMovers will freak out at high velocities, while custom physics only react the way you want them to.

1 Like

I feel like CFraming would be easier and leave less room for error

Custom physics are CFraming. The issues with BodyMovers is that, while they are perfect for stuff like vehicle simulation, they don’t work well with anything that could potentially reach 1000 studs/ second.

1 Like

Okay how do I make an object move at a certain speed given it’ll move every heartbeat?
Maybe just every heartbeat move it Speed*HeartbeatTime?

EDIT: Okay so I did this and it seems to work great. All you have to do is something like

local Force = Physics:CreateForce("Direction")
Force:Activate(Object, Vector3.new(), Vector3.new(), 100)

and now I can start to code other forces :slight_smile:
so thanks for all your help!

Code

function Physics.Forces.Direction()
	local Force = {}
	
	Force.Active = false
	
	function Force:Activate(Object, Direction, Origin, Speed) -- Instance:Object, Vector3:Direction, Vector3:Origin, Number:Speed
		if (Force.Active == true) then return end
		if (Object.Parent == nil) and (self.Object == nil) then error("No object present to activate force.") end
		self.Active = true
		self.Object = Object
		self.Direction = Direction
		self.Origin = Origin
		self.Speed = Speed
		while (self.Active == true) do
			local Interval = RunService.Heartbeat:Wait()
			local Move = CFrame.new(0, 0, -(Speed*Interval))
			Object.CFrame = Object.CFrame * Move
		end
	end
	
	function Force:Deactivate()
		if (Force.Active == false) then return end
		self.Active = false
	end
	
	return Force
end
-----


function Physics:Create(forceName) -- String:forceName
	local Force = Physics.Forces[forceName]
	if (Force) then
		return Force()
	end
end

For your case, the Euler integration you are using (velocities) will work pretty well; however, if you are working with bodies with variable forces, you should try checking Verlet integration (what I used in the no-part example). It is much less prone to imprecisions as it avoids using velocities all together by comparing positions.

An image that compares them in a harmonic oscillation. The blue line is Euler (velocity), Verlet is red (positions) and black represents the real-life value.

1 Like

What do you mean by variable forces? Aren’t all BodyMovers on here constant?

If you look at the graph I’ve shown, you’ll see that the force acts downwards when the line is above 0 and upwards when the line is below. Verlet is much better at energy conservation, while Euler builds up the energy and releases it whenever the forces change (the line above is higher and has a shorter period than the lower).

1 Like