What do you want to achieve? Keep it simple and clear!
I have a ball bouncing script that bounces the ball whenever it hits a part.
What is the issue? Include screenshots / videos if possible!
Sometimes, the raycast I get is nil, even though it only fires when the ball touches something.
What solutions have you tried so far? Did you look for solutions on the Creator Hub?
I’ve tried to change the position of where the raycast fires from, but it didn’t work.
Here’s the function in question:
local spawnBasic = game.ReplicatedStorage.SpawnBasic
local moreSpeed = game.ReplicatedStorage.BasicSpeed
local speed = 0
local blank = Vector3.new(0,15,0)
local RunService = game:GetService("RunService")
--local create = require(game.ReplicatedStorage.Modules.BallSpawn)
local function BasicBall()
local ball = Instance.new("Part")
local attachment = Instance.new("Attachment", ball)
local lv = Instance.new("LinearVelocity", attachment)
ball.Name = "ball"
ball.Parent = workspace
ball.Shape = Enum.PartType.Ball
ball.CFrame = CFrame.new(math.random(-50,50), math.random(3,20), math.random(-50,50))
ball.CanCollide = false
lv.Attachment0 = attachment
lv.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
lv.LineDirection = Vector3.new(math.random(-999,999)/1000, math.random(-999,999)/1000, math.random(-999,999)/1000)
lv.LineVelocity = speed
ball.Touched:Connect(function() -- IMPORTANT BIT!!!
local startCFrame = ball.CFrame -- Where we derive our position & normal
local normal = lv.LineDirection.Unit
local position = startCFrame.Position
local result = workspace:Raycast(position, normal*500) -- Cast ray
if result ~= nil then
local reflectedNormal = normal - (2 * normal:Dot(result.Normal) * result.Normal)
ball.AssemblyLinearVelocity = Vector3.new(0,0,0) -- super important, dont delete!!
lv.LineDirection = reflectedNormal
print("bounced!")
else
warn("dude, thats nil.")
end
end)
while true do
if (math.abs(ball.Position.X) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Y) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Z) > 100) == true then
ball.Position = blank
end
lv.LineVelocity = speed
task.wait(1)
end
end
spawnBasic.OnServerEvent:Connect(BasicBall)
moreSpeed.OnServerEvent:Connect(function() -- this makes already spawned balls faster!!!!
speed = speed + 10
print(speed)
end)
Edit again:
I’ve now got an even better trick that works, even at high speed! However, the root problem I’m trying to fix still isn’t solved, so I’m not going to mark this as the solution… https://streamable.com/t20pbf
And since I forgot to post the code last time, here’s my new fix:
ball.Touched:Connect(function() -- IMPORTANT BIT!!!
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
ball.CFrame = ball.CFrame:Lerp(CFrame.new(0,15,0),speed/10000)
local startCFrame = ball.CFrame -- Where we derive our position & normal
local normal = lv.LineDirection.Unit
local position = startCFrame.Position
local result = workspace:Raycast(position, normal*500) -- Cast ray
if result ~= nil then
local reflectedNormal = normal - (2 * normal:Dot(result.Normal) * result.Normal)
lv.LineDirection = reflectedNormal
print("bounced!")
else
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
lv.LineDirection = Vector3.new(math.random(-999,999)/1000, math.random(-999,999)/1000, math.random(-999,999)/1000)
warn("dude, thats nil.")
end
end)
while true do
if (math.abs(ball.Position.X) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Y) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Z) > 100) == true then
ball.Position = blank
end
lv.LineVelocity = speed
task.wait(1)
end
Judging from title it may be the case becouse raycast is right in the same position as a bounding box of the wall.
If it has 0 distance raycast may pass through sometimes.
with raycasting?
Cant really unless you somehow know surface normal
With other functions like workspace:GetPartBoundsInRadius ? maybe
There is a lot of solutions to a problem but workspace:GetPartBoundsInRadius would be the easiest ig
Normal is a direction surface is facing that has been hitted by a raycast; GetPartBounds does check position.
You can loop through all objects that it found and use (part:GetClosestPointOnSurface(RayStartPosition)-RayStartPosition).Magnitude if it equals 0 or near 0 like 0.00000001 then yeah this part should be right infront of raycast probably
Yeah, it’s currently printing 0. However, it’s now bouncing the ball out of bounds sometimes too.
Heres the code I used:
ball.Touched:Connect(function() -- IMPORTANT BIT!!!
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
--ball.CFrame = ball.CFrame:Lerp(CFrame.new(0,15,0),speed/10000)
local startCFrame = ball.CFrame -- Where we derive our position & normal
local normal = lv.LineDirection.Unit
local position = (ball:GetClosestPointOnSurface(ball.Position)-ball.Position) -- changed code
local magnitude = position.Magnitude
print(magnitude)
local result = workspace:Raycast(position, normal*500) -- Cast ray
if result ~= nil then
local reflectedNormal = normal - (2 * normal:Dot(result.Normal) * result.Normal)
lv.LineDirection = reflectedNormal
print("bounced!")
else
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
lv.LineDirection = Vector3.new(math.random(-999,999)/1000, math.random(-999,999)/1000, math.random(-999,999)/1000)
warn("dude, thats nil.")
end
end)
while true do
if (math.abs(ball.Position.X) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Y) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Z) > 100) == true then
ball.Position = blank
end
lv.LineVelocity = speed
task.wait(1)
end
ball.Touched:Connect(function()
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
--ball.CFrame = ball.CFrame:Lerp(CFrame.new(0,15,0),speed/10000)
local startCFrame = ball.CFrame -- Where we derive our position & normal
local normal = lv.LineDirection.Unit
local position = ball.Position
local magnitude = position.Magnitude
print(magnitude)
local result = workspace:Raycast(position, normal*500) -- Cast ray
if result ~= nil then
local reflectedNormal = normal - (2 * normal:Dot(result.Normal) * result.Normal)
lv.LineDirection = reflectedNormal
print("bounced!")
else
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
lv.LineDirection = Vector3.new(math.random(-999,999)/1000, math.random(-999,999)/1000, math.random(-999,999)/1000)
warn("dude, thats nil.")
end
end
end)
while true do
if (math.abs(ball.Position.X) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Y) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Z) > 100) == true then
ball.Position = blank
end
lv.LineVelocity = speed
task.wait(1)
end
end
basically like that smth:
and using rayPart if its not nil instead of raycast
ball.Touched:Connect(function() -- IMPORTANT BIT!!!
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
--ball.CFrame = ball.CFrame:Lerp(CFrame.new(0,15,0),speed/10000)
local startCFrame = ball.CFrame -- Where we derive our position & normal
local normal = lv.LineDirection.Unit
local rayPart:BasePart?=nil
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
if (ball:GetClosestPointOnSurface(ball.Position)-ball.Position).Magnitude<=0.001
BasePart=part
break
end
end
local result = workspace:Raycast(position, normal*500) -- Cast ray
if result ~= nil then
local reflectedNormal = normal - (2 * normal:Dot(result.Normal) * result.Normal)
lv.LineDirection = reflectedNormal
print("bounced!")
else
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
lv.LineDirection = Vector3.new(math.random(-999,999)/1000, math.random(-999,999)/1000, math.random(-999,999)/1000)
warn("dude, thats nil.")
end
end)
while true do
if (math.abs(ball.Position.X) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Y) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Z) > 100) == true then
ball.Position = blank
end
lv.LineVelocity = speed
task.wait(1)
end
relatable bro i dont know neither
basically you can make a “range cast” lets call it like that
local rayPart:BasePart?=nil
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
if (ball:GetClosestPointOnSurface(ball.Position)-ball.Position).Magnitude<=0.001 then
BasePart=part
break
end
end
rayPart is basically like result.Instance
If rayPart equals to nil then just do a regular raycast
Note that code i provided previously was just an ilustration how to make something like that and i probably deleted some important varaibles whoops
ball.Touched:Connect(function()
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
--ball.CFrame = ball.CFrame:Lerp(CFrame.new(0,15,0),speed/10000)
local startCFrame = ball.CFrame -- Where we derive our position & normal
local normal = lv.LineDirection.Unit
local position = ball.Position
local magnitude = position.Magnitude
local rayPart:BasePart?=nil
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
if (ball:GetClosestPointOnSurface(ball.Position)-ball.Position).Magnitude<=0.001 then
BasePart=part
break
end
end
local result = workspace:Raycast(position, normal*500) -- Cast ray
if result ~= nil then
local reflectedNormal = normal - (2 * normal:Dot(result.Normal) * result.Normal)
lv.LineDirection = reflectedNormal
print("bounced!")
elseif (ball:GetClosestPointOnSurface(ball.Position)-ball.Position).Magnitude<=0.001 then
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
lv.LineDirection = Vector3.new(math.random(-999,999)/1000, math.random(-999,999)/1000, math.random(-999,999)/1000)
warn("dude, thats nil.")
end
end
end
end)
while true do
if (math.abs(ball.Position.X) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Y) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Z) > 100) == true then
ball.Position = blank
end
lv.LineVelocity = speed
task.wait(1)
end
end
no…
This for loop is just like a workspace:Raycast()
you should not touch it.
It sets a part that is inside the bounds
Also you removed variable making it completelly trapped inside without way of inputing value outside of scope.
Also you should remove that (in the beggining of script)
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
It was never meant to be here in the first place.
It was meant to be used for that only:
local rayPart:BasePart?=nil
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
if (ball:GetClosestPointOnSurface(ball.Position)-ball.Position).Magnitude<=0.001 then
rayPart=part
break
end
end
Honestly, my brain is completely fried and I have no idea what to do… this is what I have now… I’ll be coming back to this tomorrow…
(also contains some other stuff in the function…)
(also, still getting nil but dont know if thats important)
local function BasicBall()
local ball = Instance.new("Part")
local attachment = Instance.new("Attachment", ball)
local lv = Instance.new("LinearVelocity", attachment)
ball.Name = "ball"
ball.Parent = workspace
ball.Shape = Enum.PartType.Ball
ball.CFrame = CFrame.new(math.random(-50,50), math.random(3,20), math.random(-50,50))
ball.CanCollide = false
lv.Attachment0 = attachment
lv.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
lv.LineDirection = Vector3.new(math.random(-999,999)/1000, math.random(-999,999)/1000, math.random(-999,999)/1000)
lv.LineVelocity = speed
ball.Touched:Connect(function()
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
--ball.CFrame = ball.CFrame:Lerp(CFrame.new(0,15,0),speed/10000)
local startCFrame = ball.CFrame -- Where we derive our position & normal
local normal = lv.LineDirection.Unit
local position = ball.Position
local magnitude = position.Magnitude
local rayPart:BasePart?=nil
local result = workspace:Raycast(position, normal*500) -- Cast ray
if result ~= nil then
local reflectedNormal = normal - (2 * normal:Dot(result.Normal) * result.Normal)
lv.LineDirection = reflectedNormal
print("bounced!")
else
warn("dude, thats nil.")
local rayPart:BasePart?=nil
for i,part in workspace:GetPartBoundsInRadius(ball.Position,0.001) do
if (ball:GetClosestPointOnSurface(ball.Position)-ball.Position).Magnitude<=0.001 then
rayPart=part
break
end
end
end
end)
My current hack I’ve developed seems to work near perfect, so I’m marking this as the solution. However, I know this probably isn’t the best way to do this. It relies on the UpVector of the part the ball bounces on…
local spawnBasic = game.ReplicatedStorage.SpawnBasic
local moreSpeed = game.ReplicatedStorage.BasicSpeed
local speed = 0
local blank = Vector3.new(0,15,0)
local RunService = game:GetService("RunService")
--local create = require(game.ReplicatedStorage.Modules.BallSpawn)
local function BasicBall()
local ball = Instance.new("Part")
local attachment = Instance.new("Attachment", ball)
local lv = Instance.new("LinearVelocity", attachment)
ball.Name = "ball"
ball.Parent = workspace
ball.Shape = Enum.PartType.Ball
ball.CFrame = CFrame.new(math.random(-50,50), math.random(3,20), math.random(-50,50))
ball.CanCollide = false
lv.Attachment0 = attachment
lv.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
lv.LineDirection = Vector3.new(math.random(-999,999)/1000, math.random(-999,999)/1000, math.random(-999,999)/1000)
lv.LineVelocity = speed
ball.Touched:Connect(function()
ball.AssemblyLinearVelocity = Vector3.new(0,0,0)
local startCFrame = ball.CFrame -- Where we derive our position & normal
local normal = lv.LineDirection.Unit
local position = ball.Position
local magnitude = position.Magnitude
local result = workspace:Raycast(position, normal*500) -- Cast ray
if result ~= nil then
local reflectedNormal = normal - (2 * normal:Dot(result.Normal) * result.Normal)
lv.LineDirection = reflectedNormal
else -- CHANGED PART
local swag = workspace:GetPartsInPart(ball,OverlapParams.new())
local reflectedNormal = normal - (2 * normal:Dot(swag[1].CFrame.UpVector.Unit) * swag[1].CFrame.UpVector.Unit)
lv.LineDirection = reflectedNormal
end
end)
while true do
if (math.abs(ball.Position.X) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Y) > 100) == true then
ball.Position = blank
end
if (math.abs(ball.Position.Z) > 100) == true then
ball.Position = blank
end
lv.LineVelocity = speed
task.wait(0.1)
end
end
spawnBasic.OnServerEvent:Connect(BasicBall)
moreSpeed.OnServerEvent:Connect(function() -- this makes already spawned balls faster!!!!
speed = speed + 10
print(speed)
end)