1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::*;

pub struct GameLoop<G, T: TimeTrait, W> {
    pub game: G,
    pub updates_per_second: u32,
    pub max_frame_time: f64,
    pub exit_next_iteration: bool,
    pub window: W,

    fixed_time_step: f64,
    number_of_updates: u32,
    number_of_renders: u32,
    last_frame_time: f64,
    running_time: f64,
    accumulated_time: f64,
    blending_factor: f64,
    previous_instant: T,
    current_instant: T,
}

impl<G, T: TimeTrait, W> GameLoop<G, T, W> {
    pub fn new(game: G, updates_per_second: u32, max_frame_time: f64, window: W) -> Self {
        Self {
            game,
            updates_per_second,
            max_frame_time,
            window,
            exit_next_iteration: false,

            fixed_time_step: 1.0 / updates_per_second as f64,
            number_of_updates: 0,
            number_of_renders: 0,
            running_time: 0.0,
            accumulated_time: 0.0,
            blending_factor: 0.0,
            previous_instant: T::now(),
            current_instant: T::now(),
            last_frame_time: 0.0,
        }
    }

    pub fn next_frame<U, R>(&mut self, mut update: U, mut render: R) -> bool
        where U: FnMut(&mut GameLoop<G, T, W>),
              R: FnMut(&mut GameLoop<G, T, W>),
    {
        let mut g = self;

        if g.exit_next_iteration {
            return false;
        }

        g.current_instant = T::now();

        let mut elapsed = g.current_instant.sub(&g.previous_instant);

        if elapsed > g.max_frame_time {
            elapsed = g.max_frame_time;
        }

        g.last_frame_time = elapsed;
        g.running_time += elapsed;
        g.accumulated_time += elapsed;

        while g.accumulated_time >= g.fixed_time_step {
            update(&mut g);

            g.accumulated_time -= g.fixed_time_step;
            g.number_of_updates += 1;
        }

        g.blending_factor = g.accumulated_time / g.fixed_time_step;

        render(&mut g);

        g.number_of_renders += 1;
        g.previous_instant = g.current_instant;

        return true;
    }

    pub fn re_accumulate(&mut self) {
        let mut g = self;

        g.current_instant = T::now();

        let prev_elapsed = g.last_frame_time;
        let new_elapsed = g.current_instant.sub(&g.previous_instant);

        let delta = new_elapsed - prev_elapsed;

        // We don't update g.last_frame_time since this additional time in the
        // render function is considered part of the current frame.

        g.running_time += delta;
        g.accumulated_time += delta;

        g.blending_factor = g.accumulated_time / g.fixed_time_step;
    }

    pub fn exit(&mut self) {
        self.exit_next_iteration = true;
    }

    pub fn fixed_time_step(&self) -> f64 {
        self.fixed_time_step
    }

    pub fn number_of_updates(&self) -> u32 {
        self.number_of_updates
    }

    pub fn number_of_renders(&self) -> u32 {
        self.number_of_renders
    }

    pub fn last_frame_time(&self) -> f64 {
        self.last_frame_time
    }

    pub fn running_time(&self) -> f64 {
        self.running_time
    }

    pub fn accumulated_time(&self) -> f64 {
        self.accumulated_time
    }

    pub fn blending_factor(&self) -> f64 {
        self.blending_factor
    }

    pub fn previous_instant(&self) -> T {
        self.previous_instant
    }

    pub fn current_instant(&self) -> T {
        self.current_instant
    }
}