-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtutorial-spaceships.html
279 lines (248 loc) · 28.7 KB
/
tutorial-spaceships.html
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Spaaace - Documentation</title>
<link rel="shortcut icon" href="http://lance.gg/favicon.ico">
<link rel="icon" sizes="16x16 32x32 64x64" href="http://lance.gg/favicon.ico">
<link rel="icon" type="image/png" sizes="196x196" href="http://lance.gg/favicon-192.png">
<link rel="icon" type="image/png" sizes="160x160" href="http://lance.gg/favicon-160.png">
<link rel="icon" type="image/png" sizes="96x96" href="http://lance.gg/favicon-96.png">
<link rel="icon" type="image/png" sizes="64x64" href="http://lance.gg/favicon-64.png">
<link rel="icon" type="image/png" sizes="32x32" href="http://lance.gg/favicon-32.png">
<link rel="icon" type="image/png" sizes="16x16" href="http://lance.gg/favicon-16.png">
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link href="https://file.myfontastic.com/DeXq9523CzrFERZkXSzP7D/icons.css" rel="stylesheet">
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
<link rel="stylesheet" href="//cdn.jsdelivr.net/highlight.js/9.8.0/styles/atelier-sulphurpool-light.min.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<h2 class="home"><a href="index.html">Lance</a></h2><h2>Concepts</h2><ul class="tutorials"><li><span class='category'>Introduction<ul><li><a href="tutorial-introduction.html">Introduction</a></li><li><a href="tutorial-introduction_prologue.html">Prologue</a></li><li><a href="tutorial-introduction_community.html">Community</a></li><li><a href="tutorial-introduction_roadmap.html">Development Roadmap</a></li><li><a href="tutorial-introduction_faq.html">Frequently Asked Questions</a></li></ul></li><li><span class='category'>Overview<ul><li><a href="tutorial-overview_architecture.html">Architecture of a Multiplayer Game</a></li><li><a href="tutorial-choosing_a_physics_engine.html">Choosing a Physics Engine</a></li><li><a href="tutorial-guide_gameengine.html">Game Engine</a></li><li><a href="tutorial-guide_serverengine.html">Server Engine</a></li><li><a href="tutorial-guide_clientengine.html">Client Engine</a></li><li><a href="tutorial-guide_renderer.html">Renderer</a></li><li><a href="tutorial-guide_gameworld.html">Game World and Game Objects</a></li><li><a href="tutorial-guide_serialization.html">Serialization and Communication</a></li></ul></li><li><span class='category'>Synchronization Methods<ul><li><a href="tutorial-guide_syncinterpolation.html">Interpolation</a></li><li><a href="tutorial-guide_syncextrapolation.html">Extrapolation</a></li></ul></li><li><span class='category'>Tutorials<ul><li><a href="tutorial-MyFirstGame.html">My First Game: Pong</a></li><li><a href="tutorial-spaceships.html">Spaaace</a></li></ul></li><li><span class='category'>Extras<ul><li><a href="tutorial-guide_tuningdebugging.html">Fine Tuning and Debugging</a></li><li><a href="tutorial-furtherreading.html">Further Reading</a></li></ul></li></ul><h2>API Reference</h2><h3 class="classes">Classes</h3><ul><li><a href="AFrameRenderer.html">AFrameRenderer</a><ul class='methods'><li data-type='method'><a href="AFrameRenderer.html#draw">draw</a></li><li data-type='method'><a href="AFrameRenderer.html#init">init</a></li></ul></li><li><a href="BaseTypes.html">BaseTypes</a></li><li><a href="CannonPhysicsEngine.html">CannonPhysicsEngine</a></li><li><a href="ClientEngine.html">ClientEngine</a><ul class='methods'><li data-type='method'><a href="ClientEngine.html#connect">connect</a></li><li data-type='method'><a href="ClientEngine.html#disconnect">disconnect</a></li><li data-type='method'><a href="ClientEngine.html#sendInput">sendInput</a></li><li data-type='method'><a href="ClientEngine.html#start">start</a></li></ul></li><li><a href="DynamicObject.html">DynamicObject</a><ul class='methods'><li data-type='method'><a href="DynamicObject.html#bendingToString">bendingToString</a></li><li data-type='method'><a href="DynamicObject.html#toString">toString</a></li></ul></li><li><a href="GameEngine.html">GameEngine</a><ul class='methods'><li data-type='method'><a href="GameEngine.html#addObjectToWorld">addObjectToWorld</a></li><li data-type='method'><a href="GameEngine.html#getPlayerGameOverResult">getPlayerGameOverResult</a></li><li data-type='method'><a href="GameEngine.html#isOwnedByPlayer">isOwnedByPlayer</a></li><li data-type='method'><a href="GameEngine.html#on">on</a></li><li data-type='method'><a href="GameEngine.html#once">once</a></li><li data-type='method'><a href="GameEngine.html#processInput">processInput</a></li><li data-type='method'><a href="GameEngine.html#registerClasses">registerClasses</a></li><li data-type='method'><a href="GameEngine.html#removeListener">removeListener</a></li><li data-type='method'><a href="GameEngine.html#removeObjectFromWorld">removeObjectFromWorld</a></li><li data-type='method'><a href="GameEngine.html#start">start</a></li><li data-type='method'><a href="GameEngine.html#step">step</a></li></ul></li><li><a href="GameObject.html">GameObject</a><ul class='methods'><li data-type='method'><a href="GameObject.html#bendingToString">bendingToString</a></li><li data-type='method'><a href="GameObject.html#hasComponent">hasComponent</a></li><li data-type='method'><a href="GameObject.html#onAddToWorld">onAddToWorld</a></li><li data-type='method'><a href="GameObject.html#onRemoveFromWorld">onRemoveFromWorld</a></li><li data-type='method'><a href="GameObject.html#syncTo">syncTo</a></li><li data-type='method'><a href="GameObject.html#toString">toString</a></li></ul></li><li><a href="GameWorld.html">GameWorld</a><ul class='methods'><li data-type='method'><a href="GameWorld.html#addObject">addObject</a></li><li data-type='method'><a href="GameWorld.html#forEachObject">forEachObject</a></li><li data-type='method'><a href="GameWorld.html#getNewId">getNewId</a></li><li data-type='method'><a href="GameWorld.html#queryObject">queryObject</a></li><li data-type='method'><a href="GameWorld.html#queryObjects">queryObjects</a></li><li data-type='method'><a href="GameWorld.html#removeObject">removeObject</a></li></ul></li><li><a href="KeyboardControls.html">KeyboardControls</a></li><li><a href="PhysicalObject2D.html">PhysicalObject2D</a><ul class='methods'><li data-type='method'><a href="PhysicalObject2D.html#onAddToWorld">onAddToWorld</a></li><li data-type='method'><a href="PhysicalObject2D.html#toString">toString</a></li></ul></li><li><a href="PhysicalObject3D.html">PhysicalObject3D</a><ul class='methods'><li data-type='method'><a href="PhysicalObject3D.html#toString">toString</a></li></ul></li><li><a href="Quaternion.html">Quaternion</a><ul class='methods'><li data-type='method'><a href="Quaternion.html#conjugate">conjugate</a></li><li data-type='method'><a href="Quaternion.html#copy">copy</a></li><li data-type='method'><a href="Quaternion.html#multiply">multiply</a></li><li data-type='method'><a href="Quaternion.html#set">set</a></li><li data-type='method'><a href="Quaternion.html#setFromAxisAngle">setFromAxisAngle</a></li><li data-type='method'><a href="Quaternion.html#slerp">slerp</a></li><li data-type='method'><a href="Quaternion.html#toAxisAngle">toAxisAngle</a></li><li data-type='method'><a href="Quaternion.html#toString">toString</a></li></ul></li><li><a href="Renderer.html">Renderer</a><ul class='methods'><li data-type='method'><a href="Renderer.html#addObject">addObject</a></li><li data-type='method'><a href="Renderer.html#draw">draw</a></li><li data-type='method'><a href="Renderer.html#init">init</a></li><li data-type='method'><a href="Renderer.html#removeObject">removeObject</a></li><li data-type='method'><a href="Renderer.html#runClientStep">runClientStep</a></li><li data-type='method'><a href="Renderer.html#stop">stop</a></li></ul></li><li><a href="Serializer.html">Serializer</a><ul class='methods'><li data-type='method'><a href="Serializer.html#.typeCanAssign">typeCanAssign</a></li><li data-type='method'><a href="Serializer.html#addCustomType">addCustomType</a></li><li data-type='method'><a href="Serializer.html#registerClass">registerClass</a></li></ul></li><li><a href="ServerEngine.html">ServerEngine</a><ul class='methods'><li data-type='method'><a href="ServerEngine.html#gameStatus">gameStatus</a></li></ul></li><li><a href="SimplePhysicsEngine.html">SimplePhysicsEngine</a></li><li><a href="ThreeVector.html">ThreeVector</a><ul class='methods'><li data-type='method'><a href="ThreeVector.html#add">add</a></li><li data-type='method'><a href="ThreeVector.html#clone">clone</a></li><li data-type='method'><a href="ThreeVector.html#copy">copy</a></li><li data-type='method'><a href="ThreeVector.html#getBendingDelta">getBendingDelta</a></li><li data-type='method'><a href="ThreeVector.html#length">length</a></li><li data-type='method'><a href="ThreeVector.html#lerp">lerp</a></li><li data-type='method'><a href="ThreeVector.html#multiplyScalar">multiplyScalar</a></li><li data-type='method'><a href="ThreeVector.html#normalize">normalize</a></li><li data-type='method'><a href="ThreeVector.html#set">set</a></li><li data-type='method'><a href="ThreeVector.html#subtract">subtract</a></li><li data-type='method'><a href="ThreeVector.html#toString">toString</a></li></ul></li><li><a href="Trace.html">Trace</a></li><li><a href="TwoVector.html">TwoVector</a><ul class='methods'><li data-type='method'><a href="TwoVector.html#add">add</a></li><li data-type='method'><a href="TwoVector.html#clone">clone</a></li><li data-type='method'><a href="TwoVector.html#copy">copy</a></li><li data-type='method'><a href="TwoVector.html#getBendingDelta">getBendingDelta</a></li><li data-type='method'><a href="TwoVector.html#length">length</a></li><li data-type='method'><a href="TwoVector.html#lerp">lerp</a></li><li data-type='method'><a href="TwoVector.html#multiplyScalar">multiplyScalar</a></li><li data-type='method'><a href="TwoVector.html#normalize">normalize</a></li><li data-type='method'><a href="TwoVector.html#set">set</a></li><li data-type='method'><a href="TwoVector.html#subtract">subtract</a></li><li data-type='method'><a href="TwoVector.html#toString">toString</a></li></ul></li></ul><h3 class="events">Events</h3><ul><li><a href="GameEngine.html#event:client__postStep">client__postStep</a></li><li><a href="GameEngine.html#event:client__preStep">client__preStep</a></li><li><a href="GameEngine.html#event:client__processInput">client__processInput</a></li><li><a href="GameEngine.html#event:client__slowFrameRate">client__slowFrameRate</a></li><li><a href="GameEngine.html#event:client__stepReset">client__stepReset</a></li><li><a href="GameEngine.html#event:client__syncReceived">client__syncReceived</a></li><li><a href="GameEngine.html#event:objectAdded">objectAdded</a></li><li><a href="GameEngine.html#event:objectDestroyed">objectDestroyed</a></li><li><a href="GameEngine.html#event:playerDisconnected">playerDisconnected</a></li><li><a href="GameEngine.html#event:playerJoined">playerJoined</a></li><li><a href="GameEngine.html#event:postStep">postStep</a></li><li><a href="GameEngine.html#event:preStep">preStep</a></li><li><a href="GameEngine.html#event:processInput">processInput</a></li><li><a href="GameEngine.html#event:server__inputReceived">server__inputReceived</a></li><li><a href="GameEngine.html#event:server__playerDisconnected">server__playerDisconnected</a></li><li><a href="GameEngine.html#event:server__playerJoined">server__playerJoined</a></li><li><a href="GameEngine.html#event:server__postStep">server__postStep</a></li><li><a href="GameEngine.html#event:server__preStep">server__preStep</a></li><li><a href="GameEngine.html#event:server__processInput">server__processInput</a></li><li><a href="GameEngine.html#event:start">start</a></li><li><a href="GameEngine.html#event:syncReceived">syncReceived</a></li></ul>
</nav>
<div id="main">
<h1 class="page-title">Spaaace</h1>
<section>
<header>
</header>
<article>
<h1>Lance Game Tutorial</h1><p>This 45-minute tutorial will guide you in the building of a
JavaScript networked game. It is meant as a more advanced
tutorial, a follow-up to <a href="tutorial-MyFirstGame.html">My First Game: Pong</a>.
This tutorial repeats the environment setup, but goes further in-depth,
introducing the concepts and basic components of a
networked game,
and sequence flows on the server and the clients.</p>
<p>All the code in this tutorial references the <a href="https://github.com/lance-gg/spaaace">spaaace</a> repository - so
first, clone that repository to see the referenced files:</p>
<pre class="prettyprint source lang-shell"><code>git clone https://github.com/lance-gg/spaaace
cd spaaace
npm install
npm start</code></pre><p>Open one or more browsers at http://localhost:3000 to see the game running.</p>
<h2>The Components of a Networked Game</h2><p>The networked game is architected around the following components:</p>
<ul>
<li><strong>The server</strong>. Represented by the <code>ServerEngine</code> class. The code in this class will run on the authoritative server only.</li>
<li><strong>The clients</strong>. Represented by the <code>ClientEngine</code> class. The code in this class will run on each browser playing the game.</li>
<li><strong>The game logic</strong>. Represented by the <code>GameEngine</code> class. In extrapolation mode, the code in this class runs on the server, but is also executed on each client, as each client attempts to extrapolate what is happening on the authoritative server.</li>
<li><strong>Multiple game objects</strong>. The <code>GameObject</code> is the base class for all kinds of game objects. Each game object will be associated with one or more render objects, as well as one or more physics objects. For example, in a game of spaceships, each spaceship is a game object, but each spaceship game object will have multiple render objects associated with it, such as the spaceship image, the engine thrust images, and an explosion image which is rendered when the spaceship explodes. In addition, a single spaceship game object is linked to physical objects in a physics engine. And the physical object will likely be a very simple shape, like a square or a triangle or a point.</li>
<li><strong>Synchronization</strong>. Lance provides several ways to synchronize between the server and the clients. The game developer must configure which synchronization method works best for any given game.</li>
</ul>
<p>As you write your game, you will need to implement
your own extensions (sub-classes) of the classes above. But most of the interesting
stuff will happen in your extension of GameEngine.</p>
<h2>Basic Flow</h2><p>The basic flow of the game can be seen as a sequence of <em>game steps</em>.
Steps will be executed both on the server and the client. Each step is numbered,
and depending on the synchronization strategy, clients may be executing a given
step before the corresponding state data has arrived from the server (i.e. extrapolation) or after (i.e. interpolation).
Ideally, a given step _N_ represents the same point in game play on both the server
and the client, and occurs at the same time. But this is not always the case.</p>
<h3>Server Flow:</h3><p>The server main entry point is a simple javascript file which initializes an instance of an extended <code>ServerEngine</code> class and an instance of an extended <code>GameEngine</code> class. In our tutorial the file is called <a href="https://github.com/lance-gg/spaaace/blob/master/main.js"><code>main.js</code></a>.</p>
<p>The server engine schedules a <code>step</code> function to be called at a regular interval. The
flow is:</p>
<ul>
<li><strong>ServerEngine</strong> - start of a single server step<ul>
<li>GameEngine - <strong>collect inputs from all players:</strong> process any inputs that arrived</li>
<li>GameEngine - start of a single game step<ul>
<li>PhysicsEngine - handle physics step</li>
</ul>
</li>
<li><strong>Broadcast game state to clients:</strong> If a game-state broadcast is due<ul>
<li>For each connected player P_i_<ul>
<li>Transmit a "world update" to player P_i_</li>
<li>This world update is called a "sync" and contains only the necessary changes to game objects</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Note: the flow described above is already implemented in Lance, and the game developer only needs to implement the <code>ServerEngine</code> class and the <code>GameEngine</code> class.</p>
<h3>Client Flow:</h3><p>The client flow is actually more complicated than the server flow, because of synchronization strategies and the rendering. The client consists of three independent work schedules: The game step logic, the render step logic, and the server update sync logic.</p>
<ul>
<li><strong>ClientEngine</strong> - Renderer draw event<ul>
<li>At the draw event, the client will typically execute one client game step</li>
<li>Check inbound messages / world updates<ul>
<li>if a world update has arrived, parse and store the data</li>
</ul>
</li>
<li>Capture local user inputs that have occurred since previous step</li>
<li>Transmit inputs to server</li>
<li>Handle inputs locally</li>
<li>GameEngine - start of a game step<ul>
<li>PhysicsEngine - handle physics step</li>
</ul>
</li>
<li>For each object in the world O_i_:<ul>
<li>update the associated render objects for O_i_</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Note: the flow described above is already implemented in Lance, and the game developer only needs to implement the <code>ClientEngine</code> class and the <code>GameEngine</code> class.</p>
<h2>Step 1: main.js and the ServerEngine</h2><h3>Build your own ServerEngine class:</h3><p>The first step is to build your own ServerEngine-derived class. For this tutorial
you can look at file <a href="https://github.com/lance-gg/spaaace/blob/master/src/server/SpaaaceServerEngine.js"><code>src/server/SpaaaceServerEngine.js</code></a></p>
<p>This file does the following:</p>
<ol>
<li>Handle player-connection logic by creating a ship for the new player. See method <code>onPlayerConnected</code>.</li>
<li>handle player-disconnection logic by removing the ship of the disconnected player. See method <code>onPlayerDisconnected</code>.</li>
<li>note the <code>makeBot()</code> method, it creates AI-controlled spaceships, which are controlled on the server only. The AI-control code is implemented in the GameEngine, but since it is only called by the ServerEngine class, it will only run on the server. This is the preferred technique to implement game code which only executes on the server.</li>
</ol>
<h3>Build the main entry point:</h3><p>The next step is to write the server entry code. For this tutorial the corresponding
file is <a href="https://github.com/lance-gg/spaaace/blob/master/main.js"><code>main.js</code></a></p>
<p>The file does the following:</p>
<ol>
<li>create an express server and configure the root route '/'</li>
<li>create a socketIO handler</li>
<li>create an instance of SpaaaceServerEngine</li>
<li>create an instance of SpaaaceGameEngine</li>
<li>start the serverEngine instance</li>
</ol>
<p>Sample entry code will look like this:</p>
<pre class="prettyprint source lang-javascript"><code>const express = require('express');
const socketIO = require('socket.io');
const path = require('path');
const PORT = process.env.PORT || 3000;
const INDEX = path.join(__dirname, './index.html');
// define routes and socket
const server = express();
server.get('/', function(req, res) { res.sendFile(INDEX); });
server.use('/', express.static(path.join(__dirname, '.')));
let requestHandler = server.listen(PORT, () => console.log(`Listening on ${ PORT }`));
const io = socketIO(requestHandler);
// Game Server
import MyServerEngine from './src/server/SpaaaceServerEngine.js';
import MyGameEngine from './src/common/SpaaaceGameEngine.js';
// Game Instances
const gameEngine = new MyGameEngine();
const serverEngine = new MyServerEngine(io, gameEngine, {
debug: {},
updateRate: 6,
timeoutInterval: 0 // no timeout
});
// start the game
serverEngine.start();</code></pre><h2>Step 2: the GameEngine</h2><p>To implement the game logic, you must create a new class which extends
<strong>GameEngine</strong> class. This is where your game mechanics (a.k.a. game rules,
or business logic) are implemented.
Remember that most of this code is meant to execute on the server
as well as on each client.</p>
<p>For this tutorial, take a look at <a href="https://github.com/lance-gg/spaaace/blob/master/src/common/SpaaaceGameEngine.js"><code>src/common/SpaaaceGameEngine.js</code></a>
The game engine logic has three major tasks:</p>
<ol>
<li>to extend the <code>processInput()</code> method. This is the logic which handles new user input such as movement, firing, activate ability, etc. In the sample code the <code>processInput()</code> method handles the keyboard inputs "up", "right", "left", "space". The inputs will cause the spaceship to accelerate, turn right or left, or to fire a missile.</li>
<li><code>makeShip()</code>. Creates a new ship, as a result of a new connection received on the server.</li>
<li><code>makeMissile()</code>. Create a new missile, as a result of one ship firing.</li>
<li><code>destroyMissile()</code>. Remove a missile.</li>
</ol>
<h2>Step 3: <code>clientMain.js</code>, <code>SpaaaceClientEngine.js</code>, and <code>SpaaaceRenderer.js</code></h2><p>The client entry code creates a game engine, a client engine, and their options. The options configure the synchronization.</p>
<p>The full sample code is in <a href="https://github.com/lance-gg/spaaace/blob/master/src/client/clientMain.js"><code>src/client/clientMain.js</code></a> and is implemented as follows:</p>
<pre class="prettyprint source lang-javascript"><code>import SpaaaceClientEngine from './SpaaaceClientEngine';
import SpaaaceGameEngine from '../common/SpaaaceGameEngine';
import '../../assets/sass/main.scss';
// sent to both game engine and client engine
const options = {
traceLevel: 1000,
delayInputCount: 8,
scheduler: 'render-schedule',
syncOptions: {
sync: 'extrapolate',
localObjBending: 0.2,
remoteObjBending: 0.5
}
};
// create a client engine and a game engine
const gameEngine = new SpaaaceGameEngine(options);
const clientEngine = new SpaaaceClientEngine(gameEngine, options);
clientEngine.start();</code></pre><h2>Step 4: DynamicObjects</h2><p>Your game objects, including monsters, spaceships, zombies, and bosses, all extend
the <code>DynamicObject</code> class. More advanced games which require a true 3D physics engine will prefer to extend the <code>PhysicsObject</code> class, but that is outside the scope of this tutorial. The <code>DynamicObject</code> base class is a
serializable class, meaning that the server can serialize any instance of the class
(and sub-class) into a binary object, and transmit it to the clients. Instances of dynamic objects (and instances of dynamic object sub-classes)
must be serializable so that the server can send updates to the clients.</p>
<p>The serialization mechanism requires sub-classes to explicitly list which attributes will be serialized. Each object must specify exactly which attribute values need to be serialized and transmitted from the server to the clients on each update. To describe the added attributes of an extended class, use the <code>netscheme</code> mechanism.</p>
<p>Take a look at the <code>netScheme()</code> getter in <a href="https://github.com/lance-gg/spaaace/blob/master/src/common/Ship.js"><code>src/common/Ship.js</code></a> and
<a href="https://github.com/lance-gg/spaaace/blob/master/src/common/Missile.js"><code>src/common/Missile.js</code></a>. The <code>netScheme()</code> getter extends the super-class netScheme. In both files you will find that the base class provides most of the needed logic for movement,
and synchronization.</p>
<h2>Step 5: Putting it all together</h2><p>For the full game, you will need to create a <a href="https://github.com/lance-gg/spaaace/blob/master/package.json"><code>package.json</code></a> file, and <a href="https://github.com/lance-gg/spaaace/blob/master/index.html"><code>index.html</code></a> file,
examples of which are available in the <a href="https://github.com/lance-gg/spaaace">spaaace</a> repository.</p>
<p>To run the server run <code>npm run build</code> followed by <code>npm start</code>.<br>Note that the server has two roles: <strong>(1)</strong> it acts as an HTTP server, serving index.html to clients
which connect to the game;
and <strong>(2)</strong> it runs the ServerEngine socket.io entry point, accepting client connections, running the server-authoritative game engine, and broadcasting updates to the clients. In a published game, you will likely want to store the static files in a CDN, and your game engine will only act in the second role.</p>
<h2>Game Events</h2><p>It is good programming practice to implement your code using event handlers,
so that it is clear what each chunk of logic is handling.</p>
<p>The full list of events is available in the <a href="GameEngine.html">API GameEngine reference</a>, so we will only list the most
important events here.</p>
<ul>
<li><code>preStep</code> and <code>postStep</code> - emitted by game engine, just before and just after step execution. The event handlers receive the step number and whether or not this step is a reenactment.</li>
<li><code>objectAdded</code> and <code>objectDestroyed</code> - emitted on object addition/destruction, the handlers receive the object as an argument.</li>
<li><code>syncReceived</code> - emitted on the client when a sync (e.g world update) is received.</li>
<li><code>playerJoined</code> and <code>playerDisconnected</code> - emitted on player connect/disconnect. The handlers receive an object describing the player. The object contains attribute playerId.</li>
</ul>
<h2>Traces & Debugging</h2><p>For debugging purposes, the game engine provides a tracing service. The trace
service exposes functions <code>trace()</code>, <code>debug()</code>, <code>info()</code>, <code>warn()</code> and <code>error()</code>.
The functions are listed above in increasing order of importance. When the
game engine is started, it can be passed a minimum trace level option. For example
a minimum trace level of <code>TRACE_INFO</code> will mean that entries of type "trace" and "debug"
will be ignored.</p>
<p>A trace message is usually recorded as follows:</p>
<pre class="prettyprint source lang-javascript"><code>gameEngine.trace.info(() => `this just happened: ${foobar()}`);</code></pre><p>The trace methods receive arrow functions as an input, because if they would receive the template string then this template string will be evaluated even in those cases where no tracing is done. This has been shown to add a heavy load to the garbage collection.</p>
<p>By default, Lance already traces a lot of information, describing
the progress of the game and each step in detail.
The traces are text files, written on the server side. The server trace
file is called <strong>server.trace</strong> and the client trace files are called <strong>client._n_.trace</strong>
where <strong>_n_</strong> is the player's id.</p>
<h3>How to enable traces</h3><p>In our tutorial example, the URL's query-string parameters become options which are
passed to the game engine constructor. So the client-side traces can be activated from the query string, simply by setting the parameter <code>&traceLevel=0</code> on the query
string. </p>
<p>On the server side traces can be enabled by setting the <code>traceLevel</code> option on the <code>GameEngine</code>, this option specifies the minimum trace level to record, so by setting this option to 0 you can get all trace messages.</p>
</article>
</section>
</div>
<br class="clear">
<footer>
Did you find a mistake? Do you have an improvement? <a href="https://github.com/lance-gg/lance/issues">Let us know!</a>
</footer>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-88335360-1', 'auto');
ga('send', 'pageview');
</script>
<script src="https://use.typekit.net/lai1bbe.js"></script>
<script>try{Typekit.load({ async: true });}catch(e){}</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="scripts/linenumber.js"></script>
</body>
</html>