State Interaction
Let's declare our state, and call it MenuState
:
extern crate amethyst;
use amethyst::ecs::Entity;
#[derive(Default)]
pub struct MenuState {
button: Option<Entity>,
}
We give it a field named button
which will hold an entity wrapped in
an Option<T>
. This simplifies things since we can now derive Default
trait on it and we can make it as our initial state that the application
will start off as.
It will also serve to hold our ui entity.
In our on_start
method of this state we can create the button as shown in
previous chapters, but here we will save the entity in our struct:
extern crate amethyst;
use amethyst::{
assets::{AssetStorage, Loader},
ecs::{Entity, World, WorldExt},
ui::{Anchor, FontHandle, Interactable, LineMode, TtfFormat, UiText, UiTransform},
prelude::{Builder, GameData, SimpleState, StateData},
};
#[derive(Default)]
pub struct MenuState {
button: Option<Entity>,
}
impl SimpleState for MenuState {
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
);
/* Create the text */
let font_handle = world.read_resource::<Loader>().load(
"font/square.ttf",
TtfFormat,
(),
&world.read_resource(),
);
let ui_text = UiText::new(
// ...
font_handle, // font
String::from("Simple Button"), // text
[1.0f32, 1.0f32, 1.0f32, 0.5f32], // color
25f32, // font_size
LineMode::Single, // line_mode
Anchor::Middle, // align
);
/* Building the entity */
let btn = world.create_entity()
.with(ui_transform)
.with(ui_text)
.with(Interactable)
.build();
/* Saving the button in our state struct */
self.button = Some(btn);
}
}
All the input received will be handled in the handle_event method of our state:
extern crate amethyst;
use amethyst::{
assets::{AssetStorage, Loader},
ecs::{Entity, World, WorldExt},
ui::{Anchor, FontHandle, Interactable, LineMode, TtfFormat, UiEventType, UiText, UiTransform},
prelude::{Builder, GameData, SimpleState, StateData, SimpleTrans},
StateEvent,
};
#[derive(Default)]
pub struct MenuState {
button: Option<Entity>,
}
impl SimpleState for MenuState {
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
);
/* Create the text */
let font_handle = world.read_resource::<Loader>().load(
"font/square.ttf",
TtfFormat,
(),
&world.read_resource(),
);
let ui_text = UiText::new(
font_handle, // font
String::from("Simple Button"), // text
[1.0f32, 1.0f32, 1.0f32, 0.5f32], // color
25f32, // font_size
LineMode::Single, // line_mode
Anchor::Middle, // align
);
/* Building the entity */
let btn = world.create_entity()
.with(ui_transform)
.with(ui_text)
.with(Interactable)
.build();
/* Saving the button in our state struct */
self.button = Some(btn);
}
fn handle_event(
&mut self,
_data: StateData<'_, GameData<'_, '_>>,
event: StateEvent) -> SimpleTrans {
if let StateEvent::Ui(ui_event) = event {
let is_target = ui_event.target == self.button.unwrap();
match ui_event.event_type {
UiEventType::Click if is_target => {
/* . . . */
},
_ => {
return SimpleTrans::None;
},
};
}
SimpleTrans::None
}
}
We only care about the UiEvent
s here, that's why we can use the if-let
pattern.
Then we check if the ui target is the same as our saved entity, in this case it
surely is since we've only built one entity. After there's a check for click
event and an additional if statement for our button entity. If it goes well it will
enter that branch.
In this branch you can do whatever you like, either quit if you have a QUIT
button
and the user clicks on it, in that case we would return a Trans::Quit
, otherwise
probably something else.
Let's assume something was pushed on top our MenuState
we would need these two methods:
Upon pushing another state the on_pause
method will run - here we can hide our button.
The way we do that is by adding a Hidden
component to our button:
extern crate amethyst;
use amethyst::{
assets::{AssetStorage, Loader},
core::Hidden,
ecs::{Entity, World, WorldExt},
ui::{Anchor, FontHandle, Interactable, LineMode, TtfFormat, UiEventType, UiText, UiTransform},
prelude::{Builder, GameData, SimpleState, StateData, SimpleTrans},
StateEvent
};
#[derive(Default)]
pub struct MenuState {
button: Option<Entity>,
}
impl SimpleState for MenuState {
// ...
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
);
/* Create the text */
let font_handle = world.read_resource::<Loader>().load(
"font/square.ttf",
TtfFormat,
(),
&world.read_resource(),
);
let ui_text = UiText::new(
font_handle, // font
String::from("Simple Button"), // text
[1.0f32, 1.0f32, 1.0f32, 0.5f32], // color
25f32, // font_size
LineMode::Single, // line_mode
Anchor::Middle, // align
);
/* Building the entity */
let btn = world.create_entity()
.with(ui_transform)
.with(ui_text)
.with(Interactable)
.build();
/* Saving the button in our state struct */
self.button = Some(btn);
}
fn handle_event(
&mut self,
_data: StateData<'_, GameData<'_, '_>>,
event: StateEvent) -> SimpleTrans {
if let StateEvent::Ui(ui_event) = event {
let is_target = ui_event.target == self.button.unwrap();
match ui_event.event_type {
UiEventType::Click if is_target => {
/* . . . */
},
_ => {
return SimpleTrans::None;
},
};
}
SimpleTrans::None
}
fn on_pause(&mut self, data: StateData<'_, GameData<'_, '_>>) {
let world = data.world;
let mut hiddens = world.write_storage::<Hidden>();
if let Some(btn) = self.button {
let _ = hiddens.insert(btn, Hidden);
}
}
}
The same goes for on_resume
if we actually want to redisplay the button:
extern crate amethyst;
use amethyst::{
assets::{AssetStorage, Loader},
core::Hidden,
ecs::{Entity, World, WorldExt},
ui::{Anchor, FontHandle, Interactable, LineMode, TtfFormat, UiEventType, UiText, UiTransform},
prelude::{Builder, GameData, SimpleState, StateData, SimpleTrans},
StateEvent
};
#[derive(Default)]
pub struct MenuState {
button: Option<Entity>,
}
impl SimpleState for MenuState {
// ...
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
);
/* Create the text */
let font_handle = world.read_resource::<Loader>().load(
"font/square.ttf",
TtfFormat,
(),
&world.read_resource(),
);
let ui_text = UiText::new(
font_handle, // font
String::from("Simple Button"), // text
[1.0f32, 1.0f32, 1.0f32, 0.5f32], // color
25f32, // font_size
LineMode::Single, // line_mode
Anchor::Middle, // align
);
/* Building the entity */
let btn = world.create_entity()
.with(ui_transform)
.with(ui_text)
.with(Interactable)
.build();
/* Saving the button in our state struct */
self.button = Some(btn);
}
fn handle_event(
&mut self,
_data: StateData<'_, GameData<'_, '_>>,
event: StateEvent) -> SimpleTrans {
if let StateEvent::Ui(ui_event) = event {
let is_target = ui_event.target == self.button.unwrap();
match ui_event.event_type {
UiEventType::Click if is_target => {
/* . . . */
},
_ => {
return SimpleTrans::None;
},
};
}
SimpleTrans::None
}
fn on_pause(&mut self, data: StateData<'_, GameData<'_, '_>>) {
let world = data.world;
let mut hiddens = world.write_storage::<Hidden>();
if let Some(btn) = self.button {
let _ = hiddens.insert(btn, Hidden);
}
}
fn on_resume(&mut self, data: StateData<'_, GameData<'_, '_>>) {
let world = data.world;
let mut hiddens = world.write_storage::<Hidden>();
if let Some(btn) = self.button {
let _ = hiddens.remove(btn);
}
}
}
This should provide you with the basic knowledge on building the UI.