Créez un jeux en ligne (MMO) en HTML5 avec Phaser et Socket.IO – partie 4

Créez un jeux en ligne (MMO) en HTML5 avec Phaser et Socket.IO – partie 4

Sommaire

Introduction

Dans la partie précédente du tutorial on a vu comment créer un système de chat basique, ça a été l’occasion de voir comment gérer la GUI de notre jeux en utilisant HTML + CSS, ansi que les bases sur l’utilisation de socket.io.

Dans ce tutorial j’essayerais d’expliquer comment les joueurs peuvent interagir avec des objets dans le jeux tout en synchronisant l’état de ces objets à travers tout les joueurs connectés.

Avant de commencer je souhaiterais indiquer que je ne vais mettre que le code nécessaire à l’explication des concepts qu’on va aborder dans l’article, le reste est disponible dans github.

Le code source sur GitHub

Objectif

L’explication des différents concepts se fera à travers la création d’un mini jeux, dans ce petit jeux le joueur devra collectionner des pièces d’or éparpillées dans la map pour augmenter son score.

Derrière se principe simpliste se pose des questions auxquels on devra répondre :

  • Comment mettre les objets à collectionner dans la map ?
  • Comment afficher les objets disponibles aux joueurs ?
  • Comment mettre à jour le score du joueur quand il collecte un objet ?
  • Comment notifier les joueurs qu’un objet viens d’être collecté ?
  • Et si deux joueurs collectent un objet en même temps ? comment décider qui aura la priorité ?

On va essayer d’aborder comment répondre à toutes ces questions durant ce tuto, c’est partit !

Demo

Voici une petite demo du résultat final, ici le joueur pourra collecter des pièces d’or pour gagner des points, vous pouvez voir le score de tout les joueurs connectés dans la barre de droite.

Important : Pour tester le code d’une manière optimale, il est préférable d’ouvrir le lien de démo depuis deux devices différents, si vous testez sur le même ordinateur, vous remarquerez que le jeux se met en pause dès que la fenêtre perd le focus.

lien de la démo ici

Gestion des objets à collecter

Les objets à collectionner ( dans notre cas les pièces d’or) sont des objets qui doivent être commun à tout les joueurs de la partie, c’est pourquoi il faudra les gérer du coté serveur pour synchroniser leurs états.

Oui, et alors ?

toutes les informations sur les objets (position, caractéristiques …) devront être gérés par le serveur, ce qui veux dire que chaque joueur devra demander la liste des objets disponibles dans la map au serveur durant sa connexion, pour ainsi connaitre quels sonts les pièces d’or disponibles.

Réception des objets

Réception des objets

Positionner les objets à collecter

Avant toute chose, il faut définir l’emplacement de nos pièces d’or dans la map, pour cela on peux soit mettre ça manuellement dans le code, soit utiliser un map editor, et j’avoue qu’un map editor est beaucoup plus simpa à utiliser.

Dans ce tuto on va utiliser Tiled pour définir l’emplacement des pièces d’or dans la map.

Utiliser Tiled pour gérer les objets à collecter

Dans la première partie du tutorial on a utilisé Tiled pour créer la map de notre jeux, la map générée fait partie du code de la partie client de notre jeux, cependant rien ne nous empêche de l’utiliser dans la partie serveur pour gérer les objets interactifs de notre jeux ( objets à collecter , pnj …)

Principe de fonctionnement

le fichier de la map sera donc une ressource partagée entre le client et le serveur, le serveur va lire à partir du fichier de la map les informations sur les objets à collecter, et le client va lire les informations sur le décor de la map.

Positionner les objets

Le but ici est de marquer les emplacements des objets à collectionner, on va donc commencer par ajouter dans Tiled une tile réservée aux objets à collectionner :

Tile des collectables dans TIled

Tile des collectables dans Tiled

On ajoute une nouvelle couche (Layer) qui va contenir les informations sur les objets à collecter, pour faire cela, dans le menu, cliquez sur Layer -> Add Object Layer

