punter.js 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/launch.json +15 -0
- package/README.MD +41 -46
- package/games/breakout.html +297 -0
- package/games/pong.html +59 -39
- package/images/breakout/ball.png +0 -0
- package/images/breakout/brick-blue.png +0 -0
- package/images/breakout/brick-green.png +0 -0
- package/images/breakout/brick-orange.png +0 -0
- package/images/breakout/brick-red.png +0 -0
- package/images/breakout/brick-yellow.png +0 -0
- package/images/breakout/paddle.png +0 -0
- package/images/pong/ball.png +0 -0
- package/package.json +3 -2
- package/punter.js +54 -35
- package/sounds/alarm1.mp3 +0 -0
- package/sounds/alarm2.mp3 +0 -0
- package/sounds/alarm3.mp3 +0 -0
- package/sounds/alarm4.mp3 +0 -0
- package/sounds/beep1.mp3 +0 -0
- package/sounds/beep2.mp3 +0 -0
- package/sounds/beep3.mp3 +0 -0
- package/sounds/beep4.mp3 +0 -0
- package/sounds/beep5.mp3 +0 -0
- package/sounds/beep6.mp3 +0 -0
- package/sounds/bling1.mp3 +0 -0
- package/sounds/bling2.mp3 +0 -0
- package/sounds/bling3.mp3 +0 -0
- package/sounds/bling4.mp3 +0 -0
- package/sounds/bling5.mp3 +0 -0
- package/sounds/cannon1.mp3 +0 -0
- package/sounds/cannon2.mp3 +0 -0
- package/sounds/chime1.mp3 +0 -0
- package/sounds/chime2.mp3 +0 -0
- package/sounds/chime3.mp3 +0 -0
- package/sounds/chime4.mp3 +0 -0
- package/sounds/chime5.mp3 +0 -0
- package/sounds/click1.mp3 +0 -0
- package/sounds/click2.mp3 +0 -0
- package/sounds/click3.mp3 +0 -0
- package/sounds/click4.mp3 +0 -0
- package/sounds/click5.mp3 +0 -0
- package/sounds/click6.mp3 +0 -0
- package/sounds/click7.mp3 +0 -0
- package/sounds/error1.mp3 +0 -0
- package/sounds/error2.mp3 +0 -0
- package/sounds/error3.mp3 +0 -0
- package/sounds/error4.mp3 +0 -0
- package/sounds/fanfare1.mp3 +0 -0
- package/sounds/fanfare2.mp3 +0 -0
- package/sounds/jingle1.mp3 +0 -0
- package/sounds/jingle2.mp3 +0 -0
- package/sounds/jingle3.mp3 +0 -0
- package/sounds/laugh1.mp3 +0 -0
- package/sounds/laugh2.mp3 +0 -0
- package/sounds/punch1.mp3 +0 -0
- package/sounds/punch2.mp3 +0 -0
- package/sounds/punch3.mp3 +0 -0
- package/sounds/resonance1.mp3 +0 -0
- package/sounds/resonance2.mp3 +0 -0
- package/sounds/resonance3.mp3 +0 -0
- package/sounds/resonance4.mp3 +0 -0
- package/sounds/scale1.mp3 +0 -0
- package/sounds/scale2.mp3 +0 -0
- package/sounds/scale3.mp3 +0 -0
- package/sounds/slide1.mp3 +0 -0
- package/sounds/slide2.mp3 +0 -0
- package/sounds/slide3.mp3 +0 -0
- package/sounds/slide4.mp3 +0 -0
- package/sounds/slide5.mp3 +0 -0
- package/sounds/slide6.mp3 +0 -0
- package/sounds/splash1.mp3 +0 -0
- package/sounds/splash2.mp3 +0 -0
- package/sounds/throw1.mp3 +0 -0
- package/sounds/throw2.mp3 +0 -0
- package/sounds/throw3.mp3 +0 -0
- package/sounds/throw4.mp3 +0 -0
- package/sounds/whoosh1.mp3 +0 -0
- package/sounds/whoosh2.mp3 +0 -0
- package/sounds/whoosh3.mp3 +0 -0
- package/sounds/whoosh4.mp3 +0 -0
- package/sounds/whoosh5.mp3 +0 -0
- package/tests/fixtures/index.html +2 -2
- package/tests/interface-spec.js +31 -1
- package/tests/scenes-spec.js +55 -0
- package/tests/sprites-spec.js +29 -29
- package/images/pong/sprites/ball.png +0 -0
- /package/images/pong/{sprites/paddle.png → paddle.png} +0 -0
package/games/pong.html
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
|
+
<title>Pong</title>
|
|
4
5
|
<meta charset="utf-8">
|
|
5
6
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Pong</title>
|
|
7
7
|
<style>
|
|
8
|
-
* {
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
html, body {
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 100%;
|
|
17
|
+
background: #1e2230;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
canvas {
|
|
22
|
+
display: block;
|
|
23
|
+
cursor: none;
|
|
24
|
+
background: #1b2125;
|
|
25
|
+
}
|
|
11
26
|
</style>
|
|
12
27
|
<script src="../punter.js"></script>
|
|
13
28
|
</head>
|
|
@@ -54,10 +69,11 @@
|
|
|
54
69
|
// =========================================================
|
|
55
70
|
// GAME SETTINGS
|
|
56
71
|
// =========================================================
|
|
57
|
-
var MARGIN = px(
|
|
58
|
-
var PADDLE_W = px(14);
|
|
59
|
-
var PADDLE_H = px(90);
|
|
60
|
-
var
|
|
72
|
+
var MARGIN = px(20); // gap between paddle and screen edge
|
|
73
|
+
var PADDLE_W = px(14); // width of paddle
|
|
74
|
+
var PADDLE_H = px(90); // height of paddle
|
|
75
|
+
var AI_PADDLE_H = px(90);
|
|
76
|
+
var BALL_SIZE = px(24);
|
|
61
77
|
var PADDLE_SPEED = px(7);
|
|
62
78
|
var AI_SPEED = px(5);
|
|
63
79
|
var BALL_SPEED = px(5);
|
|
@@ -80,11 +96,15 @@
|
|
|
80
96
|
// SETUP
|
|
81
97
|
// Load the two sprite images before the game starts.
|
|
82
98
|
// =========================================================
|
|
83
|
-
punter.
|
|
99
|
+
punter.setup({
|
|
84
100
|
canvas: '#game',
|
|
85
|
-
|
|
86
|
-
paddle: '../images/pong/
|
|
87
|
-
ball: '../images/pong/
|
|
101
|
+
images: {
|
|
102
|
+
paddle: '../images/pong/paddle.png',
|
|
103
|
+
ball: '../images/pong/ball.png'
|
|
104
|
+
},
|
|
105
|
+
sounds: {
|
|
106
|
+
bounce: '../sounds/beep6.mp3',
|
|
107
|
+
die: '../sounds/error2.mp3'
|
|
88
108
|
}
|
|
89
109
|
});
|
|
90
110
|
|
|
@@ -93,11 +113,13 @@
|
|
|
93
113
|
// =========================================================
|
|
94
114
|
punter.scene('play', function () {
|
|
95
115
|
|
|
116
|
+
score = { player: 0, ai: 0 };
|
|
117
|
+
|
|
96
118
|
// Create the three sprites punter.js will draw each frame.
|
|
97
119
|
// w/h set the draw size; the image is scaled to fit automatically.
|
|
98
|
-
playerSprite = punter.createSprite({ id: 'player',
|
|
99
|
-
aiSprite = punter.createSprite({ id: 'ai',
|
|
100
|
-
ballSprite = punter.createSprite({ id: 'ball',
|
|
120
|
+
playerSprite = punter.createSprite({ id: 'player', image: 'paddle', x: 0, y: 0, w: PADDLE_W, h: PADDLE_H });
|
|
121
|
+
aiSprite = punter.createSprite({ id: 'ai', image: 'paddle', x: 0, y: 0, w: PADDLE_W, h: AI_PADDLE_H });
|
|
122
|
+
ballSprite = punter.createSprite({ id: 'ball', image: 'ball', x: 0, y: 0, w: BALL_SIZE, h: BALL_SIZE });
|
|
101
123
|
|
|
102
124
|
// Place everything in the correct starting position
|
|
103
125
|
resetPositions();
|
|
@@ -113,8 +135,8 @@
|
|
|
113
135
|
aiSprite.x = w - MARGIN - PADDLE_W;
|
|
114
136
|
|
|
115
137
|
// --- Player: keyboard ---
|
|
116
|
-
if (punter.keys['ArrowUp'] || punter.keys['w']) playerSprite.
|
|
117
|
-
if (punter.keys['ArrowDown'] || punter.keys['s']) playerSprite.
|
|
138
|
+
if (punter.keys['ArrowUp'] || punter.keys['w']) playerSprite.moveY(-PADDLE_SPEED);
|
|
139
|
+
if (punter.keys['ArrowDown'] || punter.keys['s']) playerSprite.moveY(PADDLE_SPEED);
|
|
118
140
|
|
|
119
141
|
// --- Player: tap / click ---
|
|
120
142
|
if (punter.mouse.clicked) {
|
|
@@ -126,13 +148,13 @@
|
|
|
126
148
|
|
|
127
149
|
// --- AI: follow the ball ---
|
|
128
150
|
var aiCenterY = aiSprite.y + PADDLE_H / 2;
|
|
129
|
-
if (ballSprite.y < aiCenterY - 10) aiSprite.
|
|
130
|
-
if (ballSprite.y > aiCenterY + 10) aiSprite.
|
|
151
|
+
if (ballSprite.y < aiCenterY - 10) aiSprite.moveY(-AI_SPEED);
|
|
152
|
+
if (ballSprite.y > aiCenterY + 10) aiSprite.moveY(AI_SPEED);
|
|
131
153
|
aiSprite.y = clamp(aiSprite.y, 0, h - PADDLE_H);
|
|
132
154
|
|
|
133
155
|
// --- Move the ball ---
|
|
134
|
-
ballSprite.
|
|
135
|
-
ballSprite.
|
|
156
|
+
ballSprite.moveX(ballVel.x);
|
|
157
|
+
ballSprite.moveY(ballVel.y);
|
|
136
158
|
|
|
137
159
|
// Bounce off top and bottom walls
|
|
138
160
|
if (ballSprite.y <= 0) { ballVel.y = Math.abs(ballVel.y); ballSprite.y = 0; }
|
|
@@ -146,6 +168,8 @@
|
|
|
146
168
|
// Where on the paddle the ball hit: 0 = top edge, 1 = bottom edge, 0.5 = centre (flat return)
|
|
147
169
|
var playerHit = (ballSprite.y + BALL_SIZE / 2 - playerSprite.y) / PADDLE_H;
|
|
148
170
|
ballVel.y = (playerHit - 0.5) * BALL_SPEED * 2;
|
|
171
|
+
|
|
172
|
+
punter.playSound('bounce');
|
|
149
173
|
}
|
|
150
174
|
|
|
151
175
|
// --- Collision: ball hits AI paddle ---
|
|
@@ -156,6 +180,8 @@
|
|
|
156
180
|
// Where on the paddle the ball hit: 0 = top edge, 1 = bottom edge, 0.5 = centre (flat return)
|
|
157
181
|
var aiHit = (ballSprite.y + BALL_SIZE / 2 - aiSprite.y) / PADDLE_H;
|
|
158
182
|
ballVel.y = (aiHit - 0.5) * BALL_SPEED * 2;
|
|
183
|
+
|
|
184
|
+
punter.playSound('bounce');
|
|
159
185
|
}
|
|
160
186
|
|
|
161
187
|
// Cap ball speed so the game stays playable
|
|
@@ -163,8 +189,17 @@
|
|
|
163
189
|
if (ballVel.x < -MAX_BALL_SPEED) ballVel.x = -MAX_BALL_SPEED;
|
|
164
190
|
|
|
165
191
|
// --- Scoring ---
|
|
166
|
-
if (ballSprite.x + BALL_SIZE < 0) {
|
|
167
|
-
|
|
192
|
+
if (ballSprite.x + BALL_SIZE < 0) {
|
|
193
|
+
score.ai++;
|
|
194
|
+
punter.playSound('die');
|
|
195
|
+
resetBall(SERVE_RIGHT);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (ballSprite.x > w) {
|
|
199
|
+
score.player++;
|
|
200
|
+
punter.playSound('die');
|
|
201
|
+
resetBall(SERVE_LEFT);
|
|
202
|
+
}
|
|
168
203
|
});
|
|
169
204
|
|
|
170
205
|
// -------------------------------------------------------
|
|
@@ -175,10 +210,6 @@
|
|
|
175
210
|
var w = punter.width;
|
|
176
211
|
var h = punter.height;
|
|
177
212
|
|
|
178
|
-
// Background
|
|
179
|
-
ctx.fillStyle = COLOR_BG;
|
|
180
|
-
ctx.fillRect(0, 0, w, h);
|
|
181
|
-
|
|
182
213
|
// Dashed centre line
|
|
183
214
|
ctx.setLineDash([px(8), px(10)]);
|
|
184
215
|
ctx.strokeStyle = COLOR_NET;
|
|
@@ -189,11 +220,6 @@
|
|
|
189
220
|
ctx.stroke();
|
|
190
221
|
ctx.setLineDash([]);
|
|
191
222
|
|
|
192
|
-
// Sprites — punter.js scales and draws the PNG images
|
|
193
|
-
playerSprite.draw(ctx);
|
|
194
|
-
aiSprite.draw(ctx);
|
|
195
|
-
ballSprite.draw(ctx);
|
|
196
|
-
|
|
197
223
|
// Scores
|
|
198
224
|
ctx.fillStyle = COLOR_SCORE;
|
|
199
225
|
ctx.font = 'bold ' + px(48) + 'px monospace';
|
|
@@ -247,13 +273,7 @@
|
|
|
247
273
|
resetPositions();
|
|
248
274
|
});
|
|
249
275
|
|
|
250
|
-
|
|
251
|
-
// START — go to the play scene once everything is loaded
|
|
252
|
-
// =========================================================
|
|
253
|
-
punter.on('ready', function () {
|
|
254
|
-
score = { player: 0, ai: 0 };
|
|
255
|
-
punter.go('play');
|
|
256
|
-
});
|
|
276
|
+
punter.go('play');
|
|
257
277
|
|
|
258
278
|
</script>
|
|
259
279
|
</body>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "punter.js",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "A simple, dependency-free 2D game engine for the browser. No build step, no frameworks - just one script tag.",
|
|
5
5
|
"main": "punter.js",
|
|
6
6
|
"directories": {
|
|
7
7
|
"test": "tests"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "npx serve .",
|
|
11
|
+
"start:pong": "npx serve . -l 4000 & npx open-cli http://localhost:4000/games/pong.html",
|
|
11
12
|
"test": ". \"$NVM_DIR/nvm.sh\" && nvm use && NODE_ENV=test TZ=UTC node_modules/.bin/jasmine --config=tests/jasmine/config.json"
|
|
12
13
|
},
|
|
13
14
|
"repository": {
|
package/punter.js
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
var _initilised = false;
|
|
30
30
|
var _scenes = {};
|
|
31
31
|
var _currentScene = null;
|
|
32
|
+
var _pendingGo = null; // scene name queued before init completes
|
|
32
33
|
var _running = false;
|
|
33
34
|
var _paused = false;
|
|
34
35
|
var _frame = 0;
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
var eventHandlers = {
|
|
40
41
|
ready: function() {},
|
|
41
42
|
update: function() {},
|
|
42
|
-
draw:
|
|
43
|
+
draw: null,
|
|
43
44
|
resize: function() {},
|
|
44
45
|
go: function() {}
|
|
45
46
|
};
|
|
@@ -106,11 +107,11 @@
|
|
|
106
107
|
})();
|
|
107
108
|
|
|
108
109
|
/**
|
|
109
|
-
* Initialises the engine with canvas,
|
|
110
|
+
* Initialises the engine with canvas, images, sounds, and buttons
|
|
110
111
|
* @param {Object} config - configuration object
|
|
111
112
|
* @returns {void}
|
|
112
113
|
*/
|
|
113
|
-
function
|
|
114
|
+
function setup(config) {
|
|
114
115
|
|
|
115
116
|
config = config || {};
|
|
116
117
|
|
|
@@ -151,11 +152,11 @@
|
|
|
151
152
|
_boundsCanvas = document.createElement('canvas');
|
|
152
153
|
_boundsCtx = _boundsCanvas.getContext('2d', { willReadFrequently: true });
|
|
153
154
|
|
|
154
|
-
|
|
155
|
+
loadImages(config.images || {}).then(function() {
|
|
155
156
|
|
|
156
157
|
// precompute bounds for all sprite frames
|
|
157
|
-
if (config.
|
|
158
|
-
for (var key in config.
|
|
158
|
+
if (config.images) {
|
|
159
|
+
for (var key in config.images) {
|
|
159
160
|
var img = images[key];
|
|
160
161
|
if (img && img.complete && img.naturalWidth) {
|
|
161
162
|
// this warms up the boundingCache
|
|
@@ -175,6 +176,7 @@
|
|
|
175
176
|
_initilised = true;
|
|
176
177
|
htmlEl.removeAttribute('data-punter-loading');
|
|
177
178
|
eventHandlers.ready();
|
|
179
|
+
if (_pendingGo) { engine.go(_pendingGo); _pendingGo = null; }
|
|
178
180
|
})
|
|
179
181
|
.catch(function() {
|
|
180
182
|
_initilised = false;
|
|
@@ -182,13 +184,13 @@
|
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
/**
|
|
185
|
-
* Loads and decodes all
|
|
186
|
-
* @param {Object}
|
|
187
|
+
* Loads and decodes all images
|
|
188
|
+
* @param {Object} images - Key-value map of image keys to image URLs
|
|
187
189
|
* @returns {Promise} - Resolves when all images are loaded and decoded
|
|
188
190
|
*/
|
|
189
|
-
function
|
|
191
|
+
function loadImages(imageMap) {
|
|
190
192
|
|
|
191
|
-
var keys = Object.keys(
|
|
193
|
+
var keys = Object.keys(imageMap);
|
|
192
194
|
var total = keys.length;
|
|
193
195
|
|
|
194
196
|
if (!total) return Promise.resolve();
|
|
@@ -234,7 +236,7 @@
|
|
|
234
236
|
|
|
235
237
|
for (var i = 0; i < total; i++) {
|
|
236
238
|
var key = keys[i];
|
|
237
|
-
var url =
|
|
239
|
+
var url = imageMap[key];
|
|
238
240
|
var img = new Image();
|
|
239
241
|
img.key = key; // for debugging
|
|
240
242
|
img.onload = handleLoad.bind(img, key);
|
|
@@ -467,7 +469,7 @@
|
|
|
467
469
|
* Creates a sprite with optional animation, scaling, and collision bounds
|
|
468
470
|
* @param {Object} opts - Sprite config
|
|
469
471
|
* @param {string} opts.id - unique id for the sprite
|
|
470
|
-
* @param {string|string[]} opts.
|
|
472
|
+
* @param {string|string[]} opts.image - image name from config.images (use array for animations)
|
|
471
473
|
* @param {number} opts.x - x position
|
|
472
474
|
* @param {number} opts.y - y position
|
|
473
475
|
* @param {number} [opts.w] - width
|
|
@@ -480,13 +482,13 @@
|
|
|
480
482
|
|
|
481
483
|
if (!opts || typeof opts !== 'object') throw new Error('Sprite: missing opts param');
|
|
482
484
|
if (!opts.id || _sprites[opts.id]) throw new Error('Sprite: id must be unique');
|
|
483
|
-
if (!opts.
|
|
485
|
+
if (!opts.image) throw new Error('Sprite: missing image');
|
|
484
486
|
if (typeof opts.x === 'undefined') throw new Error('Sprite: missing x');
|
|
485
487
|
if (typeof opts.y === 'undefined') throw new Error('Sprite: missing y');
|
|
486
488
|
|
|
487
489
|
// option values
|
|
488
490
|
this.id = opts.id;
|
|
489
|
-
this.
|
|
491
|
+
this.image = opts.image;
|
|
490
492
|
this.preserveAspect = (opts.preserveAspect !== false);
|
|
491
493
|
this.collidable = (opts.collidable !== false);
|
|
492
494
|
this.outline = (typeof opts.outline === 'string') ? opts.outline : null;
|
|
@@ -521,9 +523,9 @@
|
|
|
521
523
|
}
|
|
522
524
|
|
|
523
525
|
this._frameIndex = 0;
|
|
524
|
-
this._animated = Array.isArray(this.
|
|
526
|
+
this._animated = Array.isArray(this.image);
|
|
525
527
|
|
|
526
|
-
var initialDrawKey = Array.isArray(this.
|
|
528
|
+
var initialDrawKey = Array.isArray(this.image) ? this.image[0] : this.image;
|
|
527
529
|
var img = images[initialDrawKey];
|
|
528
530
|
|
|
529
531
|
if (!img || !img.complete || !img.naturalWidth) throw new Error('Sprite: image not loaded ' + initialDrawKey);
|
|
@@ -540,12 +542,12 @@
|
|
|
540
542
|
// cache sprite in memory
|
|
541
543
|
_sprites[this.id] = this;
|
|
542
544
|
}
|
|
543
|
-
Sprite.prototype.
|
|
544
|
-
if (!this._animated) return this.
|
|
545
|
+
Sprite.prototype.getFrameImage = function () {
|
|
546
|
+
if (!this._animated) return this.image;
|
|
545
547
|
|
|
546
548
|
var index = (typeof this.frame === 'number' && this.frame >= 0) ? this.frame : this._frameIndex;
|
|
547
549
|
|
|
548
|
-
return this.
|
|
550
|
+
return this.image[index % this.image.length];
|
|
549
551
|
};
|
|
550
552
|
Sprite.prototype.update = function () {};
|
|
551
553
|
Sprite.prototype.draw = function (ctx) {
|
|
@@ -562,7 +564,7 @@
|
|
|
562
564
|
if (this.repeatX) return this.drawRepeatX(ctx);
|
|
563
565
|
if (this.repeatY) return this.drawRepeatY(ctx);
|
|
564
566
|
|
|
565
|
-
var drawKey = this.
|
|
567
|
+
var drawKey = this.getFrameImage(); // frame key to draw (single or animated)
|
|
566
568
|
var img = images[drawKey]; // loaded image object
|
|
567
569
|
if (!img || !img.complete || !img.naturalWidth) return;
|
|
568
570
|
|
|
@@ -645,7 +647,7 @@
|
|
|
645
647
|
|
|
646
648
|
if (this.destroyed) return;
|
|
647
649
|
|
|
648
|
-
var imgKey = this.
|
|
650
|
+
var imgKey = this.getFrameImage();
|
|
649
651
|
var img = images[imgKey];
|
|
650
652
|
if (!img || !img.complete || !img.naturalWidth) return;
|
|
651
653
|
|
|
@@ -710,7 +712,7 @@
|
|
|
710
712
|
* @returns {void}
|
|
711
713
|
*/
|
|
712
714
|
Sprite.prototype.computeBounds = function (img) {
|
|
713
|
-
var frameKey = this.
|
|
715
|
+
var frameKey = this.getFrameImage();
|
|
714
716
|
|
|
715
717
|
if (!this.relBounds || this._lastBoundsKey !== frameKey) {
|
|
716
718
|
this.relBounds = boundingCache.get(frameKey);
|
|
@@ -740,7 +742,7 @@
|
|
|
740
742
|
};
|
|
741
743
|
Sprite.prototype.drawRepeatX = function (ctx) {
|
|
742
744
|
|
|
743
|
-
var imgKey = this.
|
|
745
|
+
var imgKey = this.getFrameImage();
|
|
744
746
|
var img = images[imgKey];
|
|
745
747
|
if (!img || !img.complete || !img.naturalWidth) return;
|
|
746
748
|
|
|
@@ -762,7 +764,7 @@
|
|
|
762
764
|
};
|
|
763
765
|
Sprite.prototype.drawRepeatY = function (ctx) {
|
|
764
766
|
|
|
765
|
-
var imgKey = this.
|
|
767
|
+
var imgKey = this.getFrameImage();
|
|
766
768
|
var img = images[imgKey];
|
|
767
769
|
if (!img || !img.naturalHeight) return;
|
|
768
770
|
|
|
@@ -790,7 +792,7 @@
|
|
|
790
792
|
|
|
791
793
|
if (now - this._lastFrameTime >= delayBetweenFrames) {
|
|
792
794
|
this._lastFrameTime = now;
|
|
793
|
-
this._frameIndex = (this._frameIndex + 1) % this.
|
|
795
|
+
this._frameIndex = (this._frameIndex + 1) % this.image.length;
|
|
794
796
|
}
|
|
795
797
|
};
|
|
796
798
|
Sprite.prototype.moveX = function (dx) {
|
|
@@ -1003,7 +1005,7 @@
|
|
|
1003
1005
|
*/
|
|
1004
1006
|
function startLoop() {
|
|
1005
1007
|
|
|
1006
|
-
if (!_initilised) throw new Error('punter.
|
|
1008
|
+
if (!_initilised) throw new Error('punter.setup must be called first');
|
|
1007
1009
|
|
|
1008
1010
|
_canvasCtx = _canvas.getContext('2d', { alpha: true, desynchronized: true });
|
|
1009
1011
|
_canvasCtx.imageSmoothingEnabled = false;
|
|
@@ -1037,8 +1039,17 @@
|
|
|
1037
1039
|
// clear screen
|
|
1038
1040
|
_canvasCtx.clearRect(0, 0, engine.width, engine.height);
|
|
1039
1041
|
|
|
1040
|
-
// draw
|
|
1041
|
-
|
|
1042
|
+
// auto-draw all sprites
|
|
1043
|
+
for (var _id in _sprites) {
|
|
1044
|
+
if (Object.prototype.hasOwnProperty.call(_sprites, _id) && !_sprites[_id].destroyed) {
|
|
1045
|
+
_sprites[_id].draw(_canvasCtx);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// draw handler runs after sprites — use for text, HUD, overlays
|
|
1050
|
+
if (eventHandlers.draw) {
|
|
1051
|
+
eventHandlers.draw.call(_canvasCtx);
|
|
1052
|
+
}
|
|
1042
1053
|
|
|
1043
1054
|
// reset the flag after draw
|
|
1044
1055
|
_resized = false;
|
|
@@ -1067,7 +1078,7 @@
|
|
|
1067
1078
|
|
|
1068
1079
|
function pauseLoop() {
|
|
1069
1080
|
|
|
1070
|
-
if (!_initilised) throw new Error('punter.
|
|
1081
|
+
if (!_initilised) throw new Error('punter.setup must be called first');
|
|
1071
1082
|
|
|
1072
1083
|
if (_loopId !== null) {
|
|
1073
1084
|
cancelAnimationFrame(_loopId);
|
|
@@ -1090,7 +1101,7 @@
|
|
|
1090
1101
|
*/
|
|
1091
1102
|
function playSound(name, options) {
|
|
1092
1103
|
|
|
1093
|
-
if (!_initilised) throw new Error('punter.
|
|
1104
|
+
if (!_initilised) throw new Error('punter.setup must be called first');
|
|
1094
1105
|
|
|
1095
1106
|
var buffer = sounds[name];
|
|
1096
1107
|
if (!buffer) return;
|
|
@@ -1113,6 +1124,7 @@
|
|
|
1113
1124
|
|
|
1114
1125
|
source.connect(gainNode);
|
|
1115
1126
|
gainNode.connect(audioCtx.destination);
|
|
1127
|
+
if (audioCtx.state === 'suspended') audioCtx.resume();
|
|
1116
1128
|
source.start(0);
|
|
1117
1129
|
|
|
1118
1130
|
if (!options || !options.once) {
|
|
@@ -1131,7 +1143,7 @@
|
|
|
1131
1143
|
|
|
1132
1144
|
function stopSound(name) {
|
|
1133
1145
|
|
|
1134
|
-
if (!_initilised) throw new Error('punter.
|
|
1146
|
+
if (!_initilised) throw new Error('punter.setup must be called first');
|
|
1135
1147
|
|
|
1136
1148
|
var arr = playingSounds[name];
|
|
1137
1149
|
if (!arr || !arr.length) return;
|
|
@@ -1296,7 +1308,7 @@
|
|
|
1296
1308
|
var api = {
|
|
1297
1309
|
|
|
1298
1310
|
// initialization
|
|
1299
|
-
|
|
1311
|
+
setup: setup,
|
|
1300
1312
|
|
|
1301
1313
|
// scene lifecycle
|
|
1302
1314
|
scene: function (name, handler) {
|
|
@@ -1304,12 +1316,19 @@
|
|
|
1304
1316
|
},
|
|
1305
1317
|
go: function (name) {
|
|
1306
1318
|
|
|
1307
|
-
if (!_initilised) throw new Error('punter.init must be called first');
|
|
1308
1319
|
if (!_scenes[name]) throw new Error('punter.go: unknown scene "' + name + '"');
|
|
1320
|
+
if (!_initilised) { _pendingGo = name; return; }
|
|
1309
1321
|
|
|
1310
1322
|
// remove existing game loop handlers
|
|
1311
1323
|
eventHandlers.update = function () {};
|
|
1312
|
-
eventHandlers.draw =
|
|
1324
|
+
eventHandlers.draw = null;
|
|
1325
|
+
|
|
1326
|
+
// destroy all sprites from the previous scene
|
|
1327
|
+
for (var _sid in _sprites) {
|
|
1328
|
+
if (Object.prototype.hasOwnProperty.call(_sprites, _sid)) {
|
|
1329
|
+
_sprites[_sid].destroy();
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1313
1332
|
|
|
1314
1333
|
// ensure we clear all input from last scene
|
|
1315
1334
|
engine.clearInput();
|
|
@@ -1355,7 +1374,7 @@
|
|
|
1355
1374
|
|
|
1356
1375
|
// sprite factory
|
|
1357
1376
|
createSprite: function(opts) {
|
|
1358
|
-
if (!_initilised) throw new Error('createSprite: punter.
|
|
1377
|
+
if (!_initilised) throw new Error('createSprite: punter.setup must be called first');
|
|
1359
1378
|
return new Sprite(opts);
|
|
1360
1379
|
},
|
|
1361
1380
|
getSprite: function(id) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/sounds/beep1.mp3
ADDED
|
Binary file
|
package/sounds/beep2.mp3
ADDED
|
Binary file
|
package/sounds/beep3.mp3
ADDED
|
Binary file
|
package/sounds/beep4.mp3
ADDED
|
Binary file
|
package/sounds/beep5.mp3
ADDED
|
Binary file
|
package/sounds/beep6.mp3
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|