Help on custom physics

script:

function Main:CalculateShots(playerwhoshot : Player, typeofshot : string, charge : number,typee) : {Vector3}
	local settings : NewSettings = self.Settings

	if typeofshot ~= "shootend" then
		return	
	end

	local ball = game.Workspace:FindFirstChild("Ball")

	if not ball and ball.Thrown.Value and ball.Player.Value ~= playerwhoshot.Name then return end

	local CurrentPosition = (typee == "first" and playerwhoshot.Character.HumanoidRootPart.Position) or (typee == "10" and lastknownposition)

	local class = playerwhoshot.Class.Value
	local BaseForce = (class == "Duck" and 15) or (class == "Fat Bird" and 20) or (class == "Bird" and 25)
	local Direction = ball.CFrame.RightVector 

	local Velocity : Vector3 = (typee == "first" and Direction * (charge * BaseForce) / settings.Normalize) or (typee == "10" and lastknownvelocity)
	local positions = {}

	local MaxBouncesCondition = (settings.MaxBounces == "BounceUntilFinished") and math.huge or settings.MaxBounces -- if bounce till finished then itll bounce till velocity is gone

	local SlidingFriction = 0.7


	for i = 1, 10 do
		local NextPosition = CurrentPosition + Velocity * settings.TimeStep
		if Velocity.Magnitude > 0.5 then

			table.insert(positions, CurrentPosition)
			Velocity = Velocity + Vector3.new(0, -game.Workspace.Gravity*0.28, 0) * settings.TimeStep


			local raycastparams = RaycastParams.new()
			raycastparams.FilterType = Enum.RaycastFilterType.Exclude
			raycastparams.FilterDescendantsInstances = {ball, playerwhoshot.Character}

			local RaycastResult = workspace:Spherecast(CurrentPosition, 1,NextPosition - CurrentPosition, raycastparams)
			if RaycastResult then
				local normal = RaycastResult.Normal
				if math.abs(normal:Dot(Velocity)) < 0.1 then -- Check if the ball is sliding (velocity parallel to normal)
					Velocity = Velocity * SlidingFriction -- Apply sliding friction
			
				else
					Velocity = Velocity - 2 * Velocity:Dot(normal) * normal -- this reflects the velocity aka bouncing
					Velocity = Velocity * settings.BounceDamping
					print("bounce")
				end

				NextPosition = RaycastResult.Position

				if math.abs(normal:Dot(Velocity)) >= 0.1 then -- Check if the ball bounced
					self.Bounces = self.Bounces + 1
				end
			

			end

			CurrentPosition = NextPosition

			self.CurrentTime = self.CurrentTime + settings.TimeStep
		
		end
	
	end
	lastknownposition = CurrentPosition
	lastknownvelocity = Velocity

	return positions
end

the script is returning 10 simulated positions every time the function is called, however, the ball falls through the floor after losing most of its velocity?

At the end as it calculates your next position to be next to the ball instead of below the ball, it most likely doesn’t detect the ground beneath the ball and lets your gravity force push the ball down again

but wouldn’t the spherecast catch that? if theres something in between like the ground?

It depends on how big your ball is (if the radius is bigger than 1 then probably not) but otherwise it should

After rereading your script however it looks like that your sliding friction part simply multiplies the velocity (which is pushed down by gravity) by the sliding force so I’m assuming it would just push the ball down anyways

the ball is 2 by 2 by 2, I think the problem might be the bounces perhaps?

Yeah like I said I think the issue is in the sliding calculator since it doesn’t offset the gravity so the ball will continue to go downwards

hmm how would you solve this? make the velocity limit bigger? right now its at 0.5

or disable the gravity entirely if the velocity is lower than a limit

I would disable the gravity if I detect something underneath the ball (so raycast downwards)

so fire an additional ray on top of the existing one? or only when the velocity is low

Every frame no matter what yes

Or you could just disable the gravity when you detect the ball sliding

so something like this:

local GroundRaycast = workspace:Raycast(CurrentPosition,Vector3.new(CurrentPosition.X,CurrentPosition.Y-2,CurrentPosition.Z) , raycastparams)
if GroundRaycast and Velocity.Magnitude< 1 then
Velocity = Velocity + 0*Settings.TimeStep (???)
end

I would do it under the sliding bit and just cancel out the y axis of the vector

something like this?:

for i = 1, 10 do
		local NextPosition = CurrentPosition + Velocity * settings.TimeStep
		if Velocity.Magnitude > 0 then
			
			local raycastparams = RaycastParams.new()
			raycastparams.FilterType = Enum.RaycastFilterType.Exclude
			raycastparams.FilterDescendantsInstances = {ball, playerwhoshot.Character}

			local GroundRaycast = workspace:Raycast(CurrentPosition,Vector3.new(0, -1.1, 0) , raycastparams)
			table.insert(positions, CurrentPosition)
			if GroundRaycast and Velocity.Magnitude < 1 then
				Velocity = Velocity + Vector3.new(0, 0, 0) * settings.TimeStep
			else
				Velocity = Velocity + Vector3.new(0, -game.Workspace.Gravity*0.28, 0) * settings.TimeStep
			end
			


			
			local RaycastResult = workspace:Raycast(CurrentPosition, NextPosition - CurrentPosition, raycastparams)
			
			
			if RaycastResult then
				local normal = RaycastResult.Normal
				if math.abs(normal:Dot(Velocity)) < 0.1 then -- Check if the ball is sliding (velocity parallel to normal)
					Velocity = Velocity * SlidingFriction -- Apply sliding friction
				else
					Velocity = Velocity - 2 * Velocity:Dot(normal) * normal -- this reflects the velocity aka bouncing
					Velocity = Velocity * settings.BounceDamping
				end

				NextPosition = RaycastResult.Position

				if math.abs(normal:Dot(Velocity)) >= 0.1 then -- Check if the ball bounced
					self.Bounces = self.Bounces + 1
				end
			

			end

			CurrentPosition = NextPosition

			self.CurrentTime = self.CurrentTime + settings.TimeStep
		
		end
	
	end

Yes something like that

aaaaaaaaaaaaaaa

Check if you’re accidently making the next position NaN in any other bits of code because the parts usually disappear if you try set it’s position to NaN

NextPosition = RaycastResult.Position + RaycastResult.Normal*0.1

Apply a little offset so it isn’t perfectly in the floor