Tile des objets dans TIled

Couche des objets à collecter

Notez bien que cette couche sera utilisée uniquement coté serveur.

Après avoir ajouté la couches des objets, il faut maintenant configurer les types d’objets qu’on va positionner, pour ce faire, dans Tiled, on va ouvrir la fenêtre de préférences : Edit > Preferences, puis dans l’onglet « Object types » on configure les types de nos objets, pour ce mini jeu on aura un seul type qui sera appelé « Gold ».

Types d'objets dans TIled

Types d’objets dans Tiled

Comme indiqué dans l’image on ajoute les types d’objets avec lesquels le joueur pourra interagir, pour le moment on veux que notre joueur collecte des pièces d’or donc on va appeler le premier type d’objets : « Gold ».

Positionner les objets à collecter

On a mis en place la structure à utiliser, il ne reste plus qu’a positionner les objets à collecter, dans la layer « s_collectable », pour faire ça on va suivre les étapes suivantes :

  • Cliquez sur la couche « s_collectable »
  • Cliquez sur le bouton « Insert Tile » (raccourcis T)
  • Choisir la tile dédiés au objets
  • Positionner un objet dans la map
  • Choisir le type de l’objet : clic droit sur la tile de l’objet, et choisissez le type de l’objet
mmo4_position_objects_server


Les objets à collecter dans la map

configurer le type du collectable

Configurer ce collectable pour être de type « Gold »

Réorganisation dans le code

Le code va s’agrandir, et pour garder le tout lisible et facilement maintenable une réorganisation du code est nécessaire, vous remarquerez que la partie serveur contient un nouveaux dossier MapSync, voici comment il est organisé :

  • server/game/MapSync/Collectable/Gold.js: représentation des pièces d’or positionnées sur la map.
  • server/game/MapSync/Collectable/mapDataServer.js: ici on gère toute les interactions concernant les objets de la map.
  • server/game/MapSync/Collectable/playersManager.js: objet pour gérer la liste des joueurs.

Représentation de l’objet à collecter sur le serveur

Ici on va créer ce qu’on appelle un « Model » pour notre premier objet à collecter, ça sera la représentation de l’objet sur le serveur, et sera utilisé pour faciliter la gestion avec le client.

» Afficher le fichier : server/game/MapSync/Collectable/Gold.js

    function Gold(id, x, y) {
      this.uid = id;
      this.x = x;
      this.y = y;

      this.isAvailable = true;
      this.type = 'Gold';
      this.scoreValue = 10;
  }

  module.exports = Gold;
  

» Cacher

On peux voir dans ce fichier les différentes caractéristiques d’un objet de type « Gold » qui sont les suivantes :

  • id : identifiant unique d’un objet à collecter dans la map
  • x , y : coordonnées de l’objet
  • isAvailable : true si l’objet est toujours disponible pour les autres joueurs ( pas encore collecté).
  • type: le type de l’objet, cette valeur sera utilisée plus tard dans la partie client.
  • scoreValue: de combien de points le score d’un joueur sera augmenté.

Chargement des objets à collecter à partir de la map

Durant le démarrage du serveur, on va charger les objets à collecter à partir du fichier de la map, puis stocker les informations sur ces objets dans un tableaux :

» Afficher le fichier : server/game/MapSync/mapDataServer.js

  var ArrayUtils = require('../../utils/Arrays');
  var playersManager = require('./PlayersManager');
  var Gold = require('./Collectable/Gold');

  var MapFile = require('../../../client/assets/gameAssets/map/map.json');

  var collectableObjects = [];

  const COLLECTABLE_LAYER_NAME = "s_collectable";

  ...

  function init(){
      loadCollectablesFromMapFile();

       ...
  }

  function loadCollectablesFromMapFile(){
      collectableObjects = [];

      var collectableLayer = MapFile.layers.filter(function( layer ) {
          return layer.name === COLLECTABLE_LAYER_NAME;
      })[0];

      var counter = 0;
      collectableLayer.objects.forEach(function(colObject){
          if(colObject.type === 'Gold'){
              collectableObjects.push(new Gold(counter, colObject.x, colObject.y));
              counter++;
          }
      });

      console.log('** Collectable objects loaded');
  }

  ...

  

