Heya.
Some context:
I'm currently following along the Hands-on Rust book, and I want to convert the game from the turn-based structure in the book to a real-time one. I'm using the Legion ECS crate for all the ECS stuff, and Bracketlib for the rendering/console stuff.
The book implements a turn-based game structure with three Schedules (referring to the Legion ECS systems scheduler):
input_systems
player_systems
monster_systems
These are tied to three game states:
TurnState::AwaitingInput -> input_systems
TurnState::PlayerTurn -> player_systems
TurnState::MonsterTurn -> monster_systems
The Schedulers look like this:
-
build_input_scheduler
.add_system( player_input_system() )
.add_system( fov_system() )
.flush()
.add_system( map_render_system() )
.add_system( entity_render_system() )
.add_system( hud_system() )
.add_system( tooltips_system() )
.build()
-
build_player_scheduler
.add_system( combat_system() )
.add_system( flush() )
.add_system( movement_system() )
.add_system( flush() )
.add_system( fov_system() )
.add_system( flush() )
.add_system( map(), entity(), hud() )
.add_system( *end_turn_system()* )
.build()
-
build_monster_scheduler
.add_system( random_move_system() )
.add_system( chasing_system() )
.add_system( combat_system() )
.add_system( movement_system() )
.add_system( fov_system() )
.add_system( map(), entity(), hud() )
.add_system( *end_turn_system()* )
.build()
(abbreviated so as to not get too long-winded)
So right off the bat it seems like there is a lot of repetition, they seem to share a lot of the systems, but maybe legion takes care of that behind the scenes?
In the end_turn_system, there is this relevant state switching code:
let mut new_state = match current_state {
TurnState::AwaitingInput => return,
TurnState::PlayerTurn => TurnState::MonsterTurn,
TurnState::MonsterTurn => TurnState::AwaitingInput
_ => current_state
};
The TurnState enum has these variations:
- AwaitingInput
- PlayerTurn
- MonsterTurn
- GameOver
- Victory
So far I've changed the TurnState to be GamePlay, GameOver, and Victory, and added changed the schedulers to be one "main"/realtime one, and one for rendering.
The current realtime version looks like this:
pub fn build_realtime_scheduler() -> Schedule {
Schedule::builder()
.add_system(player_input::player_input_system())
.add_system(random_move::random_move_system())
.add_system(chasing::chasing_system())
.flush()
.add_system(combat::combat_system())
.flush()
.add_system(movement::movement_system())
.flush()
.add_system(fov::fov_system())
.flush()
.add_system(end_turn::end_turn_system())
.build()
}
With a separate schedule for rendering:
pub fn build_render_scheduler() -> Schedule {
Schedule::builder()
.flush()
.add_system(map_render::map_render_system())
.add_system(entity_render::entity_render_system())
.add_system(hud::hud_system())
.add_system(tooltips::tooltips_system())
.build()
}
I think I've sat on this for too long and gotten myself confused, but I can't think of how to divide up the systems when it's a real-time simulation I want -- without affecting how responsive the game feels.
I have a resource in the world for frame_time_ms, which adds the elapsed time since the last update. If that is over my pub const TURN_TIME: f32 = 60.0
, the game runs the gameplay systems.
I feel like I have the parts needed, just need some help on how to stitch it all together.
The entire code is on github.com/harofax/mt_septune I've probably forgotten some prudent info to include. (There's a branch called real-time-refactor where I've pushed how the code looks today)
Sorry for the absolutely massive post, and hello! This is my first time posting here! ^^