Swiftpack.co - Package - fireblade-engine/ecs

Fireblade ECS (Entity-Component System)

github CI travis CI license swift version platforms platforms platforms codecov documentation

This is a dependency free, lightweight, fast and easy to use Entity-Component System implementation in Swift. It is developed and maintained as part of the Fireblade Game Engine project.

See the Fireblade ECS Demo App or have a look at documentation in the wiki to get started.

๐Ÿš€ Getting Started

These instructions will get you a copy of the project up and running on your local machine and provide a code example.

๐Ÿ“‹ Prerequisites

๐Ÿ’ป Installing

Fireblade ECS is available for all platforms that support Swift 5.1 and higher and the Swift Package Manager (SPM).

Extend the following lines in your Package.swift file or use it to create a new project.

// swift-tools-version:5.1

import PackageDescription

let package = Package(
    name: "YourPackageName",
    dependencies: [
        .package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.15.3")
    ],
    targets: [
        .target(
            name: "YourTargetName",
            dependencies: ["FirebladeECS"])
    ]
)

๐Ÿ“ Code Example

๐Ÿ›๏ธ Nexus

The core element in the Fireblade-ECS is the Nexus. It acts as a centralized way to store, access and manage entities and their components. A single Nexus may (theoretically) hold up to 4294967295 Entities at a time.
You may use more than one Nexus at a time.

Initialize a Nexus with

let nexus = Nexus()

๐Ÿ‘ค Entities

then create entities by letting the Nexus generate them.

// an entity without components
let newEntity = nexus.createEntity()

To define components conform your class to the Component protocol

final class Position: Component {
	var x: Int = 0
	var y: Int = 0
}

and assign instances of it to an Entity with

let position = Position(x: 1, y: 2)
entity.assign(position)

You can be more efficient by assigning components while creating an entity.

// an entity with two components assigned.
nexus.createEntity {
	Position(x: 1, y: 2)
	Color(.red)
}

// bulk create entities with multiple components assigned.
nexus.createEntities(count: 100) { _ in
	Position()
	Color()
}

๐Ÿ‘ช Families

This ECS uses a grouping approach for entities with the same component types to optimize cache locality and ease up access to them.
Entities with the same component types may belong to one Family. A Family has entities as members and component types as family traits.

Create a family by calling .family with a set of traits on the nexus. A family that contains only entities with a Movement and PlayerInput component, but no Texture component is created by

let family = nexus.family(requiresAll: Movement.self, PlayerInput.self,
                          excludesAll: Texture.self)

These entities are cached in the nexus for efficient access and iteration. Families conform to the Sequence protocol so that members (components) may be iterated and accessed like any other sequence in Swift.
Access a family's components directly on the family instance. To get family entities and access components at the same time call family.entityAndComponents. If you are only interested in a family's entities call family.entities.

class PlayerMovementSystem {
	let family = nexus.family(requiresAll: Movement.self, PlayerInput.self,
                              excludesAll: Texture.self)

	func update() {
		family
			.forEach { (mov: Movement, input: PlayerInput) in
			
			// position & velocity component for the current entity
			
			// get properties
			_ = mov.position
			_ = mov.velocity
			
			// set properties
			mov.position.x = mov.position.x + 3.0
			...
			
			// current input command for the given entity
			_ = input.command
			...
			
		}
	}

	func update2() {
		family
			.entityAndComponents
			.forEach { (entity: Entity, mov: Movement, input: PlayerInput) in
			
			// the current entity instance
			_ = entity

			// position & velocity component for the current entity
			
			// get properties
			_ = mov.position
			_ = mov.velocity
			
			
		}
	}

	func update3() {
		family
			.entities
			.forEach { (entity: Entity) in
			
			// the current entity instance
			_ = entity
		}
	}
}

๐Ÿง‘ Singles

A Single on the other hand is a special kind of family that holds exactly one entity with exactly one component for the entire lifetime of the Nexus. This may come in handy if you have components that have a Singleton character. Single components must conform to the SingleComponent protocol and will not be available through regular family iteration.

final class GameState: SingleComponent {
    var quitGame: Bool = false
}
class GameLogicSystem {
    let gameState: Single<GameState>
    
    init(nexus: Nexus) {
        gameState = nexus.single(GameState.self)
    }
    
    func update() {
        // update your game sate here
        gameState.component.quitGame = true
        
        // entity access is provided as well
        _ = gameState.entity
    }
}

๐Ÿ”— Serialization

To serialize/deserialize entities you must conform their assigned components to the Codable protocol.
Conforming components can then be serialized per family like this:

// MyComponent and YourComponent both conform to Component and Codable protocols.
let nexus = Nexus()
let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self)

// JSON encode entities from given family.
var jsonEncoder = JSONEncoder()
let encodedData = try family.encodeMembers(using: &jsonEncoder)

// Decode entities into given family from JSON. 
// The decoded entities will be added to the nexus.
var jsonDecoder = JSONDecoder()
let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder)

๐Ÿงช Demo

See the Fireblade ECS Demo App to get started.

๐Ÿ“– Documentation

Consult the wiki for in-depth documentation.

๐Ÿท๏ธ Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

โœ๏ธ Authors