» Cacher

Explication du code

var MapFile = require('../../../client/assets/gameAssets/map/map.json');

Etant donné que la map est exportée au format json, alors pour la charger il suffit d’utiliser un require avec le chemin relatif vers la map, on a maintenant un objet Json qui représente la map, l’étape suivante est de lire les objets à collecter depuis cet objet.

var collectableLayer = MapFile.layers.filter(function( layer ) {
        return layer.name === COLLECTABLE_LAYER_NAME;
    })[0];

nos objets sonts dans la couche « s_collectable », donc on commence par charger cette couche dans la variable collectableLayer.


var counter = 0;
collectableLayer.objects.forEach(function(colObject){
    if(colObject.type === 'Gold'){
        collectableObjects.push(new Gold(counter, colObject.x, colObject.y));
        counter++;
    }
});

On parcourt ici tout les objets de cette layer, quand on rencontre un objet de type ‘Gold’, on instancie le modèle correspondant, puis on l’ajoute dans le tableau collectablesObjects, ce tableau sera utilisé pour stocker les objets de la map.

Note pour le future: le fichier de la map (map.json) est maintenant une ressource partagée entre le client et le serveur, donc ça serait bien de mettre la map dans un dossier commun pour mettre en évidence que c’est une ressource commune.

Afficher les pièces d’or sur le client (joueur)

Préparation de la sprite des pièces d’or

Pour le moment notre premier objet à collecter n’est qu’un objet Json qui se trouve dans le serveur, il a donc besoin d’une représentation graphique sur le client, on choisit donc une sprite qui va représenter notre premier objet à collecter (des pièces d’or) :

sprite des pièces d'or coté client

sprite des pièces d’or coté client

Afin de mieux gérer les objets à collecter, on va créer un GameObject qui va nous servir à afficher les collectables en choisissant la sprite correspondante selon le type.

» Afficher le fichier : client/gameObjects/CollectableObj.js

    var CollectableObj = function(config) {
        this.game = config.game;
        this.isAvailable = config.isAvailable;
        this.type = config.type;
        this.uid = config.uid;

        this.sprite = this.game.add.sprite(config.x, config.y, 'sprites', 'collectables/' + this.type + '.png');
        this.sprite.collectableObj = this;

        this.sprite.anchor.setTo(0, 1);
        this.game.physics.arcade.enable(this.sprite);
        this.game.mmo_group_collectables.add(this.sprite);
    };

    CollectableObj.prototype.destroy = function() {
        this.sprite.kill();
    };

    module.exports = CollectableObj;
  

» Cacher

Chargement des collectables depuis le serveur

Pour faciliter la gestion des objets de la map, on ajoute un objet MapDataClient qui va contenir toutes les interactions par socket concernant les données sur les objets de la map, comme vous pouvez le deviner du nom de l’objet, il est amené à communiquer avec le fichier mapDataServer qui se trouve sur le serveur.

Pour afficher les objets le client doit d’abort disposer de la liste à afficher, dans notre exemple on choisi de demander les objets à collecter dès que le joueur reçoit son identifiant unique :

» Afficher le fichier : client/network/MapDataClient.js

    function synchronize(socket, phaserState){
    ...
      serverSocket.on('SERVER_PLAYER_ID', onReadyToRequestCollectables);
    ...

    }

    function onReadyToRequestCollectables(){
        serverSocket.emit('CLIENT_GET_ALL_COLLECTABLES');
    }

» Cacher

Le client demande la liste des objets du serveur en envoyant l’evennement CLIENT_GET_ALL_COLLECTABLES voyons maintenant du coté serveur comment il va répondre à cet événement :

» Afficher le fichier : server/game/MapSync/mapDataServer.js


