How should you do matrix transformations?

103

You are correct that Transform2D does not allow these kinds of "additional transforms on top" -- mostly because the transform matrix gets recalculated whenever any underlying property changes. However, you can create a derived class, overriding the transformMatrix getter so that it applies additional transformations.

I'm not sure what's the best way for implementing isometric games in Flame, but you could try the following approach:

[World]
 +--[Ground]
 |   +--[Tile]s
 |
 +--[Overground]
     +--[Character]
     +--[Enemy]s
     +--[Structure]s
     +--...

Here, Ground would be a component that applies isometric projection before rendering its children Tiles (which are just regular PositionComponents). Thus, there's no need to multiply the transform matrix: you are just applying an extra canvas transform.

The Overground component is a bit trickier. It has many children, each of which is a regular PositionComponent with (x, y) position given in the world coordinates. The job of the Overground then is to apply the isometric projection to that position, converting it into the screen coordinates, and then translate the canvas accordingly before rendering each component. Also, Overground would need to constantly re-sort the children according to their distance from the camera.

Share:
103
anonymous-dev
Author by

anonymous-dev

Updated on January 03, 2023

Comments

  • anonymous-dev
    anonymous-dev over 1 year

    We are rendering a isometric grid with flame where each tile is a component but we found this comment in the source code of the Transform2D

    The returned matrix must not be modified by the user.

      /// The total transformation matrix for the component. This matrix combines
      /// translation, rotation, reflection and scale transforms into a single
      /// entity. The matrix is cached and gets recalculated only as necessary.
      ///
      /// The returned matrix must not be modified by the user.
      Matrix4 get transformMatrix {
        if (_recalculate) {
          // The transforms below are equivalent to:
          //   _transformMatrix = Matrix4.identity()
          //       .. translate(_position.x, _position.y)
          //       .. rotateZ(_angle)
          //       .. scale(_scale.x, _scale.y, 1)
          //       .. translate(_offset.x, _offset.y);
          final m = _transformMatrix.storage;
          final cosA = math.cos(_angle);
          final sinA = math.sin(_angle);
          m[0] = cosA * _scale.x;
          m[1] = sinA * _scale.x;
          m[4] = -sinA * _scale.y;
          m[5] = cosA * _scale.y;
          m[12] = _position.x + m[0] * _offset.x + m[4] * _offset.y;
          m[13] = _position.y + m[1] * _offset.x + m[5] * _offset.y;
          _recalculate = false;
        }
        return _transformMatrix;
      }
    

    It does seem to work, with all tiles being its own component, where each component just renders a colored rectangle. (Matrix4.isometric() is our extension).

    IsometricGrid extends PositionComponent

    IsometricGrid({
        this.tiles = const [],
        this.rows = 1,
        this.columns = 1,
        this.tileWidth = 1,
        this.tileHeight = 1,
      }) : super() {
        final isometricMatrix = Matrix4.isometric();
        transformMatrix.multiply(isometricMatrix);
        projectionMatrix = isometricMatrix;
        inverseProjectionMatrix = Matrix4.inverted(projectionMatrix);
      }
    

    And we get

    enter image description here

    But we later wanted to add a actor to the grid with IsometricGrid.addActor

      addActor(PositionComponent actor) {
        actor.transform.transformMatrix.multiply(inverseProjectionMatrix);
        add(actor);
      }
    

    enter image description here

    Which ads the character standing up right to the grid. But once we move the actor to column 2 row 3 by changing its position the matrix seems to be reset for the actor.

    enter image description here

    What is the idiomatic way of modifying a matrix on the transform in the flame engine? It seems that under some conditions the matrix just resets. I think this happens because it just recalculates the matrix when you do a transformation such as rotation, position or scale.

    We are building this with flutter web and we also notice that sometimes when alt tabed the matrix is reset.

  • anonymous-dev
    anonymous-dev over 2 years
    Hi, great answer. We did think of a solution like this, and since this seems like the only option we will try this. We hope the transform will allow these kind of transformations in a future update.
  • anonymous-dev
    anonymous-dev over 2 years
    But could you explain what you mean by "Ground would be a component that applies isometric projection before rendering its children Tiles (which are just regular PositionComponents). Thus, there's no need to multiply the transform matrix"? Without a matrix? Could you elaborate how this projection should be done without a matrix?