How do i make ball roll based on velocity?

[THIS PART IS SOLVED]
Im trying to make a custom ball system for a soccer game (where you play as bunnies) And my ball goes in random zigzags, i want to make it so the ball can smoothly roll instead of going the exact opposite way but im not sure how to do that.

My ball logic:

local BounceAmount = .65
local OldGrounded = false
local GroundPosition = math.huge
local LastPos = Vector3.zero
local AirBegin = tick()

game["Run Service"].Heartbeat:Connect(function(dt)
	BallPhysicsEnabled = not (BallHolder)
	if BallPhysicsEnabled then
		local vel:Vector3 = Ball:GetAttribute("Velocity")
		local velWithoutY = Vector3.new(vel.X,0,vel.Z)
		local params = RaycastParams.new(); params.FilterType = Enum.RaycastFilterType.Include; params.FilterDescendantsInstances = {workspace.Field}
		local rayPos = Ball.Position-Vector3.new(0,Ball.Size.Y/1.95,0)
		local CheckResolution = Ball.Size.Y/8
		local AirTime = tick()-AirBegin
		
		local rayDir = vel
		local movementRay = workspace:Raycast(rayPos,rayDir,params)
		if movementRay then
			local normal =movementRay.Normal if vel.Magnitude < 2 then normal*=Vector3.new(0,0,0) end
			vel *= (vel.Unit-normal).Unit*BounceAmount
			func:PlaySound(SoundService.SFX.BallGround,Ball)
		end
		velWithoutY = Vector3.new(vel.X,0,vel.Z)
		
		if math.abs(vel.Y) <= 0.0024 then
			vel = velWithoutY
		end
		
		local gRay = workspace:Raycast(rayPos,Vector3.new(0,-5000,0),params); local dist; if gRay then dist = gRay.Distance GroundPosition=gRay.Position.Y else dist = math.huge end
		Grounded = dist <= CheckResolution
		if not Grounded then
			vel -= Vector3.new(0,.0025,0)
			vel = Vector3.new(vel.X,math.clamp(vel.Y,-CheckResolution,math.huge),vel.Z)
			if not OldGrounded then AirBegin = tick() end
		end
		
		Ball.Position += vel; Ball.Position = Vector3.new(Ball.Position.X,math.clamp(Ball.Position.Y,GroundPosition+Ball.Size.Y/2,math.huge),Ball.Position.Z)
		Ball:SetAttribute("Velocity",velWithoutY:Lerp(Vector3.new(0,0,0),.005)+Vector3.new(0,vel.Y,0))
		OldGrounded = Grounded
	else
		Ball:SetAttribute("Velocity",Vector3.zero)
	end
	if BallHolder then
		Ball.Position = BallHolder.HumanoidRootPart.BallPosition.WorldPosition
	end
	local v:Vector3 = (LastPos-Ball.Position)/2
	Ball.CFrame *= CFrame.Angles(v.Z,0,v.X);
	LastPos = Ball.Position
end)

Im trying to make a custom ball system for a soccer game (where you play as bunnies) And my ball goes in random zigzags, i want to make it so the ball can smoothly roll instead of going the exact opposite way but im not sure how to do that.

My ball logic:

local BounceAmount = .65
local OldGrounded = false
local GroundPosition = math.huge
local LastPos = Vector3.zero
local AirBegin = tick()

game["Run Service"].Heartbeat:Connect(function(dt)
	BallPhysicsEnabled = not (BallHolder)
	if BallPhysicsEnabled then
		local vel:Vector3 = Ball:GetAttribute("Velocity")
		local velWithoutY = Vector3.new(vel.X,0,vel.Z)
		local params = RaycastParams.new(); params.FilterType = Enum.RaycastFilterType.Include; params.FilterDescendantsInstances = {workspace.Field}
		local rayPos = Ball.Position-Vector3.new(0,Ball.Size.Y/1.95,0)
		local CheckResolution = Ball.Size.Y/8
		local AirTime = tick()-AirBegin
		
		local rayDir = vel
		local movementRay = workspace:Raycast(rayPos,rayDir,params)
		if movementRay then
			local normal =movementRay.Normal if vel.Magnitude < 2 then normal*=Vector3.new(0,0,0) end
			vel *= (vel.Unit-normal).Unit*BounceAmount
			func:PlaySound(SoundService.SFX.BallGround,Ball)
		end
		velWithoutY = Vector3.new(vel.X,0,vel.Z)
		
		if math.abs(vel.Y) <= 0.0024 then
			vel = velWithoutY
		end
		
		local gRay = workspace:Raycast(rayPos,Vector3.new(0,-5000,0),params); local dist; if gRay then dist = gRay.Distance GroundPosition=gRay.Position.Y else dist = math.huge end
		Grounded = dist <= CheckResolution
		if not Grounded then
			vel -= Vector3.new(0,.0025,0)
			vel = Vector3.new(vel.X,math.clamp(vel.Y,-CheckResolution,math.huge),vel.Z)
			if not OldGrounded then AirBegin = tick() end
		end
		
		Ball.Position += vel; Ball.Position = Vector3.new(Ball.Position.X,math.clamp(Ball.Position.Y,GroundPosition+Ball.Size.Y/2,math.huge),Ball.Position.Z)
		Ball:SetAttribute("Velocity",velWithoutY:Lerp(Vector3.new(0,0,0),.005)+Vector3.new(0,vel.Y,0))
		OldGrounded = Grounded
	else
		Ball:SetAttribute("Velocity",Vector3.zero)
	end
	if BallHolder then
		Ball.Position = BallHolder.HumanoidRootPart.BallPosition.WorldPosition
	end
	local v:Vector3 = (LastPos-Ball.Position)/2
	Ball.CFrame *= CFrame.Angles(v.Z,0,v.X);
	LastPos = Ball.Position
end)

You need to gradually blend (lerp) the velocity direction to make the rolling look smooth.

when the ball hits the floor i dont want it to go the exact opposite way unless its a wall, is there an easy way to implement that?

You need to make your bounce logic check if the ball hits a mostly flat surface and then only lower its vertical speed, while only reflecting the full velocity when hitting steep surfaces like walls.

this worked for me, thanks alot!

1 Like

Oh also, i know my main issue has been fixed but could you help me with making the ball rotate based on velocity? It doesnt rotate towards the movement like i want it to and im not really that sure on how to make this. I’ve searched through alot of forum posts but none seem to match my problem


It only rotates correctly on the z axis and most of the time, not even in the right direction
(sorry for the audio)

My code:

		if vel.Magnitude > .1 then
			angVel += Ball.CFrame:VectorToWorldSpace(Vector3.new(vel.Unit.X,0,vel.Unit.Z))
		end