Introduction
Dans ce cours nous verrons comment programmer des jeux "d'action" multijoueurs. On commencera en 2D par une approche simple, naïve, pour aller vers des solutions sophistiquées et adaptatives. Nous verrons des exemples simples en 2D puis passerons ensuite à la 3D. De simples algorithmes de "comportements intelligents" seront aussi implémentés pour les entités contrôlées par l'ordinateur (ex: suivre un chemin, suivre le joueur, attaquer, fuir, atteindre un but, marcher en formation etc.)
Séance 1 : rappels sur les bases de la programmation d'un jeu, utilisation naive de websockets pour la synchronisation multi-joueurs
TP1 : implémentation d'un squelette très simple et naïf de jeu multi-joueurs
Dans ce premier TP vous allez faire fonctionner un premier squelette de jeu très simple à l'aide d'un client basé sur le canvas HTML5. Vous utiliserez une animation à 60 images par secondes pour déplacer un joueur à l'écran à l'aide du clavier ou de la souris.
Dans un second temps vous installerez un serveur NodeJS capable de servir votre jeu (en renvoyant par exemple sa page HTML).
Dans un troisième temps vous transformerez votre serveur pour qu'il devienne "un serveur de websockets" et permette par exemple aux différents joueurs de "chatter". Enfin, en vous bansant sur le principe du chat, vous échangerez des messages spéciaux contenant la position de chaque jouer. On Modifiera aussi les clients pour qu'ils envoient leur position chaque fois qu'ils bougent, et qu'ils dessinent l'ensemble des joueurs présents dans la partie chaque fois que le serveur renvoie des positions modifiées.
Séance 2 : multiplayer games network synchronization algorithms
Séance 3 : let's start programming the multiplayer layer
You will start from the last example done during the course :
And we will start adding some "tooling" for implementing some of the algorithms seen during the previous seance (in this set of slides) :
- Modify the existing code to use "time based animation" in the animation loop! All moves (players, obstacles) will depend on the time elapsed since the last image. See this example from the MOOC "HTML5 Apps and games".
- Learn how to measure time with Date.now(), the JavaScript high-resolution timer, we will use this a lot. In order to execute periodic functions, we will also rely on setInterval(function, delayInMs). We will have such a periodic function on the client side (for sending updates to the server), and on the server side (for sending game states to the connected clients).
- Implement a "ping" measure from server to client.
- The latency is 2 * this ping : on the server side, we will measure the current time, then send a "ping" event through socket.io to the client. The client will send back a "pong" event. When the servers gets it, it computes the elapsed time.
- The server will send every 500ms this "ping" event (the latency will be measured 2 times per second)
- When the "pong" arrives, it will send back to the client a "timedata" message to the client, that will displays it on the web page.
- This "timedata" message will contain : 1) the ping measure and 2) "the server time" (elapsed time since the client connected to the server, measured on the server side)
- Find a way to delay artificially all the messages sent by the client. We will call this delay "artificial latency", it will be useful for simulating latency when running client and server on a local machine. We suggest write a "send(eventType, data)" function, that will use setTimeout(function() { socket.emit(...); }, delay); for that. Add some slides to set this delay (default = 0, max=300ms).
- Implement a "heartbeat" sent by the server a certain amount of times per second (this amount will be stored in a variable named "nbUpdatesPerSeconds"). So, every 1000/nbUpdatesPerSeconds, we will call a heartbeat function on the server, that will send a "heartbeat" message to the client. For the moment, we will send nothind with this event. Later, we will send the "updated game state".
- Implements a way to change the previous value from the client, and display it. Beware, this value must be synchronized on all connected clients. We suggest that the client will receive the value of the nbUpdatesPerSeconds variable first when it connects to the server, then only when one client changes it. Changes from client will be done using a "changeNbUpdates" event.
- The client will have also a "updateClient" function called every nbClientUpdatesPerSeconds, that will send its status to the server.
- Try now to send every 100ms (nbClientUpdatesPerSeconds = 10), the current player status (x, y, speed, clientTime) to the server. The server, will "animate" server side this player, and send back to the client its own new position of the player (through the "heartbeat" function)
- The client will receive its postion computed by the player ("heartbeat" event). Try to display it in the client.
- Normally, there should be only a small difference between the position drawn and computed by the client and the one computed by the server.
- Try to add some artificial latency, or change the number of updates per second on the server. See what is happening.
- Try to display ONLY the current player when the server sends back its position
- What we have is 1) the player animated client side, like in earlier versions of the game, is a sort of "prediction" (cf the course slides), the position sent by the server is the "master" one. Normally, the next step is "reconciliation", where the client tries to "follow" as close as possible the position sent by the "master server" (we call it an "authoritative server).
- Read again the slides about "prediction and reconciliation", try to implement this correctly this time. This work will be your first assignment.