In my context, I have parts travelling hundreds or even thousands of studs per second, and these aren’t bullets but missiles, which would then need a part resembling them in workspace.
I’ve written a simple function to detect if there might be an issue with a collision detection:
local function collision_check(p:Part, vel: Vector3, dt: number?): Vector3?
local tdelta = dt or 1 / workspace:GetRealPhysicsFPS()
local rc_params = RaycastParams.new()
rc_params.FilterType = Enum.RaycastFilterType.Exclude
rc_params.FilterDescendantsInstances = {p}
local cast = workspace:Raycast(p.Position, vel, rc_params)
if cast then
if cast.Distance / (vel.Magnitude * tdelta) < 1 then
return cast.Position
end
end
return
end
It casts a ray at the velocity of the projectile, and if the ray collides with an object it checks if the distance between that object and the projectile can be crossed in a single frame taking in account the velocity.
But this has to be fired every frame for every projectile in my experience, and I’m not sure if that would be most effective.
This is to work alongside the current .touched event for parts, but I’m not sure if this is suffiecent enough.
I use linear velocity, which is modified every frame alongside the raycast, and since linear velocities also run every frame, then I guess yes, they’d be at the same speed.
local ray = workspace:Raycast(
projectile.Position,
config.vel.VectorVelocity * dt,
config.params
)
if ray then
local crosses = ray.Distance / (config.vel.VectorVelocity.Magnitude*dt)
if crosses < 1 then
projectile.Position = ray.Position
config.touch(ray.Instance)
end
end
Here’s the full section of the script for more context:
for projectile, config in pairs(RunningProjs) do
config.fuel = math.max(config.fuel - config.burn * dt, 0)
local actv = math.sign(config.fuel)
local v = config.vel.VectorVelocity.Magnitude
local rho = frm.air_density(projectile.Position.Y)
local eUP = (actv * config.lift) - (gravity * config.mass)
local eFR = (actv * config.thrust) - frm.drag(config.drag_co, rho, v)
config.vel.VectorVelocity =
Vector3.yAxis * (eUP / config.mass)
+
projectile.CFrame.LookVector * (eFR / config.mass)
local new_target = projectile.Position + config.vel.VectorVelocity * dt
local ray = workspace:Raycast(
projectile.Position,
config.vel.VectorVelocity * dt,
config.params
)
if ray then
local crosses = ray.Distance / (config.vel.VectorVelocity.Magnitude*dt)
if crosses < 1 then
projectile.Position = ray.Position
config.touch(ray.Instance)
end
end
projectile:PivotTo(CFrame.lookAt(projectile.Position, new_target))
end
The way you’ve said is likely the best way to go about it. An older module called FastCast does the same thing, raycasting every frame and detecting a hit. iirc it was extremely performant so i don’t see much of a reason this wouldn’t result in the same