function synchronizeClient(client){
... 
client.on('CLIENT_GET_ALL_COLLECTABLES', onGetAllCollectables);

  ... 
  function onGetAllCollectables() {
    client.emit('SERVER_ALL_COLLECTABLES', collectableObjects);
  }
...
}

» Cacher

Le serveur répond à la demande du client en envoyant la liste des objets à collecter précédement chargée à travers l’événement SERVER_ALL_COLLECTABLES.

Retour au client, il faut maintenant configurer la reception du message contenant la liste des objets :

» Afficher le fichier : client/network/MapDataClient.js


function synchronize(socket, phaserState){
    ...

    serverSocket.on('SERVER_ALL_COLLECTABLES', onReceiveAllCollectables);

    ...

    function onReceiveAllCollectables(collectableList) {
        destroyAllCollectables();

        collectableList.forEach(function(collectable){

            if(collectable.isAvailable){
                var colObj = new CollectableObj({
                    game : concernedPhaserState.game,
                    x: collectable.x,
                    y: collectable.y,
                    isAvailable: collectable.isAvailable,
                    type: collectable.type,
                    uid: collectable.uid
                });
            }
            collectableObjects.push(colObj);
        });
    }

    ...
}

» Cacher

Rien de bien compliqué ici, on va parcourir la liste des objets reçue depuis le serveur (collectableList), puis on instancie un CollectableObj pour ainsi avoir une représentation de l’objet du serveur sur le client.

Après execution de ce bout de code les joueurs pourront voir des pièces d’or sur la map sur les emplacements configurés dans la map.

Gestion des collisions

Le but de notre petit jeux est simple, collectionner les pièces d’or pour gagner des points, et pour collecer une pièce d’or le personnage d’un joueur doit rentrer en collision avec l’objet pour gagner un nombre de points.

Mais imaginons un cas particulier :

  1. Joueur 1 : click sur une pièce d’or, il se déplace donc vers la pièce et la collecte.
  2. Joueur 2 : click sur la même pièce d’or, sauf qu’il a une connexion lente avec le serveur, il voit donc avec un peu en retard que le joueur 1 se déplace vers la même pièce d’or
  3. Joueur 1 : entre en collision avec la pièce d’or.
  4. quelques centaines de millisecondes et à cause du lag, joueur 2 n’a pas reçu l’information que l’objet a déjà été pris, et rentre en collision avec l’objet.

Maintenant qui gagne les points ?
la réponse : à celui qui prend l’objet en premier !

L’arbitre dans ce cas est le serveur, c’est lui qui décidera à qui donner le score, et pour notre cas ça sera à celui qui lui demandera le premier, voici donc ce qui se passe entre la collision et l’ajout des points au score du joueur :

  1. Client : Joueur rentre en collision avec la pièce d’or
  2. Client : Informer le serveur de la collision
  3. Serveur : Vérifier si l’objet est toujours disponible
  4. Serveur : Si oui, ajouter des points au joueur.

On a vu le principe général qu’on va suivre durant la gestion de la collision, regardons maintenant

Collision du joueur avec un collectable

Afin de faciliter la gestion de la collision, on va mettre tout nos objets à collecter dans un groupe appelé mmo_group_collectables, on va assigner ce groupe dans l’objet game pour pouvoir le réutiliser plus tard.

...
Play.prototype = {
    create: function(){
      ...
      this.setupSpriteGroups();
      ...
    },
    setupSpriteGroups: function(){
        this.game.mmo_group_collectables = this.game.add.group();
        this.game.mmo_group_characters = this.game.add.group();
    }
    ...
}

Pour detecter la collision on a besoin de vérifier sa présence à chaque « frame », (1 seconde = idéalement 60 frames / secondes ), on va donc vérifier la collision comme suit :

  ...

  configPlayerCollisions: function(){
        var me = this;
        this.mainPlayer.setOnCollideCollectableMapAction(function(collectable) {
            collectable.destroy();
            MapDataClient.tryToCollectForPlayer(collectable, me.mainPlayer);
        });
    },

  update: function(){
        ...
        this.checkMainPlayerCollision();
    }

  ...

  checkMainPlayerCollision: function() {
        if(this.mainPlayer !== undefined) {
            this.mainPlayer.checkCollision();
        }
    },


