retro-pong-game 1.1.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/README.md +402 -0
- package/dist/pong-game.d.ts +196 -0
- package/dist/pong-game.es.js +3447 -0
- package/dist/pong-game.umd.js +280 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# retro-pong-game
|
|
2
|
+
|
|
3
|
+
A retro-style Pong game that runs in any browser. Drop it onto your website with a single constructor call. No frameworks, no extra configuration required.
|
|
4
|
+
|
|
5
|
+
- Animated starfield or nebular background
|
|
6
|
+
- Particle effects on paddle hits
|
|
7
|
+
- Countdown timer with configurable duration
|
|
8
|
+
- Adaptive AI difficulty (auto-adjusts after scoring streaks)
|
|
9
|
+
- In-game settings panel (changes saved to `localStorage`)
|
|
10
|
+
- Stereo-panned sound effects
|
|
11
|
+
- Fully configurable colors, sizes, and speeds
|
|
12
|
+
- TypeScript types included
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install retro-pong-game
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
### 1. Add a container element to your HTML
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<div id="game"></div>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The game appends a `<canvas>` inside this element automatically.
|
|
33
|
+
|
|
34
|
+
### 2. Import and create the game
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import { PongGame } from 'retro-pong-game';
|
|
38
|
+
|
|
39
|
+
const game = new PongGame(
|
|
40
|
+
{
|
|
41
|
+
canvasWidth: 600,
|
|
42
|
+
canvasHeight: 400,
|
|
43
|
+
hasSound: true,
|
|
44
|
+
colors: {
|
|
45
|
+
background: '#1a1a2e',
|
|
46
|
+
paddle: '#e94560',
|
|
47
|
+
ball: '#e94560',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
'#game'
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Clean up when done
|
|
55
|
+
|
|
56
|
+
Always call `destroy()` before removing the element from the page:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
game.destroy();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## CDN (no npm required)
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<!DOCTYPE html>
|
|
68
|
+
<html lang="en">
|
|
69
|
+
<head>
|
|
70
|
+
<meta charset="UTF-8" />
|
|
71
|
+
<title>Pong</title>
|
|
72
|
+
</head>
|
|
73
|
+
<body>
|
|
74
|
+
<div id="game"></div>
|
|
75
|
+
|
|
76
|
+
<script src="https://unpkg.com/retro-pong-game/dist/pong-game.umd.js"></script>
|
|
77
|
+
<script>
|
|
78
|
+
const game = new PongGame.PongGame(
|
|
79
|
+
{ canvasWidth: 600, canvasHeight: 400 },
|
|
80
|
+
'#game'
|
|
81
|
+
);
|
|
82
|
+
</script>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Framework examples
|
|
90
|
+
|
|
91
|
+
### React
|
|
92
|
+
|
|
93
|
+
```jsx
|
|
94
|
+
import { useEffect, useRef } from 'react';
|
|
95
|
+
import { PongGame } from 'retro-pong-game';
|
|
96
|
+
|
|
97
|
+
export default function PongWidget() {
|
|
98
|
+
const gameRef = useRef(null);
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
gameRef.current = new PongGame(
|
|
102
|
+
{ canvasWidth: 600, canvasHeight: 400 },
|
|
103
|
+
'#pong-container'
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
gameRef.current?.destroy();
|
|
108
|
+
};
|
|
109
|
+
}, []);
|
|
110
|
+
|
|
111
|
+
return <div id="pong-container" />;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Vue 3
|
|
116
|
+
|
|
117
|
+
```vue
|
|
118
|
+
<template>
|
|
119
|
+
<div id="pong-container" />
|
|
120
|
+
</template>
|
|
121
|
+
|
|
122
|
+
<script setup>
|
|
123
|
+
import { onMounted, onBeforeUnmount } from 'vue';
|
|
124
|
+
import { PongGame } from 'retro-pong-game';
|
|
125
|
+
|
|
126
|
+
let game = null;
|
|
127
|
+
|
|
128
|
+
onMounted(() => {
|
|
129
|
+
game = new PongGame({ canvasWidth: 600, canvasHeight: 400 }, '#pong-container');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
onBeforeUnmount(() => {
|
|
133
|
+
game?.destroy();
|
|
134
|
+
});
|
|
135
|
+
</script>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Vanilla HTML + ES modules
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<div id="game"></div>
|
|
142
|
+
|
|
143
|
+
<script type="module">
|
|
144
|
+
import { PongGame } from '/node_modules/retro-pong-game/dist/pong-game.es.js';
|
|
145
|
+
|
|
146
|
+
const game = new PongGame({ canvasWidth: 600, canvasHeight: 400 }, '#game');
|
|
147
|
+
</script>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### TypeScript
|
|
151
|
+
|
|
152
|
+
Import `PongGameConfig` for full type safety and editor autocomplete on every option:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { PongGame, PongGameConfig } from 'retro-pong-game';
|
|
156
|
+
|
|
157
|
+
const config: PongGameConfig = {
|
|
158
|
+
autoSize: false,
|
|
159
|
+
canvasWidth: 400,
|
|
160
|
+
canvasHeight: 500,
|
|
161
|
+
paddleWidth: 70,
|
|
162
|
+
paddleHeight: 10,
|
|
163
|
+
ballDiameter: 7,
|
|
164
|
+
ballSpeed: 3,
|
|
165
|
+
paddleMoveStep: 4,
|
|
166
|
+
timer: {
|
|
167
|
+
duration: '1:30',
|
|
168
|
+
hasBackgroundCircle: true,
|
|
169
|
+
labelColor: '#ffffff',
|
|
170
|
+
labelFontSize: 12,
|
|
171
|
+
circleColor: '#62cb31',
|
|
172
|
+
circleLineWidth: 5,
|
|
173
|
+
circleBackgroundColor: '#333',
|
|
174
|
+
},
|
|
175
|
+
colors: {
|
|
176
|
+
paddle: '#4daae8',
|
|
177
|
+
ball: '#ff7a59',
|
|
178
|
+
background: '#222',
|
|
179
|
+
centerline: '#9e9c9c',
|
|
180
|
+
score: '#9e9c9c',
|
|
181
|
+
},
|
|
182
|
+
animatedBackground: {
|
|
183
|
+
starfield: true,
|
|
184
|
+
nebular: false,
|
|
185
|
+
},
|
|
186
|
+
difficultyLevel: 1,
|
|
187
|
+
particleBounce: true,
|
|
188
|
+
particleConfig: {
|
|
189
|
+
particlesCount: 20,
|
|
190
|
+
},
|
|
191
|
+
hasSound: true,
|
|
192
|
+
onSoundToggle: (enabled: boolean) => {
|
|
193
|
+
// enabled = true → sound is ON
|
|
194
|
+
// enabled = false → sound is OFF (muted)
|
|
195
|
+
console.log('Sound toggled:', enabled);
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const game = new PongGame(config, '#game');
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Configuration
|
|
205
|
+
|
|
206
|
+
All options are optional. Omitted values fall back to built-in defaults.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
new PongGame(config: PongGameConfig, selector: string)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The config is deep-merged in this order (later layers win):
|
|
213
|
+
|
|
214
|
+
1. Built-in defaults
|
|
215
|
+
2. Config you pass to the constructor
|
|
216
|
+
3. Settings the player saved through the in-game settings panel (`localStorage`)
|
|
217
|
+
|
|
218
|
+
### `PongGameConfig`
|
|
219
|
+
|
|
220
|
+
| Option | Type | Default | Description |
|
|
221
|
+
|---|---|---|---|
|
|
222
|
+
| `autoSize` | `boolean` | `false` | Size the canvas to fill its container element at startup |
|
|
223
|
+
| `autoResize` | `boolean` | `false` | Automatically resize the canvas when the container changes size (uses `ResizeObserver`) |
|
|
224
|
+
| `canvasWidth` | `number` | `400` | Canvas width in px — ignored when `autoSize` is `true` |
|
|
225
|
+
| `canvasHeight` | `number` | `300` | Canvas height in px — ignored when `autoSize` is `true` |
|
|
226
|
+
| `paddleWidth` | `number` | `70` | Paddle width in px |
|
|
227
|
+
| `paddleHeight` | `number` | `10` | Paddle height in px |
|
|
228
|
+
| `ballDiameter` | `number` | `7` | Ball diameter in px |
|
|
229
|
+
| `ballSpeed` | `number` | `3` | Starting ball speed (px per frame) |
|
|
230
|
+
| `paddleMoveStep` | `number` | `4` | Paddle speed while an arrow key is held (px per frame) |
|
|
231
|
+
| `difficultyLevel` | `number` | `1` | Starting AI difficulty — higher = faster and more precise |
|
|
232
|
+
| `particleBounce` | `boolean` | `true` | Particle burst when the ball hits a paddle |
|
|
233
|
+
| `hasSound` | `boolean` | `false` | Enable sound effects |
|
|
234
|
+
| `onSoundToggle` | `(enabled: boolean) => void` | — | Called when the player toggles the sound icon |
|
|
235
|
+
| `timer` | [`TimerConfig`](#timerconfig) | see below | Countdown timer settings |
|
|
236
|
+
| `colors` | [`ColorConfig`](#colorconfig) | see below | Colors for game elements |
|
|
237
|
+
| `animatedBackground` | [`AnimatedBackgroundConfig`](#animatedbackgroundconfig) | see below | Background animation |
|
|
238
|
+
| `particleConfig` | [`ParticleConfig`](#particleconfig) | see below | Particle effect settings |
|
|
239
|
+
|
|
240
|
+
### `TimerConfig`
|
|
241
|
+
|
|
242
|
+
| Option | Type | Default | Description |
|
|
243
|
+
|---|---|---|---|
|
|
244
|
+
| `duration` | `string` | `"2:00"` | Match duration in `"M:SS"` format |
|
|
245
|
+
| `hasBackgroundCircle` | `boolean` | `false` | Show a circular progress ring around the timer |
|
|
246
|
+
| `labelColor` | `string` | `"#ffffff"` | Timer text color |
|
|
247
|
+
| `labelFontSize` | `number` | `12` | Timer text size in px |
|
|
248
|
+
| `circleColor` | `string` | `"#62cb31"` | Countdown ring stroke color |
|
|
249
|
+
| `circleLineWidth` | `number` | `5` | Countdown ring line width in px |
|
|
250
|
+
| `circleBackgroundColor` | `string` | `"#ff00ff"` | Countdown ring background track color |
|
|
251
|
+
|
|
252
|
+
### `ColorConfig`
|
|
253
|
+
|
|
254
|
+
| Option | Type | Default | Description |
|
|
255
|
+
|---|---|---|---|
|
|
256
|
+
| `paddle` | `string` | `"#ffffff"` | Paddle color |
|
|
257
|
+
| `ball` | `string` | `"#ffffff"` | Ball color |
|
|
258
|
+
| `background` | `string` | `"#000000"` | Canvas background color |
|
|
259
|
+
| `centerline` | `string` | `"#9e9c9c"` | Center dividing line color |
|
|
260
|
+
| `score` | `string` | `"#9e9c9c"` | Score text color |
|
|
261
|
+
|
|
262
|
+
### `AnimatedBackgroundConfig`
|
|
263
|
+
|
|
264
|
+
| Option | Type | Default | Description |
|
|
265
|
+
|---|---|---|---|
|
|
266
|
+
| `starfield` | `boolean` | `true` | Scrolling starfield effect |
|
|
267
|
+
| `nebular` | `boolean` | `false` | Scrolling nebular cloud effect |
|
|
268
|
+
|
|
269
|
+
> Enable at most one background effect at a time.
|
|
270
|
+
|
|
271
|
+
### `ParticleConfig`
|
|
272
|
+
|
|
273
|
+
| Option | Type | Default | Description |
|
|
274
|
+
|---|---|---|---|
|
|
275
|
+
| `particlesCount` | `number` | `20` | Number of particles spawned per paddle hit |
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Methods
|
|
280
|
+
|
|
281
|
+
```js
|
|
282
|
+
// Control the user paddle programmatically (e.g. on-screen buttons, touch)
|
|
283
|
+
game.pressLeft(); // start moving paddle left
|
|
284
|
+
game.releaseLeft(); // stop moving paddle left
|
|
285
|
+
game.pressRight(); // start moving paddle right
|
|
286
|
+
game.releaseRight(); // stop moving paddle right
|
|
287
|
+
|
|
288
|
+
// Enable or disable sound effects
|
|
289
|
+
game.toggleSound(true);
|
|
290
|
+
game.toggleSound(false);
|
|
291
|
+
|
|
292
|
+
// Check if sound is currently enabled
|
|
293
|
+
game.hasSound(); // → boolean
|
|
294
|
+
|
|
295
|
+
// Adjust AI difficulty manually
|
|
296
|
+
game.increaseDifficulty();
|
|
297
|
+
game.decreaseDifficulty();
|
|
298
|
+
|
|
299
|
+
// Resize the canvas when the container changes size
|
|
300
|
+
window.addEventListener('resize', () => game.resizeGame());
|
|
301
|
+
|
|
302
|
+
// Get the internal SoundHelper for advanced audio control
|
|
303
|
+
const soundHelper = game.getSoundHelper();
|
|
304
|
+
|
|
305
|
+
// Tear down the game completely (always call before unmounting)
|
|
306
|
+
game.destroy();
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Paddle control example
|
|
310
|
+
|
|
311
|
+
Use `pressLeft` / `releaseLeft` / `pressRight` / `releaseRight` to wire on-screen buttons for touch or mouse control:
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
function wireButton(btn, pressFn, releaseFn) {
|
|
315
|
+
btn.addEventListener('pointerdown', (e) => {
|
|
316
|
+
e.preventDefault();
|
|
317
|
+
btn.setPointerCapture(e.pointerId);
|
|
318
|
+
pressFn();
|
|
319
|
+
});
|
|
320
|
+
btn.addEventListener('pointerup', releaseFn);
|
|
321
|
+
btn.addEventListener('pointercancel', releaseFn);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const game = new PongGame(config, '#my-pong');
|
|
325
|
+
|
|
326
|
+
wireButton(document.getElementById('btn-left'),
|
|
327
|
+
() => game.pressLeft(), () => game.releaseLeft());
|
|
328
|
+
wireButton(document.getElementById('btn-right'),
|
|
329
|
+
() => game.pressRight(), () => game.releaseRight());
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Keyboard controls
|
|
335
|
+
|
|
336
|
+
| Key | Action |
|
|
337
|
+
|---|---|
|
|
338
|
+
| `←` Arrow Left | Move paddle left |
|
|
339
|
+
| `→` Arrow Right | Move paddle right |
|
|
340
|
+
| `P` or `Space` | Pause / resume |
|
|
341
|
+
|
|
342
|
+
The player controls the **bottom** paddle. The **top** paddle is the AI opponent.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Settings panel
|
|
347
|
+
|
|
348
|
+
The game includes a built-in settings panel (gear icon in the corner of the canvas). Players can change colors, background effects, difficulty, timer duration, and more. All changes are saved to `localStorage` and restored automatically on the next visit.
|
|
349
|
+
|
|
350
|
+
To reset all saved settings back to defaults:
|
|
351
|
+
|
|
352
|
+
```js
|
|
353
|
+
localStorage.removeItem('chriscreativecode.com');
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Resize handling
|
|
359
|
+
|
|
360
|
+
### Automatic (recommended)
|
|
361
|
+
|
|
362
|
+
Set `autoSize: true` and `autoResize: true` together. The game sizes itself to the container on startup and re-sizes automatically whenever the container changes — no extra code needed.
|
|
363
|
+
|
|
364
|
+
```js
|
|
365
|
+
const game = new PongGame(
|
|
366
|
+
{ autoSize: true, autoResize: true },
|
|
367
|
+
'#game'
|
|
368
|
+
);
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
The container must have a CSS-defined size (width and height). For example, to fill the viewport:
|
|
372
|
+
|
|
373
|
+
```css
|
|
374
|
+
#game {
|
|
375
|
+
width: 100%;
|
|
376
|
+
height: 100vh;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Manual
|
|
381
|
+
|
|
382
|
+
When `autoResize` is `false` (the default), call `resizeGame()` yourself whenever the container changes size:
|
|
383
|
+
|
|
384
|
+
```js
|
|
385
|
+
window.addEventListener('resize', () => game.resizeGame());
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### `autoSize` without `autoResize`
|
|
389
|
+
|
|
390
|
+
`autoSize: true` alone sizes the canvas once at startup. The game then has a fixed size until you call `resizeGame()` manually.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Browser support
|
|
395
|
+
|
|
396
|
+
All modern browsers with HTML Canvas support (Chrome, Firefox, Safari, Edge). When importing the ES module build, a bundler (Vite, Webpack, esbuild, Parcel) is recommended for production use.
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## License
|
|
401
|
+
|
|
402
|
+
MIT © [Chris Schardijn](https://chriscreativecode.com)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/** Configuration for the countdown timer displayed during a match. */
|
|
2
|
+
export interface TimerConfig {
|
|
3
|
+
/** Timer duration in "M:SS" format. Default: `"2:00"` */
|
|
4
|
+
duration?: string;
|
|
5
|
+
/** Show a circular progress ring behind the timer. Default: `false` */
|
|
6
|
+
hasBackgroundCircle?: boolean;
|
|
7
|
+
/** Color of the timer label text. Default: `"#ffffff"` */
|
|
8
|
+
labelColor?: string;
|
|
9
|
+
/** Font size of the timer label in pixels. Default: `12` */
|
|
10
|
+
labelFontSize?: number;
|
|
11
|
+
/** Stroke color of the countdown ring. Default: `"#62cb31"` */
|
|
12
|
+
circleColor?: string;
|
|
13
|
+
/** Line width of the countdown ring in pixels. Default: `5` */
|
|
14
|
+
circleLineWidth?: number;
|
|
15
|
+
/** Background track color of the countdown ring. Default: `"#ff00ff"` */
|
|
16
|
+
circleBackgroundColor?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Color configuration for the main game elements. */
|
|
20
|
+
export interface ColorConfig {
|
|
21
|
+
/** Paddle fill color. Default: `"#ffffff"` */
|
|
22
|
+
paddle?: string;
|
|
23
|
+
/** Ball fill color. Default: `"#ffffff"` */
|
|
24
|
+
ball?: string;
|
|
25
|
+
/** Canvas background color. Default: `"#000000"` */
|
|
26
|
+
background?: string;
|
|
27
|
+
/** Center dividing line color. Default: `"#9e9c9c"` */
|
|
28
|
+
centerline?: string;
|
|
29
|
+
/** Score text color. Default: `"#9e9c9c"` */
|
|
30
|
+
score?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Animated background effects. Enable at most one at a time. */
|
|
34
|
+
export interface AnimatedBackgroundConfig {
|
|
35
|
+
/** Enable a scrolling starfield effect. Default: `true` */
|
|
36
|
+
starfield?: boolean;
|
|
37
|
+
/** Enable a scrolling nebular cloud effect. Default: `false` */
|
|
38
|
+
nebular?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Configuration for the particle burst on paddle hits. */
|
|
42
|
+
export interface ParticleConfig {
|
|
43
|
+
/** Number of particles spawned per hit. Default: `20` */
|
|
44
|
+
particlesCount?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Full configuration for `PongGame`.
|
|
49
|
+
*
|
|
50
|
+
* All fields are optional — any omitted value falls back to its built-in default.
|
|
51
|
+
* Options passed here are deep-merged with the defaults and with any settings
|
|
52
|
+
* the player has previously saved through the in-game settings panel.
|
|
53
|
+
*/
|
|
54
|
+
export interface PongGameConfig {
|
|
55
|
+
/**
|
|
56
|
+
* Automatically size the canvas to fill its container element at startup.
|
|
57
|
+
* When `true`, `canvasWidth` and `canvasHeight` are ignored.
|
|
58
|
+
* Default: `false`
|
|
59
|
+
*/
|
|
60
|
+
autoSize?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Automatically resize the canvas whenever the container element changes size.
|
|
63
|
+
* Uses a `ResizeObserver` — no manual `resizeGame()` calls needed.
|
|
64
|
+
* Combine with `autoSize: true` to fill the container on startup as well.
|
|
65
|
+
* Default: `false`
|
|
66
|
+
*/
|
|
67
|
+
autoResize?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Canvas width in pixels. Ignored when `autoSize` is `true`.
|
|
70
|
+
* Default: `400`
|
|
71
|
+
*/
|
|
72
|
+
canvasWidth?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Canvas height in pixels. Ignored when `autoSize` is `true`.
|
|
75
|
+
* Default: `300`
|
|
76
|
+
*/
|
|
77
|
+
canvasHeight?: number;
|
|
78
|
+
/** Paddle width in pixels. Default: `70` */
|
|
79
|
+
paddleWidth?: number;
|
|
80
|
+
/** Paddle height in pixels. Default: `10` */
|
|
81
|
+
paddleHeight?: number;
|
|
82
|
+
/** Ball diameter in pixels. Default: `7` */
|
|
83
|
+
ballDiameter?: number;
|
|
84
|
+
/** Starting ball speed in pixels per frame. Default: `3` */
|
|
85
|
+
ballSpeed?: number;
|
|
86
|
+
/** Distance the paddle moves per frame while an arrow key is held. Default: `4` */
|
|
87
|
+
paddleMoveStep?: number;
|
|
88
|
+
/** Countdown timer settings. */
|
|
89
|
+
timer?: TimerConfig;
|
|
90
|
+
/** Colors for the game elements. */
|
|
91
|
+
colors?: ColorConfig;
|
|
92
|
+
/** Animated background effects. */
|
|
93
|
+
animatedBackground?: AnimatedBackgroundConfig;
|
|
94
|
+
/**
|
|
95
|
+
* Starting AI difficulty level.
|
|
96
|
+
* Higher values increase both ball speed and AI prediction precision.
|
|
97
|
+
* Default: `1`
|
|
98
|
+
*/
|
|
99
|
+
difficultyLevel?: number;
|
|
100
|
+
/** Emit particle bursts when the ball hits a paddle. Default: `true` */
|
|
101
|
+
particleBounce?: boolean;
|
|
102
|
+
/** Particle burst effect settings. */
|
|
103
|
+
particleConfig?: ParticleConfig;
|
|
104
|
+
/** Enable in-game sound effects. Default: `false` */
|
|
105
|
+
hasSound?: boolean;
|
|
106
|
+
/** Called whenever the player toggles sound on or off via the in-game icon. */
|
|
107
|
+
onSoundToggle?: (enabled: boolean) => void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A retro Pong game rendered on an HTML canvas.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```html
|
|
115
|
+
* <div id="game"></div>
|
|
116
|
+
* ```
|
|
117
|
+
* ```js
|
|
118
|
+
* import { PongGame } from 'retro-pong-game';
|
|
119
|
+
*
|
|
120
|
+
* const game = new PongGame(
|
|
121
|
+
* {
|
|
122
|
+
* canvasWidth: 600,
|
|
123
|
+
* canvasHeight: 400,
|
|
124
|
+
* hasSound: true,
|
|
125
|
+
* colors: { background: '#1a1a2e', paddle: '#e94560', ball: '#e94560' },
|
|
126
|
+
* },
|
|
127
|
+
* '#game'
|
|
128
|
+
* );
|
|
129
|
+
*
|
|
130
|
+
* // Clean up when done:
|
|
131
|
+
* game.destroy();
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare class PongGame {
|
|
135
|
+
/**
|
|
136
|
+
* Create and immediately start a new Pong game.
|
|
137
|
+
*
|
|
138
|
+
* A `<canvas>` element is appended to the container matched by `selector`.
|
|
139
|
+
* Initialization is asynchronous — the canvas and first frame appear after
|
|
140
|
+
* a microtask.
|
|
141
|
+
*
|
|
142
|
+
* @param config - Game options. Deep-merged with built-in defaults and any
|
|
143
|
+
* settings previously saved to `localStorage` via the in-game settings panel.
|
|
144
|
+
* @param selector - CSS selector for the host container element,
|
|
145
|
+
* e.g. `"#game"` or `".pong-wrapper"`.
|
|
146
|
+
*/
|
|
147
|
+
constructor(config: PongGameConfig, selector: string);
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Return the internal `SoundHelper` instance for advanced audio control.
|
|
151
|
+
* For basic muting, prefer `toggleSound()`.
|
|
152
|
+
*/
|
|
153
|
+
getSoundHelper(): unknown;
|
|
154
|
+
|
|
155
|
+
/** Return `true` if sound effects are currently enabled. */
|
|
156
|
+
hasSound(): boolean;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Enable or disable all sound effects.
|
|
160
|
+
* Also fires the `onSoundToggle` callback defined in the config.
|
|
161
|
+
*
|
|
162
|
+
* @param enabled - `true` to unmute, `false` to mute.
|
|
163
|
+
*/
|
|
164
|
+
toggleSound(enabled: boolean): void;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Increase the AI difficulty and ball speed by one step.
|
|
168
|
+
* This is also triggered automatically when the player scores 3 points in a row.
|
|
169
|
+
*/
|
|
170
|
+
increaseDifficulty(): void;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Decrease the AI difficulty and ball speed by one step.
|
|
174
|
+
* This is also triggered automatically when the AI scores 3 points in a row.
|
|
175
|
+
*/
|
|
176
|
+
decreaseDifficulty(): void;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Resize the canvas and all game elements to fit the current container size.
|
|
180
|
+
* Call this whenever the browser window or container element changes dimensions.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```js
|
|
184
|
+
* window.addEventListener('resize', () => game.resizeGame());
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
resizeGame(): void;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Fully tear down the game instance: stops the render loop, removes all
|
|
191
|
+
* event listeners, and removes the canvas from the DOM.
|
|
192
|
+
*
|
|
193
|
+
* Always call this before unmounting the page or component that hosts the game.
|
|
194
|
+
*/
|
|
195
|
+
destroy(): void;
|
|
196
|
+
}
|