Voxel Painting on Floor? Splash Ink - Help

Hi! I’m creating a game called “Splash Ink” which is a concept resembling Splatoon on Nintendo, I made all the shooting system, movement and everything and even the ground painting system, however I use the change of color for each 1x1x1 block, which poses major performance problems on the client and server side when there are many (large maps). I would like to know if there is another alternative like canvas painting which places canvases on a grid on the ground (like a layer of paint) which would replace this system, a bit like in the game “Splat Attack” (here) on Roblox? If yes, some ideas?
Thanks!

1 Like

Hey! Bump?
I very need any help! :frowning:

Do you have collisions lag, or do you have replication lag? If replication lag, then you need a better way of telling the clients which parts of the map are colored. I assume that currently the way for changing the colors is just changing every part’s colors. It is bad as for every part it needs to send data like: which instance to update, which property, to which value. Which when repeated for many voxels can be bad for network. To optimize it we can use something called Buffer. It will allow for low level bit manipulation. When player shoots the floor, you need to create a buffer and send it to the clients. Clients then need to recolor the voxels based on the data that the server sent. But how do we create a buffer? And how we should manipulate it? Ok, let’s say that player shoos the floor, we can then get the minimum and maximum range of where the player shooted so if the player shooted on (12, 0, 20) then the minimum and maximum values if the distance was for example 4 would be someting like: (8, 0, 16) and (16, 0, 24). We can now write these values to the buffer using:

buffer.writei16([created buffer], 0, min.X)
buffer.writei16([created buffer], 2, min.Y)
buffer.writei16([created buffer], 4, min.Z)
buffer.writei16([created buffer], 6, max.X)
buffer.writei16([created buffer], 8, max.Y)
buffer.writei16([created buffer], 10, max.Z)

Ok i just found out that this reply is kinda long and would probably need to be even longer, so i would just suggest you learning buffers, and if you have any questions just reply.

1 Like

I didn’t know the existence of buffers in roblox?
But as I said it’s replication lag on server and client side.
When a map gets loaded, my codes generate thousand and thousand blocks of 1x1x1 which very lagy and not possible. So I was wondering if it’s possible to do something that do nor create anything just spawn the map and then when the player shoots, it create parts of 1x0.1x0 on top of the shooted surface, I tried but when it comes to compact them with a run-length compaction system to compact parts that are the same color, I can’t. Even when the player re-shots on the painted surface to delete them and recreate -_- OMG
I really need help for that type of thing. Sorry for this not understandable paragraph…

Ok so how do you currently store the data? Do you have like a array or dictionary that says what color has a voxel on the given position and are your voxel placed in a grid or can they be rotated and misaligned?
Example of how the data may look like:

{ [Position]={ [Up]=Part, [Down]=Part, [Left]=Part, [Right]=Part, [Back]=Part, [Front]=Part } }

They are just simple parts aligned on a grid one 1x1x1, my deprecated system use a main part, like 50x50x50 and divided it so it can be filled by 1x1x1 tiny parts and then an other system use to paint them individually.

This module decompose a big part in tiny parts : – this is the problem that cause lags, i want to change it

return function(v1: BasePart, o1, o2, o3, o4)
	if not v1 or (type(v1) ~= "userdata") then
		return;
	end
	o1 = tostring(o1);
	o2 = tostring(o2);
	if o1 and not v1:GetAttribute(o1) then
		return;
	end
	local u1 = {v1.Color, v1.Material, v1.CanCollide, v1.Anchored, v1.Parent};
	local v2 = v1.Size;
	local v3 = v1.Position;
	local v4 = Vector3.new(1, .1, 1);
	local v5 = math.ceil(v2.X / v4.X);
	local v6 = math.ceil(v2.Y / v4.Y);
	local v7 = math.ceil(v2.Z / v4.Z);
	local function f1(p1)
		local v8 = o4 and o4:Clone() or Instance.new("Part")
		v8.CanQuery = false;
		v8.Name = game.HttpService:GenerateGUID(false);
		v8.Size = v4;
		v8.Position = p1;
		v8.Transparency=1;
		v8.Color = u1[1];
		v8.Material = u1[2];
		v8.CanCollide = u1[3];
		v8.Anchored = u1[4];
		v8.Parent = v1;
		if o2 then
			v8:SetAttribute(o2, true);
		end
	end
	for x = 0, v5 - 1 do
		for y = 0, v6 - 1 do
			for z = 0, v7 - 1 do
				local v9 = Vector3.new(
					v3.X - v2.X / 2 + v4.X / 2 + x * v4.X,
					v3.Y - v2.Y / 2 + v4.Y / 2 + y * v4.Y,
					v3.Z - v2.Z / 2 + v4.Z / 2 + z * v4.Z
				);
				f1(v9);
			end
		end
	end
	if not o3 then
		v1:Destroy();
	else
		v1.Transparency = 1;
		v1.CanCollide = false;
		v1.CanTouch = false;
		v1.CanQuery = false;
		v1.Position = v1.Position + Vector3.new(0,0.005,0);
	end
end

This script (when a remote is called with security verify such as bullet registering) paint the hit floor :

--[[
  p = player;
  v1 = part from the loop;
]]


local splashRadius = 5
local maxSplashProbability = 0.9
local splashColor = Color3.new(1, 0, 0)
local particleSize = Vector3.new(1, 1, 1)
-- not shown variables ^

local offset = Vector3.new(x, y, z)
local particlePosition = position + offset
local distance = offset.Magnitude

if distance <= splashRadius then
	local distanceFactor = (splashRadius - distance) / splashRadius
	local splashProbability = maxSplashProbability * distanceFactor

	if math.random() < splashProbability then
		v1.BrickColor = p.TeamColor;
	end
end

Sorry for my scripts being not readable.

The second script I did recreate it because I delete him, so it may not work.
But the first one do.

So the problem isn’t replicating color, but generating the 1x1x1 parts?

Yes, I ask someone if there’s an alternative and he says that one of them was canvas painting on floor with run-length compacting system? How?

Use surface GUIs and frames. Im fairly sure that’s what Splat Attack is using.

Ok so there are multiple ways:
-SurfaceGuis:
You could create SurfaceGuis for every part on the map on every surface, then you could place inside it multiple Frames that would act as pixels. But too many SurfaceGuis is bad for performance.

-EditableImage:
You could create EditableImage for every part of the map on every surface just like SurfaceGuis. It is probably best for performance but it is currently in Beta and cannot be used in games, only for testing.

-Parts:
You generate flat parts and put them on every part on every surface.

So currently the best way is using Parts. Here is how you can generate them:

local function GenrateParts(MainPart, Axis)
   local InvertAxis = Vector3.new(1 - math.abs(Axis.X), 1 - math.abs(Axis.Y), 1 - math.abs(Axis.Z))
   local S = MainPart.Size / 2
   for x = -S.X, S.X do
      local X = x * InvertAxis.X
      for y = -S.Y, S.Y do
         local Y = y * InvertAxis.Y
         for z = -S.Z, S.Z do
            local Z = z * InvertAxis.Z
            local FinalPosition = Vector3.new(X, Y, Z) + Axis * S
            -- Make a script here that will create the part and will set it'd position to FinalPosition variable.
         end
      end
   end
end

You can use the FinalPosition variable to place the parts.

Thanks for you help! I manage to create them using your code (a little bit modified).

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.