5 unstable releases
0.3.2 | Mar 28, 2023 |
---|---|
0.3.1 | Feb 2, 2023 |
0.3.0 | Jan 30, 2023 |
0.2.0 | Jan 29, 2023 |
0.1.0 | Jan 28, 2023 |
#777 in Game dev
28 downloads per month
30KB
219 lines
Solstack
Solstack is a library that enables the management of an application's control flow through a state stack machine.
A Stack
struct
holds any number of State
s. When said Stack
is ticked, it executes the main methods of the topmost State
. This means that only the State
at the top of the Stack
is executed, while the ones below it are effectively paused. An exception are the State
's on_shadow_tick
method introduced in v0.3.0
which is always executed independently of the State
's position on the Stack
.
- You can read The Book.
- Take a look at the examples on the git repository.
- Search documentation at the crate's docs.
- Watch the project develop through its changelog!
Project is in early development, things may change around! Take a look at the changelog before updating. Some text may imply that things are done, when they're not, like The Book. There may be bugs, though the tests should guarantee there aren't many.
Overview
State
s have all of the methods you'll need. Here's an overview:
on_start
— executed when thisState
is first pushed onto theStack
.on_stop
— executed when thisState
is popped from theStack
.on_pause
— executed when anotherState
is pushed on top of this one in theStack
.on_resume
— executed when this all of theState
s above this one are popped from theStack
.on_tick
— executed every time theStack
holding thisState
has it'stick
method called.on_shadow_tick
— same ason_tick
, but is always run independently of thisState
's position in theStack
.
To hop between State
s you'll return a Trans
ition enum
from on_tick
or similar methods, requesting the Stack
to perform such transition next tick. Here's an overview:
Trans::None
— requests theStack
to do nothing.Trans::Quit
— requests theStack
toPop
everyState
it is holding.Trans::Push(Box::new(State))
— requests theStack
toPush
the providedState
on top.Trans::Pop
— requests theStack
to pop the topmostState
it is holding, deleting it.Trans::Replace(Box::new(State))
— requests theStack
toPop
once andPush
the providedState
.Trans::Isolate(Box::new(State))
— requests theStack
toPop
everything andPush
the providedState
.
Transitions may be requested directly of the
Stack
or by returning aTrans
from inside aon_tick
oron_shadow_tick
method of aState
.
Features
-
An easy to implement
State
trait
. -
An easy to use State
Stack
Machinestruct
. -
An easy to use set of
Trans
itions betweenStates
. -
New in
v0.3.0
:on_shadow_tick
is provided onState
s; similar toon_tick
but is always executed independently of theState
's position in theStack
.
Use case
Imagine you're writing a game. You need a way of controlling the flow of your program. From the main menu to the game itself; from the game to the pause menu; or from the pause menu to quitting the program.
Solstack will help you structure these State
s and the Trans
itions between them.
Let's model something. Here are our State
s (prefixed with S
):
SMainMenu
: where the player lands on initializing the game.SGame
: where the actual gameplay logic resides.SPauseMenu
: where the player can save, resume playing or exit.
Since the SMainMenu
is the first thing the user will encounter, we'll manually Push
that State
on our Stack
when the program begins. Then we'll tick the Stack
in a main loop until there are no State
s inside it anymore.
You can manually perform transitions on a
Stack
by using it's methods on your local instance.The loop can be achieved by using the
Stack
'sis_running
method.
The Stack
, at the beginning of our program, will look like this:
SMainMenu
With only one sate on the Stack
, it is on top; and so it will have it's methods called. SMainMenu
's logic is simple: when the player presses START, it requests the Stack
to Push
an SGame
. If that happens, the Stack
would look like this:
SGame
SMainMenu
Since only the topmost state is run by the stack's tick
, SMainMenu
just sits there. Now the player is enjoying their game; but they wish to pause! Well, inside SGame
all we have to do is request the stack to Push
an SPauseMenu
if the player ever presses ESC. Simple! Let's see the Stack
again:
SPauseMenu
SGame
SMainMenu
Now SPauseMenu
is at the top. SGame
will be paused; it's still there, but it is not being executed. Inside the SPauseMenu
there should be logic saying that if the player presses ESC again, the Stack
should Pop
. Pop
ping means removing or completely deleting the topmost State
at the Stack
. In this case, SPauseMenu
itself. The Stack
would then, again, look like this:
SGame
SMainMenu
Finally, SGame
is at the top again! And so, it will resume exactly where it left off! If the player chooses to quit the game, you simply request the Stack
to Quit
, which will Pop
every State
it has, making the main loop end.
This concept can be extended to much bigger patterns; and hopefully you'll find joy in structuring your application with solstack
!
Get started
This is just a very simplistic example in code. Take a look at the links at the start of this page, specially the examples on the project's repository. The tests are also a good way of seeing how the library works internally.
use solstack::prelude::*;
use solstack::macros::*; // easy abstractions over boilerplate-y code.
// data available to `State`s for writing and reading
#[derive(Default)]
struct GameData {
value: i32
}
// a `State` that does what it says
struct AddOneAndPrintState;
impl State<GameData> for AddOneAndPrintState {
// run when this `State` is first pushed onto a `Stack`
fn on_start(&mut self, data: &mut GameData) {
data.value = 41;
println!("on_start `make data be 41` > GameData({})", data.value);
}
// run every time the `Stack` is ticked.
fn on_tick(&mut self, data: &mut GameData) -> Trans<GameData> {
data.value += 1;
println!("on_tick `add one to data` > GameData({})", data.value);
Trans::None
}
}
fn main() {
// initializing
let mut data = GameData::default();
let mut stack = Stack::<GameData>::new();
assert_eq!(data.value, 0);
// manually pushing and ticking the `Stack`
stack_push!(stack, data, AddOneAndPrintState);
assert_eq!(data.value, 41);
stack_tick!(stack, data);
assert_eq!(data.value, 42);
stack_tick!(stack, data);
assert_eq!(data.value, 43);
}
Thanks
The documentation will always be up to date.
Thank you for using solstack
!
By Sol solmateusbraga@gmail.com