Hachaisse le site

6 septembre 2013

Tutorial pour Unity 3D (3)

Classé dans : Article, Informatique — Mots-clefs :, , — admin @ 19 h 43 min

Seconde partie du tutorial : http://raphp.fr/blog/?p=227

Démarrage de la partie

Nous avons la base du jeu, le menu et les cartes.

Cependant elles sont visibles dès le début du jeu. Hors nous ne devons afficher que le menu. Nous allons donc les masquer quand c’est inutile et ainsi gérer le fait de tourner la carte par codage. On ajoute dans la classe MyGUI 2 liens (2 membres publics) pour recevoir 2 objets de la scène :

public GameObject PlayerOne, PlayerTwo;

Le public permettra d’avoir 2 GameObject disponible dans l’Inspector qu’on peut affecter :

Ici on déplace l’objet 3D de la carte PlayerOne depuis « Hierarchy » vers le lien « Player One » du script et pareillement avec le 2è.

Maintenant notre script peut utiliser nos 2 objets.

Il est possible de chercher l’objet sans avoir du drag&drop :

Depuis les boutons à droite :

On affiche la liste des objets disponibles :

Avec un filtre de recherche, cela peut faire gagner du temps si on a une « hierarchy » remplie d’objets.

Avant de continuer je vais vous conseiller de « Tager » vos objets.

Vous comprendrez pourquoi dans le prochain chapitre.

Pour ajouter un tag :

Ou depuis le menu « Edit -> Projects Settings -> Tags »

Ajouter ensuite les 2 tags suivants :

Ensuite on sélectionne la carte 1 et on lui affecte le bon tag :

Pareillement avec la deuxième.

Maintenant on va se concentrer sur le code presque jusqu’à la fin du tutoriel.

On retourne donc compléter la classe :

case MenuStep.Start:

PlayerOne.renderer.enabled = false;

PlayerTwo.renderer.enabled = false;

if( GUI.Button(new Rect(10f,MenuY += MenuHeight + MenuBlank,MenuWidth,MenuHeight), « Démarrer une partie ») )

{

mMenuStep = MenuStep.InGame;

}

Si on lance le jeu, nous avons donc notre menu sans nos cartes.

Le Renderer constitue le « rendu » de l’objet. En fait c’est l’état de sortie visible de notre objet, il suffit de le désactiver pour que le moteur 3D ne l’affiche plus.

Remarque :

Si on ajoute dans le MenuStep.Start :

if( GUI.Button(new Rect(10f,MenuY += MenuHeight + MenuBlank,MenuWidth,MenuHeight), « Démarrer une partie ») )

{

mMenuStep = MenuStep.InGame;

PlayerOne.renderer.enabled = true;

PlayerTwo.renderer.enabled = true;

}

Nous pourrions afficher nos cartes une fois le menu « Démarrer » cliqué. Mais nous n’allons pas le gérer dans OnGUI() donc on le garde de côté.

Avant cela, on ajoute de nouveau une gestion d’étape dans le jeu:

