Assigning custom classes to instance properties

I have no idea how to phrase the question in the title so I’ll do my best to explain the predicament I’m in here.

So if I’m making a custom object wrapper, like a Color3 wrapper that allows math operations on Color3, is there a way to make it so when you assign a Color3 property to an instance, it doesn’t error?

So for example, let’s say I have this:

local color3 = {}

__add = function(self,otherValue) -- let's say for simplicity we know for sure otherValue is another custom Color3 object, one returned from this module
    return color3.new(self.R + otherValue.R, self.G + otherValue.G, self.B + otherValue.B) -- note this returns another custom color3, not a Roblox one
end

function color3.new(r,g,b)
    local actualColor3 = Color3.new(r,g,b)
    local newColor3 = {}
    newColor3.R = actualColor3.R
    newColor3.G = actualColor3.G
    newColor3.B = actualColor3.B
    return setmetatable(newColor3, color3) -- okay, this will return the custom Color3 which supports adding two Color3's
end

Now, if we require the module, it’ll work as normal:

local color3 = require(path.to.module)
local newColor3 = color3.new(1,1,1) -- ok
-- however, if we attempt to assign the Color3 to a part's Color property for example,
part.Color = newColor3 -- this will error because I'm trying to assign a non-native Color3 to an actual Color3 value

So, my question is, is there a way to seamlessly implement this Color3 wrapper that allows the custom Color3 object to be assigned to the property without having to retrieve the actual Roblox Color3 from this new object’s properties?

I had a bunch of problems with this, my solution is using the unary operator.

So I have been working with proxies for around 2 years straight now, and I have come to this issue multiple times, if you set a __unr operator to return the color3 value then you can do something like this:

local color3 = require(path.to.module)
local newColor3 = color3.new(1,1,1) -- ok
-- however, if we attempt to assign the Color3 to a part's Color property for example,
part.Color = -newColor3 -- We use the unary operator, it will return the color3 property, so any time you need the color3 just use the unr
1 Like

You can add a metamethod like this:

local color3 = {}

__unm = function(self)
    return Color3.new(self.R, self.G, self.B)
end

__add = function(self,otherValue) -- let's say for simplicity we know for sure otherValue is another custom Color3 object, one returned from this module
    return color3.new(self.R + otherValue.R, self.G + otherValue.G, self.B + otherValue.B) -- note this returns another custom color3, not a Roblox one
end

function color3.new(r,g,b)
    local actualColor3 = Color3.new(r,g,b)
    local newColor3 = {}
    newColor3.R = actualColor3.R
    newColor3.G = actualColor3.G
    newColor3.B = actualColor3.B
    return setmetatable(newColor3, color3) -- okay, this will return the custom Color3 which supports adding two Color3's
end

print(color3.new()) --Returns the table
print(-color3.new()) --Returns the color3

I see, this seems to be the most simple way, and chances are this is probably the best way with least modifications but what if I wanted to support the actual __unm metamethod?

Omg yes, I am so sorry I always confuse myself. It is __unm not __unr

1 Like

So you want to also have that for other things right? So using unr is not an option

You can also use __call, and do something like this:

local myColor = color3.new()
myColor()

Actually, I just realized doing - on a Color3 would be useless since it would default to Color3.new(0,0,0) which is black, can’t get darker than that, so I wouldn’t really need to implement the actual __unm operator, it is a hacky solution but I suppose it would suffice for now. I’m gonna check and see if any metamethods are called before the error is thrown, to which I’ll post an update but if none are called (and I doubt any are), I’ll mark your post as a solution.

If there’s an actual implementation though, anyone’s free to reply.

You can also use len, you don’t need the length of a color3:

#color3.new()

And for your question maybe you could use the unr operator for inverting colors, which would be a cool function to have in the color3. One that you definitely not need is length so you can use that too

1 Like