How to prevent Z-Fighting when filling instances?

I’m fixing a !fill command for my game, where it clones instances from point A to point B like a square, like this:
vibeo
However, there seems to be z-fighting if your size is not a exact cube, e.g. 4x4x4, 2x2x2, 1x1x1, etc.

How could I fix this?

	fill = {
		RankLevel = 1,
		Description = "Fills a area with blocks of the users choice [250 non VIP, 500 with VIP]",
		Creator = "gravitycoil828",
		OtherPeople = 0,
		ParseTargetAsInfo = true,
		ShowOnList = true,
		AllowEZ = true,
		EZType = "Singular",
		ExName = "fill",
		Alias = {"fill"},
		Function = function(player, target)
			local guid = game:GetService("HttpService"):GenerateGUID(false)
			local combinedText
			local event = game.ReplicatedStorage.Files.Events.NonNative.GetPos
			local function fillprocess()
				local ezactive = false
				if game.Players[player.Name].PlayerGui.UserGUIHandler.Windows.BuildUI.Properties.DoNotApply.EZ.Value == true then
					ezactive = true
				end
				local block = nil
				local block2 = nil
				local con1 = event.OnServerEvent:Connect(function(plra, response)
					if plra == player then
						if block == nil then
							block = response
							local params = {
								buttonCount = 1,
								Image = 0,
								CloseAllowed = false,
								Time = 0,
								messageText = "Click the second point in the radius to be filled.",
								button1Text = "Okay",
								targetPlayerName = player.Name
							}	 
							messageapi.Question(params)
							event:FireClient(player)
						else
							block2 = response
						end
					end
				end)
				local params = {
					buttonCount = 1,
					Image = 0,
					CloseAllowed = false,
					Time = 0,
					messageText = "Click the first point in the radius to be filled.",
					button1Text = "Okay",
					targetPlayerName = player.Name
				}	 
				messageapi.Question(params)
				event:FireClient(player)
				repeat
					task.wait()
				until block and block2
				local blockdata = nil
				local event2 = game.ReplicatedStorage.Files.Events.NonNative.GetCurrentBlockData
				event2.OnServerEvent:Connect(function(plr, content)
					if plr == player then
						blockdata = content
					end
				end)
				event2:FireClient(player)
				repeat
					task.wait()
				until blockdata
				local tables = blockdata
				local function getgridsize()
					if tables.Size == Vector3.new(4,4,4) then
						return 4
					elseif tables.Size == Vector3.new(2,2,2) then
						return 2
					elseif tables.Size == Vector3.new(1,1,1) then
						return 1
					else
						local numberString = tables.Size.X .. " " .. tables.Size.Y .. " " .. tables.Size.Z
						local counts = {}
						for num in numberString:gmatch("%d+") do
							num = tonumber(num)
							counts[num] = (counts[num] or 0) + 1
						end
						local leastCommonNumber = nil
						local leastFrequency = math.huge
						for num, count in pairs(counts) do
							if count < leastFrequency then
								leastFrequency = count
								leastCommonNumber = num
							end
						end
						return leastCommonNumber
					end
				end
				if game.Players[player.Name].PlayerGui.UserGUIHandler.Windows.BuildUI.Properties.DoNotApply.EZ.Value == false 
					and ezactive == true then
					return
				end
				local gridsize = getgridsize()
				local differenceX = math.abs(block2.X - block.X)
				local differenceY = math.abs(block2.Y - block.Y)
				local differenceZ = math.abs(block2.Z - block.Z)
				local startX = math.min(block.X, block2.X)
				local startY = math.min(block.Y, block2.Y)
				local startZ = math.min(block.Z, block2.Z)
				local filllimit = 250
				local userprof =  require(script.Parent.Parent.NonNative.GetStoreType)
					.GetDataStore("GZ8R1K7X5L0V2D4T3Y9J")
					:GetAsync("N3J7F8M2W1L5Z0R4T6P9-" .. player.UserId)
				if userprof.VIP == true then
					filllimit = 500
				end
				local totalfilled = 0
				for x = startX, startX + differenceX, gridsize do
					for y = startY, startY + differenceY, gridsize do
						for z = startZ, startZ + differenceZ, gridsize do
							if totalfilled >= filllimit then
								break
							end
							local transparency = tables.Transparency
							local reflectance = tables.Reflectance
							local cancollide = tables.Collision
							local lightRadius = tables.LightRadius
							local lightBrightness = tables.LightBrightness
							local lightColor = tables.LightColor
							local shape = tables.Shape
							local color = tables.Color
							local material = tables.Material
							local orientation = tables.Position
							local size = tables.Size
							local clonedblock = game.ReplicatedStorage.Files.BlockTypes:FindFirstChild(shape):Clone()
							local shouldapply = tables.CountToBits
							clonedblock.Parent = game.Workspace.Blocks
							clonedblock.Position = Vector3.new(x,y,z)
							clonedblock.Orientation = orientation
							clonedblock.Transparency = transparency
							clonedblock.Reflectance = reflectance
							clonedblock.Material = Enum.Material[material]
							clonedblock.Color = color
							clonedblock.CanCollide = cancollide
							if shape == "Cube" or shape == "Wheel" then
								clonedblock.Size = Vector3.new(size.X - 0.1, size.Y - 0.1, size.Z - 0.1)
								if shape == "Cube" then
									local gyro = game.ReplicatedStorage.Files.ExampleGyro:Clone()
									gyro.Parent = clonedblock
									gyro.Name = "CubeGyro"
									gyro.CFrame = clonedblock.CFrame
								end
							else
								clonedblock.Size = size
							end
							if lightBrightness ~= 0 and lightRadius ~= 0 then
								local light = Instance.new("PointLight")
								light.Parent = clonedblock
								light.Color = lightColor
								light.Range = lightRadius
								light.Brightness = lightBrightness
							end
							if clonedblock.Anchored == false then
								local a = Instance.new("Vector3Value")
								a.Name = "SpawnArea"
								a.Parent = clonedblock
								a.Value = clonedblock.Position
							end
							totalfilled = totalfilled + 1
							for _, plr in pairs(game.Players:GetPlayers()) do
								plr.PlayerGui.UserGUIHandler.Windows.MoreWindows.WorldMenu.AboutPage.BlockCount.Text = #game.Workspace.Blocks:GetChildren() - 1 .. " blocks"
							end
						end
					end
				end
				if game.Players[player.Name].PlayerGui.UserGUIHandler.Windows.BuildUI.Properties.DoNotApply.EZ.Value == true
					and game.Players[player.Name].PlayerGui.UserGUIHandler.Windows.BuildUI.Properties.DoNotApply.EZSingular.Value == true then
					fillprocess()
				end
			end
			fillprocess()
		end
	},
