Having trouble making a bullet trajectory system with raycasts

  1. What do you want to achieve? Keep it simple and clear!
    I want to create a path for a raycast so the bullets can damage humanoids without glitching due to the physics engine.
  2. What is the issue? Include screenshots / videos if possible!
    I have the script create a raycast but for some reason it goes in a straight line.
  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I’ll be honest that it is my first time attempting anything with raycasting, I have been looking on the forums for anything that could possibly help me figure it out, but most solutions were FastCast which I don’t want to use, or others were too vague for me to understand.
1 Like

I am using ApplyImpulse() for the bullets but I want to use the raycasts to get accurate collision, here is part of the script:

		local BulletPos = BulletPart.Position
		local Direction = (Pos - BulletPos).Unit
		local Force = (Direction * BulletXYZForce.Value) + Vector3.new(0, BulletYForce.Value, 0)
		local Bullet = BulletPart:Clone()
		Bullet.Parent = workspace.Debris
		Bullet.Position = BulletPos
		Bullet:ApplyImpulse(Force * Bullet.AssemblyMass)
		
		local raycast = workspace:Raycast(BulletPos, Direction)
		if raycast then
			task.wait(raycast.Distance/(BulletXYZForce.Value + BulletYForce.Value))
			local tempbullet = Instance.new("Part")
			tempbullet.Anchored = true
			tempbullet.CanCollide = false
			tempbullet.Parent = workspace.Debris
			tempbullet.Size = Vector3.new(0.1,0.1,0.1)
			tempbullet.Position = raycast.Position
			Bullet:Destroy()
			print(raycast.Instance)
			if raycast.Instance.Parent:FindFirstChild("Humanoid") then
				--do thingy here
			end
			return
		end

if u want to use “custom” physics for projectiles rather than relying on roblox’s physics, i think you should try the following, assuming you’re using RunService and are doing this on every frame:

  • see if the projectile’s lifetime has expired. if yes, stop everything.
  • calculate how far the projectile should travel (multiply the “delta time” *and “desired speed” of the projectilr).
  • perform a raycast in the direction of travel.

now, if this raycast hits something:

  • then disconnect the connection (maybe even destroy a cosmetic bullet that visualized said projectile.)

BUT… if the raycast hits nothing:

  • update the projectile’s position and direction.
  • move a cosmetic object to match the new position if desired.

EDIT: use a “gravity” parameter to pull it downward and as the gravity changes the velocity, the direction should be recalculated.

Okay so the physics stuff with the cosmetic bullet works fine as I’m using ApplyImpulse() but I’m mainly having trouble with raycasting. I’m not to sure how I should get the raycast to have the same trajectory, basically like some games where you’d need to shoot slightly above a npc or player to hit them. Also to specify I’m not using RunService currently. If you could show me some sort of example that would helpful to show me what I should be doing here.

personally i just leave the cosmetic bullet anchored and just set the position to the current position of the projectile, and as for an example, this is the main raycast function from a module that i’ve written:

connection = RunService.Heartbeat:Connect(function(deltaTime)
		if tick() - initialTime > config.lifetime then
			connection:Disconnect()
			if cosmeticObject then
				cosmeticObject:Destroy()
			end
			return
		end

		local travelDistance = velocity * deltaTime
		local rayDirection = currentDirection * travelDistance

		local result
		if config.radius then
			result = workspace:Spherecast(currentPosition, config.radius, rayDirection, config.raycastParams)
		else
			result = workspace:Raycast(currentPosition, rayDirection, config.raycastParams)
		end

		if result then
			if config.onHit then
				config.onHit(result)
			end
			connection:Disconnect()
			if cosmeticObject then
				cosmeticObject:Destroy()
			end
		else
			local newPosition, velocityVector = updatePosition(deltaTime)
			currentPosition = newPosition

			if cosmeticObject then
				cosmeticObject.Position = currentPosition
				cosmeticObject.CFrame = CFrame.lookAt(currentPosition, currentPosition + velocityVector)
			end
		end
	end)

the configuration table looks like this:

 projectile:Cast(config: {
	start: Vector3, 
	direction: Vector3, 
	speed: number,  -- studs per second
	lifetime: number, 
	gravity: number, 
	hasCosmetic: boolean, 
	cosmeticTemplate: Instance?, -- cosmetic 'projectile' that follows the path of the real one
	raycastParams: RaycastParams,
	radius: number?, -- if provided, performs spherecast (improves game feel)
	onHit: (RaycastResult?) -> () -- callback for custom behavior (i.e. damage and stuff)
	})

Okay this is kind of confusing but I will try to figure this out. I’ll let you know if I get some of it working