public class MyGUI : MonoBehaviour {

public enum MenuStep

{

Start,

VideoSettings,

InGame,

MenuInGame,

Player1Win,

Player2Win

}

public enum GameStep

{

PlayerOneBeforeTurn,

PlayerOneTurning,

PlayerOneAfterTurn,

PlayerTwoBeforeTurn,

PlayerTwoTurning,

PlayerTwoAfterTurn

}

protected MenuStep mMenuStep = MenuStep.Start;

protected GameStep mGameStep = GameStep. PlayerOneBeforeTurn;

[…]

On complète de nouveau notre start :

if( GUI.Button(new Rect(10f,MenuY += MenuHeight + MenuBlank,MenuWidth,MenuHeight), « Démarrer une partie ») )

{

mMenuStep = MenuStep.InGame;

mGameStep = GameStep. PlayerOneBeforeTurn;

PlayerOne.transform.rotation = Quaternion.Euler(0,0,0);

PlayerTwo.transform.rotation = Quaternion.Euler(0,0,0);

}

Attention, ici ce ne sont pas des angles en ° car c’est un Quaternion, nous devons indiquer ici la position par rapport au « monde », donc l’état d’origine de la carte était bien 0.

Je le montre juste pour exemple, personnellement je préfère utiliser la fonction Rotate qui permet de faire la rotation de l’angle voulu.

Nous aurions pu écrire :

PlayerOne.transform.Rotate(new Vector3(0,180,0));

PlayerTwo.transform.Rotate(new Vector3(0,180,0));

Alors attention, n’oubliez pas que nous somme dans la fonction OnGUI !

Celle-ci s’exécute en boucle à chaque frame du jeu !

Donc si on fait un code de ce genre :

void OnGUI()

{

PlayerOne.transform.Rotate(new Vector3(0,1,0));

}

La carte tourne à l’infini.

Il existe également plusieurs méthodes, celle-ci sert à modifier l’objet sous tous les angles.

Mais dans notre cas nous n’avions besoin que du y, et ainsi écrire :

.Rotate(Vector3.up * 180);

Nous pouvons aussi gérer le temps écoulé entre les frames (pratique si ça ram) Ce qui n’est pas utile dans notre cas mais si vous souhaiter tourner un objet indéfiniment :

.Rotate(Vector3.right * Time.deltaTime);

Notre jeu démarre à présent proprement.

Création de l’interface du jeu

Maintenant nos joueurs doivent pouvoir donner leurs cartes !

Il y a plusieurs méthodes.

J’aime bien l’idée de cliquer sur chacune des cartes pour les révéler une par une, un peu comme dans la vrai vie ou l’autre joueur peut faire durer le suspens.

Mais on fera également un bouton pour retourner les deux d’un coup plus tard.

De plus, la première méthode est aussi une bonne solution pour apprendre à cliquer sur un objet visible sans tester sa position.

Nous avons juste besoin des tags indiqués précédemment sur nos objets pour distinguer notre clique sur l’objet. Il y a plusieurs solutions, je l’admets, mais on fait simple.

void OnClick(int playerturn)

{

if (Input.GetMouseButtonDown(0))

{

Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

RaycastHit hit;

if(Physics.Raycast(ray, out hit, 10f))

{

switch( hit.collider.gameObject.tag )

{

case « PlayerOneCard »:

if( playerturn==1 )

mGameStep = GameStep.PlayerOneTurning;

break;

case « PlayerTwoCard »:

if( playerturn==2 )

mGameStep = GameStep.PlayerTwoTurning;

break;

}

}else{

}

}

}

Donc ScreenPointToRay permet de lancer un « rayon » où on clique avec le mousePosition.

Ensuite on se sert du Physics.Raycast pour vérifier ce que l’on croise en objet « physique », cela ne marche pas si l’objet n’est pas considéré comme « solide ».

On a en 3ème paramètre la distance de colision, ici 10 c’est bien plus loin qu’il n’en faut vu que nos cartes ne sont même pas à 1 de distance de la camera.

Donc on détecte le clique sur la carte 1 ou 2 et on modifie l’état du jeu pour lancer la découverte de la carte.

On ajoute le membre suivant pour garder quelques secondes à l’écran le résultat des 2 cartes retournées.

protected float mSeconds = 0;

On utilisera enfin la méthode Update. Je rappelle que OnGUI est fait pour l’interface alors que Update gère les objets, soit c’est plus fluide, soit le traitement est mieux réalisé.

void Update () {

switch( mMenuStep )

{

case MenuStep.InGame:

PlayerOne.renderer.enabled = true;

PlayerTwo.renderer.enabled = true;

switch( mGameStep )

{

case GameStep.PlayerOneBeforeTurn:

OnClick(1);

break;

case GameStep.PlayerOneTurning:

PlayerOne.transform.Rotate(Vector3.up * 5);

if(  Mathf.Round(PlayerOne.transform.rotation.eulerAngles.y) == 180 )

mGameStep = GameStep.PlayerOneAfterTurn;

break;

case GameStep.PlayerOneAfterTurn:

mGameStep = GameStep.PlayerTwoBeforeTurn;

break;

case GameStep.PlayerTwoBeforeTurn:

OnClick(2);

break;

case GameStep.PlayerTwoTurning:

PlayerTwo.transform.Rotate(Vector3.up * 5);

if(  Mathf.Round(PlayerTwo.transform.rotation.eulerAngles.y) == 180 )

{

mGameStep = GameStep.PlayerTwoAfterTurn;

mSeconds = 0;

}

break;

case GameStep.PlayerTwoAfterTurn:

// Resultat

//

mSeconds += Time.deltaTime;

if( mSeconds > 1 )

{

mSeconds = 0 ;

mGameStep = GameStep.PlayerOneBeforeTurn;

PlayerOne.transform.rotation = Quaternion.Euler(0,0,0);

PlayerTwo.transform.rotation = Quaternion.Euler(0,0,0);

}

break;

}

break;

}

}

Nous avons donc nos cartes qui se tournent après un clic dessus.

Lors du prochain chapitre nous étudierons le noyau du jeu, c’est-à-dire la vraie distribution de carte et le résultat de victoire entre les 2 cartes. Faut admettre que le noyau du jeu n’est que du c# pur et dur.

C’est pourquoi on va d’abord préparer un système de paquet de cartes qui nous permet de manipuler un peu Unity3D.

On se contentera de donner automatiquement les 2 cartes au joueur 1 sans encore tester le résultat de victoire.

Il nous faut déjà une liste pour contenir tous les objets 3D. Important, car on souhaitera les effacer donc on doit mémoriser tous les objets quelque part.

Remarque : Techniquement, il est possible de retrouver un objet par son nom.

Nous avons donc pour le joueur 1 et 2, une pile de cartes de départ et une 2ème pour celles qu’on gagne.

ArrayList Player1_3DCardsWin, Player1_3DCardsPack;

ArrayList Player2_3DCardsWin, Player2_3DCardsPack;

On instancie nos listes :

void Start () {

Debug.Log(« MyGUI – Start »);

Player1_3DCardsWin = new ArrayList();

Player2_3DCardsWin = new ArrayList();

Player1_3DCardsPack = new ArrayList();

Player2_3DCardsPack = new ArrayList();

}

Dans la fonction de résultat on modifie le code suivant :

if( mSeconds > 1 )

{

mSeconds = 0 ;

Add3DCard(PlayerOne, ref Player1_3DCardsWin, -0.3f, « PlayerOne_Win » + Player1_3DCardsWin.Count, « PlayerOneWin »);

Add3DCard(PlayerOne, ref Player1_3DCardsWin, -0.3f, « PlayerOne_Win » + Player1_3DCardsWin.Count, « PlayerOneWin »);

mGameStep = GameStep.PlayerOneBeforeTurn;

PlayerOne.transform.rotation = new Quaternion(0,0,0,0);

PlayerTwo.transform.rotation = new Quaternion(0,0,0,0);

}

On va donc faire en sorte de tourner la carte tant qu’elle n’est pas complètement retournée à 180° donc face visible à la caméra.

Et aussi dire au jeu lorsque les 2 cartes sont révélées que le joueur 1 gagne les 2 cartes dans sa pile de victoire.

La méthode pour gérer la création d’un objet 3D en live :

void Add3DCard(GameObject original, ref ArrayList list, float posx, string name, string tag = «  »)

{

GameObject newcard = (GameObject) Instantiate(original);

if( !string.IsNullOrEmpty(tag ))

newcard.tag = tag;

newcard.name = name;

newcard.renderer.enabled = true;

newcard.transform.position = new Vector3(original.transform.position.x + posx,-0.5f +(float)(0.002* list.Count),original.transform.position.z);

newcard.transform.Rotate(new Vector3(90,0,0));

list.Add(newcard);

}

Avec l’instantiate on crée un GameObject avec l’aide d’un objet prédéfini, il est possible d’utiliser une ressource mais dans notre cas on fera une copie d’un objet déjà présent dans la scène.

Comme elles seront retournés, on se moque de la face visible qui correspond ou non.

On la déplace vers le bas de l’écran et on la tourne pour faire une belle pile face caché.

On utilise le nombre de cartes déjà présentes pour calculer la position en y pour simuler une pile qui s’agrandit.

Maintenant quand on retourne les deux cartes, le joueur 1 gagne les deux cartes dans sa pile.

Et tout cela sans utiliser l’interface graphique d’Unity3D.

Je lui donne un nouveau TAG, car sinon un clique sur la pile des victoires fera le même code que la carte principale et retournera la carte. Il prend donc par défaut le tag de l’objet copié, ce qu’il est important de noter.

Je lui donne un nom différent pour le retrouver plus facilement sur la scène, ou si je recherche par nom un objet.

Remarque : quand on lance la lecture du jeu, lorsque l’on crée un objet, on le retrouve dans l’onglet « Hierarchy » ainsi que la scène. Mais ils disparaissent une fois la lecture terminée.

J’ai également crée les deux méthodes suivantes pour me permettre d’effacer les cartes 3D que l’on utilisera plus tard :

void Remove3DCard(ref ArrayList list)

{

if( list != null )

{

if( list.Count>= 1 )

{

Destroy((GameObject)list[list.Count-1]);

list.RemoveAt(list.Count-1);

}

}

}

void RemoveAll3DCards(ref ArrayList list)

{

for(int i = 0; i < list.Count; i++ )

{

GameObject go = (GameObject)list[i];

Destroy(go);

}

list.Clear();

}

Il suffit d’avoir l’objet et d’utiliser Destroy. Tout simplement.

Pour discuter de l’article, c’est ici : http://raphp.fr/fofo/viewtopic.php?f=2&t=2339

Pas de commentaire

Pas encore de commentaire.

Flux RSS des commentaires de cet article. Adresse web de rétrolien

Désolé, les commentaires sont fermés pour le moment.

Propulsé par WordPress