Dans le code ci-dessus on configure une fonction à appeler quand une collision est detectée à l’aide de this.mainPlayer.setOnCollideCollectableMapAction, puis on effectue une vérification de la collision durant chaque frame.

» Afficher le fichier : CharacterObj.js

  ...
  /*
    Set an external function to be executed when the Player collide with a collectable
 */
  CharacterObj.prototype.setOnCollideCollectableMapAction = function(callback){
      collideWithCollectableMapAction = callback;
  };

  /*
   Check if the Character sprite collide with a collectable object sprite and set the function
   to execute when a collision occurs
   */
  CharacterObj.prototype.checkCollision = function(){
      this.sprite.callOnCollideWithCollectableSprite(this.onCollideWithCollectable);
  };

  CharacterObj.prototype.onCollideWithCollectable = function(collectableSpr){
      var collectableObj = collectableSpr.collectableObj;

      if(collideWithCollectableMapAction) {
          collideWithCollectableMapAction(collectableObj);
      }
  };
  ...
  

» Cacher

Ici on met en place la fonction à appeler au moment de la collision, après cela on effectue un test sur la collision au niveau de la sprite du personnage (CharacterSpr), puis enfin on appelle la fonction précédemment configurée avec comme paramètre l’objet avec lequel le joueur est rentré en collision.

» Afficher le fichier : CharacterSpr.js

    ...
  CharacterSpr.prototype.callOnCollideWithCollectableSprite = function(callback){
    this.game.physics.arcade.overlap(this, this.game.mmo_group_collectables, function(playerSpr, collectableSpr){
        callback(collectableSpr);
    });
  };
    ...
  

» Cacher

Gestion du score

La dernière étape est de décider si le joueur est effectivement le premier à rentrer en collision avec la pièce d’or, et ainsi vérifier si il doit gagner des points.

» Afficher le fichier : play.js

  ...
  configPlayerCollisions: function(){
        var me = this;
        this.mainPlayer.setOnCollideCollectableMapAction(function(collectable) {
            collectable.destroy();
            MapDataClient.tryToCollectForPlayer(collectable, me.mainPlayer);
        });
    },
    ...
  

» Cacher

» Afficher le fichier : MapDataClient.js

  ...
  function tryToCollectForPlayer(collectable, player){
    serverSocket.emit('CLIENT_TRY_TO_COLLECT', { collectableId: collectable.uid, playerId: player.uid});
  }
  ...
  

» Cacher

Ce bout de code va appeler une fonction pour demander au serveur d’ajouter des points au score du joueur, ce dernier va vérifier pour si l’objet n’a pas été déjà pris par un autre joueur avant d’ajouter des points au score du joueur.

» Afficher le fichier : MapDataServer.js

  ...
  function synchronizeClient(client){
    ...

    client.on('CLIENT_TRY_TO_COLLECT', onClientAskToCollect);

    ...

    function onClientAskToCollect(collisionInfo){
        var targetColectable = ArrayUtils.getObjectInArrayById(collectableObjects, collisionInfo.collectableId);
        if(targetColectable.isAvailable){
            targetColectable.isAvailable = false;

            notifyCollectableDestroy(targetColectable);
            addPlayerScore(collisionInfo.playerId, targetColectable.scoreValue);
            sendPlayersScores();
        }
    }
  }
  ...
  

» Cacher

Le serveur reçoit la demande du joueur de collecter l’objet, il vérifie si l’objet en question est toujours disponible, si c’est le cas il va faire les choses suivantes :

  • Notifier les autres joueurs que cet objet n’est plus disponible
  • Ajouter des points au score du joueur
  • Mettre à jour le score chez tout les joueurs

Affichage du score

