POLL Gameobject scale property API

  • Go for it! Align gameobject scaling API to use vector3 as default
  • Implement special solution for game object scaling

0 voters

Hello Defold users!

The past few days we in the engine team have been looking at how we handle scaling across the different components in Defold. We want the API to be uniform and easy to use whether you are setting the scale of a game object at init or animating the scale of a sprite in runtime.

The initial task that prompted this discussion was to enable go.animate on elements in the gameobject scale vector, e.g. do:

go.animate(".", "scale.x", go.PLAYBACK_ONCE_FORWARD, 1, go.EASING_LINEAR, 1, 0)

Overview

Currently the API to set scale on a game object looks like this:

go.set(“.”, “scale”, 2.0) FLOAT
go.set_scale(2.0) FLOAT
go.set_scale(vmath.vector3(1.0, 2.0, 3.0)) VECTOR3

go.get(“.”, “scale”) FLOAT
go.get_scale() FLOAT
go.get_scale_vector() VECTOR3

In the beginning it was only possible to do uniform scaling of game objects. When the need arose to do non-uniform scaling go.set_scale was added that takes both float and vector3. The value can be retrieved with go.get_scale() (float) or go.get_scale_vector() (vector3).

To fulfil backward compatibility go.set and go.get still return floats instead of the scaling vector3.

Taking a step back and looking at the API; We now have a go property "scale" that will return a float when used with go set/get, but is really a vector3 in every other sense. Adding the possibility to animate for example “scale.x” means that we have a property that is a float in some cases, but has elements that you can animate!

Solution

After some discussion within the team it really makes sense to straighten out the API now rather than create more special properties and pollute the API with more ways to handle scaling. We would like the handling of scale for gameobjects to align with how scale is handled for example in sprite- and label-components. So rather than introduce special solutions to this problem, we want to:

  • Flip the current API, default to vector3 instead of uniform scale factor
  • go.set will take both float and vector3
  • go.get will return vector3 instead of float
  • go.get_scale() will return vector3 instead of float
  • Deprecate go.get_scale_vector() and add function go.get_scale_uniform() instead.

This will result in

  • The "scale" property is treated as a vector3 throughout
  • It is very explicit when the user get a uniform scale instead of the vector
  • Better alignment with scaling on other components instead of making a special for this specific case
  • Breaking somewhat backward compatibility since go.get will return a vector3 instead of a float

Since we make this engine for you who are using it we would very much like to here what the forums think of this. Please post any thoughts you have and please fill out the poll :slight_smile:

//Johan and the Defold team

13 Likes

I think this might be a good time to actually break backwards compatibility to unify the APIs!

Did you consider returning multiple values for go.get() and go.get_scale()?

local uniform, vector = go.get_scale()
local uniform, vector = go.get("scale")

It would keep things backwards compatible and still allow access to both values from the same two API methods without the need for go.get_scale_vector() or go.get_scale_uniform().

Or if we do the change and return a scale vector we could also return the scale uniform as a second value and skip go.get_scale_uniform().

On the other hand it’s messier with multiple return values, especially if you want to use the value immediately in a go.animate() call…

6 Likes

It’s always a good idea to fix API sooner than later.

I like the proposal, except go.get_scale_uniform(). If I scale the object non-uniformly, say (2.0, 0.5, 0.75), what go.get_scale_uniform() will return after that? It doesn’t make sense since the scaling is no longer uniform. Please correct me if I am missing something here.

If users have to change the code to replace go.get_scale() with go.get_scale_uniform(), they might change it to go.get_scale().x instead. Since it’s uniform, it doesn’t matter what vector component they will choose.

Additionally, for users who can’t spend time to alter their code, a simple Lua module can be created that will wrap go.get_scale() into a function which internally converts the vector to a scalar. If that way will be chosen, it also would make sense not to allow a scalar for go.set() and go.set_scale() to match other API, but introduce backward compatibility by the Lua module. It is tempting to allow a scalar, but wouldn’t that lead to unpredicted behaviour and hard to find bugs? If a user by mistake mixes up the value.

In my opinion, implementing compatibility layer in the user Lua space is a better option than polluting the core code with additional hooks, which will have to be removed later.

3 Likes

If I scale the object non-uniformly, say (2.0, 0.5, 0.75), what go.get_scale_uniform() will return after that? It doesn’t make sense since the scaling is no longer uniform. Please correct me if I am missing something here.

Good question! It is possible to get this behaviour today with the current API as it is. If you do for example go.set_scale(math.vector3(2.0, 0.5, 0.75)), a call to go.get_scale() will attempt to return the “uniform” scale which currently is implemented as the min element value of the scale vector (in this example it will return 0.5 as the uniform scale).

If users have to change the code to replace go.get_scale() with go.get_scale_uniform(), they might change it to go.get_scale().x instead. Since it’s uniform, it doesn’t matter what vector component they will choose.

In the case of a uniform vector go.get_scale().x will return the same value as go.get_scale_uniform() just like you said. The point with go.get_scale_uniform() and deprecating go.get_scale_vector() is to be explicit about when we are using a scalar value and not the vector, instead of the other way around. It is also the same heuristic we use internally when setting scale on the physics engine, only the uniform scale is used.

It is difficult to get around users having to change some code when breaking backward compatibility. We will try and make the required modifications to user code as minimal as possible.

  • go.animate will accept either scalar or vector3 as input when animating the scale property
  • go.set(".", "scale", ...) will take either scalar or vector3
  • go.set_scale(...) will also take either scalar or vector3

This would allow a user to only work with only uniform scale if it is desired, while granting the flexibility for users who want to use non-uniform scaling. It will be possible to get, set, animate the scale regardless.

The breaking changes are:

  • go.get(".", "scale") will return vector3 instead of a scalar uniform scale factor
  • go.get_scale() will return vector3, use go.get_scale_uniform() to get scalar value instead
  • go.get_world_scale() will also return vector3, use go.get_world_scale_uniform() instead
2 Likes

That makes more sense now, thanks.

But still I am not convinced that *_uniform() functions are needed. get_position() and get_rotation() don’t have it and won’t have it, so scale would still remain too special with a separate kind of functions.

Instead, I suggest to introduce a couple new functions to the vmath module.

vmath.min(v|q) and vmath.max(v|q).

They will return the minimal and maximal elements of the supplied vector or quaternion respectively.

This way go.get_scale_uniform() becomes vmath.min(go.get_scale()). And these two new functions can actually be used with position and rotation vectors for various reasons. Which brings position, rotation and scale back together into a single group.

Additionally with vmath.min() it’s obvious what algorithm is used for go.get_scale_uniform(), eliminating any misunderstanding. One for example might think that *_uniform() might produce an average value instead, that is (x + y + z) / 3.

4 Likes

Well, go.get_scale_uniform() is more of a convenience function really. Both to help users who are affected by the breaking changes, and as posted previously it is the same heuristics used for the physics in Defold so there is some value in supplying it to users in that form. I can’t really see a use case for uniform position or rotation. Uniform of the scale vector means just that, uniform scale.

Additionally with vmath.min() it’s obvious what algorithm is used for go.get_scale_uniform(), eliminating any misunderstanding. One for example might think that *_uniform() might produce an average value instead, that is (x + y + z) / 3.

Good point, it should of course be documented how the uniform scale is calculated.

4 Likes

I agree that the _uniform method doesn’t really provide anything that the vector method doesn’t already cover, and that additional math functions would be a better way to go.

2 Likes