From fad0358553f9b45f028e9053e5752b078edb3aed Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Sat, 28 Nov 2020 10:12:00 -0800 Subject: [PATCH] Experiment with animation --- experiment/src/animation.rs | 72 +++++++++++++++++++++++++++++++++++++ experiment/src/game.rs | 35 ++++++++++++++---- experiment/src/main.rs | 1 + 3 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 experiment/src/animation.rs diff --git a/experiment/src/animation.rs b/experiment/src/animation.rs new file mode 100644 index 0000000000..d4a8bb4d9c --- /dev/null +++ b/experiment/src/animation.rs @@ -0,0 +1,72 @@ +use geom::{Duration, Pt2D, Time}; +use widgetry::{Drawable, EventCtx, GeomBatch, GfxCtx}; + +pub struct Animator { + time: Time, + active: Vec, + draw_current: Drawable, +} + +struct Effect { + start: Time, + end: Time, + orig: GeomBatch, + center: Pt2D, + // Scaling is the only transformation for now + lerp_scale: (f64, f64), +} + +impl Animator { + pub fn new(ctx: &EventCtx) -> Animator { + Animator { + time: Time::START_OF_DAY, + active: Vec::new(), + draw_current: Drawable::empty(ctx), + } + } + + pub fn add( + &mut self, + duration: Duration, + lerp_scale: (f64, f64), + center: Pt2D, + orig: GeomBatch, + ) { + self.active.push(Effect { + start: self.time, + end: self.time + duration, + orig, + lerp_scale, + center, + }); + } + + pub fn event(&mut self, ctx: &mut EventCtx) { + if let Some(dt) = ctx.input.nonblocking_is_update_event() { + self.time += dt; + if self.active.is_empty() { + return; + } + let mut batch = GeomBatch::new(); + let time = self.time; + self.active.retain(|effect| effect.update(time, &mut batch)); + self.draw_current = ctx.upload(batch); + } + } + + pub fn draw(&self, g: &mut GfxCtx) { + g.redraw(&self.draw_current); + } +} + +impl Effect { + fn update(&self, time: Time, batch: &mut GeomBatch) -> bool { + let pct = (time - self.start) / (self.end - self.start); + if pct > 1.0 { + return false; + } + let scale = self.lerp_scale.0 + pct * (self.lerp_scale.1 - self.lerp_scale.0); + batch.append(self.orig.clone().scale(scale).centered_on(self.center)); + true + } +} diff --git a/experiment/src/game.rs b/experiment/src/game.rs index add8b0f3f5..5d046530ae 100644 --- a/experiment/src/game.rs +++ b/experiment/src/game.rs @@ -10,6 +10,7 @@ use widgetry::{ Line, Outcome, Panel, State, Text, TextExt, Transition, UpdateType, VerticalAlignment, Widget, }; +use crate::animation::Animator; use crate::controls::{Controller, InstantController, RotateController}; const ZOOM: f64 = 10.0; @@ -18,6 +19,7 @@ pub struct Game { panel: Panel, controls: Box, minimap: SimpleMinimap, + animator: Animator, sleigh: Pt2D, state: SleighState, @@ -71,6 +73,7 @@ impl Game { panel, controls: Box::new(InstantController::new()), minimap: SimpleMinimap::new(ctx, app, with_zorder), + animator: Animator::new(ctx), sleigh, state, @@ -166,15 +169,27 @@ impl State for Game { } if let Some(b) = self.over_bldg.key() { - if self.state.has_energy() && self.state.present_dropped(ctx, app, b) { - self.over_bldg.clear(); - self.update_panel(ctx); + if self.state.has_energy() { + if let Some(increase) = self.state.present_dropped(ctx, app, b) { + self.over_bldg.clear(); + self.update_panel(ctx); + self.animator.add( + Duration::seconds(0.5), + (1.0, 4.0), + app.map.get_b(b).label_center, + Text::from(Line(format!("+{}", prettyprint_usize(increase)))) + .bg(Color::RED) + .render_to_batch(ctx.prerender) + .scale(0.1), + ); + } } } if let Some(t) = self.minimap.event(ctx, app) { return t; } + self.animator.event(ctx); match self.panel.event(ctx) { Outcome::Clicked(x) => match x.as_ref() { @@ -242,6 +257,7 @@ impl State for Game { Color::RED, Circle::new(self.sleigh, Distance::meters(5.0)).to_polygon(), ); + self.animator.draw(g); } } @@ -367,16 +383,21 @@ impl SleighState { self.draw_all_depots = ctx.upload(batch); } - // True if state change - fn present_dropped(&mut self, ctx: &mut EventCtx, app: &SimpleApp, id: BuildingID) -> bool { + // If something changed, return the update to the score + fn present_dropped( + &mut self, + ctx: &mut EventCtx, + app: &SimpleApp, + id: BuildingID, + ) -> Option { if let Some(BldgState::Undelivered { score, cost }) = self.houses.get(&id).cloned() { self.score += score; self.houses.insert(id, BldgState::Done); self.energy -= cost; self.redraw(ctx, app); - return true; + return Some(score); } - false + None } // True if state change diff --git a/experiment/src/main.rs b/experiment/src/main.rs index da0a5f6889..1a3a34b31e 100644 --- a/experiment/src/main.rs +++ b/experiment/src/main.rs @@ -1,3 +1,4 @@ +mod animation; mod controls; mod game; mod upzone;