You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+96-51Lines changed: 96 additions & 51 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,7 +5,7 @@
5
5
[🕹️ CLICK HERE TO PLAY 🕹️](https://croquet.github.io/multiblaster-tutorial/step9.html) – _then scan the QR code or share the generated session URL to invite other players._
6
6
7
7
Each HTML file in [this repository](https://github.com/croquet/multiblaster-tutorial/)
8
-
contains an increasingly complete multiplayer game built using [Croquet](https://croquet.io/docs/).
8
+
contains an increasingly complete multiplayer game built using [Croquet](https://github.com/croquet/croquet).
9
9
10
10
It's a 2D game, and its visuals are intentionally kept simple so that the code is more understandable.
11
11
@@ -16,7 +16,7 @@ They can also shoot blasts which cause asteroids to break up and vanish.
16
16
Successful blasts increase the player's score, while colliding with an asteroid
17
17
causes a ship to be destroyed and lose all points.
18
18
19
-
**📖 Please use our [Documentation](https://croquet.io/docs/croquet/) alongside this tutorial, and join our [Discord](https://croquet.io/discord) for questions 🤔**
19
+
**📖 Please use our [Documentation](https://multisynq.io/docs/croquet/) alongside this tutorial, and join our [Discord](https://multisynq.io/discord) for questions 🤔**
20
20
21
21
## Step 0: Asteroids floating without Croquet 🪨≠🪨
22
22
@@ -84,19 +84,28 @@ by drawing the asteroids on a canvas. These parts are subclassed from
84
84
The last few lines instruct Croquet to join a session for a particular model and view class
85
85
via `Croquet.Session.join()`. The name and password for this session are taken from
86
86
the current URL, or generated automatically using `autoSession()` and `autoPassword`.
87
-
It also needs an API key. You should fetch your own key from [croquet.io/keys](https://croquet.io/keys/).
87
+
It also needs an API key. You should fetch your own key from [multisynq.io/coder](https://multisynq.io/coder/).
88
88
89
89
This version has only 20 lines more than the non-Croquet one from step 0.
90
90
91
-
Notice that the computation looks exactly the same, no special data structures need to be used,
92
-
all models are synchronized between machines without any special markup.
91
+
Notice that the computation looks exactly the same.
92
+
_No special data structures need to be used._
93
+
All models are synchronized between machines without any special markup.
93
94
94
95
```js
95
-
move() {
96
-
this.x= (this.x+this.dx+1000) %1000;
97
-
this.y= (this.y+this.dy+1000) %1000;
98
-
this.a= (this.a+this.da+Math.PI) %Math.PI;
99
-
this.future(50).move();
96
+
classAsteroidextendsCroquet.Model {
97
+
98
+
...
99
+
100
+
move() {
101
+
this.x= (this.x+this.dx+1000) %1000;
102
+
this.y= (this.y+this.dy+1000) %1000;
103
+
this.a= (this.a+this.da+Math.PI) %Math.PI;
104
+
this.future(50).move();
105
+
}
106
+
107
+
...
108
+
100
109
}
101
110
```
102
111
@@ -105,31 +114,38 @@ The only new construct is the line
105
114
this.future(50).move();
106
115
```
107
116
inside of the `move()` method. This causes `move()` to be called again 50 ms in the future,
108
-
similarly to the `timeout()` call in step 0. Future messages are how you define an object's behavior over
109
-
time in Croquet.
117
+
similarly to the `timeout()` call in step 0.
118
+
_Future messages are how you define an object's behavior over time in Croquet._
110
119
111
120
Drawing happens exactly the same as in the non-Croquet case:
112
121
113
122
```js
114
-
for (constasteroidofthis.model.asteroids) {
115
-
const { x, y, a, size } = asteroid;
116
-
this.context.save();
117
-
this.context.translate(x, y);
118
-
this.context.rotate(a);
119
-
this.context.beginPath();
120
-
this.context.moveTo(+size, 0);
121
-
this.context.lineTo( 0, +size);
122
-
this.context.lineTo(-size, 0);
123
-
this.context.lineTo( 0, -size);
124
-
this.context.closePath();
125
-
this.context.stroke();
126
-
this.context.restore();
123
+
classDisplayextendsCroquet.View {
124
+
125
+
...
126
+
127
+
update() {
128
+
...
129
+
for (constasteroidofthis.model.asteroids) {
130
+
const { x, y, a, size } = asteroid;
131
+
this.context.save();
132
+
this.context.translate(x, y);
133
+
this.context.rotate(a);
134
+
this.context.beginPath();
135
+
this.context.moveTo(+size, 0);
136
+
this.context.lineTo( 0, +size);
137
+
this.context.lineTo(-size, 0);
138
+
this.context.lineTo( 0, -size);
139
+
this.context.closePath();
140
+
this.context.stroke();
141
+
this.context.restore();
142
+
}
143
+
}
127
144
}
128
145
```
129
146
130
-
Notice that the view's `update()` method can read the asteroid positions directly from the model
131
-
for drawing. Unlike in server-client computing, these positions do not need to be transmitted
132
-
via the network. They are already available locally.
147
+
Notice that the view's `update()` method can read the asteroid positions directly from the model for drawing.
148
+
_Unlike in server-client computing, these positions do not need to be transmitted via the network._ They are already available locally.
133
149
134
150
However, you must take care to not accidentally modify any model properties directly,
135
151
because that would break the synchronization. See the next step for how to interact with the model.
@@ -145,6 +161,8 @@ For each player joining, another spaceship is created by subscribing to the sess
145
161
`view-join` and `view-exit` events:
146
162
147
163
```js
164
+
classGameextendsCroquet.Model {
165
+
148
166
init() {
149
167
...
150
168
this.ships=newMap();
@@ -162,33 +180,39 @@ For each player joining, another spaceship is created by subscribing to the sess
162
180
this.ships.delete(viewId);
163
181
ship.destroy();
164
182
}
183
+
184
+
...
165
185
```
166
186
Each ship subscribes to that player's input only, using the player's `viewId` as an event scope.
167
187
This is how the shared model can distinguish events sent from different user's views:
The ship's `move()` method uses the stored thruster values to accelerate or rotate the ship:
@@ -198,10 +222,12 @@ move() {
198
222
if (this.forward) this.accelerate(0.5);
199
223
if (this.left) this.a-=0.2;
200
224
if (this.right) this.a+=0.2;
201
-
...
202
-
}
225
+
this.x=...
226
+
this.y=...
203
227
```
204
228
229
+
Again, the ship's new rotation `a` and position `x,y` _do not need to be published to other players._ This computation happens synchronized on each player's machine, based on the `left`, `right`, and `forward` properties that were set via the following thruster events.
230
+
205
231
In the local view, key up and down events of the arrow keys publish the events to enable and disable the thrusters:
206
232
207
233
```js
@@ -292,6 +318,21 @@ mainLoop() {
292
318
this.checkCollisions();
293
319
this.future(50).mainLoop();
294
320
}
321
+
322
+
checkCollisions() {
323
+
for (constasteroidofthis.asteroids) {
324
+
constminx=asteroid.x-asteroid.size;
325
+
constmaxx=asteroid.x+asteroid.size;
326
+
constminy=asteroid.y-asteroid.size;
327
+
constmaxy=asteroid.y+asteroid.size;
328
+
for (constblastofthis.blasts) {
329
+
if (blast.x> minx &&blast.x< maxx &&blast.y> miny &&blast.y< maxy) {
330
+
asteroid.hitBy(blast);
331
+
break;
332
+
}
333
+
}
334
+
}
335
+
}
295
336
```
296
337
297
338
When an asteroid was hit by a blast, it shrinks itself and changes direction perpendicular to the shot.
@@ -313,6 +354,10 @@ hitBy(blast) {
313
354
}
314
355
```
315
356
357
+
The remarkable thing about this code is how unremarkable it is.
358
+
There is nothing "fancy" required of the programmer, it reads almost the same as if it were a single-player game.
359
+
And there is no network congestion even if hundreds of blasts are moving because their positions are never sent over the network.
360
+
316
361
## Step 5: Turn ship into debris after colliding with asteroids 🚀➡💥
@@ -614,8 +659,8 @@ There's an even more polished game with some gimmicks at
614
659
615
660
One of its gimmicks is that if the initials contain an emoji, it will be used for shooting. The trickiest part of that is properly parsing out the emoji, which can be composed of many code points 😉
616
661
617
-
You can play it online at [croquet.io/multiblaster](https://croquet.io/multiblaster/).
662
+
You can play it online at [apps.multisynq.io/multiblaster](https://apps.multisynq.io/multiblaster/).
618
663
619
664
## Further Information 👀
620
665
621
-
Please use our [Documentation](https://croquet.io/docs), and join our [Discord](https://croquet.io/discord) for questions!
666
+
Please use our [Documentation](https://multisynq.io/docs/croquet/) alongside this tutorial, and join our [Discord](https://multisynq.io/discord) for questions!
0 commit comments