Terrain generator offset bug

I’ve been attempting to debug this for 2 hours now. This is the whole script. When ran when the player walks around terrain will generate around the player. The red cell indicates what the script reads as the center as where the player is standing. However the farther the player gets from the origin the more accurate the generator gets moving the terrain away from the player.

I assume I’ve made some mathematical error that I have not been able to notice.

local Players = game:GetService("Players")
local Player = Players.LocalPlayer

local function GetCharacter()
	local Character = Player.Character
	if not Character or not Character.Parent then
		Character = Player.CharacterAdded:wait()
	end
	return Character
end

local function RoundUp(Number, Rounder, State)
	local Remainder = Number % Rounder
	local RoundDown = Number - Remainder
	if State == true then
		return RoundDown + Number
	else
		return RoundDown
	end
end

local CellSize = 8
local MapSize = CellSize * 64
local DetailLevel = {
	{Cells = 9, Size = 8},
	{Cells = 10, Size = 18},
	{Cells = 8, Size = 45},
	{Cells = 6, Size = 90},
	{Cells = 6, Size = 135},
}
local Seed = 0.1245355356436
local Influence = 100
local Roughness = 10

local function GetCellV2(Position)
	local ChunkX = math.ceil(RoundUp(Position.X, CellSize, true) / CellSize)
	local ChunkZ = math.ceil(RoundUp(Position.Z, CellSize, true) / CellSize)
	return Vector2.new(ChunkX, ChunkZ)
end

local function CellDistance(CellV2, PlayerV2)
	return PlayerV2 - CellV2
end

local function CellCorners(CellV2, Size)
	local ActualPos = CellV2 * Size
	local Size = Size / 2
	return (ActualPos + Vector2.new(-Size, Size)), (ActualPos + Vector2.new(Size, Size)), (ActualPos + Vector2.new(-Size, -Size)), (ActualPos + Vector2.new(Size, -Size))
end

function DrawTriangle(a, b, c) --Credit to EgoMoose for this function
	local edges = {
		{longest = (c - b), other = (a - b), position = b};
		{longest = (a - c), other = (b - c), position = c};
		{longest = (b - a), other = (c - a), position = a};
	};
	table.sort(edges, function(a, b) return a.longest.magnitude > b.longest.magnitude end);
	local edge = edges[1];
	-- get angle between two vectors
	local theta = math.acos(edge.longest.unit:Dot(edge.other.unit)); -- angle between two vectors
	-- SOHCAHTOA
	local s1 = Vector2.new(edge.other.magnitude * math.cos(theta), edge.other.magnitude * math.sin(theta));
	local s2 = Vector2.new(edge.longest.magnitude - s1.x, s1.y);
	-- positions
	local p1 = edge.position + edge.other * 0.5; -- wedge1's position
	local p2 = edge.position + edge.longest + (edge.other - edge.longest) * 0.5; -- wedge2's position
	-- rotation matrix facing directions
	local right = edge.longest:Cross(edge.other).unit;
	local up = right:Cross(edge.longest).unit;
	local back = edge.longest.unit;
	-- put together the cframes
	local cf1 = CFrame.new( -- wedge1 cframe
		p1.x, p1.y, p1.z,
		-right.x, up.x, back.x,
		-right.y, up.y, back.y,
		-right.z, up.z, back.z
	);
	local cf2 = CFrame.new( -- wedge2 cframe
		p2.x, p2.y, p2.z,
		right.x, up.x, -back.x,
		right.y, up.y, -back.y,
		right.z, up.z, -back.z
	);
	-- put it all together by creating the wedges
	local wedge = Instance.new("WedgePart");
	wedge.Anchored = true;
	wedge.TopSurface = Enum.SurfaceType.Smooth;
	wedge.BottomSurface = Enum.SurfaceType.Smooth;
	local w1 = wedge:Clone();
	local w2 = wedge:Clone();
	w1.Size = Vector3.new(0.2, s1.y, s1.x);
	w2.Size = Vector3.new(0.2, s2.y, s2.x);
	w1.CFrame = cf1;
	w2.CFrame = cf2;
	--PaintTerrain(ChunkSize, w1, w2, a, b, c)
	return w1, w2
end;

local Terrain = workspace:WaitForChild("TerrainFolder")
local LastPosition = Vector3.new(0, 0, 0)
while wait() do
	if Player.Character and Player.Character.Parent then
		local Character = GetCharacter()
		if (Character.Head.Position - LastPosition).magnitude > 25 then
			LastPosition = Character.Head.Position
			local CellV2 = GetCellV2(LastPosition)
			Terrain:ClearAllChildren()
			local NewSeed = Seed * Roughness
			for _,LevelTable in pairs(DetailLevel) do
				if LevelTable.Cells % 2 == 1 then
					for X = CellV2.X - (LevelTable.Cells - 1) / 2, CellV2.X + (LevelTable.Cells - 1) / 2 do
						for Y = CellV2.Y - (LevelTable.Cells - 1) / 2, CellV2.Y + (LevelTable.Cells - 1) / 2 do
							local TL, TR, BL, BR = CellCorners(Vector2.new(X, Y), LevelTable.Size)
							local TLV3 = Vector3.new(TL.X, math.noise(NewSeed / MapSize * TL.X, NewSeed, NewSeed / MapSize * TL.Y) * Influence + 20, TL.Y)
							local TRV3 = Vector3.new(TR.X, math.noise(NewSeed / MapSize * TR.X, NewSeed, NewSeed / MapSize * TR.Y) * Influence + 20, TR.Y)
							local BLV3 = Vector3.new(BL.X, math.noise(NewSeed / MapSize * BL.X, NewSeed, NewSeed / MapSize * BL.Y) * Influence + 20, BL.Y)
							local BRV3 = Vector3.new(BR.X, math.noise(NewSeed / MapSize * BR.X, NewSeed, NewSeed / MapSize * BR.Y) * Influence + 20, BR.Y)
							local W1, W2 = DrawTriangle(TLV3, TRV3, BLV3)
							local W3, W4 = DrawTriangle(TRV3, BRV3, BLV3)
							W1.Parent = Terrain
							W2.Parent = Terrain
							W3.Parent = Terrain
							W4.Parent = Terrain
							if Vector2.new(X, Y) == CellV2 then
								W1.BrickColor = BrickColor.new("Bright red")
								W2.BrickColor = BrickColor.new("Bright red")
								W3.BrickColor = BrickColor.new("Bright red")
								W4.BrickColor = BrickColor.new("Bright red")
							end
						end
					end
				end
			end
		end
	end
end

If the issue is to do with the distance from the origin, it’s probably a floating point error. It’s an issue with the actual number data type itself, and not anything on your end.

I’m unsure about your wording in this, though. Could you explain it?

I have the game divided into 8x8 cells for triangle terrain. I don’t think this has to do with the floating point error but a mathematical error I’ve made as if I walk a single cell (8 studs) away from the origin the terrain shifts 16 studs away and this amount grows the farther you walk.

After a bit of rigorous testing I was able to identify the flaw in my RoundUp function. Pulled this function off the DevForums.

local function RoundUp(Number, Divider)
	Divider = Divider or 1
	return (math.ceil((Number/Divider)+0.5)*Divider)
end
1 Like