/
index.ts
162 lines (142 loc) · 4.4 KB
/
index.ts
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
import './style.css';
// @ts-ignore
import keyboardSVG from '/keyboard.svg?raw';
// @ts-ignore
import mouseSVG from '/mouse.svg?raw';
import {
Action,
ActionManager,
Axis2dAction,
BooleanAction,
BooleanMapping,
DownTrigger,
EmulatedAxis2dMapping,
KeyboardBinding,
KeyboardDevice,
LongPressTrigger,
MouseBinding,
MouseDevice,
PressTrigger,
} from 'manette-js';
import {Game} from './game.js';
// Input interface, contains the list of commands.
const ui = document.getElementById('ui')!;
// Overlay displaying "Restarting...".
const restartOverlay = document.querySelector('.overlay') as HTMLElement;
/**
* Append an action in the UI.
*
* @param action The action to append.
*/
function updateUI(action: Action) {
let last = (ui.children[0] as HTMLElement).querySelector('p[action-id]');
if (!last || last.getAttribute('action-id') !== action.id) {
const div = document.createElement('div');
div.classList.add('action');
div.innerHTML = `
${action.device!.id === 'keyboard' ? keyboardSVG : mouseSVG}
<p action-id=${action.id} count="0">Action</p>
`;
last = div.children[1];
ui.prepend(div);
}
const count = `${parseInt(last.getAttribute('count')!) + 1}`;
last.setAttribute('count', count);
last.innerHTML = `${action.id} x${count}`;
}
// Contains the game logic, i.e., player, bullets & enemy logic.
const game = new Game(document.getElementsByTagName('canvas')[0]);
window.onresize = () => game.resize();
/*
* This example contains two devices:
*
* - MouseDevice
* - KeyboardDevice
*
* Devices process raw inputs from device such as mouse, keyboard,
* gamepads.
*/
const mouse = new MouseDevice('mouse');
mouse.enable(document.body);
const keyboard = new KeyboardDevice('keyboard');
keyboard.enable(document.body);
/*
* Action definition.
*
* Every action is used to perform logic in the game, such as
* moving the player, or firing a bullet.
*/
// Triggered when the player moves.
const move = new Axis2dAction('Move');
// Triggered when the player shoots.
const fire = new BooleanAction('Fire');
fire.completed.add(() => game.spawnBullet()); // Fire bullet upon event.
// Triggered when the player holds the restart button.
const reset = new BooleanAction('ResetGame');
reset.started.add(() => (restartOverlay.style.display = 'initial'));
reset.canceled.add(() => (restartOverlay.style.display = 'none'));
reset.completed.add(() => {
restartOverlay.style.display = 'none';
game.reset();
});
// Whenever any of the action is completed, update the UI.
for (const action of [fire, move, reset]) {
action.completed.add(updateUI);
}
/*
* The ActionManager is in charge of linking actions to their mapping.
*
* The manager must be updated by calling `manager.update(dt)` every
* frame in order to notify the attached triggers.
*/
const manager = new ActionManager();
manager.add(
fire,
[
new BooleanMapping(mouse, MouseBinding.Primary),
new BooleanMapping(keyboard, KeyboardBinding.Enter),
],
new PressTrigger()
);
manager.add(
move,
[
new EmulatedAxis2dMapping(keyboard, {
// WASD to [-1; 1]
maxY: KeyboardBinding.KeyW,
minX: KeyboardBinding.KeyA,
minY: KeyboardBinding.KeyS,
maxX: KeyboardBinding.KeyD,
}).setTrigger(new DownTrigger()),
new EmulatedAxis2dMapping(keyboard, {
// Arrows to [-1; 1]
maxY: KeyboardBinding.ArrowUp,
minX: KeyboardBinding.ArrowLeft,
minY: KeyboardBinding.ArrowDown,
maxX: KeyboardBinding.ArrowRight,
}),
],
new DownTrigger()
);
manager.add(reset, [
new BooleanMapping(keyboard, KeyboardBinding.Space).setTrigger(
new LongPressTrigger(1.0)
),
]);
function animate() {
const now = performance.now();
const dt = (now - game.previousTime) / 1000.0;
// The manager needs to be updated with the delta time due to some
// triggers being time-based.
manager.update(dt);
// The move action is considered a "value" action. The value is
// used as-is to move the player at each frame.
//
// This is different than event-based actions, such as the `fire` one.
game.moveSpeed = move.value[1];
game.update(dt, mouse.absolute);
game.render();
game.previousTime = now;
window.requestAnimationFrame(animate);
}
animate();