Core Concepts

The Engine and Game Project

Chisel is split into two projects: the Engine and your game project. The engine handles all the low-level systems like physics, rendering, sound, entity management, and so on. Your game project references the engine and sits on top of it, providing all game-specific logic.

The entry point of your game is GameEngine.cs, which inherits from the engine's MainEngine class. This is where you initialize your game, load content, and hook into the update and draw loops. The FPS template provides a fully working GameEngine.cs to build from, which also has some handy utilities like shooting bullets and handling tracers, decals, particles, and the actual ray casting.

Entities

Everything that exists in the world is built from two parts working together: a WorldEntity and an EntityController.

The WorldEntity is the data side; it holds position, rotation, scale, physics body, properties, and the input/output connections set up in the editor. You create one by inheriting from WorldEntity and configuring it in the constructor:

[EntityDescriptor()]
public class Player : WorldEntity
{
    public Player()
    {
        controller = new BasePlayerController();
        bounds = new BoundingBox(...);
        isSimulated = true;
    }
}

You can see that we're actually setting up a few things here in this constructor; namely controller and bounds. Setting the controller in the constructor is vital, as it ensures that OnSpawn will properly be called for the controller. It is not advised to set or change controllers for an entity after this, but there isn't anything stopping you. The bounds are equally important to setup here. Without some kind of valid bounding box, the physics engine will fail to create a physics body for the entity, and will never try again. This can result in a lot of issues for you, so even if you're going to change the bounds immediately in the controller, just be sure to set something here.

The isSimulated flag is also important. It signifies that there should be a physics body created for this entity, and that physics body should be affected by physical influences like gravity and other entities. You can turn this off, and for certain select entities that might be the desired effect, but for most use cases this should be turned on. Noteable exceptions in the engine's own code are things like lights, logic entities, etc. Though, those also turn on the ignoreCollision flag, which should be pretty self explanitory.

The [EntityDescriptor] attribute is what makes the entity visible to the editor and the compiler. Without it, the editors won't know of it's existence. Sometimes this is desired, usually not.

The EntityController is the logic side; this is the class you write that gives the entity its behavior. It receives callbacks throughout the entity's lifetime:

OnSpawn()

Called when the entity enters the world. Initialize state, register inputs, and set up physics here.

OnUpdate(GameTime)

Called every frame. This is where your game logic lives.

OnRender(GameTime)

Called every frame during the render pass. Draw your models here.

OnBeforeRender(GameTime)

Called just before the main render pass. Used for things like rendering shadow textures.

OnTakeDamage(DamageInfo)

Called when the entity receives damage.

OnDespawn()

Called when the entity is removed from the world. Clean up anything that needs it here.

From inside a controller, you access the parent entity via entity. For example, entity.position, entity.velocity, or entity.AddImpulse().

Entity Inputs & Outputs

Chisel uses a map logic system nearly identical to Source Engine's input/output system. Entities expose named outputs that fire when something happens, and named inputs that can be triggered by other entities. Connections between them are set up entirely in the map editor.

For example, a button entity could have a Pressed output. In the editor you'd connect that output to a door entity's Open input, optionally with a delay or a number of times it can fire.

Inputs and outputs are declared using attributes on your WorldEntity subclass:

[RegisterEntityOutputs("DamageTaken", "Destroyed")]
[RegisterEntityInputs("AddVelocity", "SetPosition", "Destroy")]
[EntityDescriptor()]
public class MyEntity : WorldEntity { ... }

Declaring them with attributes makes them visible in the editor. To wire up the actual logic for an input, call RegisterInputLocally() inside your controller's OnSpawn():

RegisterInputLocally("Destroy", (s, f) => {
    EntityManager.DespawnEntity(entity);
});

Outputs are fired from code by calling entity.CallOutput(), which automatically invokes whatever connections the level designer has set up:

entity.CallOutput("DamageTaken", this);

A few extra features beyond Source's system: outputs support passing variables through to inputs using the ! prefix in the parameter field, and outputs can be configured to refire a set number of times with a delay between each firing.

Note "Pass Variables", the variables accessible with the '!' prefix, are kind of a weird topic. You can read more about them and other advanced output stuff here.

Maps

A map is a level file. It contains all of the brushes (solid geometry) and entities that make up a level. Maps are created in Rockwall 2, Chisel's map editor.

Raw maps are stored in the .rok format, a JSON-based file that holds everything the editor needs, including brushes in their editor form. Before the engine can load a map it has to be compiled. The map compiler processes the .rok file and produces a set of .clm and .cmap files, which are what the engine reads at runtime.

Compiled maps include baked lighting, BSP tree data for fast raycasting, PVS culling, and the final mesh geometry.

Note You'll never need to touch .clm or .cmap files directly. Always work with the .rok source file in Rockwall 2 and let the compiler handle the rest.

Brushes

Brushes are the solid geometry of a map; walls, floors, ceilings, platforms, and so on. They are convex solids constructed in the map editor represented by a collection of planes, similar to how geometry works in Quake or Source. Each face of a brush can have its own material applied to it.

Brushes can also be marked as trigger volumes, turning them into invisible regions that detect when entities enter them. Trigger brushes are commonly used to script level events without any visible geometry.

A special type of brush is a BrushEntity; a brush tied to an entity, giving it behavior. Doors, platforms, and other moving geometry are typically brush entities.