2 Likes

You can’t really fix Z-Fighting.
If you can’t prevent overlapping in the first place, you can slightly offset your part in the Z-axis relative to the other to prevent it.

2 Likes

You can simply check beforehand if there’s a path already in the area you want to fill, then add a minuscule offset of around 0.001 which would be enough to prevent Z-Fighting and small enough to not be noticeable at all.

1 Like

The issue is, it shouldn’t be colliding at all.
Think of it like a tile system. You wouldn’t want it to be like this:


You want it to be like this:

How would it not be colliding? Does your code check if there’s already a part there?

Your current code is quite hard to understand. I think it’s best if you explain a bit in detail on how your code actually functions and what exactly you’re trying to accomplish. In the post you asked a question on fixing Z-Fighting, now you’re changing it by saying you want to tile it?

No, the tile’s an example,
Like,


Here is an example of a “SlabY” block from my game. The size of it is 4,2,4 [Vector3].
This is how it is meant to fill:

Blocks should not collide with eachother, unlike this image:

which is how it is currently filling.

I know where the problem might stem from in my code.

local function getgridsize()
					if tables.Size == Vector3.new(4,4,4) then
						return 4
					elseif tables.Size == Vector3.new(2,2,2) then
						return 2
					elseif tables.Size == Vector3.new(1,1,1) then
						return 1
					else
						local numberString = tables.Size.X .. " " .. tables.Size.Y .. " " .. tables.Size.Z
						local counts = {}
						for num in numberString:gmatch("%d+") do
							num = tonumber(num)
							counts[num] = (counts[num] or 0) + 1
						end
						local leastCommonNumber = nil
						local leastFrequency = math.huge
						for num, count in pairs(counts) do
							if count < leastFrequency then
								leastFrequency = count
								leastCommonNumber = num
							end
						end
						return leastCommonNumber
					end
				end
				if game.Players[player.Name].PlayerGui.UserGUIHandler.Windows.BuildUI.Properties.DoNotApply.EZ.Value == false 
					and ezactive == true then
					return
				end
				local gridsize = getgridsize()
				local differenceX = math.abs(block2.X - block.X)
				local differenceY = math.abs(block2.Y - block.Y)
				local differenceZ = math.abs(block2.Z - block.Z)
				local startX = math.min(block.X, block2.X)
				local startY = math.min(block.Y, block2.Y)
				local startZ = math.min(block.Z, block2.Z)

The grid size.
It’s detecting too little of a difference and I don’t know how to fix it.

1 Like

Ah, that makes a lot more sense.

Then why not just set the blocks’ horizontal size to 1 and only modifying it’s vertical height?

This is because the size must stay.
The user can pick several different sizes, materials, etc…
( ✦ ) [:hammer_and_wrench: GRID FIXED!!!] Rebuild [BETA :tada:] - Roblox

1 Like

Okay.

I made a small adjustment here:

for x = startX, startX + math.round(differenceX/gridsize)*gridsize, gridsize do
	for y = startY, startY + math.round(differenceY/gridsize)*gridsize, gridsize do
		for z = startZ, startZ + math.round(differenceZ/gridsize)*gridsize, gridsize do

I’m sure you can figure out where this goes. It basically rounds the range of the loop to the grid size.

I’d also recommend printing the given values like startX, difference, gridsize, etc.

1 Like

You should offset your CFrame by half the block size:

Hi, sadly didn’t work.
However, by using print, I did find out something.
On certain blocks it determines that the grid size is “2” when it is meant to be “4”, if I had a horizontal 4x2x4 block, however, in cases where I have the block rotated to be vertical, it will still break.
Maybe some way to determine the grid size via the rotation?

Interesting results. Perhaps just determine it by the size?
Something like:

local gridSize = (block.Size.X + block.Size.Z) / 2