Predicting a ball's position after a given time

Put simply, what I’m trying to achieve is to predict the position of a ball after a given time.

I’ve tried using this post as somewhat of a guide, but most of the time the prediction is inaccurate.

P.S The ball I use has an additional VectorForce that allows it to fall at a slower rate.

1 Like

Could you share the code you’re using to launch the ball, as well as any other parameters that may affect its motion like the VectorForce?

1 Like

Sure, here’s the properties of the VectorForce; as for the code that launches the ball, all I’m trying to do is predict the position of the ball after a given time based off its current velocity and position.

image

1 Like

You should also show you code to know what you have done.

Otherwise I have to assume you do not know how to account this additional vector force within the ball.

For that you should decrease the gravity according to the acceleration caused by the vector force like so:

--F = ma, Newton second law
-- F/m = a, F = vector force N, m = mass, a = acceleration
local g = Vector3.new(0, game.Workspace.Gravity - 270/ball.AssemblyMass, 0)

local function x(t, v0, x0)
	return 0.5*g*t*t + v0*t + x0
end
1 Like

Sure, the code I use to run the prediction calculation is a slightly modified version of the module found in this post

local TERRAIN = game.Workspace.Terrain

local BEAM = Instance.new("Beam")
BEAM.Color = ColorSequence.new(Color3.new(1, 0, 0))
BEAM.Transparency = NumberSequence.new(0)
BEAM.FaceCamera = true
BEAM.Segments = 20
BEAM.Width0 = 0.1
BEAM.Width1 = 0.1

local TimerClass = require(script:WaitForChild("Iteration"))

-- Class

local Trajectory = {}
Trajectory.__index = Trajectory

-- Private Functions

local function reflect(v, n)
	return -2*v:Dot(n)*n + v
end

local function drawBeamProjectile(g, v0, x0, t)
	local c = 0.5*0.5*0.5
	local p3 = 0.5*g*t*t + v0*t + x0
	local p2 = p3 - (g*t*t + v0*t)/3
	local p1 = (c*g*t*t + 0.5*v0*t + x0 - c*(x0+p3))/(3*c) - p2
	
	local curve0 = (p1 - x0).Magnitude
	local curve1 = (p2 - p3).Magnitude
	
	local b = (x0 - p3).unit
	local r1 = (p1 - x0).unit
	local u1 = r1:Cross(b).unit
	local r2 = (p2 - p3).unit
	local u2 = r2:Cross(b).unit
	b = u1:Cross(r1).unit
	
	local cfA = CFrame.fromMatrix(x0, r1, u1, b)
	local cfB = CFrame.fromMatrix(p3, r2, u2, b)
	
	local A0 = Instance.new("Attachment")
	local A1 = Instance.new("Attachment")
	local Beam = BEAM:Clone()
	
	A0.CFrame = cfA
	A0.Parent = TERRAIN
	A1.CFrame = cfB
	A1.Parent = TERRAIN
	
	Beam.Attachment0 = A0
	Beam.Attachment1 = A1
	Beam.CurveSize0 = curve0
	Beam.CurveSize1 = -curve1
	Beam.Parent = TERRAIN
end

-- Public Constructors

function Trajectory.new(gravity, timeStep, maxTime)
	local self = setmetatable({}, Trajectory)
	
	self.Gravity = gravity
	self.TimeStep = timeStep or 0.1
	self.MaxTime = maxTime or 0.5
	self.MinSpeed = 5
	self.MaxBounce = 5
	
	return self
end

-- Public Methods

function Trajectory:Velocity(v0, t)
	-- g*t + v0
	return self.Gravity*t + v0
end

function Trajectory:Position(x0, v0, t)
	-- 0.5*g*t^2 + v0*t + x0
	return 0.5*self.Gravity*t*t + v0*t + x0
end

function Trajectory:PlaneQuadraticIntersection(x0, v0, p, n)
	local a = (0.5*self.Gravity):Dot(n)
	local b = v0:Dot(n)
	local c = (x0 - p):Dot(n)
	
	if (a ~= 0) then
		local d = math.sqrt(b*b - 4*a*c)
		return (-b - d)/(2*a)
	else
		return -c / b
	end
end

function Trajectory:CalculateSingle(x0, v0, filter)
	local t = 0
	local hit, pos, normal, material
	
	repeat
		local p0 = self:Position(x0, v0, t)
		local p1 = self:Position(x0, v0, t + self.TimeStep)
		t = t + self.TimeStep
		
		local ray = Ray.new(p0, p1 - p0)
		
		local params = RaycastParams.new()
		params.FilterDescendantsInstances = filter
		params.FilterType = Enum.RaycastFilterType.Include
		
		local raycast = workspace:Raycast(p0, p1 - p0, params)
		
		hit, pos, normal, material = raycast and raycast.Instance, raycast and raycast.Position, raycast and raycast.Normal, raycast and raycast.Material
	until (hit or t >= self.MaxTime)
	
	if (hit) then
		local t = self:PlaneQuadraticIntersection(x0, v0, pos, normal)
		local x1 = self:Position(x0, v0, t)
		return {hit, pos, normal, PhysicalProperties.new(material)}
	else
		return {hit}
	end
end

--

return Trajectory
1 Like

Apologies, that is still not enough information you will also need to send the script using the module script. This is because you need to confirm that the gravity and timestep is correct.

BTW a, smaller timestep number will be more accurate for fast moving projectile however will cause more lag.

Apologies for the late response, here’s the code that uses the module script:

local trajectory = require(path.to.Trajectory).new(Vector3.new(0, workspace.Gravity - (270 / ball.AssemblyMass), 0), 0.05)
local prediction = trajectory:Position(ball.Position, ball.AssemblyLinearVelocity, 0.5)

local part = Instance.new("Part", workspace)
part.Name = "prediction"
part.Anchored = true
part.CanCollide = false
part.Shape = Enum.PartType.Ball
part.Size = ball.Size
part.Transparency = 0.7
part.Color = Color3.fromRGB(255, 0, 0)
part.CastShadow = false
part.TopSurface = Enum.SurfaceType.Smooth
part.BottomSurface = Enum.SurfaceType.Smooth
part.Position = prediction

game:GetService("Debris"):AddItem(part, 5)