Skip to content

planetis-m/breakout

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

breakout-ecs

This is a port of rs-breakout "A Breakout clone written in Rust using a strict ECS architecture" to Nim. It was done for learning purposes. It also incorporates improvements done by me. These are explained below.

Entity management was redesigned

The original codebase when updating a system or creating a new entity, it iterates up to MAX_ENTITIES. This was eliminated by using a specialized data structure.

For entity management (creation, deletion) a SlotMap is used. It also holds a dense sequence of set[HasComponent] which is the "signature" for each entity. A signature is a bit-set describing the component composition of an entity. This is used for iterating over all entities, skipping the ones that don't match a system's "registered" components. These are encoded as Query, another bit-set and the check performed is: signature * Query == Query.

Fixed timestep with interpolation

Alpha value is used to interpolate between next and previous transforms. Interpolation function for angles was implemented.

Improvements to the hierarchical scene graph

As explained by the original authors in their documentation for backcountry

Transforms can have child transforms attached to them. We use this to group entities into larger wholes (e.g. a character is a hierarchy of body parts, the hat and the gun).

I changed the implementation of children: [Option<usize>; MAX_CHILDREN] with the design described at skypjack's blog. Now it is a seperate Hierarchy component following the unconstrained model. Immediate updates are implemented by traversing this hierarchy using dfs traversal.

Custom vector math library

A type safe vector math library was created for use in the game. distinct types are used to prohibit operations that have no physical meaning, such as adding two points.

type
  Rad* = distinct float32

func lerp*(a, b: Rad, t: float32): Rad =
  # interpolates angles

type
  Vec2* = object
    x*, y*: float32

  Point2* {.borrow: `.`.} = distinct Vec2

func `+`*(a, b: Vec2): Vec2
func `-`*(a, b: Point2): Vec2
func `+`*(p: Point2, v: Vec2): Point2
func `-`*(p: Point2, v: Vec2): Point2
func `+`*(a, b: Point2): Point2 {.
    error: "Adding 2 Point2s doesn't make physical sense".}

Blueprints DSL

build is a macro that allows you to declaratively specify an entity and its components. It produces mixin proc calls that register the components for the entity with the arguments specified. The macro also supports nested entities (children in the hierarchical scene graph) and composes perfectly with user-made procedures. These must have signature proc (w: World, e: Entity, ...): Entity and tagged with entity.

Examples

  1. Creates a new entity, with these components, returns the entity handle.
let ent1 = game.build(blueprint(with Transform2d(), Fade(step: 0.5),
    Collide(size: vec2(100.0, 20.0)), Move(speed: 600.0)))
  1. Specifies a hierarchy of entities, the children (explosion particles) are built inside a loop. The build macro composes with all of Nim's control flow constructs.
proc createExplosion*(world: var World, parent: Entity, x, y: float32): Entity =
  let explosions = 32
  let step = Tau / explosions.float
  let fadeStep = 0.05
  result = world.build(blueprint(id = explosion)):
    with(Transform2d(translation: Vec2(x: x, y: y), parent: parent))
    children:
      for i in 0 ..< explosions:
        blueprint:
          with:
            Transform2d(parent: explosion)
            Draw2d(width: 20, height: 20, color: [255'u8, 255, 255, 255])
            Fade(step: fadeStep)
            Move(direction: Vec2(x: sin(step * i.float), y: cos(step * i.float)), speed: 20.0)

It expands to:

let explosion = createEntity(world)
mixTransform2d(world, explosion, mat2d(), Vec2(x: x, y: y), Rad(0), vec2(1, 1),
               parent)
for i in 0 ..< explosions:
  let :tmp_1493172298 = createEntity(world)
  mixTransform2d(world, :tmp_1493172298, mat2d(), vec2(0, 0), Rad(0),
                 vec2(1, 1), explosion)
  mixDraw2d(world, :tmp_1493172298, 20, 20, [255'u8, 255, 255, 255])
  mixFade(world, :tmp_1493172298, fadeStep)
  mixMove(world, :tmp_1493172298,
          Vec2(x: sin(step * float(i)), y: cos(step * float(i))), 20.0)
explosion

Acknowledgments

License

This library is distributed under the MIT license.

About

Breakout game implemented using strict ECS architecture. Used as a testbed.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages