Adding a Ball Config

For simplicity, we will wrap all of our Config objects into a single PongConfig object backed by a single config.ron file, but know that you can just as easily keep them in separate files and read from each file separately.

To prepare for our BallConfig, add the following line to the top of config.rs:

use amethyst::core::math::Vector2;

The BallConfig will replace the BALL_VELOCITY_X, BALL_VELOCITY_Y, BALL_RADIUS, and BALL_COLOR variables. We'll use a Vector2 to store the velocity for simplicity and to demonstrate how to add a non-trivial data type to a RON file. The BALL_COLOR was originally an array, but [Serde][serde] and RON handle arrays as tuples, so it will read in a tuple and convert the color values to an array if needed by a particular function (e.g., in pong.rs).

#[derive(Debug, Deserialize, Serialize)]
pub struct BallConfig {
    pub velocity: Vector2<f32>,
    pub radius: f32,
    pub color: (f32, f32, f32, f32),
}

We'll also add the Default trait to this config that will match what the full example uses.

impl Default for BallConfig {
    fn default() -> Self {
        BallConfig {
            velocity: Vector2::new(75.0, 50.0),
            radius: 2.5,
            color: (1.0, 0.0, 0.0, 1.0),
        }
    }
}

Still in config.rs, add the following structure definition at the very bottom. This structure will be backed by the whole config.ron file.

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct PongConfig {
    pub arena: ArenaConfig,
    pub ball: BallConfig,
}

Replacing Ball Constants

Now we need to replace our usage of the BALL_* constants with our new BallConfig.

We use these values in pong.rs in the initialise_ball() function, so the substitution is even simpler than the ArenaConfig.

In pong.rs, underneath our loading of the ArenaConfig, add the following lines

let (velocity_x, velocity_y, radius, color) = {
    let config = world.read_resource::<BallConfig>();
    let c: [f32; 4] = [
        config.color.0,
        config.color.1,
        config.color.2,
        config.color.3,
    ];
    (config.velocity.x, config.velocity.y, config.radius, c)
};

Our functions expect a [f32; 4] array, so we had to convert the tuple to an array. This is relatively simple to do, but for more complex arrays it might be worth it to add a function to the impl BallConfig to avoid duplicating this effort.

Now, within the initialise_ball function, replace BALL_VELOCITY_X with velocity_x, BALL_VELOCITY_Y with velocity_y, BALL_RADIUS with radius, and BALL_COLOR with color.

Modifying the initialisation

Now we will modify our application initialisation. We don't want everyone to always access all the config files, so we need to add each resource separately so systems can use only what they want.

First, we need to change what main.rs is using. Change

use crate::config::ArenaConfig;

to

use crate::config::PongConfig;

Now, modify the run() function, from

let arena_config = ArenaConfig::load(&config)?;
[..]
    .with_bundle(PongBundle::default())?
[..]
    .with_resource(arena_config)

to

let pong_config = PongConfig::load(&config)?;
[..]
    .with_bundle(PongBundle::default())?
[..]
    .with_resource(pong_config.arena)
    .with_resource(pong_config.ball)

Adding the BallConfig to config.ron

Now we need to modify our configuration file to allow multiple structures to be included. This is actually very easy with RON; we just add an additional level of nesting.

(
    arena: (
        height: 100.0,
        width: 100.0,
    ),
    ball: (
        velocity: Vector2(
            x: 75.0,
            y: 50.0,
        ),
        radius: 2.5,
        color: (1.0, 0.647, 0.0, 1.0),
    ),
)

This configuration sets the ball to be orange, while retaining the same size and velocity as the original example.