1 /** 2 * Tick by Grant Skinner. Dec 5, 2010 3 * Visit www.gskinner.com/blog for documentation, updates and more free code. 4 * 5 * 6 * Copyright (c) 2010 Grant Skinner 7 * 8 * Permission is hereby granted, free of charge, to any person 9 * obtaining a copy of this software and associated documentation 10 * files (the "Software"), to deal in the Software without 11 * restriction, including without limitation the rights to use, 12 * copy, modify, merge, publish, distribute, sublicense, and/or sell 13 * copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following 15 * conditions: 16 * 17 * The above copyright notice and this permission notice shall be 18 * included in all copies or substantial portions of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 * OTHER DEALINGS IN THE SOFTWARE. 28 **/ 29 30 31 32 // constructor: 33 /** 34 * The Tick class uses a static interface (ex. Tick.getPaused()) and should not be instantiated. 35 * @class Provides a centralized tick or heartbeat. Listeners can subscribe to the tick, and identify whether they are pausable or not. 36 **/ 37 function Tick() { 38 throw "Tick cannot be instantiated."; 39 } 40 41 // private static properties: 42 /** @private **/ 43 Tick._listeners = null; 44 /** @private **/ 45 Tick._pauseable = null; 46 /** @private **/ 47 Tick._paused = false; 48 /** @private **/ 49 Tick._inited = false; 50 /** @private **/ 51 Tick._startTime = 0; 52 /** @private **/ 53 Tick._pausedTime=0; 54 /** @private **/ 55 Tick._ticks = 0; 56 /** @private **/ 57 Tick._pausedTicks = 0; 58 /** @private **/ 59 Tick._interval = 50; // READ-ONLY 60 /** @private **/ 61 Tick._intervalID = null; 62 /** @private **/ 63 Tick._lastTime = 0; 64 65 // public static methods: 66 /** 67 * Adds a listener to the tick. The listener object must expose a .tick() method, which will be called once each tick. The exposed tick method can optionally accept a single parameter, which will include the elapsed time between the previous tick and the current one. 68 * @param o The object to add as a listener. 69 * @param pausable If false, the listener will continue to have tick called even when Tick is paused via Tick.pause(). Default is false. 70 * @static 71 **/ 72 Tick.addListener = function(o, pauseable) { 73 if (!Tick._inited) { 74 Tick._inited = true; 75 Tick.removeAllListeners(); 76 Tick.setInterval(Tick._interval); 77 } 78 79 var index = Tick._listeners.indexOf(o); 80 if (index != -1) { return; } 81 82 Tick._pauseable[Tick._listeners.length] = pauseable; 83 Tick._listeners.push(o); 84 } 85 86 /** 87 * Removes the specified listener. 88 * @param o The listener to remove. 89 * @static 90 **/ 91 Tick.removeListener = function(o) { 92 if (Tick._listeners == null) { return; } 93 var index = Tick._listeners.indexOf(o); 94 if (index != -1) { 95 Tick._listeners.splice(index,1); 96 Tick._pauseable.splice(index,1); 97 } 98 } 99 100 /** 101 * Removes all listeners. 102 * @static 103 **/ 104 Tick.removeAllListeners = function() { 105 Tick._listeners = []; 106 Tick._pauseable = []; 107 } 108 109 /** 110 * Sets the time (in milliseconds) between ticks. Default is 50 (20 FPS). 111 * @param interval Time in milliseconds between ticks. 112 * @static 113 **/ 114 Tick.setInterval = function(interval) { 115 if (Tick._intervalID != null) { clearInterval(Tick._intervalID); } 116 Tick._lastTime = Tick._getTime(); 117 Tick._interval = interval; 118 Tick._intervalID = setInterval(Tick._tick, interval); 119 } 120 121 /** 122 * Returns the current time between ticks, as set with setInterval. 123 * @static 124 **/ 125 Tick.getInterval = function() { 126 return Tick._interval; 127 } 128 129 /** 130 * Returns the frame rate in frames per second (FPS). For example, with an interval of 40, getFPS() will return 25 (1000ms per second divided by 40 ms per tick = 25fps). 131 * @static 132 **/ 133 Tick.getFPS = function() { 134 return 1000/Tick._interval; 135 } 136 137 /** 138 * While Tick is paused, pausable listeners are not ticked. See addListener for more information. 139 * @param value Indicates whether to pause (true) or unpause (false) Tick. 140 * @static 141 **/ 142 Tick.setPaused = function(value) { 143 Tick._paused = value; 144 } 145 146 /** 147 * Returns a boolean indicating whether Tick is currently paused, as set with setPaused. 148 * @static 149 **/ 150 Tick.getPaused = function() { 151 return Tick._paused; 152 } 153 154 /** 155 * Returns the number of milliseconds that have elapsed since Tick was loaded. For example, you could use this in a time synchronized animation to determine the exact amount of time that has elapsed. 156 * @param pauseable Indicates whether to return the elapsed time for pauseable, or unpauseable listeners. If true, time that elapsed while Tick was paused is not included. 157 * @static 158 **/ 159 Tick.getTime = function(pauseable) { 160 return Tick._getTime() - Tick._startTime - (pauseable ? Tick._pausedTime : 0); 161 } 162 163 /** 164 * Returns the number of ticks that have elapsed while Tick was active. 165 * @param pauseable Indicates whether to return the elapsed ticks for pauseable, or unpauseable listeners. 166 * @static 167 **/ 168 Tick.getTicks = function(pauseable) { 169 return Tick._ticks - (pauseable ?Tick._pausedTicks : 0); 170 } 171 172 // private static methods: 173 /** @private **/ 174 Tick._tick = function() { 175 Tick._ticks++; 176 177 var time = Tick.getTime(false); 178 var elapsedTime = time-Tick._lastTime; 179 var paused = Tick._paused; 180 181 if (paused) { 182 Tick._pausedTicks++; 183 Tick._pausedTime += elapsedTime; 184 } 185 Tick._lastTime = time; 186 187 var pauseable = Tick._pauseable; 188 var listeners = Tick._listeners; 189 190 var l = listeners.length; 191 for (var i=0; i<l; i++) { 192 var p = pauseable[i]; 193 var listener = listeners[i]; 194 if (listener == null || (paused && p) || listener.tick == null) { continue; } 195 listener.tick(elapsedTime); 196 } 197 } 198 199 /** @private **/ 200 Tick._getTime = function() { 201 new Date().getTime(); 202 } 203 Tick._startTime = Tick._getTime(); 204