How can I restore a destroyed region of terrain correctly?

  1. What do you want to achieve? Keep it simple and clear!
    I’m trying to make a terrain destruction system but i want the destroyed regions to come back to normal after a couple of seconds
  2. What is the issue? Include screenshots / videos if possible!
    I could do the destroying part easily using :FillBall() but restoring the destroying part is the problem. I’ve tried to save voxels data of the region before being destroyed using :ReadVoxels() then use :WriteVoxels(), But the problem comes when i destroy the same region more than once.

In the 2nd attempt I clicked twice, so it bugs and does not restore the original terrain

game.ReplicatedStorage.RemoteEvent.OnClientEvent:Connect(function(Hit)
	local Character = script.Parent.Parent
	
	local Part = script.Parent.Part:Clone()
	Part.Parent = workspace
	Part.Position = Hit.Position
	local Region = PartToRegion3(Part)

	local Material,Occupancy = Terrain:ReadVoxels(Region,4)

	Terrain:FillBall(Part.Position,10,Enum.Material.Air)
		
   
   task.wait(1)

   Terrain:WriteVoxels(Region,4,Material,Occupancy)
end)

Is not there a method to like ignore air while using :WriteVoxels() ?

Any help is highly appreciated.

4 Likes

Oh wait nvm I see

Rather than always saving the terrain when you destroy it, only save it when you don’t already have a save. This way, you wouldn’t accidentally save in the air created from the first destruction.

That, or literally just save the entire terrain into a table and then get the part you need and fill it

2 Likes

In the 2nd attempt I clicked twice, so it bugs and does not restore the original terrain

2 Likes

ok updated I think I understand now

2 Likes

How would i get that part of the terrain?

2 Likes

Ok wait I just realized that first method wont work and the 2nd is more complicated then I thought

1 Like

I tried to copy the whole terrain and throw it away and make that clone a template to read voxels from but i could not get the right position from which i should read

2 Likes

Yea I just realized you’d need some sort of chunk based system or to just literally clone the terrain everytime you want to restore it in this way

lemme think rq

2 Likes

Ok wait, would you be fine if your system was changed a bit? It would be way simpler if you simply ordered the repairing of the terrain so that the latest destruction in a row would be repaired first.

This might not work for what you want, but I feel like I should mention it in case it would.

(The issue with this is that if you were, say, mining, the tunnel would have to either keep existing until you stop mining or you’d get pushed out)

2 Likes

I’ve tried that but didn’t work well, also there are more than one player and this method wont likely work cuz the terrain will be destroyed alot

2 Likes

Otherwise, I’ll try to explain how the 2nd method might be able to work.

At the start, you want to save your entire map as chunks into a 3 dimensional array. Go chunk by chunk across the entire map saving the terrain into the array with x, y, and z as the 3 layers of the array. Afterwards, whenever you create destruction, you want to find all chunks which the destruction intersects with and restore them by using their center xyz coordinates to access them from the original array.

To find the chunks which you intersect, you could potentially find 6 points on the surface of the sphere of the destroyed terrain which are each exactly 90 degrees away from each other. Using these 6 points, you can form a cubic region which should then intersect with all terrain that your ball intersects with. Use rounding and the 8 corners of the cube to then find the chunks which you want.

3 Likes

hmmm this could really work, but the problem is…
I REALLY don’t know how to do allat :sob:

Any tutorial bout chunks and these stuff ?

1 Like

Saving chunks should be easy enough, just go through the world N studs at a time, stopping at (0+k1N, 0+k2N, 0+k3N) for k1, k2, k3 being integers and saving them into the array at location [k1][k2][k3]. (Might sound a bit complicated but this part really isn’t, I’m just kinda sleepy and can’t think of a better way to explain it lol)

As for getting the chunks which intersect with the thing that you cut up, heres a diagram:
Screen Shot 2024-08-13 at 2.19.28 AM
Each purple square (or cube, since its supposed to be 3D) is a chunk.
The red circle marks where you’ve cut,
With the 5 (techinically 6, but since its a topdown view, the middle one represents 2) blue dots representing the points you’re supposed to find.
The black arrows are the rounding you have to do to the points you found (depending on which side of the center point its at, you have to either round it up or down so that it would become kN for k being a integer)
After doing the rounding, you would end up with the yellow square (cube), which is a box made up of lines that pass through the most outer layer of the group of chunks you want. Using the value of N and the location of the vertices of the cube, you can then find all the other chunks.

