Skip to content

Insality/shooting_circles

Repository files navigation

Shooting Circles

This project was created for the Explosion Community challenge, and it goes a bit beyond the initial scope.

Shooting Circles is a simple game example built using only the ECS architecture for Defold (I think it pretty close to Pure ECS). Below, you will find a detailed description of the different parts of the project. If you are interested in the code structure and ECS, welcome!

Overview

Restrictions

  • Use the tiny-ecs library without modifications.
  • Do not use any wrappers around the tiny-ecs library.
  • Entities are created using regular tables {}.
  • No external require() calls in systems to maintain portability between projects.
  • Components can only be modified within one system. For example, entity.transform.x = 10 cannot be set in a non-transform system.
  • Entities should only contain data, not logic.
  • Sure no any global variables.
  • The GUI collection should be able to run as the game’s bootstrap collection.

Notes

I overuse here event entities to avoid using the update() function in systems for checking each entity's actions. This results in a delay between actions, especially in the following sequence:

  1. The physics callback handler creates a collision_event entity.
  2. One frame is skipped.
  3. The on_collision_explosion system creates an explosion_command entity.
  4. One frame is skipped.
  5. The explosion system creates a physics_command entity.
  6. One frame is skipped.
  7. Physics handles the command.

For more reactive logic in ECS, the update() function could be used to iterate and check each entity. However, I prefer to avoid this constant update checking in systems. I'm considering an "event" bus, but I’m not sure about it yet.

Game Flow

The initial script is /loader/loader.script, which initializes all libraries and loads the game.collection. The /game/game.script script creates a world, loads systems, and loads a level with level_loader_command. All other logic is handled through the ECS systems, located in the /systems folder.

Systems

  • All systems are placed in the /systems folder.
  • Systems can return multiple sub-systems.
  • Systems are divided into "system", "system_command", and "system_event":
    • System: Filters entities by required components and processes them. It usually returns up to three sub-systems: system, system_command, and system_event, and contains all system logic.
    • System Command: Describes an external API for the system with the "system_command" component and a list of "event" components to be triggered by the system. To spawn a command for the system, create an entity with the "system_command" component. This entity will be processed and removed by the system in the next frame.
    • System Event: Describes the event fields generated by the system. To spawn an event for the system, create an entity with the "system_event" component. This entity will be processed and removed by the system in the next frame.
  • Since systems can return a set of systems, we can't use world:addSystem(...), so we use world:add(...) instead for them.

Creating a New System

To create a new system, I use the system_* template located in /decore/templates and replace TEMPLATE with the system name. Then, add the system to the game.script in the system list. That’s all it takes to create a new system.

Decore

Decore is a library that manages data collections for ECS and allows the creation of entities from prefabs.

  • All game components are described in the resources/components.json file. Decore uses this file to create components by their prefab_id and fill default values for components.
  • Entities that are described manually (not in Tiled Editor) are placed in resources/entities.json. Items like bullets and different utility entities are easily described in this file rather than creating entities in a Tiled tileset.
  • At game start, all components, entities, and worlds are registered in Decore.

Detiled

Detiled is a library that converts Tiled maps and tilesets to Decore entities. It allows setting up components, entities, and worlds from the Tiled map editor.

  • Rotation and scale changes for entities are supported. However, if an entity has a physics body, the scale should be uniform, as the physics body will be scaled by the same value as the entity.

Tiled

To view the Tiled project, open tiled/game_shooting_circle.tiled-project.

Exporting

Each map and tileset is exported as JSON and placed in custom resources folders:

  • Maps are placed in the resources/maps folder.
  • Tilesets are placed in the resources/tilesets folder.
  • resources/maps_list.json contains the list of maps to be loaded by the game.
  • resources/tilesets_list.json contains the list of tilesets to be loaded by the game.

This loading happens in the loader.script in the init_detiled() function.

Tiled Custom Types Editor

Tiled has a Custom Types Editor that is well-suited for describing ECS entities. Open View -> Custom Types Editor to see the components defined for this project.

To use these components, add them to an entity in the tileset or instance in the world and override the fields. You can also use the "object" type of property field to link to other entities in the world.

Overriding Properties

Default values are always used in the custom types. Only describe the fields you need to manipulate; other fields will use the default values from resources/components.json.

  • Add components and set default property values for each tileset entity.
  • In maps, you can override the properties of the entity. However, doing so will replace the entire component with the new one. Therefore, changes to Tileset Entity properties will not be reflected in the map.

Tests

I included a few tests to check how we can add unit tests to the systems. Look at /test folder for more details.

Unsolved Problems/Questions

  • How to correctly work with events?
  • How to make system excluding works correct? If components are still created, commenting out the game_object system will cause other systems requiring the game_object component to fail when trying access game_object component fields like "root" or "game_objects."
  • How to debug the chain of events? I want to see the chain of events.
  • What tools are needed to debug and interact with ECS? Possibly an admin panel or entity viewer.