flame icon indicating copy to clipboard operation
flame copied to clipboard

Children are not ordered by priority following add and remove

Open justinbot opened this issue 2 years ago • 2 comments

Current bug behavior

After a particular sequence of adding and removing component children with different priority values, the children will become ordered incorrectly resulting in incorrect rendering.

Expected behavior

Children are always ordered and rendered by priority.

Steps to reproduce

The following example reproduces the issue on flame 1.14.0:

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

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

class MyGame extends FlameGame {
  @override
  Future<void> onLoad() async {
    camera.viewport.add(ButtonComponent(
      button: TextComponent(text: 'Go'),
      onPressed: () async {
        final redCircle = CircleComponent(
          position: Vector2(-512, 0),
          radius: 128,
          priority: 10,
          paint: Paint()..color = const Color(0xffff0000),
        );
        await world.add(redCircle);

        await Future.delayed(const Duration(seconds: 1));
        final greenCircle = CircleComponent(
          position: Vector2(0, 0),
          radius: 128,
          priority: 9,
          paint: Paint()..color = const Color(0xff00ff00),
        );
        await world.add(greenCircle);

        await Future.delayed(const Duration(seconds: 1));
        final yellowCircle = CircleComponent(
          position: Vector2(-512, 0),
          radius: 128,
          priority: 10,
          paint: Paint()..color = const Color(0xff0000ff),
        );
        world.remove(redCircle);
        await world.add(yellowCircle);

        await Future.delayed(const Duration(seconds: 1));
        final purpleCircle = CircleComponent(
          position: Vector2(64, 0),
          radius: 128,
          priority: 8,
          paint: Paint()..color = const Color(0xffff00ff),
        );
        await world.add(purpleCircle);

        await Future.delayed(const Duration(seconds: 1));
        print(world.children.map((p) => p.priority).toList());
        // Outputs:
        // [9, 8, 10]
      },
    ));
  }
}

After the sequence is done:

  1. Children are no longer in priority order.
  2. The purple circle (priority 8) is incorrectly rendered on top of the green circle (priority 9). Screenshot 2024-01-11 at 1 36 35 PM

Note if we then call world.children.rebalanceAll() the children become ordered correctly again.

Flutter doctor output

[✓] Flutter (Channel stable, 3.13.9, on macOS 14.2.1 23C71 darwin-arm64, locale en-US)
    • Flutter version 3.13.9 on channel stable at /opt/homebrew/Caskroom/flutter/3.13.7/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision d211f42860 (3 months ago), 2023-10-25 13:42:25 -0700
    • Engine revision 0545f8705d
    • Dart version 3.1.5
    • DevTools version 2.25.0

justinbot avatar Jan 11 '24 18:01 justinbot

And they are not in the correct order after the next update tick has passed?

spydon avatar Jan 11 '24 19:01 spydon

They remain in the incorrect order until world.children.rebalanceAll() is called.

justinbot avatar Jan 11 '24 19:01 justinbot

Found the same issue after several hours investigation. New children are added to end of render queue ignoring old children higher priority.

ooleynich avatar Mar 01 '24 15:03 ooleynich

This was a really nasty bug, it was all the way down in the OrderedSet package. It is now solved at least and we'll soon release a version of OrderedSet that includes it.

spydon avatar Apr 16 '24 22:04 spydon

You can run pub upgrade in you projects to use the new version of ordered_set (v5.0.3). Please write in here once you've confirmed that it works like it should. :)

spydon avatar Apr 16 '24 22:04 spydon