How does roblox store the information about Unions/UnionOperations that allows it to completely reverse and separate it into the original parts?
I’m trying to recreate roblox studio inside of roblox studio, so I’m starting off with unions. The one function that doesn’t exist, is the separate function.
In the video, I use this exact code to union them together, same as how I’d do it in game.
local mainPart = workspace.PartA
local otherParts = { workspace.PartB, workspace.PartC }
-- Perform union operation
local success, newUnion = pcall(function()
return mainPart:UnionAsync(otherParts)
end)
-- If operation succeeds, position it at the same location and parent it to the workspace
if success and newUnion then
newUnion.Parent = Workspace
end
-- Destroy original parts which remain intact after operation
mainPart:Destroy()
for _, part in otherParts do
part:Destroy()
end
Yet, in studio, I can separate it manually, and even after using the script to union the parts, close the game, uninstall roblox studio, reinstall it, it still somehow somewhere saves the information the separate the union. How could I access this info to make my own separate function? Thanks!
A PartOperation is fully reversable because it internally stores the inputs (BasePart or PartOperation) that it was provided. This is essentially the idea behind Constructive Solid Geometry (CSG), an operation is the result of combining various primitives (or the results from previous combinations) to make more complex geometry.
So, a PartOperation is just the base class for the result after performing any “solid modeling” operation. Solid Modeling is just how the docs refer to union, subtract, intersect, or negate operations (although CSG is really the more accurate name for what’s occurring).
When you perform any of these operations, you provide it some inputs (BasePart or PartOperation), and in return you get a (subclass of a) PartOperation. There’s a decent analogy to math here so I’ll try and use that:
Let’s say I have two numbers:
local A = 6
local B = 4
I can add those two numbers together (A + B) and get 10 or I can subtract them (A - B) and get 2.
Addition and Subtraction are operations just like UnionAsync and SubtractAsync.
The key here is thinking of the result I get after doing these operations not as its raw value (10 or 2) but as the steps taken to arrive at that value. In otherwords if C = A + B, C doesn’t equal 10 - it might look like 10, but really C = 6 + 4. I could repeat this process and say D = C + B. D might look like 14, but really D = (6 + 4) + 4.
There might be a UnionOperation that looks how it does, but it’s still just the sum of its Parts.
Okay, I understood the math analogy! That also explains why when I separate a union, it remembers the colors of what the parts were. I also see the parenthesis defining the order of which the parts were unioned. Very well. Now, since you know seemingly everything about this, is it possible for me to access the formula and create my own separate function? Thanks!
The only way I’m aware of separating a PartOperation with code is with the plugin-security level method: plugin:Separate(). This is likely isolated to plugin use only because when PartOperation are used in a live game setting, it is likely that some condensed representation is sent to clients instead of the entire operation history as would be kept for studio’s purposes.
Custom union serialization would be possible under two conditions:
There are no unknown operations that the serialization system is unaware of - aka you have to start from only Parts and no PartOperations (additionally this system must always be made aware of any operations that take place)
If there are existing PartOperations, the system which serializes them must have plugin-security permission level in order to reverse the operations and retroactively construct the steps required
What do you mean by ‘the serialization system’? The script that serializes? I have two functions in a localscript that currently serialize, I just need to somehow add union operations. The localscript then sends the table to the server for it to save.
When a union is made, store its components. I would use a stack-like structure so that you can continually add on operations. The separate function would either pop one from the stack or pop the stack until it’s empty. You would then have to re-instance the union (if it wasn’t a base-level union) and the part(s) would be available from the popped stack
Since the functionality to separate unions is not provided, you will have to remember the parts that went into the union so that you can eventually separate them out. What I suggested doing is using a stack for that. I will explain further now.
A stack is a type of data structure, very much like an array. In an array, items correspond to indices, so you can access any the items via indexing “[index]”. A stack is different than an array in the aspect of accessing the items held in the stack. You usually read and remove the most recent item added in a stack. This is referred to as Last-In First-Out data structuring.
Lua has built in support for stacks using tables:
local stack = {}
for i=1,5 do
table.insert(stack, i) -- insert number i into stack
end
for i=1,#stack do -- pop all elements of the stack. "#stack" is only called at the start of the loop, not every iteration
local popped = table.remove(stack)
print(popped)
end
--[[
Output:
5
4
3
2
1
]]
I hope you can see how this might be useful for your case. What I meant by re-instancing is the actual creation of the union. Every time you modify the stack by pushing/popping, you will have to re-create the actual union the stack represents as it will not automatically render.