(So for example, our yellow square {ill use square here since its simpler} has vertices 0,0 0,5 5,5 5,0 and N = 1. We can then get that all the coordinates between 0,0 and 0,5 are the ones we want as long as they follow the rule that it is 0,0+kN for any integer k where kN<=5. We then also know that all of the columns between 0,0 and 5,0 are ones we want, where we can get all chunks in each row by doing the previous operation we did with 0,0 and 0,5, and then by incrementing the X value by 1 until it also reaches the ending point of 5. We thus get that the chunks we want are the following 2D array:

[[0, 0], [0, 1] … [0, 5]
[1, 0], [1, 1] … [1, 5]

[5, 0]. [5, 1] … [5, 5]]

We can then get all the chunk data by getting the reference we want from the original array.

As we only used the k value and not the kN value in the orignal array, we have to divide each X and Y (and Z, since your code should be 3D) value by N. So for example, to get the stored information for the chunk at [0, 0], we should reference Chunks[0/N][0/N], which gets us Chunks[0][0])

I believe I did a pretty bad job at explaining this, but hopefully its just well enough that you could understand. If not, it might be worth the time to search up some sort of tutorial about storing chunks of the world into arrays.

Theres also a hacky way of finding the intersection:

Place an invisible, cancollide false part on each chunk, and make sure that it is the exact size of that chunk (maybe slightly smaller in case of errors). Then, when you remove terrain, create a sphere the exact size of the removed terrain, and call getPartsInPart on that sphere to get all of the parts which intersect the sphere. This would give you all the parts which correspond to the chunks that your destruction intersects, and you can then simply use the X, Y, and Z value of the parts to get the saved information from the list.

Do note that this method may be laggy if you have larger maps.

1 Like

Ty man, i will search deeper bout chunks and storing them as arrays since i’ve js learned bout dealin with terrain yesterday lol

2 Likes

Ok wait, new idea (just writing it here in case you’re more familiar with something like this)

Store all the terrain data you need in a table until any specific terrain the restored. When restoring terrain, check if area restored overlaps another area that will be restored later. If so, do not remove that terrain, and instead somehow remember its relationship with the other terrain. When the other terrain is restored, restore this terrain again.

Or alternatively, in order to prevent old terrain from accidentally restoring terrain too soon, store all terrain data you will restore into a table. When restoring terrain, check if area restored overlaps with another area that will be restored later. If so, remove the terrain data but keep some sort of reference to remember that this location is still needed. Right before the other terrain is restored, save the current state of this terrain and restore it instantly after the other terrain is restored.

Actually, best solution that may not be easily doable:

Before you restore terrain, ReadVoxel there again to get the current status of that location which you will restore soon. Then, combine the 2 terrain data so that you keep the non-air location of both (which should be stored as non-0 values according to the documentations. You may have to experiment with specifically how terrain data is stored). Then, restore with the final, joint variation of that part of the terrain so that you wont accidentally wipe away any non-air things.

1 Like

I see the vision, but how to combine 2 terrain data ?

sorry for my non-stop question

1 Like

I’m not sure, and I can’t really test right now. According to the documentation, the ReadVoxels function returns 2 3D tuples. You could likely construct a new 3D array by combining the occupancies tuples and or-ing each value so that as long as it is occupied in one of the 2 arrays, it will be occupied in the final one.

Combining the material array would likely be a bit more complicated, but considering the material shouldn’t be different in any situation, you could probably just construct a new 3D array with the first material tuple, and then fill in the blanks with the 2nd one.

3 Likes

TY TY your idea worked very well

local function CombineMaterial(oldMaterialData,newMaterialData)
	local Size = newMaterialData.Size
	for x = 1,Size.X do
		for y = 1,Size.Y do
			for z = 1,Size.Z do
				if newMaterialData[x][y][z] ~= Enum.Material.Air then
				oldMaterialData[x][y][z] = newMaterialData[x][y][z]					
               end
			end
		end
	end 
end

local function CombineOccupancy(oldOccupancyData,newOccupancyData)
	local Size = newOccupancyData.Size
	for x = 1,Size.X do
		for y = 1,Size.Y do
			for z = 1,Size.Z do
				if  newOccupancyData[x][y][z] > 0 then
				local n = newOccupancyData[x][y][z] + oldOccupancyData[x][y][z]
				oldOccupancyData[x][y][z] = newOccupancyData[x][y][z]		
				end			
			end
		end
	end 
end

I do appreciate your help dude. :heart:

2 Likes

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