La gestion du score dans cet exemple est une simple liste contenant les scores des joueurs, le principe est le même que celui abordé dans la gestion de la gui de la deuxième partie du tutorial, je vais donc juste mettre le code concerné.

» Afficher le fichier : MapDataClient.js

  ...
  function synchronize(socket, phaserState){
    ...
    serverSocket.on('SERVER_UPDATE_PLAYER_SCORES', onReceiveScores);

    // initialize score board
    scoreBoard.init();
  }

  function onReceiveScores(playersList){
    scoreBoard.setScores(playersList);
}

  ...
  

» Cacher

» Afficher le fichier : ScoreBoard.js

  'use strict';

var DomHelper = require('client/utils/DomHelper');
var scoreList;


function init(){
    var scoreContainer = DomHelper.createElement('div', 'game-scoreboard');
    scoreContainer.style.left = DomHelper.getX(800) + 'px';
    scoreContainer.style.top = DomHelper.getY(0) + 'px';

    var title = document.createElement('h3');
    title.innerHTML = 'Scores';

    scoreList = DomHelper.createElement('ul', 'game-scorelist');

    scoreContainer.appendChild(title);
    scoreContainer.appendChild(scoreList);

    DomHelper.addToContainer(scoreContainer);
}

function setScores(scores){
    // empty the list
    while (scoreList.firstChild) {
        scoreList.removeChild(scoreList.firstChild);
    }

    scores.sort(orderByScore)
          .forEach(addScoreElement);

    function orderByScore(a, b) {
        return parseFloat(b.score) - parseFloat(a.score);
    }
    function addScoreElement(scoreInfo){
        var listElement = document.createElement('li');
        listElement.innerHTML = '<strong>' + scoreInfo.nickname + '</strong>' + ' : ' + scoreInfo.score;

        scoreList.appendChild(listElement);
    }
}

module.exports = {
    init: init,
    setScores: setScores
};
  

» Cacher

» Afficher le fichier : _scoreboard.less

  .game-scoreboard {
  position: relative;
  background: white;
  width: 200px;
  height: 480px;
  margin: 0;
  text-align: center;
  overflow: auto;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;


  h2 {
    margin: 0;
  }

  ul{
    padding: 0;
  }

  li {
    list-style-type: none;
    position: relative;
    display: block;
    padding: 10px 15px;
    margin-bottom: -1px;
    background-color: #fff;
    border: 1px solid #ddd;
  }

  li:first-child {
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
  }
}

  

» Cacher

Conclusion

Encore une fois un article assez chargé, on a vu dans un premier temps comment utiliser Tiled pour gérer les objet à collectionner du coté du serveur, ensuite on a vu comment afficher nos pièces d’or sur la map du coté du client, après cela on a abordé la gestion de collision pour expliquer le rôle du serveur dans la gestion de la collision dans un jeux multijoueur, et enfin j’ai présenté comment afficher le score chez le joueur.

Si il n’y avait qu’un seul point à retenir de ce tuto je dirais que c’est celui sur la gestion du score entre le joueur et le serveur, rappelez vous toujours de la règle d’or : un joueur ne modifie jamais directement une information commune à tout les joueurs.

Avec cet article on aurait vu quelques concepts basiques qu’on trouve dans des jeux en ligne, j’ajouterais dans le future d’autres parties pour des concepts plus pointues ( le pvp et pve),mais ça tardera certainement car je voudrais aborder d’autres sujets 🙂

Le code de ce petit jeux est disponible sur github, il est loin d’être parfait, donc toute contribution au repo est la bienvenue.

Le code source sur GitHub

8 commentaires

javier mt Publié le2:41 - Nov 1, 2015

Je souhaite que votre grande contribution suivante tutoriels très bon travail

Ouadie Publié le1:37 - Nov 23, 2015

ça a l’air sympa le tutorial. merci Marwane de m’avoir fait découvrir PhaserJS 🙂

n0cturne Publié le2:33 - Fév 3, 2016

