Filling gaps (like starvants plugin) but live in game

local function resize(part, worldOffset)
	local offset = part.CFrame:ToObjectSpace(worldOffset)
	part.Size = part.Size + absVector3(offset)
	part.CFrame = part.CFrame + offset / 2
end

local function adjust(wall, newLength)
	local worldOffset = wall.dir * (newLength - wall.length)
	resize(wall.model.Wall1, worldOffset)
	resize(wall.model.Wall2, worldOffset)
	wall.length = newLength
end

Hmm ok,

local function resize(part, worldOffset)
	local offset = part.CFrame:ToObjectSpace(worldOffset) -- error
	part.Size = part.Size + absVector3(offset)
	part.CFrame = part.CFrame + offset / 2
end

[bad argument #1 to ‘ToObjectSpace’ (CFrame expected, got Vector3)]

which not sure how it’s getting a Vector3 if it’s being called like this

resize(wall.model.Wall1, worldOffset)

as Wall1 is just a part

Should be :VectorToObjectSpace() instead of :ToObjectSpace().


local function getAdditionalLength(angleBetweenWalls)
    return math.sin(angleBetweenWalls / 2) * 0.4 / 2 -- LINE 40
end

and it’s being called from here

local length = getAdditionalLength(getAngle(dir, otherWall.dir)) -- LINE 92

It is a really simple fix. Just missing a keyword. You should be able to fix the simple bugs but let me know if there is anything major.

@NinjoOnline

getAngle didn’t return the value:

local function getAngle(wallDirA, wallDirB)
    math.acos(wallDirA:Dot(wallDirB))
end

local function getAngle(wallDirA, wallDirB)
    return math.acos(wallDirA:Dot(wallDirB))
end
edit: the heck i swear i clicked reply


Welp, got no errors, but at the same, it doesn’t work :confused: the walls are being resized, but it’s just making one wall skinnier, not actually filling the gap

Just testing more, it actually makes 1 wall thicker, and then moves the smaller wall over a bit. So in that pic, the skinnier wall stays at 0.2 for each wall (making it 0.4 thick) but the fatter wall gets given a random thickness (like 0.382 for each wall, making it 0.382*2) in thickness

Ah, I see the issues. Try this:

local abs = math.abs
local function absVector3(v3)
	return Vector3.new(
		abs(v3.X),
		abs(v3.Y),
		abs(v3.Z)
	)
end

local function resize(part, worldOffset)
	local offset = part.CFrame:VectorToObjectSpace(worldOffset)
	part.Size = part.Size + absVector3(offset)
	part.CFrame = part.CFrame + worldOffset / 2
end

local function adjust(wall, newLength)
	-- Need to offset toward the poll, not away from it.
	local worldOffset = wall.dir * (wall.length - newLength)
	resize(wall.model.Wall1, worldOffset)
	resize(wall.model.Wall2, worldOffset)
	wall.length = newLength
end

Upon closer examination I’ll bet you’ll find the wall width didn’t decrease, but the wall halves were moved closer together.

Ahhh it’s getting so close :weary:



Not sure if coloring helps show the intersections?


Also tiny gap when walls are 90 degrees

I used the opposite dir in my code than what the original code I gave you wanted. I’ve reversed it in two places. In addToPoll():

	local dir = (poll - otherPoll).Unit

and in adjust() I changed the worldOffset back:

	local worldOffset = wall.dir * (newLength - wall.length)

Here is code with most of the changes:

local wallModel = game.ReplicatedStorage.Wall

local WALL_HEIGHT = 5
local WALL_OFFSET = Vector3.new(0, WALL_HEIGHT, 0)
local UP = Vector3.new(0, 1, 0)

local function getAdditionalLength(angleBetweenWalls)
    return math.sin(angleBetweenWalls / 2) * 0.5 / 2
end

local function getAngle(wallDirA, wallDirB)
    return math.acos(wallDirA:Dot(wallDirB))
end

local function snapToGrid(pos)
	return Vector3.new(
		math.floor(pos.X + 0.5),
		math.floor(pos.Y + 0.5),
		math.floor(pos.Z + 0.5)
	)
end

local abs = math.abs
local function absVector3(v3)
	return Vector3.new(
		abs(v3.X),
		abs(v3.Y),
		abs(v3.Z)
	)
end

local function resize(part, worldOffset)
	local offset = part.CFrame:VectorToObjectSpace(worldOffset)
	part.Size = part.Size + absVector3(offset)
	part.CFrame = part.CFrame + worldOffset / 2
end

local function adjust(wall, newLength)
	local worldOffset = wall.dir * (newLength - wall.length)
	resize(wall.model.Wall1, worldOffset)
	resize(wall.model.Wall2, worldOffset)
	wall.length = newLength
end

local POLL_KEY = '%d %d %d'
local function getWalls(polls, pos)
	local key = POLL_KEY:format(pos.X, pos.Y, pos.Z)
	local walls = polls[key]
	if not walls then
		walls = {}
		polls[key] = walls
	end
	return walls
end

local function addToPoll(polls, poll, otherPoll, model)
	local walls = getWalls(polls, poll)
	local maxLength = 0
	local dir = (poll - otherPoll).Unit
	local wall = {
		length = maxLength;
		model = model;
		dir = dir;
	}

	for otherWall in next, walls do
		local length = getAdditionalLength(getAngle(dir, otherWall.dir))
		if length > maxLength then
			maxLength = length
		end
		if length > otherWall.length then
			adjust(otherWall, length)
		end
	end
	adjust(wall, maxLength)

	walls[wall] = true
end

local function placeWall(polls)
	local mouse = game.Players.LocalPlayer:GetMouse()

	mouse.Button1Down:Wait()
	local pollA = snapToGrid(mouse.Hit.p)

	mouse.Button1Down:Wait()
	local pollB = snapToGrid(mouse.Hit.p)

	local model = wallModel:Clone()
	model:SetPrimaryPartCFrame(CFrame.fromMatrix(
		(pollA + pollB) / 2 + WALL_OFFSET,
		(pollA - pollB).Unit,
		UP
	))

	addToPoll(polls, pollA, pollB, model)
	addToPoll(polls, pollB, pollA, model)
end


Still has the same problem of them being sized too big :confused:

Made the change in adjust

local function adjust(wall, newLength)
	-- Need to offset toward the poll, not away from it.
	local worldOffset = wall.dir * (newLength - wall.length)
	resize(wall.model.Wall1, worldOffset)
	resize(wall.model.Wall2, worldOffset)
	wall.length = newLength
end

and addToPole

local function addToPoll(polls, poll, otherPoll, model)
	local walls = getWalls(polls, poll)
	local maxLength = 0
	local dir = (poll - otherPoll).Unit
	local wall = {
		length = maxLength;
		model = model;
		dir = dir;
	}

	for otherWall in next, walls do
		local length = getAdditionalLength(getAngle(dir, otherWall.dir))
		if length > maxLength then
			maxLength = length
		end
		if length > otherWall.length then
			adjust(otherWall, length)
		end
	end
	adjust(wall, maxLength)

	walls[wall] = true
end

Sorry, I had to go do something else. I redid the angle math and got a different result. Here is my diagram:


(Note, the theta should be halved in the image in the equation for x.)

Which means that this function needs to be different:

local WALL_THICKNESS = 1
local function getAdditionalLength(angleBetweenWalls)
    return math.tan(angleBetweenWalls / 2) * WALL_THICKNESS / 2
end

Ended up making the walls even longer :confused:


I did edit the wall thickness to 0.4 instead of 1, as each wall is 0.2 in thickness (0.2+0.2=0.4)


Or, should wall thickness be set to 0.2? Technically the wall model is 0.4 in thickness, but is that code based on the individual parts thickness?

Holy what… ok, so I tried setting the thickness to 0.2, and got this :open_mouth:



It’s so beautiful :heart_eyes:

Unfortunately, it kinda stops there


90 degree still has the lil gap

So for some reason, certain angles work like magic, other angles just aren’t right :confused:

Messing around with more angles


lol

After staring at the code I saw it and hit myself on the head.

This:

local function getAngle(wallDirA, wallDirB)
    return math.acos(wallDirA:Dot(wallDirB))
end

Should be this:

local function getAngle(wallDirA, wallDirB)
    return wallDirA:Dot(wallDirB)
end

I think that is the last bug!

Also, the thickness should be 0.4 in that code I gave you, but you could also use this:

local WALL_HALF_THICKNESS = 0.2
local function getAdditionalLength(angleBetweenWalls)
    return math.tan(angleBetweenWalls / 2) * WALL_HALF_THICKNESS
end
local function getAngle(wallDirA, wallDirB)
    return wallDirA:Dot(wallDirB)
end

tried that and it did nothing :grimacing: now the walls don’t resize at all


Also are you sure it should be 0.4? Cause it worked like a charm with 0.2 for some of the angles, but didnt work at all with 0.4

Is there a test place you can show me? I’d like to see the behavior in action. We could also chat in game / group until we have a solution others could use.

local WALL_THICKNESS = 0.4
local function getAdditionalLength(angleBetweenWalls)
    return math.tan(angleBetweenWalls / 2) * WALL_THICKNESS / 2
end

local function getAngle(wallDirA, wallDirB)
    return math.acos(wallDirA:Dot(wallDirB))
end

This seemed to work with 90 degree walls


But was adding like 2-3 studs on any other angle

I can invite you to a team create?? i think you need to be on my friends list tho

1 Like

Good news! Last night we got the last script posted here working and we also added a couple more features. It now handles both halves of each wall (inner / outer or in other words left / right) differently so that when coloring them they don’t glitch the graphics where they overlap. In addition, we discovered that when the smallest angle between the walls is less than 90 degrees, the walls can’t be brought together to form a clean point. Doing so makes the wall’s thickness stick out the other end and form an hour glass looking shape around the poll. So, one wall is chosen to form the end and the other wall stops inside of that wall. This code also handles multiple-wall intersections by resizing the left half with the right half of the wall on its left side, and the right half with the left half of the wall on its right side (forward being toward the poll).

If anyone runs across a similar issue at some point, here is the code. Sorry if the math variables are not well named, there so many related in various special ways that it became difficult to find good names for them.

local WALL_HEIGHT = 12
local WALL_OFFSET = Vector3.new(0, WALL_HEIGHT, 0)
local UP = Vector3.new(0, 1, 0)
local WALL_HALF_THICKNESS = 0.2

local abs = math.abs
local function absVector3(v3)
	return Vector3.new(
		abs(v3.X),
		abs(v3.Y),
		abs(v3.Z)
	)
end

local function setSize(wall, half, length)
	local delta = length - wall.length[half]
	local dir = wall.dir * delta
	local part = wall[half]
	local offset = part.CFrame:VectorToObjectSpace(dir)
	part.Size = part.Size + math.sign(delta) * absVector3(offset)
	part.CFrame = part.CFrame + dir / 2
	wall.length[half] = length
end

local function adjust(wall, other)
	local angle = math.acos(wall.rightDir:Dot(-other.rightDir))
	local wallLength, otherLength
	if math.abs(angle - math.pi/2) < 0.001 then
		if wall.dir:Dot(other.rightDir - wall.rightDir) > 0 then
			wallLength = WALL_HALF_THICKNESS
			otherLength = 0
		else
			wallLength = 0
			otherLength = -WALL_HALF_THICKNESS
		end
	elseif angle > math.pi/2 then
		local otherAngle = math.acos(wall.dir:Dot(other.dir))
		local sign = -math.sign(wall.rightDir:Dot(other.dir))
		wallLength = sign * WALL_HALF_THICKNESS / math.tan(otherAngle)
		otherLength = sign * math.sqrt(wallLength^2 + WALL_HALF_THICKNESS^2)
	else
		wallLength = math.tan(angle / 2) * WALL_HALF_THICKNESS
		otherLength = wallLength
	end
	setSize(wall, 'left', wallLength)
	setSize(other, 'right', otherLength)
end

local POLL_KEY = '%d %d %d'
local function getWalls(polls, pos)
	local key = POLL_KEY:format(pos.X, pos.Y, pos.Z)
	local walls = polls[key]
	if not walls then
		walls = {}
		polls[key] = walls
	end
	return walls
end

local function addToPoll(polls, poll, otherPoll, model)
	local walls = getWalls(polls, poll)
	local dir = (poll - otherPoll).Unit
	local rightDir = dir:Cross(UP)
	
	local right, left = model.Wall1, model.Wall2
	local toRight = right.Position - left.Position
	if toRight:Dot(rightDir) < 0 then
		right, left = left, right
	end

	local wall = {
		length = {
			left = 0;
			right = 0;
		};
		model = model;
		right = right;
		left = left;
		dir = dir;
		rightDir = rightDir;
	}
	
	if not next(walls) then
		walls[wall] = true
		return
	end

	local leftIsFound
	local leftDot
	local leftWall
	
	local rightIsFound
	local rightDot
	local rightWall
	for otherWall in next, walls do
		local dot = dir:Dot(otherWall.dir)
		local isRight = rightDir:Dot(otherWall.dir) < 0
		if isRight then
			if not rightIsFound or not rightDot or dot > rightDot then
				rightIsFound = true
				rightDot = dot
				rightWall = otherWall
			end
			if not leftIsFound and (not leftDot or dot < leftDot) then
				leftDot = dot
				leftWall = otherWall
			end
		else
			if not rightIsFound and (not rightDot or dot < rightDot) then
				rightDot = dot
				rightWall = otherWall
			end
			if not leftIsFound or not leftDot or dot > leftDot then
				leftIsFound = true
				leftDot = dot
				leftWall = otherWall
			end
		end
	end

	adjust(wall, leftWall)
	adjust(rightWall, wall)
	walls[wall] = true
end

local function placeWall(polls)
	local mouse = game.Players.LocalPlayer:GetMouse()

	mouse.Button1Down:Wait()
	local pollA = snapToGrid(mouse.Hit.p)

	mouse.Button1Down:Wait()
	local pollB = snapToGrid(mouse.Hit.p)

	local model = wallModel:Clone()
	model:SetPrimaryPartCFrame(CFrame.fromMatrix(
		(pollA + pollB) / 2 + WALL_OFFSET,
		(pollA - pollB).Unit,
		UP
	))

	addToPoll(polls, pollA, pollB, model)
	addToPoll(polls, pollB, pollA, model)
	model.Parent = workspace
end
10 Likes