Build your own!

In this chapter we will guide you through building your own button in Amethyst!

Bulding blocks

The components you can use in order to build your button are as goes:

  • UiTransform - used for positioning your button on the screen (same as Transform but for the UI elements)

  • UiText - if you want your button to have any text displayed

  • UiImage - if you want your button to display a texture

You don't have to use all three at the same time of course but variations of two (UiTransfrom is always needed!).

Creating the UiTransform

One way of defining a UiTransform is like so:

extern crate amethyst;
use amethyst::ui::{Anchor, UiTransform};

let ui_transform = UiTransform::new(
    String::from("simple_button"), // id
    Anchor::Middle,                // anchor
    Anchor::Middle,                // pivot
    0f32,                          // x
    0f32,                          // y
    0f32,                          // z
    100f32,                        // width
    30f32,                         // height
);

The id field of the transform is basically like the name. You can use this in combination with the UiFinder to fetch the transfrom through a system.

Assuming the entity has no parent, whatever is set as the anchor field will be placed relative to the screen. In our case we set it to Anchor::Middle and it will be drawn in the middle of the screen. The pivot field will center the widget relative to itself - this in turn is the reason why our x and y fields are 0f32. The z field of this struct is used for "depth" ordering of the ui elements.

The width and height fields are also important. They represent the area that will register the events like hovering over with the mouse, clicking and dragging. If you built the entity with the UiText component this also determines if the text will be rendered, meaning you need to set the area big enough for the text to fit in!

Creating the UiText

extern crate amethyst;
use amethyst::assets::{AssetStorage, Loader};
use amethyst::ui::{Anchor, FontAsset, get_default_font, LineMode, UiText};
use amethyst::prelude::{World, WorldExt};

fn some_function(world: &mut World) {
   let font_handle = {
       let loader = world.read_resource::<Loader>();
       let font_storage = world.read_resource::<AssetStorage<FontAsset>>();
       get_default_font(&loader, &font_storage)
   };
    let ui_text = UiText::new(
        font_handle,                   // font
        String::from("Simple Button"), // text
        [1.0, 1.0, 1.0, 0.5],          // color
        25f32,                         // font_size
        LineMode::Single,              // line mode
        Anchor::Middle,                // alignment
    );
}

The text field of this struct is pretty self explanatory. It's what you would want to access if you were to dynamically change the text on the screen through systems.

You also need to load a specific font handle and provide it for the text.

If you had some state implemented you can create the button on its on_start method:

extern crate amethyst;
use amethyst::assets::{AssetStorage, Loader};
use amethyst::ui::{
    Anchor, FontAsset, get_default_font, LineMode, UiText, UiTransform,
};
use amethyst::prelude::{Builder, GameData, SimpleState, SimpleTrans, StateData, Trans, World, WorldExt};

pub struct State;

impl SimpleState for State {

fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) {
    let world = data.world;

    /* Create the transform */
    let ui_transform = UiTransform::new(
        // ...
        String::from("simple_button"), // id
        Anchor::Middle,                // anchor
        Anchor::Middle,                // pivot
        0f32,                          // x
        0f32,                          // y
        0f32,                          // z
        100f32,                        // width
        30f32,                         // height
    );

   let font_handle = {
       let loader = world.read_resource::<Loader>();
       let font_storage = world.read_resource::<AssetStorage<FontAsset>>();
       get_default_font(&loader, &font_storage)
   };

    /* Create the text */
    let ui_text = UiText::new(
        // ...
      font_handle,                   // font
      String::from("Simple Button"), // text
      [1.0, 1.0, 1.0, 0.5],          // color
      25f32,                         // font_size
      LineMode::Single,
      Anchor::Middle,
    );

    /* Building the entity */
    let _ = world.create_entity()
        .with(ui_transform)
        .with(ui_text)
        .build();
}

    fn update(&mut self, data: &mut StateData<'_, GameData<'_, '_>>) -> SimpleTrans {
        Trans::None
    }
}

It is recommended to keep the entity either in your state or some kind of resource so you can hide or delete it when you change the states (like changing menus)!

If you were to run this you would get a button in the middle of the screen saying "Simple Button", but you won't be able to interact with it (which doesn't actually make it a button yet)!

Interacting with the button!

In order for the ui to generate events you need to add an Interactable component to your entity (either when building it or dynamically).

This will not work if the entity doesn't have a UiTransform component!

The code snippet would look like this now:

extern crate amethyst;
use amethyst::assets::{AssetStorage, Loader};
use amethyst::ui::{
    Anchor, FontAsset, get_default_font, LineMode, UiText, UiTransform, Interactable,
};
use amethyst::ecs::{Builder, World, WorldExt};
use amethyst::prelude::{GameData, SimpleTrans, StateData};

fn some_function(world: &mut World) {
   let ui_transform = UiTransform::new(
        String::from("simple_button"), // id
        Anchor::Middle,                // anchor
        Anchor::Middle,                // pivot
        0f32,                          // x
        0f32,                          // y
        0f32,                          // z
        100f32,                        // width
        30f32,                         // height
   );

   let font_handle = {
       let loader = world.read_resource::<Loader>();
       let font_storage = world.read_resource::<AssetStorage<FontAsset>>();
       get_default_font(&loader, &font_storage)
   };
   /* Create the text */
   let ui_text = UiText::new(
      font_handle,                   // font
      String::from("Simple Button"), // text
      [1.0, 1.0, 1.0, 0.5],          // color
      25f32,                         // font_size
      LineMode::Single,
      Anchor::Middle,
   );

let _ = world.create_entity()
    .with(ui_transform)
    .with(ui_text)
    .with(Interactable)
    .build();
}