Oxygen

We (the Flame organization) built an ECS (Entity Component System) named Oxygen.

If you want to use Oxygen specifically for Flame as a replacement for the FCS(Flame Component System) you should use our bridge library flame_oxygen and if you just want to use it in a Dart project you can use the oxygen library directly.

If you are not familiar with Oxygen yet we recommend you read up on its documentation.

To use it in your game you just need to add flame_oxygen to your pubspec.yaml, as can be seen in the Oxygen example and in the pub.dev installation instructions.

OxygenGame (Game extension)

If you are going to use Oxygen in your project it can be a good idea to use the Oxygen specific extension of the Game class.

It is called OxygenGame and it will give you full access to the Oxygen framework while also having full access to the Flame game loop.

Instead of using onLoad, as you are used to with Flame, OxygenGame comes with the init method. This method is called in the onLoad but before the world initialization, allowing you to register components and systems and do anything else that you normally would do in onLoad.

A simple OxygenGame implementation example can be seen in the example folder.

The OxygenGame also comes with it’s own createEntity method that automatically adds certain default components on the entity. This is especially helpful when you are using the BaseSystem as your base.

Systems

Systems define the logic of your game. In FCS you normally would add your logic inside a component with Oxygen we use systems for that. Oxygen itself is completely platform agnostic, meaning it has no render loop. It only knows execute, which is a method equal to the update method in Flame.

On each execute Oxygen automatically calls all the systems that were registered in order. But in Flame we can have different logic for different loops (render/update). So in flame_oxygen we introduced the RenderSystem and UpdateSystem mixin. These mixins allow you to add the render method and the update method respectively to your custom system. For more information see the RenderSystem and UpdateSystem section.

If you are coming from FCS you might expect certain default functionality that you normally got from the PositionComponent. As mentioned before components do not contain any kind of logic, but to give you the same default functionality we also created a class called BaseSystem. This system acts almost identical to the prerender logic from the PositionComponent in FCS. You only have to subclass it to your own system. For more information see the BaseSystem section.

Systems can be registered to the world using the world.registerSystem method on OxygenGame.

mixin GameRef

The GameRef mixin allows a system to become aware of the OxygenGame instance its attached to. This allows easy access to the methods on the game class.

class YourSystem extends System with GameRef<YourGame> {
  @override
  void init() {
    // Access to game using the .game propery
  }

  // ...
}

mixin RenderSystem

The RenderSystem mixin allows a system to be registered for the render loop. By adding a render method to the system you get full access to the canvas as you normally would in Flame.

class SimpleRenderSystem extends System with RenderSystem {
  Query? _query;

  @override
  void init() {
    _query = createQuery([/* Your filters */]);
  }

  void render(Canvas canvas) {
    for (final entity in _query?.entities ?? <Entity>[]) {
      // Render entity based on components
    }
  }
}

mixin UpdateSystem

The MixinSystem mixin allows a system to be registered for the update loop. By adding a update method to the system you get full access to the delta time as you normally would in Flame.

class SimpleUpdateSystem extends System with UpdateSystem {
  Query? _query;

  @override
  void init() {
    _query = createQuery([/* Your filters */]);
  }

  void update(double dt) {
    for (final entity in _query?.entities ?? <Entity>[]) {
      // Update components values
    }
  }
}

BaseSystem

The BaseSystem is an abstract class whoms logic can be compared to the PositionComponent from FCS. The BaseSystem automatically filters all entities that have the PositionComponent and SizeComponent from flame_oxygen. On top of that you can add your own filters by defining a getter called filters. These filters are then used to filter down the entities you are interested in.

The BaseSystem is also fully aware of the game instance. You can access the game instance by using the game property. This also gives you access to the createEntity helper method on OxygenGame.

