flame
flame copied to clipboard
HasDecorator with PositionComponent
What could be improved
Currently, the HasDecorator
mixin works by wrapping the component's renderTree()
with the decorator's apply()
method. This works fine for position-independent decorators (such as those that manipulate the paint), but is problematic for decorators that need to know the component's coordinates.
Specifically, the PositionComponent
in its renderTree
method applies the coordinate transformation, which affects the component's render()
and its children. However, this coordinate transform does not affect the decorator, which is applied outside. This is problematic for such decorators as Rotate3DDecorator
or Shadow3DDecorator
when the underlying component moves, since it requires some external logic to keep updating the decorators so that they are in sync with the underlying component.
How to improve
I'm currently considering the following approaches:
-
Option 1: Add
decorator
property directly to thePositionComponent
, so that itsrenderTree
can apply the decorator after the coordinate transform.Pros:
- Cleaner code;
- No need to explicitly add
HasDecorator
mixin to position components;
Cons:
- Extra property
decorator
inside thePositionComponent
class. Though if unused, it will benull
, and won't take up much resources.
-
Option 2: Inside the
HasDecorator
mixin check if the current component is aPositionComponent
, and if it is, then performrenderTree
which does not invokesuper.renderTree
but instead applies coordinate transform and renders children manually.Pros:
- There is no
decorator
property unless explicitly requested by the user;
Cons:
- Duplicates code from
renderTree
methods ofPositionComponent
andComponent
; - More bug-prone in case the
renderTree
code is updated in those classes;
- There is no
I'm leaning towards Option 1 here, but was wondering what do you guys think or if there's perhaps an Option 3 that I'm missing ?
Hmm, I'm undecided, seems like 1 is more convenient, but also it breaks the separation in a way that might not be very nice.
We discussed this on the meeting now and generally we wouldn't add new fields on PositionComponent
, but this one is probably important enough to make an exception. So go ahead with option 1 if you want. Hopefully we can get this in before the release right? :)
I've been thinking about this, and here's what comes to mind:
- Since
PositionComponent
is the ancestor for almost every other component in the game, we definitely want to make sure that it is as lean and efficient as possible. Which means we ought to be really cautious about adding new fields there. - Currently,
PositionComponent
is not as lean as possible: all its properties areValueNotifier
s, which means they contain lists of callbacks inside. Those lists, even if empty, increase the weight of the class.
- One way around this is to convert
NotifyingVector
s into regular vectors, but then have a special mixin that would override regular getters with those that produce notifying vectors. This way if the user wants to be notified about changes in a particular component, they'd have to request this functionality explicitly. Or we could just leave a single notifier on the overalltransform2D
matrix and do not expose separate notifiers for individual fields. This all would need further discussion, obviously.
-
Currently, the main function of the
PositionComponent
is to apply atransform2d
matrix to the canvas before rendering the component and its children. This operation -- modifying the canvas around certain drawing operations -- is, by definition, aDecoration
, even though we do not model it as such.But we CAN start modeling it like this. This would be option 3, in fact: model the current PositionComponent's operation as a decorator, and then tack on any additional decorators onto that through some chaining logic that we will add. This wouldn't be much different than option 1, but it could enable some interesting possibilities like replacing the canvas-transforming decorator with some other user logic, like introduction of skew or 3d transforms.
Option 3 sounds like quite a big breaking change, or would it be possible to do it in a non-breaking way? And would we be able to get this done before the release (which is on friday at the latest)?