Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/insality/shooting_circles
Defold ECS game example
https://github.com/insality/shooting_circles
defold defold-game ecs example game lua
Last synced: about 2 months ago
JSON representation
Defold ECS game example
- Host: GitHub
- URL: https://github.com/insality/shooting_circles
- Owner: Insality
- Created: 2024-07-04T18:55:08.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2024-10-22T22:13:40.000Z (2 months ago)
- Last Synced: 2024-10-24T02:01:40.986Z (about 2 months ago)
- Topics: defold, defold-game, ecs, example, game, lua
- Language: Lua
- Homepage: https://insality.github.io/shooting_circles/
- Size: 4.57 MB
- Stars: 27
- Watchers: 1
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
Awesome Lists containing this project
README
![](media/logo.png)
# Shooting Circles
This project was created for the [Explosion Community](https://forum.defold.com/t/community-challenge-explosions/77315) 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](https://github.com/bakpakin/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](https://github.com/Insality/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](https://github.com/Insality/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](https://www.mapeditor.org/) 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.![](media/custom_types.png)
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.