-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathengine.js
More file actions
256 lines (219 loc) · 9.31 KB
/
engine.js
File metadata and controls
256 lines (219 loc) · 9.31 KB
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/* Engine.js
*
* Outline taken from Udacity.com,
* modified by Andy Davies: Engine, init(), updateEntities(), renderEntities().
* implemented by Andy Davies: reset(), renderFinalScore().
*
* This file provides the game loop functionality (update entities and render),
* draws the initial game board on the screen, and then calls the update and
* render methods on the player and enemy objects (defined in app.js).
*
* The game engine works by drawing the entire game screen over and over, presenting
* the illusion of animation. The engine is available globally via
* the Engine variable and it also makes the canvas' context (ctx) object
* globally available to make writing app.js a little simpler to work with.
*
*/
var Engine = (function(global) {
/* Predefine the variables we'll be using within this scope,
* create the canvas element, grab the 2D context for that canvas
* set the canvas elements height/width and add it to the DOM.
*/
var doc = global.document,
win = global.window,
canvas = doc.createElement('canvas'),
ctx = canvas.getContext('2d'),
lastTime;
// initialise highest score
var highestScore = 0;
canvas.width = 505;
canvas.height = 606;
// doc.body.appendChild(canvas);
// modified to add to portfolio site
doc.getElementById("game").appendChild(canvas);
/* This function serves as the kickoff point for the game loop itself
* and handles properly calling the update and render methods.
*/
function main() {
/* Get our time delta information which is required if your game
* requires smooth animation. Because everyone's computer processes
* instructions at different speeds we need a constant value that
* would be the same for everyone (regardless of how fast their
* computer is) - hurray time!
*/
var now = Date.now(),
dt = (now - lastTime) / 1000.0;
/* Call our update/render functions, pass along the time delta to
* our update function since it may be used for smooth animation.
*/
update(dt);
render();
/* Set our lastTime variable which is used to determine the time delta
* for the next time this function is called.
*/
lastTime = now;
/* Use the browser's requestAnimationFrame function to call this
* function again as soon as the browser is able to draw another frame.
*/
// if seconds <= 0 stop the engine
if (seconds > 0) {
win.requestAnimationFrame(main);
}
}
/* This function does some initial setup that should only occur once,
* particularly setting the lastTime variable that is required for the
* game loop.
*/
function init() {
/* seconds in global scope so it can be accessed by timer.update and doesn't have to
be passed through several functions */
seconds = 20;
lastTime = Date.now();
// initialise collectables
collectables.forEach(function(collectable) {
collectable.reset();
});
main();
}
/* This function is called by main (our game loop) and itself calls all
* of the functions which may need to update entity's data. Based on how
* you implement your collision detection (when two entities occupy the
* same space, for instance when your character should die), you may find
* the need to add an additional function call here. For now, we've left
* it commented out - you may or may not want to implement this
* functionality this way (you could just implement collision detection
* on the entities themselves within your app.js file).
*/
function update(dt) {
updateEntities(dt);
}
/*
* reset called when timer reaches zero. Handles end-of-game and restart.
*/
function reset() {
// wait 3 seconds then restart
var waitTime = 3;
window.setTimeout(function(){
init();
// reset player position
player.reset();
}, waitTime * 1000);
// show score message, pass waitTime so function can access it
renderFinalScore(waitTime);
}
/* This is called by the update function and loops through all of the
* objects within your allEnemies array as defined in app.js and calls
* their update() methods. It will then call the update function for your
* player object. These update methods should focus purely on updating
* the data/properties related to the object. Do your drawing in your
* render methods.
*/
function updateEntities(dt) {
allEnemies.forEach(function(enemy) {
enemy.update(dt);
});
player.update();
// pass reset function to timer.update so it has it in scope for when time=0
timer.update(dt, reset);
// update collectables
collectables.forEach(function(collectable) {
collectable.update();
});
}
/* This function initially draws the "game level", it will then call
* the renderEntities function. Remember, this function is called every
* game tick (or loop of the game engine) because that's how games work -
* they are flipbooks creating the illusion of animation but in reality
* they are just drawing the entire screen over and over.
*/
function render() {
/* This array holds the relative URL to the image used
* for that particular row of the game level.
*/
var rowImages = [
'img/water-block.png', // Top row is water
'img/stone-block.png', // Row 1 of 3 of stone
'img/stone-block.png', // Row 2 of 3 of stone
'img/stone-block.png', // Row 3 of 3 of stone
'img/grass-block.png', // Row 1 of 2 of grass
'img/grass-block.png' // Row 2 of 2 of grass
],
numRows = 6,
numCols = 5,
row, col;
/* Loop through the number of rows and columns we've defined above
* and, using the rowImages array, draw the correct image for that
* portion of the "grid"
*/
for (row = 0; row < numRows; row++) {
for (col = 0; col < numCols; col++) {
/* The drawImage function of the canvas' context element
* requires 3 parameters: the image to draw, the x coordinate
* to start drawing and the y coordinate to start drawing.
* We're using our Resources helpers to refer to our images
* so that we get the benefits of caching these images, since
* we're using them over and over.
*/
ctx.drawImage(Resources.get(rowImages[row]), col * 101, row * 83);
}
}
renderEntities();
}
/* This function is called by the render function and is called on each game
* tick. Its purpose is to then call the render functions you have defined
* on your enemy and player entities within app.js
*/
function renderEntities() {
/* Loop through all of the objects within the allEnemies array and call
* the render function you have defined.
*/
allEnemies.forEach(function(enemy) {
enemy.render();
});
player.render();
timer.render();
collectables.forEach(function(collectable) {
collectable.render();
});
interimScoreBoard.render();
totalScoreBoard.render();
}
/* this function manipulates the DOM to display
the total score on game end and the highest score in that session */
function renderFinalScore(waitTime) {
// display score
document.getElementById("score_alert").innerHTML = "You scored " + totalScore + " points!";
// update highest score
if (totalScore > highestScore) {
highestScore = totalScore;
}
// wait, then clear when game restarts
window.setTimeout(function(){
// update highest score
document.getElementById("score_alert").innerHTML = "Your highest score is " + highestScore;
// reset scores
interimScore = 0;
totalScore = 0;
}, waitTime * 1000);
}
/* Go ahead and load all of the images we know we're going to need to
* draw our game level. Then set init as the callback method, so that when
* all of these images are properly loaded our game will start.
*/
Resources.load([
'img/stone-block.png',
'img/water-block.png',
'img/grass-block.png',
'img/enemy-bug.png',
'img/char-boy.png',
'img/Gem Blue.png',
'img/Gem Orange.png',
'img/Heart.png'
]);
Resources.onReady(init);
/* Assign the canvas' context object to the global variable (the window
* object when run in a browser) so that developers can use it more easily
* from within their app.js files.
*/
global.ctx = ctx;
})(this);