Search code examples
flutterdartflame

SpriteComponent positioning with Flame


UPDATE: I created a smaller example that probably better illustrates the problem in this issue: https://github.com/flame-engine/flame/issues/200

short version:

I'm instantiating SpriteComponents at the positions (50, 50), (50, 100), (100, 50), (100, 100).

However, when rendered they look like this: enter image description here

What is producing this strange offset? How do I correctly render SpriteComponents?

longer version (with code): I would like to test the performance of flame by implementing something like bunnymark. As a first attempt at saving performance, I'm loading a single Sprite and using it to produce many SpriteComponents.

The SpriteComponent is wrapped in a Bunny class. For now it does nothing, eventually it will handle movement:

import 'dart:ui';

import 'package:flame/components/component.dart';
import 'package:flame/sprite.dart';

class Bunny {
  SpriteComponent bunny;

  Bunny(Sprite bunnySprite, double x, double y, double w, double h)
      : this.bunny =
            SpriteComponent.fromSprite(w, h, bunnySprite) {
    bunny.x = x;
    bunny.y = y;
  }

  void render(Canvas c) {
    bunny.render(c);
  }

  void update(double t) {}
}

The bunnies are instantiated in my game class:

import 'dart:ui';

import 'package:bunnymark/components/bunny.dart';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';
import 'package:flame/sprite.dart';
import 'package:flutter/cupertino.dart';

class BunnyMark extends Game {
  Size screenSize;
  List<Bunny> bunnies;
  Sprite bunnySprite;

  BunnyMark() {
    initialize();
  }

  void initialize() async {
    resize(await Flame.util.initialDimensions());

    bunnies = List<Bunny>();
    bunnySprite = Sprite('rabbitv3_tron.png');

    bunnies.add(Bunny(bunnySprite, 50, 50, 30, 30));
    bunnies.add(Bunny(bunnySprite, 50, 100, 30, 30));
    bunnies.add(Bunny(bunnySprite, 100, 100, 30, 30));
    bunnies.add(Bunny(bunnySprite, 100, 50, 30, 30));
  }

  void render(Canvas canvas) {
    Rect bgRect = Rect.fromLTWH(0, 0, screenSize.width, screenSize.height);
    Paint bgPaint = Paint();
    bgPaint.color = Color(0xff000000);
    canvas.drawRect(bgRect, bgPaint);

    bunnies.forEach((bunny) {
      bunny.render(canvas);
    });
  }

  void update(double t) {
    bunnies.forEach((bunny) => bunny.update(t));
  }

  void resize(Size size) {
    screenSize = size;
    super.resize(size);
  }
}

The game is started from main.dart:

import 'package:bunnymark/bunnymark.dart';
import 'package:flame/util.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  Util flameUtil = Util();
  await flameUtil.fullScreen();
  await flameUtil.setOrientation(DeviceOrientation.portraitUp);

  BunnyMark game = BunnyMark();
  runApp(game.widget);
}

Solution

  • Since you are using Game and not BaseGame (which would do that for you), you are responsible on your render implementation to reset the canvas every sprite.

    Components are allowed to make any modifications they desire on the Canvas, and BaseGame (or in your case, Game) is required to reset.

    So when you do

    bunnies.forEach((bunny) {
      bunny.render(canvas);
    });
    

    You probably want to do something like:

    bunnies.forEach((bunny) {
      canvas.save();
      bunny.render(canvas);
      canvas.restore();
    });
    

    Or, as an alternative, take a look at the BaseGame class that solves a lot of those problems for you :)