How would I make it so a tweened part has to stay in a certain area/part?

Currently, I’m try to get this type of effect originally seen in Baldi’s Basics Plus.
2024-11-1019-12-45online-video-cutter.com-ezgif.com-optimize
As you can see, the 1 balloon just bounces off of the wall and tweens to a random position in a random direction for a random amount of time. Really random.

Right now, this is how it is in game (I have AlwaysOnTop checked on).
2024-11-1019-24-30online-video-cutter.com-ezgif.com-optimize

As you can see again, the 9 balloon just kinda phases through the walls. I’ve tried looking at some older DevForum posts (like this) and I’m kind of confused. Also I’m using the “outdated” version of the script that was used in the DevForum thread because it’s the only one that worked for some odd reason. If the newer one works that was posted by the same guy a year later, I’ll use it i guess
This is my current script for the part to float around and to stay in bounds of the BalloonBase part, which just covers the whole room that it’s supposed to stay in.

local base = script.Parent.Parent.BalloonBase
local part = script.Parent
local startPos = part.Position
local respawn = Vector3.new(-69, 10, 369)

while true do
	local BaseSizeX = math.random(1,30)
	local BaseSizeZ = math.random(1,10)
	local waitTime = math.random(4,6)
	
	TweenService:Create(part, TweenInfo.new(waitTime,Enum.EasingStyle.Linear,Enum.EasingDirection.In,0,false,0), {
		Position = startPos + Vector3.new(BaseSizeX, 0, BaseSizeZ)
	}):Play()
	
	local Vectors = {Vector3.new(0,0,1), Vector3.new(0,0,-1), Vector3.new(1,0,0), Vector3.new(-1,0,0), Vector3.new(0,1,0), Vector3.new(0,-1,0)}

	local function CheckIfPart()
		for _, Vector in pairs(Vectors) do
			local BaseEnd = base.Position + (base.Size * Vector)/2 --Finds the edge of the base part
			local ControlEnd = part.Position + (part.Size * Vector)/2 --Finds the edge of the control part

			local BaseDirection = (base.Position - BaseEnd).Unit
			local ControlDirection = (BaseEnd - ControlEnd).Unit * BaseDirection

			--What we'll end up doing is checking the unit direction of the base part and the control part, compared it to the BaseDirection 
			--What ends up happening is if the direction is negative, it's in bounds. if it's positive, it's out of bounds
			if ControlDirection.X > 0 or ControlDirection.Y > 0 or ControlDirection.Z > 0 then
				return false
			end
		end
		local partsound = Instance.new("Part")
		partsound.Transparency = 1
		partsound.CanCollide = false
		partsound.Anchored = true
		partsound.Name = "POP"
		partsound.Position = part.Position
		local sound = Instance.new("Sound")
		sound.SoundId = "rbxassetid://117051487554708"
		sound.RollOffMinDistance = 1
		sound.RollOffMaxDistance = 50
		sound.Volume = 10
		sound:Play()
		
		part.Position = respawn
		return true
	end
	print(CheckIfPart()) **-- In the GIF of how it currently looks in game, you can see how it always prints out false, which false is SUPPOSED to mean that it's in the BalloonBase, even though it's not. Why? I dunno!**
	task.wait(waitTime)
end

Basically I just want a tweened part to check its position constantly to make sure it doesn’t go out of the room/BalloonBase part and it bounces off of other parts if necessary, like the walls. Help is appreciated!

1 Like

simply, only make the random positions within the certain room, like if I have a room thats 100 by 100, simply do

 local BaseSizeX = math.random(1,100)
-- do for rest

I kind of already did this with

local BaseSizeX = math.random(1,30)
local BaseSizeZ = math.random(1,10)

Sadly I figured this doesn’t work because every time the while true do loops, it “generates” a new area of where the balloon is already positioned, and that can make it go out of the area forever. It’s kinda hard to explain so sorry if that’s messy-ish.

ah! I understand dont worry, why can’t you just generate a new position in the room, keep the starting position the same. and when then after the balloon tweens to the random position, tween it back to the starting position.

I was originally going to make it bounce around the room like in the first GIF (and if you still have a way to do that, that’d be cool!) but honestly you won’t see the balloon for that long so I’m pretty sure no one will notice.
But I like this idea and it does work, so thanks!

Simply tween using

Enum.EasingStyle.InOut

glad I could help.

1 Like

you could randomize a direction instead of a position and send it on its way (using raycasts to get collision/impact points and direction post-bounce)

I’ve got the random direction part of the script down, but how would I do the Raycast so if it hits a part it goes into the opposite direction? If I did do it that way, how would I cancel the current tween to do the opposite direction tween?

using the RaycastResult (more specifically the .Normal) from Workspace:Raycast(), you would reflect the direction.
this might be a good resource idk, here’s a snippet displaying the formula you might use

TweenService has the neat behavior of canceling the previous tween in place if its on the same object with the same property, so you don’t have to; just make a new tween. Use Tween:Cancel() if you do need to stop it yourself.

1 Like

Nice! I’ll have to check this out tomorrow since I’m heading to bed. But thanks!

Edit: It works! I did have to change the script up a bit so it wouldn’t go so fast and it would make the Y a random orientation, but other than that, thank you!

1 Like

So just one more thing. I basically got the script from the tutorial you sent, but how would I make sure the ray doesn’t change its Y position?

Remove the Y component of the direction you’re using, so something like

local Direction = Vector3.new(math.random(), 0, math.random()) -- this wouldn't work for generating a random 0-360deg direction but it gets the point across

and you could do the same thing for the reflected direction (post-raycast calculation) (maybe use .Unit or something as well so the direction is normalized)

I knew that already, but honestly the script is a bit confusing for me (I’ve never used raycast before haha) and I dunno where I would put it.

Oh you could just raycast in the loop that moves the balloon, something like this

local angle = math.rad(math.random()*360)
local Direction = Vector3.new(math.cos(randomAngle), 0, math.sin(randomAngle)) 
while Balloon do -- while the balloon exists
	local Result = workspace:Raycast(Balloon.Position, Direction*10000, RaycastParams.new()) -- the 10000 is there to represent the distance you want to raycast
	assert(Result, "Something went wrong, ray didn't hit anything") -- maybe have a fallback case here
	local Tween = TweenService:Create(Ballon, TweenInfo.new(Result.Distance / 10), {Position = Result.Position}) -- distance / speed = time
	Tween:Play()
	Tween.Completed:Wait() -- idk how reliable tweens are so this might hang
	Direction = Direction - 2(Direction * Result.Normal) * result.Normal -- using the above formula, i didn't check this and idk if its right. same thing you'd use for a bullet ricochet
end

psuedo looks like

  • Generate a random direction
  • Loop until the balloon no longer exists v
  • Raycast to find the nearest wall
  • Tween the balloon (at a constant speed)
  • Once the tween ends, calculate the new direction (== mirrored direction)
  • Loop ^

should kinda work like one of those tv screensavers that hit the corner

1 Like