The Piston core is now 0.1 and working on Rust Beta!

There are still some important libraries that requires Rust Nightly for overall game and app development, so Rust Nightly is recommended until further notice.

Getting started

To get started, I recommend looking at the piston-examples or piston-tutorials.

The Piston core

Today, the Piston core is minimalistic in design, and consists of 4 libraries:

  • pistoncore-event
  • pistoncore-event_loop
  • pistoncore-window
  • pistoncore-input

These 4 libraries are reexported in one crate called “piston”. The main purpose of the core is to provide an event loop:

for e in piston::events(window) { ... }

This loop emits events that tells you when to render, update or handle input. It is deterministic, uses fixed time step for updates and maximum frame rate. By default this is 120 ups and 60 max fps. You can pick your own settings:

for e in piston::events(window).max_fps(30).ups(200) { ... }

Some glue code is required to initialize the window. To do that you need to pick a window back-end. Piston has back-ends for GLFW/Glutin/SDL2. To learn how to set up a window, check out the examples

The iterator design for the game loop makes it easier to swap context between scenes. For example, if you are making an adventure game you could make one game loop per room, or nest them within each other!

for e in piston::events(window.clone()) {
    // Inside the temple of doom.
    ...
    if open_secret_door {
        for e in piston::events(window.clone()) {
            // Grab the ancient sword
            ...
        }
    }
}

Another usage is having a separate loop for an in-game editor. The flexibility of the loop makes it possible to load assets in a separate step from application setup, such that when the player dies in a level he or she does not have to wait for the level to reload. Using the stack for reloading a level also eliminates a class of bugs where the application state is not properly reset.

Most window APIs in Rust provides an event loop, but not a game loop. Often you have to write your own, and this is hard to get right.

In some applications running background tasks can make it less responsive. The “idle” event tells you how many seconds the thread is expected to sleep, and when running micro background tasks taking shorter than this interval, the input accurary becomes improved, making it more responsive! This is also true for input events that takes some time to process.

for e in piston::events(window) {
    if let Some(args) = e.idle_args() {
        if args.dt > 0.001 {
          // Run a micro background task..
        }
    }
}

In some games, the rendering might take too long time for a few frames, and fixing this to stay at max fps can be very hard. This leads to a small delay in gameplay. One way to fix this by extrapolating the delta time in the render event, and use this to interpolate animation/physics when rendering.

for e in piston::events(window) {
    if let Some(args) = e.render_args() {
        // Extrapolate the motion to make it appear smoother.
        let p = pos + dir * args.ext_dt;
        ...
        // render
    }
}

Event programming

piston::event::GenericEvent is a trait that uses Any to build an abstraction for other events. For example, RenderEvent is implemented for all objects that implements GenericEvent. When you are calling a method that takes E: GenericEvent, you do not know which events it handles. This type of design is called “event threading”.

This has both drawbacks and benefits, but the major benefits are:

  • Custom events can be added to a specific back-end without changing code
  • Supporting future hardware without major redesign
  • When using a controller, you only need to call one function
  • It is easy to combine with other event logic patterns

A widget often captures the all input from the user. In a game, the user input often goes to the player controller object. For example, feeding events to a player object could look like this:

player.event(&e);

This is so simple design as it can be, with no event handlers to keep track of.

For more advanced event logic there are various libraries you can use. There is a lot of research going on in this area, including AI behavior trees and Functional Reactional Programming. These patterns targets more complex event logic problems.

AI behavior trees is a way to describe sequantial, conditional and parallel events in a declarative manner which gets transformed into a state machine. This is suitable to problems like “the character is running down the stairs while pointing with the gun”, with other words, things that happens at the same time but simulated sequentially. One can assign a “behavior” to objects and this automatically keeps track of the state. A surprising attribute of this logic is that it is very intuitive and close to common sense, and therefore nice for programming game AI. The groundwork for this was completed last year, and needs more real world testing.

Functional Reactional Programming is hot topic in event logic. If you are interested in this, then you might want to check out Carboxyl. This is not currently an active research topic in the Piston project, but if you want to work on this, then you are very welcome!

Graphics

Piston is a bit special in the way that the graphics API is decoupled from the core. You can write a game using any 2D/3D API you like.

Piston-Graphics is a project to create a back-end agnostic 2D graphic API for 2D games or widgets.

The goal is that by plugging in a graphics back-end into your project, you can use the libraries in the Piston ecosystem together with the API you choose. This makes it easier to share code across projects.

Conrod is an immediate mode user interface for Piston. It is built on top of piston-graphics and completely back-end agnostic. This is still early in the development, but we welcome people who wants to contribute!

Other projects - find a project you are interested in, or start your own!

Some projects running in parallel:

  • image a library for encoding/decoding images and image processing
  • Hematite has the goal of making client/server for Minecraft in Rust
  • skeletal_animation character animation with import from Collada 3D
  • VisualRust a Rust plugin to Visual Studio
  • sprite a library for sprite animation

If you are interested in starting your own project, please open an issue on the Piston repo, we can help you!