tout d’abord merci pour tes tutos qui m’apprennent beaucoup 🙂
je suis un développeur débutant et honnêtement des tutos bien écrit et clair comme ceux la sont rare (maintenant que j’ai fini de te brosser dans le sens du poil place a la question qui fâche) compte tu finir ton tuto sur phaser / mmo?

j’imagine que ça prend du temps et que ça ne se « pond » pas comme ça mais j’aimerais bien voir la fin de se projet 😉

merci beaucoup d’avance

Marts Publié le12:52 - Juil 17, 2016

Super série de tutoriel ! Bravo! J’ai tout lu et mis en pratique, des cours de cette qualité sont rarissime. Tout est pris en compte, rien n’est négligé ou fait à moitié (comme la sécurité au sain de l’application par exemple).

Personnellement, je vais essayer d’intégrer ton tuto avec MangoDB et Cordova. Encore merci et vivement la suite! 🙂

    kiko Publié le6:18 - Sep 19, 2016

    Ola tudo bem?

    Conseguiu adicionar inimigos ao game e BD MongoDB?

    Muito com esse tutorial.

le_gigante Publié le9:02 - Mai 16, 2017

bonjour,

Merci pour tes tutos bien expliqués mais je vois comme une faille dans l’histoire de l’arbitrage serveur pour savoir qui a pris la pièce en premier.

Si je suis un joueur malveillant, je peux bidouiller le code client et par exemple me téléporter sur la pièce ou envoyer au serveur qu’il entre en colision avec la pièce alors qu’il est de l’autre coté de la map.

Une solution plus sûr ne serait-elle pas de gérer la physique coté serveur ? Le client n’envoi que les input au client.

1) client A envoi click aux coordonnées x;y
2) serveur calcule le pathfinder et update la position du joueur, et en informe tout le monde
3) quand le serveur voit la collision avec la pièce, il informe tout le monde que celle-ci a disparu et que le joueur A à un point en plus.

En plus dans le cas ou un client lag, il peut quand même arriver en premier. Il ne se vera pas prendre la pièce, mais les autres le verront. Et c’est bien lui qui aura le point.

Mais peut-être que c’est trop gourmand pour le serveur car il doit calculer le pathfinder de x joueur 60X par secondes :s ?

Que penses-tu d’une telle implémentation ? Et aurait tu des conseils car la doc de phaser n’est pas très riche coté serveur je trouve.

    Marwane Belahcen Publié le1:19 - Juin 4, 2017

    Bonjour,

    Merci pour la remarque pertinente,

    Je suis d’accord avec toi, le code est sensible à cette faille si tu développe un jeux multijoueur en temps réel ( ex : league of legends, dota, starcraft, counter strike … ), une solution ressemblant à ce que tu as proposé est effectivement obligatoire.

    à noter que tu va ajouter de la complexité coté serveur afin de gérer des cas qu’on ne gère pas dans l’implémentation actuelle, exemple :

    – le temps de calcul du pathfinding doit être très bas, pour donner l’impression que le calcul du pathfinding de tout les joueurs est parallèle ?
    – que fait on quand le joueur spam les clic droit/gauche pour faire des mouvements ( je penses à league of legends et à starcraft par exemple ) ?
    – qu’en est il des obstacles qui bougent ? comment s’assurer que le calcul sera correct ?

    Comme tu peux le voir, créer un jeux en ligne en temps réel est un casse tête, c’est pourquoi je me suis restreint au calcul coté client, c’est simple certes, mais ça reste valable pour les jeux en lignes où la précision de la position en temps réel n’est pas importante ( Dofus par exemple ) alors à mon avis tu pourra te limiter à calculer le pathfinding coté client.

    Je te propose ce livre si tu veux creuser le sujet de la programmation de jeux multijoueurs en lignes :

    https://www.amazon.com/Multiplayer-Game-Programming-Architecting-Networked/dp/0134034309

    Bonne journée.

Charrier Publié le9:19 - Oct 30, 2018

Est ce que la suite de ce tutoriel est passé à l’abandon ?