On each render loop the BaseSystem will prepare your canvas the same way the PositionComponent from FCS would (translating, rotating and setting the anchor. After that it will call the renderEntity method so you can add your own render logic for that entity on a prepared canvas.

The following components will be checked by BaseSystem for the prepartion of the canvas:

  • PositionComponent (required)

  • SizeComponent (required)

  • AnchorComponent (optional, defaults to Anchor.topLeft)

  • AngleComponent (optional, defaults to 0)

class SimpleBaseSystem extends BaseSystem {
  @override
  List<Filter<Component>> get filters => [];

  @override
  void renderEntity(Canvas canvas, Entity entity) {
    // The canvas is translated, rotated and fully prepared for rendering.
  }
}

ParticleSystem

The ParticleSystem is a simple system that brings the Particle API from Flame to Oxygen. This allows you to use the ParticleComponent without having to worry about how it will render or when to update it. As most of that logic is already contained in the Particle itself.

Simply register the ParticleSystem and the ParticleComponent to your world like so:

world.registerSystem(ParticleSystem());

world.registerComponent<ParticleComponent, Particle>(() => ParticleComponent);

You can now create a new entity with a ParticleComponent. For more info about that see the ParticleComponent section.

Components

Components in Oxygen are different than the ones from FCS mainly because instead of containing logic they only contain data. This data is then used in systems which in turn define the logic.

To accomdate people who are switching from FCS to Oxygen we implemented a few components to help you get started. Some of these components are based on the multiple functionalities that the PositionComponent from FCS has. Others are just easy wrappers around certain Flame API functionality, they are often accompanied by predefined systems that you can use.

Components can be registered to the world using the world.registerComponent method on OxygenGame.

PositionComponent

The PositionComponent is as its name implies is a component that describe the position of an entity. And it is registered to the world by default.

Creating a positioned entity using OxygenGame:

game.createEntity(
  position: Vector2(100, 100),
  size: // ...
);

Creating a positioned entity using the World:

world.createEntity()
  ..add<PositionComponent, Vector2>(Vector2(100, 100));

SizeComponent

The SizeComponent is as its name implies is a component that describe the size of an entity. And it is registered to the world by default.

Creating a sized entity using OxygenGame:

game.createEntity(
  position: // ...
  size: Vector2(50, 50),
);

Creating a sized entity using the World:

world.createEntity()
  ..add<SizeComponent, Vector2>(Vector2(50, 50));

AnchorComponent

The AnchorComponent is as its name implies is a component that describe the anchor position of an entity. And it is registered to the world by default.

This component is especially useful when you are using the BaseSystem. But can also be used for your own anchoring logic.

Creating an anchored entity using OxygenGame:

game.createEntity(
  position: // ...
  size: // ...
  anchor: Anchor.center,
);

Creating an anchored entity using the World:

world.createEntity()
  ..add<AnchorComponent, Anchor>(Anchor.center);

AngleComponent

The AngleComponent is as its name implies is a component that describe the angle of an entity. And it is registered to the world by default. The angle is in radians.

This component is especially useful when you are using the BaseSystem. But can also be used for your own angle logic.

Creating an angled entity using OxygenGame:

game.createEntity(
  position: // ...
  size: // ...
  angle: 1.570796,
);

Creating an angled entity using the World:

world.createEntity()
  ..add<AngleComponent, double>(1.570796);

FlipComponent

The FlipComponent can be used to flip your rendering on either the X or Y axis. It is registered to the world by default.

This component is especially useful when you are using the BaseSystem. But can also be used for your own flipping logic.

Creating an entity that is flipped on it’s X axis using OxygenGame:

game.createEntity(
  position: // ...
  size: // ...
  flipX: true
);

Creating an entity that is flipped on it’s X axis using the World:

world.createEntity()
  ..add<FlipComponent, FlipInit>(FlipInit(flipX: true));

SpriteComponent

The SpriteComponent is as its name implies is a component that describe the sprite of an entity. And it is registered to the world by default.

This allows you to assigning a Sprite to an Entity.

Creating an entity with a sprite using OxygenGame:

game.createEntity(
  position: // ...
  size: // ...
)..add<SpriteComponent, SpriteInit>(
  SpriteInit(await game.loadSprite('pizza.png')),
);

Creating an entity with a sprite using World:

world.createEntity()
  ..add<SpriteComponent, SpriteInit>(
    SpriteInit(await game.loadSprite('pizza.png')),
  );

TextComponent

The TextComponent is as its name implies is a component that adds a text component to an entity. And it is registered to the world by default.

This allows you to add text to your entity, combined with the PositionComponent you can use it as a text entity.

Creating an entity with text using OxygenGame:

game.createEntity(
  position: // ...
  size: // ...
)..add<TextComponent, TextInit>(
  TextInit(
    'Your text',
    config: const TextPaintConfig(),
  ),
);

Creating an entity with text using World:

world.createEntity()
  ..add<TextComponent, TextInit>(
    TextInit(
      'Your text',
      config: const TextPaintConfig(),
    ),
  );

ParticleComponent

The ParticleComponent wraps a Particle from Flame. Combined with the ParticleSystem you can easily add particles to your game without having to worry about how to render a particle or when/how to update one.

Creating an entity with a particle using OxygenGame:

game.createEntity(
  position: // ...
  size: // ...
)..add<ParticleComponent, Particle>(
  // Your Particle.
);

Creating an entity with a particle using World:

world.createEntity()
  ..add<ParticleComponent, Particle>(
    // Your Particle.
  );