mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-25 11:44:25 +03:00
start the sandbox mode, just with sim controls. remove unused sim score
plugin.
This commit is contained in:
parent
ce25f8bbf6
commit
1fabf29747
@ -1,4 +1,5 @@
|
||||
use crate::edit::EditMode;
|
||||
use crate::sandbox::SandboxMode;
|
||||
use crate::state::{Flags, UIState};
|
||||
use crate::tutorial::TutorialMode;
|
||||
use crate::ui::UI;
|
||||
@ -23,9 +24,10 @@ pub struct GameState {
|
||||
|
||||
pub enum Mode {
|
||||
SplashScreen(Wizard, Option<(Screensaver, XorShiftRng)>),
|
||||
Playing,
|
||||
Legacy,
|
||||
Edit(EditMode),
|
||||
Tutorial(TutorialMode),
|
||||
Sandbox(SandboxMode),
|
||||
}
|
||||
|
||||
impl GameState {
|
||||
@ -33,7 +35,7 @@ impl GameState {
|
||||
let splash = !flags.no_splash;
|
||||
let mut rng = flags.sim_flags.make_rng();
|
||||
let mut game = GameState {
|
||||
mode: Mode::Playing,
|
||||
mode: Mode::Legacy,
|
||||
ui: UI::new(UIState::new(flags, prerender, true), canvas),
|
||||
};
|
||||
if splash {
|
||||
@ -50,8 +52,7 @@ impl GameState {
|
||||
}
|
||||
|
||||
impl GUI for GameState {
|
||||
// TODO Don't display this unless mode is Playing! But that probably means we have to drag the
|
||||
// management of more ezgui state here.
|
||||
// TODO Totally get rid of this...
|
||||
fn top_menu(&self, canvas: &Canvas) -> Option<TopMenu> {
|
||||
self.ui.top_menu(canvas)
|
||||
}
|
||||
@ -76,7 +77,7 @@ impl GUI for GameState {
|
||||
}
|
||||
EventLoopMode::Animation
|
||||
}
|
||||
Mode::Playing => {
|
||||
Mode::Legacy => {
|
||||
let (event_mode, pause) = self.ui.new_event(ctx);
|
||||
if pause {
|
||||
self.mode = Mode::SplashScreen(Wizard::new(), None);
|
||||
@ -85,6 +86,7 @@ impl GUI for GameState {
|
||||
}
|
||||
Mode::Edit(_) => EditMode::event(self, ctx),
|
||||
Mode::Tutorial(_) => TutorialMode::event(self, ctx),
|
||||
Mode::Sandbox(_) => SandboxMode::event(self, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,9 +96,10 @@ impl GUI for GameState {
|
||||
self.ui.draw(g);
|
||||
wizard.draw(g);
|
||||
}
|
||||
Mode::Playing => self.ui.draw(g),
|
||||
Mode::Legacy => self.ui.draw(g),
|
||||
Mode::Edit(_) => EditMode::draw(self, g),
|
||||
Mode::Tutorial(_) => TutorialMode::draw(self, g),
|
||||
Mode::Sandbox(_) => SandboxMode::draw(self, g),
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,10 +168,11 @@ fn splash_screen(
|
||||
maybe_screensaver: &mut Option<(Screensaver, XorShiftRng)>,
|
||||
) -> Option<Mode> {
|
||||
let mut wizard = raw_wizard.wrap(&mut ctx.input, ctx.canvas);
|
||||
let play = "Play";
|
||||
let sandbox = "Sandbox mode";
|
||||
let load_map = "Load another map";
|
||||
let edit = "Edit map";
|
||||
let tutorial = "Tutorial";
|
||||
let legacy = "Legacy mode (ignore this)";
|
||||
let about = "About";
|
||||
let quit = "Quit";
|
||||
|
||||
@ -177,11 +181,11 @@ fn splash_screen(
|
||||
match wizard
|
||||
.choose_string(
|
||||
"Welcome to A/B Street!",
|
||||
vec![play, load_map, edit, tutorial, about, quit],
|
||||
vec![sandbox, load_map, edit, tutorial, legacy, about, quit],
|
||||
)?
|
||||
.as_str()
|
||||
{
|
||||
x if x == play => break Some(Mode::Playing),
|
||||
x if x == sandbox => break Some(Mode::Sandbox(SandboxMode::new())),
|
||||
x if x == load_map => {
|
||||
let current_map = ui.state.primary.map.get_name().to_string();
|
||||
if let Some((name, _)) = wizard.choose_something::<String>(
|
||||
@ -197,7 +201,7 @@ fn splash_screen(
|
||||
let mut flags = ui.state.primary.current_flags.clone();
|
||||
flags.sim_flags.load = PathBuf::from(format!("../data/maps/{}.abst", name));
|
||||
*ui = UI::new(UIState::new(flags, ctx.prerender, true), ctx.canvas);
|
||||
break Some(Mode::Playing);
|
||||
break Some(Mode::Sandbox(SandboxMode::new()));
|
||||
} else if wizard.aborted() {
|
||||
break Some(Mode::SplashScreen(Wizard::new(), maybe_screensaver.take()));
|
||||
} else {
|
||||
@ -210,6 +214,7 @@ fn splash_screen(
|
||||
ctx.canvas.center_to_map_pt(),
|
||||
)))
|
||||
}
|
||||
x if x == legacy => break Some(Mode::Legacy),
|
||||
x if x == about => {
|
||||
if wizard.acknowledge(LogScroller::new(
|
||||
"About A/B Street".to_string(),
|
||||
|
@ -4,6 +4,7 @@ mod game;
|
||||
mod objects;
|
||||
mod plugins;
|
||||
mod render;
|
||||
mod sandbox;
|
||||
mod state;
|
||||
mod tutorial;
|
||||
mod ui;
|
||||
|
@ -50,10 +50,6 @@ pub trait AmbientPlugin {
|
||||
fn draw(&self, _g: &mut GfxCtx, _ctx: &DrawCtx) {}
|
||||
}
|
||||
|
||||
pub trait AmbientPluginWithPrimaryPlugins {
|
||||
fn ambient_event_with_plugins(&mut self, _ctx: &mut PluginCtx, _plugins: &mut PluginsPerMap);
|
||||
}
|
||||
|
||||
pub trait NonblockingPlugin {
|
||||
// True means active; false means done, please destroy.
|
||||
fn nonblocking_event(&mut self, _ctx: &mut PluginCtx) -> bool;
|
||||
|
@ -1,209 +0,0 @@
|
||||
use crate::plugins::{AmbientPluginWithPrimaryPlugins, PluginCtx};
|
||||
use crate::state::PluginsPerMap;
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::EventLoopMode;
|
||||
use geom::Duration;
|
||||
use sim::{Benchmark, Sim, TIMESTEP};
|
||||
use std::mem;
|
||||
use std::time::Instant;
|
||||
|
||||
const ADJUST_SPEED: f64 = 0.1;
|
||||
|
||||
pub struct SimControls {
|
||||
desired_speed: f64, // sim seconds per real second
|
||||
state: State,
|
||||
}
|
||||
|
||||
enum State {
|
||||
Paused,
|
||||
Running {
|
||||
last_step: Instant,
|
||||
benchmark: Benchmark,
|
||||
speed: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl SimControls {
|
||||
pub fn new() -> SimControls {
|
||||
SimControls {
|
||||
desired_speed: 1.0,
|
||||
state: State::Paused,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_sim(&mut self, primary_sim: &mut Sim) {
|
||||
self.state = State::Running {
|
||||
last_step: Instant::now(),
|
||||
benchmark: primary_sim.start_benchmark(),
|
||||
speed: "running".to_string(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl AmbientPluginWithPrimaryPlugins for SimControls {
|
||||
fn ambient_event_with_plugins(
|
||||
&mut self,
|
||||
ctx: &mut PluginCtx,
|
||||
primary_plugins: &mut PluginsPerMap,
|
||||
) {
|
||||
if ctx.input.action_chosen("slow down sim") {
|
||||
self.desired_speed -= ADJUST_SPEED;
|
||||
self.desired_speed = self.desired_speed.max(0.0);
|
||||
}
|
||||
if ctx.input.action_chosen("speed up sim") {
|
||||
self.desired_speed += ADJUST_SPEED;
|
||||
}
|
||||
if ctx.input.action_chosen("reset sim") {
|
||||
// TODO Handle secondary sim
|
||||
// TODO Will the sudden change mess up the state in other plugins, or will they detect
|
||||
// the time change correctly?
|
||||
// TODO savestate_every gets lost
|
||||
ctx.primary.sim = Sim::new(
|
||||
&ctx.primary.map,
|
||||
ctx.primary.current_flags.sim_flags.run_name.clone(),
|
||||
None,
|
||||
);
|
||||
self.state = State::Paused;
|
||||
}
|
||||
|
||||
if ctx.secondary.is_some() && ctx.input.action_chosen("swap the primary/secondary sim") {
|
||||
println!("Swapping primary/secondary sim");
|
||||
// Check out this cool little trick. :D
|
||||
let (mut secondary, mut secondary_plugins) = ctx.secondary.take().unwrap();
|
||||
mem::swap(ctx.primary, &mut secondary);
|
||||
mem::swap(primary_plugins, &mut secondary_plugins);
|
||||
*ctx.secondary = Some((secondary, secondary_plugins));
|
||||
*ctx.recalculate_current_selection = true;
|
||||
}
|
||||
|
||||
match self.state {
|
||||
State::Paused => {
|
||||
if ctx.input.action_chosen("save sim state") {
|
||||
ctx.primary.sim.save();
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
s.sim.save();
|
||||
}
|
||||
}
|
||||
if ctx.input.action_chosen("load previous sim state") {
|
||||
match ctx
|
||||
.primary
|
||||
.sim
|
||||
.find_previous_savestate(ctx.primary.sim.time())
|
||||
.and_then(|path| Sim::load_savestate(path, None).ok())
|
||||
{
|
||||
Some(new_sim) => {
|
||||
// TODO From the perspective of other SimMode plugins, does this just
|
||||
// look like the simulation stepping forwards?
|
||||
ctx.primary.sim = new_sim;
|
||||
*ctx.recalculate_current_selection = true;
|
||||
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
s.sim = Sim::load_savestate(
|
||||
s.sim.find_previous_savestate(s.sim.time()).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
None => println!(
|
||||
"Couldn't load previous savestate {:?}",
|
||||
ctx.primary
|
||||
.sim
|
||||
.find_previous_savestate(ctx.primary.sim.time())
|
||||
),
|
||||
};
|
||||
}
|
||||
if ctx.input.action_chosen("load next sim state") {
|
||||
match ctx
|
||||
.primary
|
||||
.sim
|
||||
.find_next_savestate(ctx.primary.sim.time())
|
||||
.and_then(|path| Sim::load_savestate(path, None).ok())
|
||||
{
|
||||
Some(new_sim) => {
|
||||
ctx.primary.sim = new_sim;
|
||||
*ctx.recalculate_current_selection = true;
|
||||
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
s.sim = Sim::load_savestate(
|
||||
s.sim.find_next_savestate(s.sim.time()).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
None => println!(
|
||||
"Couldn't load next savestate {:?}",
|
||||
ctx.primary.sim.find_next_savestate(ctx.primary.sim.time())
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if ctx.input.action_chosen("run/pause sim") {
|
||||
self.run_sim(&mut ctx.primary.sim);
|
||||
} else if ctx.input.action_chosen("run one step of sim") {
|
||||
ctx.primary.sim.step(&ctx.primary.map);
|
||||
|
||||
*ctx.recalculate_current_selection = true;
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
s.sim.step(&s.map);
|
||||
}
|
||||
}
|
||||
}
|
||||
State::Running {
|
||||
ref mut last_step,
|
||||
ref mut benchmark,
|
||||
ref mut speed,
|
||||
} => {
|
||||
if ctx.input.action_chosen("run/pause sim") {
|
||||
self.state = State::Paused;
|
||||
} else {
|
||||
ctx.hints.mode = EventLoopMode::Animation;
|
||||
|
||||
if ctx.input.nonblocking_is_update_event() {
|
||||
// TODO https://gafferongames.com/post/fix_your_timestep/
|
||||
// TODO This doesn't interact correctly with the fixed 30 Update events
|
||||
// sent per second. Even Benchmark is kind of wrong. I think we want to
|
||||
// count the number of steps we've done in the last second, then stop if
|
||||
// the speed says we should.
|
||||
let dt_s = elapsed_seconds(*last_step);
|
||||
if dt_s >= TIMESTEP.inner_seconds() / self.desired_speed {
|
||||
ctx.input.use_update_event();
|
||||
ctx.primary.sim.step(&ctx.primary.map);
|
||||
|
||||
*ctx.recalculate_current_selection = true;
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
s.sim.step(&s.map);
|
||||
}
|
||||
*last_step = Instant::now();
|
||||
|
||||
if benchmark.has_real_time_passed(Duration::seconds(1.0)) {
|
||||
// I think the benchmark should naturally account for the delay of
|
||||
// the secondary sim.
|
||||
*speed = ctx.primary.sim.measure_speed(benchmark);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ctx.hints.osd.pad_if_nonempty();
|
||||
ctx.hints.osd.add_line(ctx.primary.sim.summary());
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
ctx.hints.osd.add_line("A/B test running!".to_string());
|
||||
ctx.hints.osd.add_line(s.sim.summary());
|
||||
}
|
||||
if let State::Running { ref speed, .. } = self.state {
|
||||
ctx.hints.osd.add_line(format!(
|
||||
"Speed: {0} / desired {1:.2}x",
|
||||
speed, self.desired_speed
|
||||
));
|
||||
} else {
|
||||
ctx.hints.osd.add_line(format!(
|
||||
"Speed: paused / desired {0:.2}x",
|
||||
self.desired_speed
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
pub mod controls;
|
||||
pub mod diff_all;
|
||||
pub mod diff_trip;
|
||||
pub mod show_score;
|
||||
pub mod time_travel;
|
||||
|
@ -1,84 +0,0 @@
|
||||
use crate::objects::DrawCtx;
|
||||
use crate::plugins::{NonblockingPlugin, PluginCtx};
|
||||
use ezgui::{Color, GfxCtx, HorizontalAlignment, Text, VerticalAlignment};
|
||||
use geom::Duration;
|
||||
use sim::ScoreSummary;
|
||||
|
||||
pub struct ShowScoreState {
|
||||
last_time: Duration,
|
||||
txt: Text,
|
||||
}
|
||||
|
||||
impl ShowScoreState {
|
||||
pub fn new(ctx: &mut PluginCtx) -> Option<ShowScoreState> {
|
||||
if ctx.input.action_chosen("show/hide sim info sidepanel") {
|
||||
return Some(panel(ctx));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl NonblockingPlugin for ShowScoreState {
|
||||
fn nonblocking_event(&mut self, ctx: &mut PluginCtx) -> bool {
|
||||
if ctx.input.action_chosen("show/hide sim info sidepanel") {
|
||||
return false;
|
||||
}
|
||||
if self.last_time != ctx.primary.sim.time() {
|
||||
*self = panel(ctx);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _ctx: &DrawCtx) {
|
||||
g.draw_blocking_text(
|
||||
&self.txt,
|
||||
(HorizontalAlignment::Right, VerticalAlignment::BelowTopMenu),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn panel(ctx: &mut PluginCtx) -> ShowScoreState {
|
||||
let mut txt = Text::new();
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
// TODO More coloring
|
||||
txt.add_line(ctx.primary.sim.get_name().to_string());
|
||||
summarize(&mut txt, ctx.primary.sim.get_score());
|
||||
txt.add_line(String::new());
|
||||
txt.add_line(s.sim.get_name().to_string());
|
||||
summarize(&mut txt, s.sim.get_score());
|
||||
} else {
|
||||
summarize(&mut txt, ctx.primary.sim.get_score());
|
||||
}
|
||||
ShowScoreState {
|
||||
last_time: ctx.primary.sim.time(),
|
||||
txt,
|
||||
}
|
||||
}
|
||||
|
||||
fn summarize(txt: &mut Text, summary: ScoreSummary) {
|
||||
txt.add_styled_line(
|
||||
"Walking".to_string(),
|
||||
None,
|
||||
Some(Color::RED.alpha(0.8)),
|
||||
None,
|
||||
);
|
||||
txt.add_line(format!(
|
||||
" {}/{} trips done",
|
||||
(summary.total_walking_trips - summary.pending_walking_trips),
|
||||
summary.pending_walking_trips
|
||||
));
|
||||
txt.add_line(format!(" {} total", summary.total_walking_trip_time));
|
||||
|
||||
txt.add_styled_line(
|
||||
"Driving".to_string(),
|
||||
None,
|
||||
Some(Color::BLUE.alpha(0.8)),
|
||||
None,
|
||||
);
|
||||
txt.add_line(format!(
|
||||
" {}/{} trips done",
|
||||
(summary.total_driving_trips - summary.pending_driving_trips),
|
||||
summary.pending_driving_trips
|
||||
));
|
||||
txt.add_line(format!(" {} total", summary.total_driving_trip_time));
|
||||
}
|
199
editor/src/sandbox/mod.rs
Normal file
199
editor/src/sandbox/mod.rs
Normal file
@ -0,0 +1,199 @@
|
||||
use crate::game::{GameState, Mode};
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Text, Wizard};
|
||||
use geom::Duration;
|
||||
use sim::{Benchmark, Sim, TripID};
|
||||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
|
||||
const ADJUST_SPEED: f64 = 0.1;
|
||||
|
||||
pub struct SandboxMode {
|
||||
desired_speed: f64, // sim seconds per real second
|
||||
state: State,
|
||||
|
||||
following: Option<TripID>,
|
||||
}
|
||||
|
||||
enum State {
|
||||
Paused,
|
||||
Running {
|
||||
last_step: Instant,
|
||||
benchmark: Benchmark,
|
||||
speed: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl SandboxMode {
|
||||
pub fn new() -> SandboxMode {
|
||||
SandboxMode {
|
||||
desired_speed: 1.0,
|
||||
state: State::Paused,
|
||||
following: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(state: &mut GameState, ctx: &mut EventCtx) -> EventLoopMode {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
match state.mode {
|
||||
// TODO confusing name? ;)
|
||||
Mode::Sandbox(ref mut mode) => {
|
||||
let mut txt = Text::new();
|
||||
txt.add_styled_line("Sandbox Mode".to_string(), None, Some(Color::BLUE), None);
|
||||
txt.add_line(state.ui.state.primary.sim.summary());
|
||||
if let State::Running { ref speed, .. } = mode.state {
|
||||
txt.add_line(format!(
|
||||
"Speed: {0} / desired {1:.2}x",
|
||||
speed, mode.desired_speed
|
||||
));
|
||||
} else {
|
||||
txt.add_line(format!(
|
||||
"Speed: paused / desired {0:.2}x",
|
||||
mode.desired_speed
|
||||
));
|
||||
}
|
||||
ctx.input
|
||||
.set_mode_with_new_prompt("Sandbox Mode", txt, ctx.canvas);
|
||||
|
||||
if ctx.input.modal_action("quit") {
|
||||
// TODO This shouldn't be necessary when we plumb state around instead of
|
||||
// sharing it in the old structure.
|
||||
state.ui.state.primary.sim = Sim::new(
|
||||
&state.ui.state.primary.map,
|
||||
state
|
||||
.ui
|
||||
.state
|
||||
.primary
|
||||
.current_flags
|
||||
.sim_flags
|
||||
.run_name
|
||||
.clone(),
|
||||
None,
|
||||
);
|
||||
state.mode = Mode::SplashScreen(Wizard::new(), None);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
state.ui.handle_mouseover(ctx, None);
|
||||
|
||||
if ctx.input.modal_action("slow down sim") {
|
||||
mode.desired_speed -= ADJUST_SPEED;
|
||||
mode.desired_speed = mode.desired_speed.max(0.0);
|
||||
}
|
||||
if ctx.input.modal_action("speed up sim") {
|
||||
mode.desired_speed += ADJUST_SPEED;
|
||||
}
|
||||
if ctx.input.modal_action("reset sim") {
|
||||
// TODO savestate_every gets lost
|
||||
state.ui.state.primary.sim = Sim::new(
|
||||
&state.ui.state.primary.map,
|
||||
state
|
||||
.ui
|
||||
.state
|
||||
.primary
|
||||
.current_flags
|
||||
.sim_flags
|
||||
.run_name
|
||||
.clone(),
|
||||
None,
|
||||
);
|
||||
mode.state = State::Paused;
|
||||
}
|
||||
|
||||
match mode.state {
|
||||
State::Paused => {
|
||||
if ctx.input.modal_action("save sim state") {
|
||||
state.ui.state.primary.sim.save();
|
||||
}
|
||||
if ctx.input.modal_action("load previous sim state") {
|
||||
let prev_state = state
|
||||
.ui
|
||||
.state
|
||||
.primary
|
||||
.sim
|
||||
.find_previous_savestate(state.ui.state.primary.sim.time());
|
||||
match prev_state
|
||||
.clone()
|
||||
.and_then(|path| Sim::load_savestate(path, None).ok())
|
||||
{
|
||||
Some(new_sim) => {
|
||||
state.ui.state.primary.sim = new_sim;
|
||||
//*ctx.recalculate_current_selection = true;
|
||||
}
|
||||
None => {
|
||||
println!("Couldn't load previous savestate {:?}", prev_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
if ctx.input.modal_action("load next sim state") {
|
||||
let next_state = state
|
||||
.ui
|
||||
.state
|
||||
.primary
|
||||
.sim
|
||||
.find_next_savestate(state.ui.state.primary.sim.time());
|
||||
match next_state
|
||||
.clone()
|
||||
.and_then(|path| Sim::load_savestate(path, None).ok())
|
||||
{
|
||||
Some(new_sim) => {
|
||||
state.ui.state.primary.sim = new_sim;
|
||||
//*ctx.recalculate_current_selection = true;
|
||||
}
|
||||
None => println!("Couldn't load next savestate {:?}", next_state),
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.input.modal_action("run/pause sim") {
|
||||
mode.state = State::Running {
|
||||
last_step: Instant::now(),
|
||||
benchmark: state.ui.state.primary.sim.start_benchmark(),
|
||||
speed: "...".to_string(),
|
||||
};
|
||||
} else if ctx.input.modal_action("run one step of sim") {
|
||||
state.ui.state.primary.sim.step(&state.ui.state.primary.map);
|
||||
//*ctx.recalculate_current_selection = true;
|
||||
}
|
||||
EventLoopMode::InputOnly
|
||||
}
|
||||
State::Running {
|
||||
ref mut last_step,
|
||||
ref mut benchmark,
|
||||
ref mut speed,
|
||||
} => {
|
||||
if ctx.input.modal_action("run/pause sim") {
|
||||
mode.state = State::Paused;
|
||||
} else if ctx.input.nonblocking_is_update_event() {
|
||||
// TODO https://gafferongames.com/post/fix_your_timestep/
|
||||
// TODO This doesn't interact correctly with the fixed 30 Update events sent
|
||||
// per second. Even Benchmark is kind of wrong. I think we want to count the
|
||||
// number of steps we've done in the last second, then stop if the speed says
|
||||
// we should.
|
||||
let dt_s = elapsed_seconds(*last_step);
|
||||
if dt_s >= sim::TIMESTEP.inner_seconds() / mode.desired_speed {
|
||||
ctx.input.use_update_event();
|
||||
state.ui.state.primary.sim.step(&state.ui.state.primary.map);
|
||||
//*ctx.recalculate_current_selection = true;
|
||||
*last_step = Instant::now();
|
||||
|
||||
if benchmark.has_real_time_passed(Duration::seconds(1.0)) {
|
||||
// I think the benchmark should naturally account for the delay of
|
||||
// the secondary sim.
|
||||
*speed =
|
||||
state.ui.state.primary.sim.measure_speed(benchmark, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
EventLoopMode::Animation
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(state: &GameState, g: &mut GfxCtx) {
|
||||
state.ui.new_draw(g, None, HashMap::new());
|
||||
}
|
||||
}
|
@ -2,8 +2,7 @@ use crate::colors::ColorScheme;
|
||||
use crate::objects::{DrawCtx, RenderingHints, ID};
|
||||
use crate::plugins;
|
||||
use crate::plugins::{
|
||||
debug, edit, view, AmbientPlugin, AmbientPluginWithPrimaryPlugins, BlockingPlugin,
|
||||
NonblockingPlugin, PluginCtx,
|
||||
debug, edit, view, AmbientPlugin, BlockingPlugin, NonblockingPlugin, PluginCtx,
|
||||
};
|
||||
use crate::render::DrawMap;
|
||||
use abstutil::{MeasureMemory, Timer};
|
||||
@ -59,11 +58,9 @@ pub struct UIState {
|
||||
|
||||
// These are stackable modal plugins. They can all coexist, and they don't block other modal
|
||||
// plugins or ambient plugins.
|
||||
show_score: Option<plugins::sim::show_score::ShowScoreState>,
|
||||
pub legend: Option<plugins::view::legend::Legend>,
|
||||
|
||||
// Ambient plugins always exist, and they never block anything.
|
||||
pub sim_controls: plugins::sim::controls::SimControls,
|
||||
pub layers: debug::layers::ToggleableLayers,
|
||||
|
||||
pub enable_debug_controls: bool,
|
||||
@ -83,9 +80,7 @@ impl UIState {
|
||||
secondary: None,
|
||||
exclusive_blocking_plugin: None,
|
||||
exclusive_nonblocking_plugin: None,
|
||||
show_score: None,
|
||||
legend: None,
|
||||
sim_controls: plugins::sim::controls::SimControls::new(),
|
||||
layers: debug::layers::ToggleableLayers::new(),
|
||||
enable_debug_controls,
|
||||
cs,
|
||||
@ -104,7 +99,7 @@ impl UIState {
|
||||
|
||||
// The exclusive_nonblocking_plugins don't color_obj.
|
||||
|
||||
// show_score, legend, hider, sim_controls, and layers don't color_obj.
|
||||
// legend, hider, and layers don't color_obj.
|
||||
for p in &self.primary_plugins.ambient_plugins {
|
||||
if let Some(c) = p.color_for(id, ctx) {
|
||||
return Some(c);
|
||||
@ -264,18 +259,6 @@ impl UIState {
|
||||
}
|
||||
|
||||
// Stackable modal plugins
|
||||
if self.show_score.is_some() {
|
||||
if !self
|
||||
.show_score
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.nonblocking_event(&mut ctx)
|
||||
{
|
||||
self.show_score = None;
|
||||
}
|
||||
} else if let Some(p) = plugins::sim::show_score::ShowScoreState::new(&mut ctx) {
|
||||
self.show_score = Some(p);
|
||||
}
|
||||
if self.legend.is_some() {
|
||||
if !self.legend.as_mut().unwrap().nonblocking_event(&mut ctx) {
|
||||
self.legend = None;
|
||||
@ -333,8 +316,6 @@ impl UIState {
|
||||
}
|
||||
|
||||
// Ambient plugins
|
||||
self.sim_controls
|
||||
.ambient_event_with_plugins(&mut ctx, &mut self.primary_plugins);
|
||||
for p in self.primary_plugins.ambient_plugins.iter_mut() {
|
||||
p.ambient_event(&mut ctx);
|
||||
}
|
||||
@ -360,9 +341,6 @@ impl UIState {
|
||||
}
|
||||
|
||||
// Stackable modals
|
||||
if let Some(ref p) = self.show_score {
|
||||
p.draw(g, ctx);
|
||||
}
|
||||
if let Some(ref p) = self.legend {
|
||||
p.draw(g, ctx);
|
||||
}
|
||||
|
@ -59,19 +59,9 @@ impl GUI for UI {
|
||||
Folder::new(
|
||||
"Simulation",
|
||||
vec![
|
||||
(Some(Key::LeftBracket), "slow down sim"),
|
||||
(Some(Key::RightBracket), "speed up sim"),
|
||||
(Some(Key::O), "save sim state"),
|
||||
(Some(Key::Y), "load previous sim state"),
|
||||
(Some(Key::U), "load next sim state"),
|
||||
(Some(Key::Space), "run/pause sim"),
|
||||
(Some(Key::M), "run one step of sim"),
|
||||
(Some(Key::Dot), "show/hide sim info sidepanel"),
|
||||
(Some(Key::T), "start time traveling"),
|
||||
(Some(Key::D), "diff all A/B trips"),
|
||||
(Some(Key::S), "seed the sim with agents"),
|
||||
(Some(Key::LeftAlt), "swap the primary/secondary sim"),
|
||||
(None, "reset sim"),
|
||||
],
|
||||
),
|
||||
Folder::new(
|
||||
@ -180,7 +170,7 @@ impl GUI for UI {
|
||||
),
|
||||
ModalMenu::new(
|
||||
"Stop Sign Editor",
|
||||
vec![(Key::Enter, "quit"), (Key::R, "reset to default")],
|
||||
vec![(Key::Escape, "quit"), (Key::R, "reset to default")],
|
||||
),
|
||||
ModalMenu::new(
|
||||
"Traffic Signal Editor",
|
||||
@ -197,6 +187,20 @@ impl GUI for UI {
|
||||
(Key::M, "add a new pedestrian scramble cycle"),
|
||||
],
|
||||
),
|
||||
ModalMenu::new(
|
||||
"Sandbox Mode",
|
||||
vec![
|
||||
(Key::Escape, "quit"),
|
||||
(Key::LeftBracket, "slow down sim"),
|
||||
(Key::RightBracket, "speed up sim"),
|
||||
(Key::O, "save sim state"),
|
||||
(Key::Y, "load previous sim state"),
|
||||
(Key::U, "load next sim state"),
|
||||
(Key::Space, "run/pause sim"),
|
||||
(Key::M, "run one step of sim"),
|
||||
(Key::R, "reset sim"),
|
||||
],
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -421,14 +421,22 @@ impl Sim {
|
||||
}
|
||||
|
||||
if benchmark.has_real_time_passed(Duration::seconds(1.0)) {
|
||||
println!("{}, {}", self.summary(), self.measure_speed(&mut benchmark));
|
||||
println!(
|
||||
"{}, speed = {}",
|
||||
self.summary(),
|
||||
self.measure_speed(&mut benchmark, true)
|
||||
);
|
||||
}
|
||||
callback(self, map);
|
||||
if Some(self.time()) == time_limit {
|
||||
panic!("Time limit {} hit", self.time);
|
||||
}
|
||||
if self.is_done() {
|
||||
println!("{}, {}", self.summary(), self.measure_speed(&mut benchmark));
|
||||
println!(
|
||||
"{}, speed = {}",
|
||||
self.summary(),
|
||||
self.measure_speed(&mut benchmark, true)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -458,7 +466,11 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
if benchmark.has_real_time_passed(Duration::seconds(1.0)) {
|
||||
println!("{}, {}", self.summary(), self.measure_speed(&mut benchmark));
|
||||
println!(
|
||||
"{}, speed = {}",
|
||||
self.summary(),
|
||||
self.measure_speed(&mut benchmark, true)
|
||||
);
|
||||
}
|
||||
if self.time() == time_limit {
|
||||
panic!(
|
||||
@ -530,19 +542,19 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn measure_speed(&self, b: &mut Benchmark) -> String {
|
||||
pub fn measure_speed(&self, b: &mut Benchmark, details: bool) -> String {
|
||||
let dt = Duration::seconds(abstutil::elapsed_seconds(b.last_real_time));
|
||||
if dt == Duration::ZERO {
|
||||
return format!("speed = instantly ({})", self.scheduler.describe_stats());
|
||||
return "...".to_string();
|
||||
}
|
||||
let speed = (self.time - b.last_sim_time) / dt;
|
||||
b.last_real_time = Instant::now();
|
||||
b.last_sim_time = self.time;
|
||||
format!(
|
||||
"speed = {:.2}x ({})",
|
||||
speed,
|
||||
self.scheduler.describe_stats()
|
||||
)
|
||||
if details {
|
||||
format!("{:.2}x ({})", speed, self.scheduler.describe_stats())
|
||||
} else {
|
||||
format!("{:.2}x", speed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user