Experiment with animation

This commit is contained in:
Dustin Carlino 2020-11-28 10:12:00 -08:00
parent 4b28cfe806
commit fad0358553
3 changed files with 101 additions and 7 deletions

View File

@ -0,0 +1,72 @@
use geom::{Duration, Pt2D, Time};
use widgetry::{Drawable, EventCtx, GeomBatch, GfxCtx};
pub struct Animator {
time: Time,
active: Vec<Effect>,
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
}
}

View File

@ -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<dyn Controller>,
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<SimpleApp> for Game {
}
if let Some(b) = self.over_bldg.key() {
if self.state.has_energy() && self.state.present_dropped(ctx, app, b) {
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<SimpleApp> 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<usize> {
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

View File

@ -1,3 +1,4 @@
mod animation;
mod controls;
mod game;
mod upzone;