-
Notifications
You must be signed in to change notification settings - Fork 35
/
game.js
150 lines (130 loc) · 3.95 KB
/
game.js
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
function Game(canvas) {
var self = this
this.context = canvas.getContext("2d")
this.width = canvas.width
this.height = canvas.height
// Keep track of key states
// Eg.:
// game.keyPressed.up === true // while UP key is pressed)
// game.keyPressed.up === false // when UP key is released)
this.keyPressed = {}
$(canvas).on('keydown keyup', function(e) {
// Convert key code to key name
var keyName = Game.keys[e.which]
if (keyName) {
// eg.: `self.keyPressed.up = true` on keydown
// Will be set to `false` on keyup
self.keyPressed[keyName] = e.type === 'keydown'
e.preventDefault()
}
})
}
// Some key code to key name mappings
Game.keys = {
32: 'space',
37: 'left',
38: 'up',
39: 'right',
40: 'down'
}
Game.prototype.update = function() {
this.entities.forEach(function(entity) {
if (entity.update) entity.update()
})
}
Game.prototype.draw = function() {
var self = this
this.entities.forEach(function(entity) {
if (entity.draw) entity.draw(self.context)
})
}
// A simple (but unreliable) game loop.
//
// The problem with this approach is that the update and draw operations
// are glued together. If the timer is not able to run at the proper interval
// (if CPU is too busy for example), the game will appear sluggish.
//
// Game.prototype.start = function() {
// var self = this,
// fps = 60,
// interval = 1000 / fps // ms per frame
//
// setInterval(function() {
// self.update()
// self.draw()
// }, interval)
// }
// Here is a real game loop. Similar to the ones you'll find in most games.
Game.prototype.start = function() {
var self = this
this.lastUpdateTime = new Date().getTime()
// The loop
onFrame(function() {
// A turn in the loop is called a step.
// Two possible modes:
self.fixedTimeStep()
// or
// self.variableTimeStep()
})
}
// Instead of relying on a timer, we use a special browser function called
// `requestAnimationFrame(callback)`. It calls the `callback` at interval
// synced with the display refresh rate.
// More info at:
// https://developer.mozilla.org/en/docs/Web/API/window.requestAnimationFrame
var onFrame = function(callback) {
if (window.requestAnimationFrame) {
requestAnimationFrame(function() {
callback()
// requestAnimationFrame only calls our callback once, we need to
// schedule the next call ourself.
onFrame(callback)
})
} else {
// requestAnimationFrame is not supported by all browsers. We fall back to
// a timer.
var fps = 60
setInterval(callback, 1000 / fps)
}
}
// With fixed time steps, each update is done at a fixed interval.
Game.prototype.fixedTimeStep = function() {
var fps = 60,
interval = 1000 / fps,
updated = false
// While we're not up to date ...
while (this.lastUpdateTime < new Date().getTime()) {
this.update()
updated = true
// We jump at fixed intervals until we catch up to the current time.
this.lastUpdateTime += interval
}
// No need to draw if nothing was updated
if (updated) this.draw()
updated = false
}
// With a variable time steps, update are done whenever we need to draw.
// However we do partial updates. Only updating a percentage of what a fixed
// time step would normally do.
Game.prototype.variableTimeStep = function() {
var currentTime = new Date().getTime(),
fps = 60,
interval = 1000 / fps,
timeDelta = currentTime - this.lastUpdateTime,
percentageOfInterval = timeDelta / interval
// NOTE: This requires changing the update function
// to support partial updating.
//
// Eg.:
//
// Entity.prototype.update = function(percentage) {
// this.x += this.xVelocity * percentage
// this.y += this.yVelocity * percentage
// }
//
// Also don't forget to pass that argument in Game.prototype.update.
this.update(percentageOfInterval)
this.draw()
this.lastUpdateTime = new Date().getTime()
}
;//