-
What do you want to achieve? Keep it simple and clear!
I need to make a throw arc as such:
-
What is the issue? Include screenshots / videos if possible!
Im not sure how to do it besides raycasting but other then that i dont have much other direction -
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
if tried a couple dev fourum posts but they never seemed to get me what i wanted, i have a gravity of 200 (In a vector force) and i have a “Power” variable and a “Direction” Variable… Ima spend some time learning raycasting so thanks for any help!
Forgot to say this, i need it to follow the path of
tmateRoot.AssemblyLinearVelocity = (Direction + Vector3.new(0,1,0)).unit * Power
Also for now the rays are 1 block apart and when it hits the ground itl put a point there
Here is a great tutorial for this:
The TL;DR is you use physics equations to predict where the ball will go in the future. You can also do collisions for this using raycasting along the path determined by the physics.
i had an idea that might work, what if i just throw an invisable clone of the ball thats being thrown and put a point every 1 stud?
That would work though it would be less efficient and you couldn’t calculate the full path very quickly (i.e. you’d need to wait for the physics updates).
good point (Characterlimmetttttt)
Using the code you provided, a simpler equation looks something like this:
local function predict(position, initialVelocity, dt)
local gravity = workspace.Gravity
local futurePosition = position + initialVelocity * dt - Vector3.new(0, 0.5, 0) * gravity * dt * dt
return futurePosition
end
-- Example usage:
local velocity = (Direction + Vector3.new(0,1,0)).unit * Power
local position = ball.Position
for i = 1, 10 do
-- Predict where the ball will be in i seconds based on the initial velocity and position
local delta_time = i
local position = predict(position, velocity, delta_time)
-- Here you can place markers at `position` to show where the ball will be at 1 sec, 2 sec, etc
end
Edit: @ElipsGames I wrote something incorrect that I fixed in an edit. Please make sure to use the code that looks like - Vector3.new(0, 0.5, 0) * gravity * dt * dt
not - Vector3.new(0, -1, 0) * gravity * dt * dt
Sick thanks i was kinda pulling my hair out trina understand egomooses code, ill implement it
How would i make this update every 1 stud with the beam so it calculates x stud away depending on how much power there is so there will be say power is 24 its 24 points
You would probably need to do raycasting for that.
For example, you could have a for loop like the one above but have a very small dt (to be more accurate) like 1/30, then raycast between the last prediction and the current prediction until there is a hit. The hit is then where the ball is predicted to land. (Then you can do the distance between the ball.Position and the raycast hit to calculate the distance the ball would go.)
How does your current system work? ide prolly have to go with raycasting then
You’d do something like this:
local function predict(position, initialVelocity, dt)
local gravity = workspace.Gravity
local futurePosition = position + initialVelocity * dt - Vector3.new(0, -1, 0) * gravity * dt * dt
return futurePosition
end
local function predictLanding(position, initialVelocity, maxFlightTime, stepTime)
local dt = stepTime
local lastPosition = position
while dt < maxFlightTime do
local position = predict(position, initialVelocity, dt)
-- TODO: Raycast from lastPosition to position to get `hit`
if hit then
-- Found a landing position
return hit.Position
end
lastPosition = position
dt += stepTime
end
return nil -- No hit
end
local velocity = (Direction + Vector3.new(0,1,0)).unit * Power
local position = ball.Position
local landing = predictLanding(position, velocity, 30, 1/10)
if landing then
local distance = (landing - position).Magnitude
print(distance)
end
Where does the “hit” Come in? Also the ball = tmateRoot
local RunService = game:GetService("RunService")
local DEFAULT_TEXTURE = "rbxgameasset://Images/guide"
local DEFAULT_TRANSPARENCY = NumberSequence.new {
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.01, 1),
NumberSequenceKeypoint.new(0.15, 0.35),
NumberSequenceKeypoint.new(0.4, 0.885),
NumberSequenceKeypoint.new(0.75, 0.95),
NumberSequenceKeypoint.new(1, 1)
}
local ROT_OFFSET = {
[0] = CFrame.Angles(0, math.rad(90), 0),
[1] = CFrame.Angles(0, math.rad(-90), 0)
}
local function solveParabola(x, a, b, c)
return (a * x * x) + (b * x) + c
end
local function solveParabolaDeriv(x, a, b, c)
return (2 * a * x) + b
end
local TrajectoryBeam = {}
TrajectoryBeam.__index = TrajectoryBeam
function TrajectoryBeam.new(originAttachment)
local self = setmetatable({}, TrajectoryBeam)
self.launchVelocity = 250
self.enabled = false
self.connections = {}
self.originAttachment = originAttachment
self.part = self.originAttachment.Parent
self.att0 = Instance.new("Attachment")
self.att0.Name = "Attachment0"
self.att0.Parent = self.part
self.att1 = Instance.new("Attachment")
self.att1.Name = "Attachment1"
self.att1.Parent = self.part
self.beam = Instance.new("Beam")
self.beam.Enabled = false
self.beam.Texture = DEFAULT_TEXTURE
self.beam.LightEmission = 1
self.beam.Transparency = DEFAULT_TRANSPARENCY
self.beam.TextureLength = 10
self.beam.TextureSpeed = 0.25
self.beam.Segments = 50
self.beam.CurveSize0 = 0
self.beam.CurveSize1 = 0
self.beam.Width0 = 0
self.beam.Width1 = 0
self.beam.Attachment0 = self.att0
self.beam.Attachment1 = self.att1
self.beam.Parent = self.part
return self
end
function TrajectoryBeam:setLaunchVelocity(launchVelocity)
self.launchVelocity = launchVelocity
end
function TrajectoryBeam:setEnabled(enabled)
if self.enabled == enabled then return end
self.enabled = enabled
self.beam.Enabled = enabled
if self.enabled then
table.insert(self.connections, RunService.RenderStepped:Connect(function(dt) self:_onRenderStep(dt) end))
else
for i, v in pairs(self.connections) do
v:Disconnect()
end
self.connections = {}
end
end
function TrajectoryBeam:_onRenderStep(dt)
local x0 = 0
local x1 = x0
local a, b, c = 1, 0, 0
local startVelocity = self.launchVelocity
local startAngle = math.asin(self.originAttachment.WorldCFrame.lookVector.Y)
local gravity = workspace.Gravity
a = -gravity / (2 * startVelocity * startVelocity * (math.cos(startAngle) * math.cos(startAngle)))
b = math.tan(startAngle)
c = 0
x1 = math.clamp((startVelocity * startVelocity * math.sin(2 * startAngle)) / gravity, 0, 150)
self:_setBeamParabola(x0, x1, a, b, c)
end
function TrajectoryBeam:_setControlPoint(idx, pos)
local part = self.part
local attachment = self["att" .. idx]
local attachmentPos = attachment.WorldPosition
local vecFromAttachment = pos - attachmentPos
local curveSize = vecFromAttachment.Magnitude
attachment.CFrame = part.CFrame:toObjectSpace(CFrame.new(attachmentPos, pos) * ROT_OFFSET[idx] * CFrame.Angles(math.rad(90), 0, 0))
self.beam["CurveSize" .. idx] = curveSize
end
function TrajectoryBeam:_setBeamParabola(x0, x1, a, b, c)
local startPos = self.originAttachment.WorldPosition
local startCF = CFrame.new(startPos, (startPos + self.originAttachment.WorldCFrame.lookVector * Vector3.new(1, 0, 1)))
local partCF = self.part.CFrame
local midpointX = (x0 + x1) / 2
local domainSize = (x1 - x0)
local p0 = startCF:pointToWorldSpace(Vector3.new(0, solveParabola(x0, a, b, c), -x0))
local p1 = startCF:pointToWorldSpace(Vector3.new(0, solveParabola(x1, a, b, c), -x1))
local c = startCF:pointToWorldSpace(Vector3.new(0, solveParabola(x0, a, b, c) + (solveParabolaDeriv(x0, a, b, c) * (domainSize / 2)), -midpointX))
local c0 = (2/3) * c + (1/3) * p0
local c1 = (2/3) * c + (1/3) * p1
self.att0.Position = partCF:pointToObjectSpace(p0)
self.att1.Position = partCF:pointToObjectSpace(p1)
self:_setControlPoint(0, c0)
self:_setControlPoint(1, c1)
self.beam.Width0 = math.clamp(domainSize / 10, 0.1, 5)
self.beam.Width1 = self.beam.Width0
end
return TrajectoryBeam
Something I wrote a long time ago. Use it like so:
local TrajectoryBeam = require(game.ReplicatedStorage.TrajectoryBeam)
local beam = TrajectoryBeam.new(character.RightHand.RightGripAttachment)
beam:setLaunchVelocity(velocity)
beam:setEnabled(true)
I used it for the cannon trajectories in Bootleg Buccaneers. Works pretty well for that. You’ll need to replace the image if you want that.
Hit is the raycast result’s instance. Where it says TODO you’d add a raycast and if the raycast found a result (ie the return value from the raycast function is non-nil), set a variable named hit
to the result.Instance
.
I’d maybe use a beam and make it connect to wherever the mouse is hitting? and then modify the curve based on the distance or the velocity used to throw it to that specific position
Im kinda busy rn but i got it working how i wanted it to work so ill show off my code in a bit but ty for all the help
This is part of a MUCH larger script (Like 600 lines)
if Power then
local StartPos = plrRoot.Position
local Direction = plrRoot.CFrame.LookVector -- Fix these when i get extra info tmr @ElipsGames
local ball = tmateRoot -- Your ball part
local initialPosition = ball.Position
local mass = ball:GetMass()
local initialVelocity = (Direction + Vector3.new(0, 1, 0)).unit * Power
local acceleration = Vector3.new(0, -200 / mass, 0)
local predictionSteps = 3
local timeStep = 0.1
for _, v in pairs(workspace:GetChildren()) do
if v:IsA("Part") and v.Name == "PredictionDot" then
v:Destroy()
end
end
for i = 0, predictionSteps do
local t = i * timeStep
local futurePosition = initialPosition
+ initialVelocity * t
+ 0.5 * acceleration * t^2
local dot = Instance.new("Part")
dot.Position = futurePosition
dot.Size = Vector3.new(0.3, 0.3, 0.3)
dot.Anchored = true
dot.CanCollide = false
dot.BrickColor = BrickColor.new("Bright yellow")
dot.Material = Enum.Material.Neon
dot.Name = "PredictionDot"
dot.Parent = workspace
end
end