· 9 years ago · Oct 10, 2016, 10:46 PM
1diff --git a/lib/impact/debug/graph-panel.js b/lib/impact/debug/graph-panel.js
2index 12f1523..349615a 100644
3--- a/lib/impact/debug/graph-panel.js
4+++ b/lib/impact/debug/graph-panel.js
5@@ -21,13 +21,24 @@ ig.Game.inject({
6 update: function() {
7 ig.graph.beginClock('update');
8 this.parent();
9- ig.graph.endClock('update');
10+ ig.graph.pauseClock('update');
11 },
12
13
14 checkEntities: function() {
15 ig.graph.beginClock('checks');
16 this.parent();
17+ ig.graph.pauseClock('checks');
18+ }
19+});
20+
21+
22+ig.System.inject({
23+ run: function( timestamp ) {
24+ this.parent( timestamp );
25+
26+ // end accumulative clocks
27+ ig.graph.endClock('update');
28 ig.graph.endClock('checks');
29 }
30 });
31@@ -101,6 +112,8 @@ ig.DebugGraphPanel = ig.DebugPanel.extend({
32 this.container.appendChild( legend );
33
34 this.clocks[name] = {
35+ paused: true,
36+ tempValue: 0, // accumulates time between pauses
37 description: description,
38 color: color,
39 current: 0,
40@@ -112,14 +125,32 @@ ig.DebugGraphPanel = ig.DebugPanel.extend({
41
42
43 beginClock: function( name, offset ) {
44- this.clocks[name].start = Date.now() + (offset || 0);
45+ var c = this.clocks[name];
46+ if( c.paused ) { c.paused = false; }
47+ c.start = performance.now() + (offset || 0);
48+ },
49+
50+
51+ pauseClock: function( name ) {
52+ var c = this.clocks[name];
53+ if( c.paused ) { return; }
54+ c.paused = true;
55+ c.tempValue += performance.now() - c.start;
56 },
57
58
59 endClock: function( name ) {
60 var c = this.clocks[name];
61- c.current = Math.round(Date.now() - c.start);
62+
63+ // uninitialized clock
64+ if( c.paused && c.tempValue === 0 ) { return; }
65+
66+ // finalize tempValue
67+ if( !c.paused ) { this.pauseClock( name ); }
68+
69+ c.current = c.tempValue;
70 c.avg = c.avg * 0.8 + c.current * 0.2;
71+ c.tempValue = 0;
72 },
73
74
75diff --git a/lib/impact/debug/menu.js b/lib/impact/debug/menu.js
76index 4bb44d6..0c326eb 100644
77--- a/lib/impact/debug/menu.js
78+++ b/lib/impact/debug/menu.js
79@@ -9,9 +9,9 @@ ig.module(
80
81
82 ig.System.inject({
83- run: function() {
84+ run: function( timestamp ) {
85 ig.debug.beforeRun();
86- this.parent();
87+ this.parent( timestamp );
88 ig.debug.afterRun();
89 },
90
91diff --git a/lib/impact/game.js b/lib/impact/game.js
92index 29f3cea..8651690 100644
93--- a/lib/impact/game.js
94+++ b/lib/impact/game.js
95@@ -166,12 +166,6 @@ ig.Game = ig.Class.extend({
96 },
97
98
99- run: function() {
100- this.update();
101- this.draw();
102- },
103-
104-
105 update: function(){
106 // load new level?
107 if( this._levelToLoad ) {
108@@ -204,8 +198,15 @@ ig.Game = ig.Class.extend({
109 }
110 }
111 },
112-
113-
114+
115+
116+ // "spiral of death" handler, called if are updates not terminating quick enough
117+ panic: function() {
118+ ig.system._delta = 0; // discard the unsimulated time
119+ throw 'Game Panicked';
120+ },
121+
122+
123 updateEntities: function() {
124 for( var i = 0; i < this.entities.length; i++ ) {
125 var ent = this.entities[i];
126diff --git a/lib/impact/impact.js b/lib/impact/impact.js
127index 551ee5c..0b7a0a2 100644
128--- a/lib/impact/impact.js
129+++ b/lib/impact/impact.js
130@@ -418,11 +418,30 @@ window.ig = {
131
132 // -----------------------------------------------------------------------------
133 // Provide ig.setAnimation and ig.clearAnimation as a compatible way to use
134-// requestAnimationFrame if available or setInterval otherwise
135+// requestAnimationFrame
136
137-// Use requestAnimationFrame if available
138 ig.normalizeVendorAttribute( window, 'requestAnimationFrame' );
139-if( window.requestAnimationFrame ) {
140+
141+// Polyfill requestAnimationFrame and cancelAnimationFrame if necessary
142+// This polyfill is adapted from the MIT-licensed
143+// https://github.com/underscorediscovery/realtime-multiplayer-in-html5
144+var requestAnimationFrame = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : (function() {
145+ var lastTimestamp = Date.now(),
146+ now,
147+ timeout;
148+ return function(callback) {
149+ now = Date.now();
150+ timeout = Math.max(0, timestep - (now - lastTimestamp));
151+ lastTimestamp = now + timeout;
152+ return setTimeout(function() {
153+ callback(now + timeout);
154+ }, timeout);
155+ };
156+})(),
157+cancelAnimationFrame = typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame : clearTimeout;
158+
159+// Define ig.setAnimation and ig.clearAnimation
160+{
161 var next = 1,
162 anims = {};
163
164@@ -430,10 +449,10 @@ if( window.requestAnimationFrame ) {
165 var current = next++;
166 anims[current] = true;
167
168- var animate = function() {
169+ var animate = function( timestamp ) {
170 if( !anims[current] ) { return; } // deleted?
171 window.requestAnimationFrame( animate, element );
172- callback();
173+ callback( timestamp );
174 };
175 window.requestAnimationFrame( animate, element );
176 return current;
177@@ -444,16 +463,6 @@ if( window.requestAnimationFrame ) {
178 };
179 }
180
181-// [set/clear]Interval fallback
182-else {
183- window.ig.setAnimation = function( callback, element ) {
184- return window.setInterval( callback, 1000/60 );
185- };
186- window.ig.clearAnimation = function( id ) {
187- window.clearInterval( id );
188- };
189-}
190-
191
192 // -----------------------------------------------------------------------------
193 // Class object based on John Resigs code; inspired by base2 and Prototype
194diff --git a/lib/impact/system.js b/lib/impact/system.js
195index a5739ef..9741b90 100644
196--- a/lib/impact/system.js
197+++ b/lib/impact/system.js
198@@ -15,7 +15,10 @@ ig.System = ig.Class.extend({
199 realHeight: 240,
200 scale: 1,
201
202+ _delta: 0, // accumulates time for scheduling game updates
203+ _lastRun: 0, // timestamp run() was last called
204 tick: 0,
205+ tickRate: 60,
206 animationId: 0,
207 newGameClass: null,
208 running: false,
209@@ -73,12 +76,14 @@ ig.System = ig.Class.extend({
210
211
212 setDelegate: function( object ) {
213- if( typeof(object.run) == 'function' ) {
214- this.delegate = object;
215- this.startRunLoop();
216- } else {
217- throw( 'System.setDelegate: No run() function in object' );
218+ if( typeof object.update !== 'function' ) {
219+ throw 'System.setDelegate: No update() function in object';
220 }
221+ if( typeof object.draw !== 'function' ) {
222+ throw 'System.setDelegate: No draw() function in object';
223+ }
224+ this.delegate = object;
225+ this.startRunLoop();
226 },
227
228
229@@ -101,13 +106,33 @@ ig.System = ig.Class.extend({
230 },
231
232
233- run: function() {
234- ig.Timer.step();
235- this.tick = this.clock.tick();
236-
237- this.delegate.run();
238- ig.input.clearPressed();
239-
240+ run: function( timestamp ) {
241+
242+ // Track the accumulated time that hasn't been simulated yet
243+ this._delta += timestamp - this._lastRun;
244+ this._lastRun = timestamp;
245+
246+ // Simulate the total elapsed time in fixed-size chunks
247+ var count = 0;
248+ var timestep = 1000 / this.tickRate;
249+ while( this._delta >= timestep ) {
250+ ig.Timer.step();
251+ this.tick = this.clock.tick();
252+
253+ this.delegate.update();
254+ ig.input.clearPressed();
255+
256+ this._delta -= timestep;
257+
258+ // Sanity check
259+ if (++count >= 240) {
260+ this.delegate.panic(); // fix things
261+ break; // bail out
262+ }
263+ }
264+
265+ this.delegate.draw();
266+
267 if( this.newGameClass ) {
268 this.setGameNow( this.newGameClass );
269 this.newGameClass = null;
270diff --git a/lib/impact/timer.js b/lib/impact/timer.js
271index d87747f..0682c1e 100644
272--- a/lib/impact/timer.js
273+++ b/lib/impact/timer.js
274@@ -57,16 +57,13 @@ ig.Timer = ig.Class.extend({
275 }
276 });
277
278-ig.Timer._last = 0;
279 ig.Timer.time = Number.MIN_VALUE;
280 ig.Timer.timeScale = 1;
281-ig.Timer.maxStep = 0.05;
282
283 ig.Timer.step = function() {
284- var current = Date.now();
285- var delta = (current - ig.Timer._last) / 1000;
286- ig.Timer.time += Math.min(delta, ig.Timer.maxStep) * ig.Timer.timeScale;
287- ig.Timer._last = current;
288+ var ms = ( 1000 / ig.system.tickRate );
289+ var seconds = ( ms / 1000 );
290+ ig.Timer.time += seconds * ig.Timer.timeScale;
291 };
292
293 });
294\ No newline at end of file