Skip to main content

Spaces

Games often consist of multiple rooms, levels or areas. In Easel, these are called Spaces. Each space is an independent physics simulation, with its own set of bodies, as well as its own gravity and timescale parameters.

Some examples of what spaces could be used for:

  • Partitioning your game world into different areas, for example, a town with multiple buildings, where the indoor and outdoor areas are in different spaces.
  • Creating multiple levels or instances of the same area, for example, a game where you go deeper into the mines and each level is a new space.
  • A game where you walk around a ship and perform tasks on various control panels. Each panel's minigame could be a different space.

Defining Spaces

Any entity can be used as a space. You can either reuse an existing entity, or Spawn a new one. In many cases, you already have an entity that represents the level or area, so you can just use that as the space.

Fixed Spaces

In some games, you only have a fixed set of spaces, for example, a game with an inside and an outside area. In this case, you could define them upfront somewhere and assign them to constants:

// This game has two spaces
const Space:Inside = Spawn
const Space:Outside = Spawn

// Trees are always outside
pub fn obstacle.Tree(pos) {
use body=this
Body(pos=, space=Space:Outside)
PolygonCollider(shape=Circle(radius=1), category=Category:Tree)
// ...
}

Dynamic Spaces

In other games, the spaces are spawned dynamically, for example, a game where you go deeper into the mines and each level is a new space.

In this situation, you would use your level entity as a space, and assign it to the space parameter of your bodies. To make this even easier, declare the space parameter as a context parameter so it gets automatically passed to everywhere that needs it.

This is an example of using the level entity as a space by assigning it to space in the context. The Rock automatically gets put into space:

pub game fn World.Main() {
Spawn level {
use space=this // the level is the space
Spawn obstacle {
Rock(@(10, 10)) // Rock takes `space` from context
}
}
}

// Automatically receives `space` from context
pub fn obstacle.Rock(pos, [space]) {
use body=this
// Body receives `space` from context automatically.
// This is the same as writing: `Body(pos=, space=space)`
Body(pos=)
PolygonCollider(shape=Circle(radius=1), category=Category:Rock)
// ...
}

Default Space

The World entity is the default space for all bodies. If you create a body and do not specify a space, it will belong to the World space by default.

Null Space

Setting the space to null, either upon creating the Body or by changing the PhysicsSpace later, will move the body into the null space, which means it will not collide with anything, but also will no longer be simulated. More specifically, the position of the body will not be updated by the physics engine, even if its velocity is nonzero. Move the body back into a space to have it simulated again.

Querying Spaces

All the querying functions, like QueryNearest take a mandatory space parameter. They will only ever search one space at a time, whether that be one you specify, or the default World space.