summaryrefslogtreecommitdiff
path: root/jaws/src/game_loop.js
blob: aa773e5184f17220056fe5f3432c780c8ae79741 (plain)
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
if(typeof require !== "undefined") { var jaws = require("./core.js"); }

var jaws = (function(jaws) {

// requestAnim shim layer by Paul Irish
window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          window.oRequestAnimationFrame      ||
          window.msRequestAnimationFrame     ||
          function(/* function */ callback, /* DOMElement */ element){
            window.setTimeout(callback, 16.666);
          };
})();

/**
 * @class A classic game loop forever looping calls to update() / draw() with given framerate. "Field Summary" contains options for the GameLoop()-constructor.
 *
 * @property {int} tick_duration  duration in ms between the last 2 ticks (often called dt)
 * @property {int} fps  the real fps (as opposed to the target fps), smoothed out with a moving average
 * @property {int} ticks  total amount of ticks since game loops start
 *
 * @example
 *
 * game = {}
 *  draw: function() { ... your stuff executed every 30 FPS ... }
 * }
 *
 * game_loop = new jaws.GameLoop(game, {fps: 30})
 * game_loop.start()
 *
 * // You can also use the shortcut jaws.start(), it will:
 * // 1) Load all assets with jaws.assets.loadAll()
 * // 2) Create a GameLoop() and start it
 * jaws.start(MyGameState, {fps: 30})
 *
 */
jaws.GameLoop = function GameLoop(game_object, options, game_state_setup_options) {
  if( !(this instanceof arguments.callee) ) return new arguments.callee( game_object, options );

  this.tick_duration = 0
  this.fps = 0
  this.ticks = 0
  
  var update_id
  var paused = false
  var stopped = false
  var that = this
  var mean_value = new MeanValue(20) // let's have a smooth, non-jittery FPS-value

  /** 
   * returns how game_loop has been active in milliseconds 
   * does currently not factor in pause-time
   */
  this.runtime = function() {
    return (this.last_tick - this.first_tick)
  }

  /** Start the game loop by calling setup() once and then loop update()/draw() forever with given FPS */
  this.start = function() {
    jaws.log.info("Game loop start", true)
  
    this.first_tick = (new Date()).getTime();
    this.current_tick = (new Date()).getTime();
    this.last_tick = (new Date()).getTime(); 

    if(options.setup !== false && game_object.setup) { game_object.setup(game_state_setup_options) }
    step_delay = 1000 / options.fps;
   
    if(options.fps == 60) {
      requestAnimFrame(this.loop)
    }
    else {
      update_id = setInterval(this.loop, step_delay);
    }
  }
  
  /** The core of the game loop. Calculate a mean FPS and call update()/draw() if game loop is not paused */
  this.loop = function() {
    that.current_tick = (new Date()).getTime();
    that.tick_duration = that.current_tick - that.last_tick
    that.fps = mean_value.add(1000/that.tick_duration).get()

    if(!stopped && !paused) {
      if(game_object.update) { game_object.update() }
      if(game_object.draw)   { game_object.draw() }
      that.ticks++
    }
    if(options.fps == 60 && !stopped) requestAnimFrame(that.loop);
    that.last_tick = that.current_tick;
  }
  
  /** Pause the game loop. loop() will still get called but not update() / draw() */
  this.pause = function()   { paused = true }
  
  /** unpause the game loop */
  this.unpause = function() { paused = false }

  /** Stop the game loop */
  this.stop = function() { 
    if(update_id) clearInterval(update_id); 
    stopped = true;
  }
}

/** @ignore */
function MeanValue(size) {
  this.size = size
  this.values = new Array(this.size)
  this.value
  
  this.add = function(value) {
    if(this.values.length > this.size) {  // is values filled?
      this.values.splice(0,1)
      this.value = 0
      for(var i=0; this.values[i]; i++) {
        this.value += this.values[i]
      }
      this.value = this.value / this.size
    }
    this.values.push(value)
    
    return this
  }

  this.get = function() {
    return parseInt(this.value)
  }

}

return jaws;
})(jaws || {});