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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

use std::time::{Duration, Instant};

const NANOS_PER_SECOND: f64 = 1_000_000_000.0;

pub struct FixedStep {
    last_time: Instant,
    update_interval: Duration,
    accumulator: Duration,
    update_counter: u32,
    update_limit: u32,
}

impl FixedStep {
    /// Create and start a new fixedstep timer with the given frequency in Hz
    pub fn start(hz: f64) -> Self {
        let seconds = 1.0 / hz;
        let full_seconds = seconds as u64;
        let remaining_nanos = (seconds.fract() * NANOS_PER_SECOND) as u32;
        FixedStep {
            update_interval: Duration::new(full_seconds, remaining_nanos),
            last_time: Instant::now(),
            accumulator: Duration::new(0, 0),
            update_counter: 0,
            update_limit: 3,
        }
    }

    /// Set the limit for how many updates can be performed between rendering.
    /// ie: the maximum number of times update() will return true between calls to render_delta
    ///
    /// Use this if rendering on time is more important than keeping the simulation on time
    /// (which is usually the case for video games).
    pub fn limit(mut self, limit: u32) -> Self {
        self.update_limit = limit;
        self
    }

    /// Remove the update limit
    pub fn unlimit(mut self) -> Self {
        self.update_limit = ::std::u32::MAX;
        self
    }

    /// Restarts the timer at the current time and clears any waiting updates.
    pub fn reset(&mut self) {
        self.last_time = Instant::now();
        self.update_counter = 0;
        self.accumulator = Duration::new(0, 0);
    }

    /// Returns true if enough time has elapsed to perform another update.
    pub fn update(&mut self) -> bool {
        let now = Instant::now();
        self.accumulator += now - self.last_time;
        self.last_time = now;
        if self.accumulator >= self.update_interval {
            // Time for another update
            self.update_counter += 1;
            if self.update_counter > self.update_limit {
                // If too many updates have occured since the last render,
                // skip any waiting updates and return false
                self.accumulator = Duration::new(0, 0);
                self.update_counter = 0;
                false
            } else {
                self.accumulator -= self.update_interval;
                true
            }
        } else {
            // Not ready for another update yet
            false
        }
    }

    /// Return the amount of time (relative to the update period) since the last update tick.
    ///
    /// Also refreshes the update counter (see the `limit` method)
    pub fn render_delta(&mut self) -> f64 {
        self.update_counter = 0;
        duration_to_float(self.accumulator) / duration_to_float(self.update_interval)
    }
}

fn duration_to_float(dur: Duration) -> f64 {
    (dur.as_secs() as f64 + dur.subsec_nanos() as f64 / NANOS_PER_SECOND)
}

// Legacy macro
#[deprecated]
#[macro_export]
macro_rules! fixedstep_loop {
    {
        Step($ticks:expr, $skip:expr),
        Update => $Update:block,
        Render($delta:pat) => $Render:block,
    } => {
        {
            use std::time::{Duration, Instant};
            let ticks = 1.0 / $ticks as f64;
            let ticks_s = ticks as u64;
            let ticks_ns = (ticks.fract() * 1000_000_000.0) as u32;
            let update_interval = Duration::new(ticks_s, ticks_ns);
            let skip_threshold: i32 = 3;

            let mut last = Instant::now();
            let mut accumulator = Duration::new(0, 0);

            let mut should_close = false;

            while !should_close
            {
                let now = Instant::now();
                accumulator += now - last;
                last = now;

                let mut update_count = 0;
                while accumulator > update_interval && update_count < skip_threshold
                {
                    should_close = $Update;
                    update_count += 1;
                    accumulator -= update_interval;
                }

                // Frame skip
                // Do not use for simulations
                if $skip && accumulator > update_interval
                {
                    accumulator = Duration::new(0, 0);
                }

                let elapsed = last.elapsed();
                let $delta = ((elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1000_000_000.0) / ticks).min(1.0);
                $Render
            }
        }
    };
    {
        Step($ticks:expr),
        Update => $Update:block,
        Render($delta:pat) => $Render:block,
    } => {
        fixedstep_loop!(
            Step($ticks, true),
            Update => $Update,
            Render($delta) => $Render,
        )
    };
    {
        Update => $Update:block,
        Render($delta:pat) => $Render:block,
    } => {
        fixedstep_loop!(
            Step(60),
            Update => $Update,
            Render($delta) => $Render,
        )
    };
}