mercredi 28 novembre 2012

Tutorial: La mise en réseau sous Unity3D - Partie 4


Salut tout le monde !

Quatrième et dernière partie du tutorial. Si vous arrivez en cours de route, ça commence par-là.
Dans cette partie on va apprendre à déclencher un évènement sur le réseau. En l’occurrence l'activation d'un ascenseur.

Vous vous demandez peut-être pourquoi on ne se contente pas de synchroniser la position de l'ascenseur via une NetworkView. Si on fait ça, l'ascenseur ne saura pas s'il est en position haute ou en position basse. Le mouvement de l'ascenseur sera également plus fluide avec notre façon de faire et ça économisera de précieuses ressources réseau. Mais il y aurait bien entendu moyen d'obtenir le même résultat en jouant avec une NetworkView et en observant la position.y de l'ascenseur pour savoir s'il est en haut ou en bas.

Mise en place de la scène



On va améliorer un peu notre arène. Je vous laisse faire ça comme vous voulez mais en gros il faut une partie surélevée qu'on ne pourra atteindre qu'avec l'ascenseur.

Placez au moins un ascenseur (un simple cube plat) muni d'un levier/bouton ou truc quelconque pour l'activer. Je vous conseille de lui mettre un pied de support comme moi, ça nous permettra d’appeler l'ascenseur si on est en bas et lui en haut.

Voila à quoi ressemble ma map:



Et voila mon ascenseur pour ceux qui veulent:
lift.fbx

Si vous l'utilisez voila comment régler les options d'importation.


Désactivez aussi l'animation de l'objet. et placez une BoxCollider sur chacune des parties.

Bien, on va régler rapidement l'ascenseur:
  • Placez une BoxCollider en mode isTrigger qui englobe tout l'ascenseur (nacelle et pilier). C'est cette box qu'on activera.
  • Créez un nouveau script en javascript et nommez-le Lift.js
  • Assignez le script à votre/vos ascenseur(s)

Ça devrait ressembler à ça

On va aussi ajouter un composant essentiel à notre Cuby, un champs d'action. Voyez ça comme un espèce de bras virtuel, ça lui permettra d'activer les objets qui sont devant lui.
  • Créez un nouvel Empty GameObject et appelez-le "ActionField".
  • Ajoutez-lui une CollisionBox en mode isTrigger et donnez-lui une forme de lame verticale qui s'étendra devant Cuby. 
  • Attachez-là à Cuby.




Scripting


  • Ouvrez le script Lift.js dans votre éditeur de code
On va commencer par l'écrire sans tenir compte du réseau.

var down:boolean; //L'ascenseur est il en position basse ou en haut?
var latence:float = 0.1; //plus elle est grande, plus l'ascenseur ira lentement
private var isMoving:boolean; //si l'ascenseur est en mouvement, on ne peux pas l'activer

var upPosition:float; //coordonnée y de la position haut de l'ascenseur
var downPosition:float; //idem mais position basse


function OnSwitched()
{
if(!isMoving)
{
isMoving = true;
if(down)
{
//monte
while(transform.localPosition.y < upPosition)
{
yield new WaitForSeconds(latence);
transform.position.y += 0.1;
}
down = false;
}
else
{
//descend
while(transform.localPosition.y > downPosition)
{
yield new WaitForSeconds(latence);
transform.position.y -= 0.1;
}
down = true;
}
isMoving = false;
}
}

function OnTriggerStay(col:Collider)
{
if(col.transform.name == "ActionField")
{
if(col.transform.root.networkView.isMine) //Evite de pouvoir activer l'ascenseur si c'est l'ennemi qui est dans la bonne position pour l'activer
{
if(Input.GetKeyDown(KeyCode.E) || Input.GetKeyDown(KeyCode.Return))
    {
    OnSwitched();
    }
    }
    }
}

  • Configurez ensuite le script dans l'inspector. Voila ma config, les coordonnées seront très probablement différentes dans votre projet.


