Search code examples
flutterflame

How to add a sprite on top of another sprite in Flame Flutter?


The app has a background image using SpriteComponent and I try to add another Tile image on top of the background image. Why the crate.png image doesn't appear on top?

import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';

void main() async {
  runApp(GameWidget(
    game: CGame(),
  ));
}

class CGame extends FlameGame {
  @override
  Future<void>? onLoad() async {
    SpriteComponent crate = SpriteComponent()
      ..sprite = await loadSprite('crate.png')
      ..size = Vector2(100, 100)
      ..position = Vector2(250, 300);

    add(crate);
    add(Background());   //<== comment this line to see the loaded crate.png
    add(Crate());

    return super.onLoad();
  }
}

class Background extends SpriteComponent with HasGameRef {
  @override
  Future<void>? onLoad() async {
    sprite = await Sprite.load('background.png');
    size = gameRef.size;

    return super.onLoad();
  }

  @override
  void render(Canvas canvas) {
    super.render(canvas);
  }
}

class Crate extends SpriteComponent with HasGameRef {
  @override
  Future<void>? onLoad() async {
    sprite = await Sprite.load('crate.png');
    size = Vector2(100, 100);
    position = Vector2(200, 200);

    return super.onLoad();
  }
}

Solution

  • Your crate will be behind the background because it loads faster and therefore is added to the component tree faster, to solve this you'll have to set the priority of the crate:

    import 'package:flame/components.dart';
    import 'package:flame/game.dart';
    import 'package:flutter/material.dart';
    
    void main() async {
      runApp(GameWidget(
        game: CGame(),
      ));
    }
    
    class CGame extends FlameGame {
      @override
      Future<void>? onLoad() async {
        add(Background());
        add(Crate());
    
        return super.onLoad();
      }
    }
    
    class Background extends SpriteComponent with HasGameRef {
      @override
      Future<void>? onLoad() async {
        sprite = await Sprite.load('background.png');
        size = gameRef.size;
    
        return super.onLoad();
      }
    }
    
    class Crate extends SpriteComponent with HasGameRef {
      Crate() : super(priority: 1); // <-- This is what changed
    
      @override
      Future<void>? onLoad() async {
        sprite = await Sprite.load('crate.png');
        size = Vector2(100, 100);
        position = Vector2(200, 200);
    
        return super.onLoad();
      }
    }
    

    The default priority of components are 0 and the higher the priority is the "closer" to you the component will be, like a higher z-index. So here when we set the crate's priority to 1 it will always appear in front of the background which still has the default priority 0.

    And a side note: you don't have to call the render method manually in Background, it is done automatically.