See also the list of contributors who participated in this project.

๐Ÿ” License

This project is licensed under the MIT License - see the LICENSE file for details

๐Ÿ™ Acknowledgments

Inspired by

Github

link
Stars: 36

Dependencies

Used By

Total: 0

Releases

Support WebAssembly (WASM) - 2020-08-11 20:42:47

- 2020-08-07 09:22:42

  • Adds createMember function builder to families
  • Adds deleteMembers to families
  • Cleanups

- 2020-08-06 20:52:15

  • Generalize Entity Identifier Generator + provide Nexus API
  • Cleanups

- 2020-08-06 19:51:14

  • Adds stencil based family implementation + test generation
  • Removes obsoleted Relatives API
  • Adds Github CI
  • Cleanups

- 2020-08-06 13:32:20

  • Hotfix for https://github.com/fireblade-engine/ecs/issues/21
  • Xcode 12 beta / Swift 5.3 compilation works again

- 2020-08-03 10:16:54

  • Adds function builder entity creation API
  • Adds entity bulk creation API
  • Adds automatic documentation generation via CI
  • Fixes bug in UnorderedSparseSet
  • Performance improvements
  • Deprecates relatives API
  • Cleanups

Family serialization - 2020-07-23 20:54:27

  • Adds serialization/deserialization of entities to families; all family required components must conform to Codable
  • Adds createMember() convenience function to families
  • Cleanups

- 2020-07-21 09:03:31

  • Add Swift language version to package manifest.
  • Add subscripts to entity for convenient component access.

- 2020-07-15 19:42:59

  • Add EntityIdentifierGenerator to better manage entity id generation
  • Removed centralized component identifier mapping
  • Cleanups
  • Add some entity tests

- 2020-07-10 08:23:22

  • Refined UnorderedSparseSet (https://github.com/fireblade-engine/ecs/pull/10, https://github.com/fireblade-engine/ecs/pull/11)
  • Fixed string hashing overflow on 32-bit builds (https://github.com/fireblade-engine/ecs/pull/9, https://github.com/fireblade-engine/ecs/pull/12)
  • Added codecov.io support
  • Deprecated Nexus.knownUniqueComponentTypes

- 2020-05-26 13:26:04

  • Add allComponents() Sequence to entity
  • Delete #if DEBUG in tests

- 2020-05-09 11:29:24

  • Fix typos
  • Cleanups
  • Consistent Swift 5.1+

Improvements - 2020-04-30 20:21:47

  • Optimize storage
  • Rework ComponentIdentifier handling
  • Rework Equatable and other conformances
  • Fix event delegate
  • Remove abandoned code
  • Prepare code for Codable conformance
  • Cleanups & lint

๐Ÿ‘ซ Relatives - 2019-10-01 09:56:20

New

  • Introduce Relatives, a directed acyclic graph integration

Add

  • Gitlab-ci config
  • Makefile for convenience actions
  • Conform EntityIdentifier to Codable

Update

  • Swiftlint rules

Fix

  • Swiftlint warnings
  • Cleanups

- 2019-08-22 12:57:48

  • Add Linux to travis.ci

We are family - 2019-08-21 12:42:41

  • rework family implementation
    • removed TypedFamily*
    • introduced: Family<Requires*<*>>
  • minor changes to NexusEventDelegate
  • optimisations
  • cleanups

Cleanups & Optimizations - 2019-08-20 20:52:39

Optimisations

  • Entity creation: nexus.create(entity:) is now nexus.createEntity()
  • Drop per entity name (may be added as name component instead)
  • Family member update iteration
  • rework EntityIdentifier
  • rework ComponentIdentifier
  • nexus storage

Cleanups

  • nexus event delegate handling
  • remove unnecessary typealiases

Swift 5 - 2019-05-21 09:59:14

  • Swift 5
  • Fix CI pipeline
  • Cleanups

- 2019-03-08 17:11:54

  • Fixes a bug in the sparse set implementation
  • Add Swift 5 compatibility
  • Improvements
  • Cleanups

For all the Singles - 2019-02-14 17:20:21

  • Adds concept of a Single
  • Families are now structs
  • Bugfixes
  • Improve code coverage
  • Divide tests into unit tests and performance tests
  • Cleanups

- 2018-12-04 07:21:36

  • Conform TypedFamily to Sequence
  • Cleanups
  • Account for number of components in nexus for equatable comparison

0.5.0 typed families - 2018-09-29 17:23:30

  • rework of the family access to support LazySequenceProtocol
  • renamed Family to TypedFamily[1-5]
  • family members (aka components) may be accessed directly on the family instance (sequence)
  • family's entities may be accessed via family.entities (sequence)
  • family's entities + components may be accessed via family.entityAndComponents (sequence)
  • extended tests
  • added more performance measures

- 2018-09-27 20:21:18

  • Cleanups
  • ACL
  • Travis CI integration

- 2018-06-07 18:57:52

Adds support for Swift 4.2

- 2018-05-10 18:10:59

  • Refactor family member iterate API
  • Extend family member iterate API
  • Fix major flaws in SparseSet implementation
  • Rename SparseSet to UnorderedSparseSet
  • Increase Test Coverage to 78%
  • Cleanups & fixes