Normalement, votre ascenseur doit fonctionner en local ("E" ou "Enter" pour l'activer).

Quelques conseils:
    • Le script n'est pas très précis, c'est pourquoi la downPosition de l'ascenseur est différente de sa position de départ.
    • Le mouvement de l'ascenseur n'est pas du tout fluide (si on vous demande, dites que c'est pour un effet ascenseur antique^^). Ça peut s'arranger en jouant avec la variable latence et le changement de position (ex: transform.position.y -= 0.1; ).
    • Vous pouvez aussi utiliser la fonction transform.Translate plutôt que de changer directement la position.y

La partie réseau



Si vous avez testé le jeu en multi, vous avez du vous rendre compte que l'ascenseur ne s'active pas sur les autres clients.

On va remédier à ça très facilement.
  • Commencez par ajouter une NetworkView à votre ascenseur. Cette NetworkView ne doit rien observer ("none").
Pour que l'ascenseur se synchronise entre les clients on va utiliser ce qu'on appelle un RPC (Remote Procedure Call). Comme le nom l'indique, on va appeler (call) une fonction (une procédure) à distance (remote). Vous allez voir, c'est très facile.

On va d'abord préciser à Unity que notre fonction OnSwitched() est une RPC. Pour ça, on ajoute "@RPC" devant la déclaration de la fonction.


On va ensuite simplement changer la manière dont on appelle cette fonction.

Au lieu de:

if ( Input.GetKeyDown(KeyCode.E) || Input.GetKeyDown(KeyCode.Return))
{
 OnSwitched();
}

Tapez:

if ( Input.GetKeyDown(KeyCode.E) || Input.GetKeyDown(KeyCode.Return))
{
networkView.RPC("OnSwitched", RPCMode.All);
}

Rien de bien neuf ici, comme pour l'instanciation en réseau, le RPCMode détermine sur quel groupe de clients on doit appeler la fonction.

Chez moi les joueurs non-serveurs ne décollent pas avec l'ascenseur, je vous conseille donc de tout de même synchroniser la position (le Transform) de l'ascenseur avec la NetworkView. De cette façon, il n'y aura aucun problème et l'état up ou down de l'ascenseur sera bien synchronisé par notre RPC.

Voici le script final:

Cliquez pour le voir en grand

Vos ascenseurs devraient maintenant fonctionner comme prévu.


Conclusion



Comme promis, cette partie du tutorial était très courte. Les RPC sont simples a utiliser mais c'est un outil vraiment puissant que vous utiliserez très régulièrement.

On a passé en revue tous les éléments de base de la mise en réseau et vous devriez maintenant être aptes à réaliser vos propres petits jeux en réseau puisque vous savez désormais:
  • Synchroniser la position et l'orientation d'objets
  • Instancier des objets tels que les joueurs ou les projectiles sur le réseau
  • Appeler des fonctions locales à distance sur toutes les machines connectées
Vous remarquerez qu'il manque le quatrième pilier d'un jeu en réseau: la synchronisation de variables (ex: synchroniser le contenu de deux coffres dans un jeu où leur contenu est sélectionné au hasard en début de partie). Je ne maîtrise malheureusement pas assez le sujet que pour écrire un tutorial là dessus. Je vous donne quelques pistes un peu plus loin.

On a également créé un véritable jeu qui bien que très basique et à des années lumières d'un Unreal Tournament par exemple, pourra vous occuper un petit moment avec des amis. N'hésitez pas à le peaufiner et à y ajouter des features le temps de vous familiariser avec le réseau. Je serai ravi de voir ce que vous en faites donc n'hésitez surtout pas à poster vos versions modifiées dans les commentaires.

J'espère que ce tutorial vous a plût et que je ne vous ai pas donné trop de mauvais conseils. Je posterai des annexes si je fais des découvertes intéressantes.

Voila mon projet Unity terminé:
Télécharger


Aller plus loin



Je vous parlais de la synchronisation de variables, cela se fait via la fonction OnSerializeNetworkView().

Voila le passage de la documentation Unity parlant de ça. Et bien entendu voici le Network Reference Guide d'Unity où vous trouverez pas mal d'informations importantes.

Il ne me reste plus qu'à vous donner un dernier conseil: pratiquez ! Pratiquez, pratiquez et pratiquez encore. Rome ne s'est pas faites en un jour donc ne vous découragez pas si vous n'arrivez pas tout de suite aux résultats escomptés. Faire un jeu en réseau n'est pas la chose la plus simple du monde mais avec un peu de persévérance vous devriez y arriver.

N'hésitez pas non plus à demander de l'aide sur les forums où des gens plus qualifiés pourront vous aider. A ce propos je vous déconseille Unity Answers vu que vos messages doivent être validés pour apparaître  ce qui vous fera perdre un temps précieux. Personnellement je demande généralement de l'aide sur Reddit/r/Unity3D. La communauté est très sympa et généralement de bon conseil.

Bonne continuation :)