[DB-AL] Déplacement "Case Par Case"

Pour les scripts écrits en C#
Règles du forum
Merci de respecter la NOMENCLATURE suivante pour vos TITRES de messages :

Commencez par le niveau de vos scripts
DB = Débutant
MY = Moyen
CF = Confirmé

Puis le domaine d'application
-RS = Réseau
-AL = Algorithmie

Exemple :

[DB-RS] Mouvement perso multijoueur
djulio74
Messages : 682
Inscription : 19 Déc 2009 22:55

Re: [DB-AL] Déplacement "Case Par Case"

Message par djulio74 » 07 Nov 2018 13:11

soit patient, n'ayant jamais fait de 2D, je découvre les analogie entre 2D et 3D et je te pond un truc du tonnere. :super:

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

SidCamelot
Messages : 27
Inscription : 16 Oct 2018 23:14

[Résolu][DB-AL] Déplacement "Case Par Case"

Message par SidCamelot » 07 Nov 2018 13:25

:coeur: :rouge:
Dernière édition par SidCamelot le 08 Nov 2018 10:48, édité 1 fois.

djulio74
Messages : 682
Inscription : 19 Déc 2009 22:55

Re: [DB-AL] Déplacement "Case Par Case"

Message par djulio74 » 07 Nov 2018 14:57

Alors me revoilà. ;-)
Je t'ai refait un bout de script car de mon avis ( qui n'engage que moi) la solution de l'array et des cases est la meilleur. rien que par le fait que ça te permet de détecter obstacle/adversaire et limite du terrain.

Dans le code que tu va te faire un plaisir a déchiffrer ( même si j'ai annoté au mieux) :

- définition de la taille d'une case
- définition de la taille du terrain
- positionnement de la caméra automatiquement
- assignation automatique de certaine case comme étant occupée
- traçage quadrillage + cases occupée
- traçage du vecteur direction du robot
- déplacement et rotation fluide
- détection obstacle et bord du terrain + visuel
- traçage du parcours que va effectuer le robot

En mode 2D,juste une cam et un sprite, juste placer le script sur le 2D object/sprite.
dans l'inspector, defini la taille d'une case et la taille du terrain, et => 'Play'
penser à activer les gizmos dans l'onglet "Game"

Code : Tout sélectionner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class robot : MonoBehaviour
{

	// taille d'un case
		[Range(1,10)]
		public float tailleCase = 1.0f;
	// taille du terrain
		[Range(4,64)]
		public int taille = 16;
	// liste dses cases
		private Vector3[] cases;
	// liste de bool pour savoir si une case est occupée
		private bool[] occupe;
	// case sur laquelle se trouve ton robot
		private int pos;

	// liste des déplacement possible (+taille, -taille, +1,-1)
		private int[] dep;
	// l'élément de "dep" en cours, en fonction de la rotation du robot
		private int depActu = 0;

	// liste des déplacement a venir/enregistré
		private List<int> deplacement = new List<int> ();
	// liste ds rotation a venir/enregistré
		private List<int> rot = new List<int> ();

	// couleur du vecteur direction
		private Color Col = Color.green;

	// boolean pour savoir si le robot est en phase mouvement ou non
		private bool IsStatic = true;


	void Start ()
	{
		// on defini la taille des array cases et occupé
		cases = new Vector3[taille * taille];
		occupe = new bool[taille * taille];

		for (int i = 0; i < taille; i++) {  
			for (int j = 0; j < taille; j++) { 

				cases [i * taille + j] = new Vector3 (j*tailleCase, i*tailleCase, 0);

				// définition aléatoire de case occupée/obstacle, environ 5% de chance qu'une cases soit occupée
				int RandomOccupe = Random.Range (0, 100);
				if (RandomOccupe < 5) {
					occupe [i * taille + j] = true;
				} else {
					occupe [i * taille + j] = false;
				}
			}
		}

		// on defini les déplacement possible, en fonction de la taille du terrain ( taille x taille)
		dep = new int[4];
		dep [0] = taille;
		dep [1] = 1;
		dep [2] = -taille;
		dep [3] = -1;

		// on defini sur quelle case poser le robot, ici celle du millieu du terrain 
		pos = (taille / 2) * taille + taille / 2; 
		// on place le robot a la case "pos"
		transform.position = cases [pos]; 
		// on s'assure que la case ou on pose le robot ne soit pas un occupée/obstacle, on la marque false
		occupe [pos] = false; 

		// on place la camera au centre du terrain 
		Camera.main.transform.position = new Vector3 ((taille - 1) * 0.5f*tailleCase, (taille - 1) * 0.5f*tailleCase, -10);
		//et on ajuste sa taille pour couvrir tout le terrain
		Camera.main.orthographicSize = ((taille * 0.5f) + 1)*tailleCase;
	}

	void Update ()
	{
		// seulement si robot a l'arret, on enregistre les deplacement.rotation
		if( IsStatic){
			// appuie de la fleche haut => le robot avancera devant lui
			if (Input.GetKeyDown (KeyCode.UpArrow)) {
				// on ajoute a la liste de deplacement
				deplacement.Add (dep [depActu]); 
				// on ajoute une rotation nulle, pour avoir le même nombre de rot/deplacement
				rot.Add (0);	
			}

			// appuie de la fleche bas => le robot reculera
			if (Input.GetKeyDown (KeyCode.DownArrow)) {	
				// on ajoute a la liste de deplacement
				deplacement.Add (-dep [depActu]); 
				// on ajoute une rotation nulle, pour avoir le même nombre de rot/deplacement
				rot.Add (0); 
			}

			// appui fleche gauche => le robot tournera
			if (Input.GetKeyDown (KeyCode.LeftArrow)) {
				// on ajoute un deplacement nulle pour garder même nombre de rot/deplacement
				deplacement.Add (0);
				// on ajoute un ordre de rotation a la liste
				rot.Add (90); 
				// on change depActu car l'orientation du robot a chagé
				depActu = (int)Mathf.Repeat (depActu - 1, 4); 
			}

			// appui fleche droite => le robot tournera
			if (Input.GetKeyDown (KeyCode.RightArrow)) {
				// on ajoute un deplacement nulle pour garder même nombre de rot/deplacement
				deplacement.Add (0); 
				// on ajoute un ordre de rotation a la liste
				rot.Add (-90);  
				// on change depActu car l'orientation du robot a chagé
				depActu = (int)Mathf.Repeat (depActu + 1, 4); 
			}

			// appui sur entré => on exectute la liste des actions enregistrée
			if (Input.GetKeyDown (KeyCode.Return)) {
				// on lance tout les déplacement dans l'ordre entré
				StartCoroutine (Move ()); 
			}
		}


//===============// DEBUG VISUEL //===============//
		//debug un rayon marquant la direction du robot ( dans quel sens il se deplacera)
		Debug.DrawRay (transform.position, transform.up * 3.0f, Col);

		// on trace un quadrillage
		for ( int i = 1; i< taille; i++){
			Debug.DrawRay ( new Vector3 (-0.5f+i, -0.5f, 0)*tailleCase, Vector3.up * (taille)*tailleCase, new Color(1,1,1,0.3f));
			Debug.DrawRay (new Vector3 (-0.5f, -0.5f+i, 0)*tailleCase, Vector3.right * (taille)*tailleCase, new Color(1,1,1,0.3f));
		}

		// on trace chaque case marquée occupée
		for (int i = 0; i < cases.Length; i++) {
			if (occupe [i] == true) {
				Vector3 positionCase = cases [i];
				Debug.DrawRay (positionCase + new Vector3 (-0.5f, -0.5f, 0)*tailleCase, Vector3.up*tailleCase);
				Debug.DrawRay (positionCase + new Vector3 (-0.5f, -0.5f, 0)*tailleCase, Vector3.right*tailleCase);
				Debug.DrawRay (positionCase + new Vector3 (-0.5f, 0.5f, 0)*tailleCase, Vector3.right*tailleCase);
				Debug.DrawRay (positionCase + new Vector3 (0.5f, -0.5f, 0)*tailleCase, Vector3.up*tailleCase);
			}
		}
		// trace le pourtour tu terrain
		Debug.DrawRay (new Vector3 (-0.5f, -0.5f, 0)*tailleCase, Vector3.up * (taille)*tailleCase);
		Debug.DrawRay (new Vector3 (-0.5f, -0.5f, 0)*tailleCase, Vector3.right * (taille)*tailleCase);
		Debug.DrawRay (new Vector3 (-0.5f, (taille - 1) + 0.5f, 0)*tailleCase, Vector3.right * (taille)*tailleCase);
		Debug.DrawRay (new Vector3 ((taille - 1) + 0.5f, -0.5f, 0)*tailleCase, Vector3.up * (taille)*tailleCase);

		// tracage du parcour que va effecture le robot
		// uniquement si robot ne bouge pas (mode enregistrement)
		if( IsStatic){
			int pos1 = pos;
			for ( int i = 0 ; i < deplacement.Count; i++){
				if( (pos1 + deplacement[i])>=0 && (pos1 + deplacement[i])< taille*taille && (cases [pos1] - cases [pos1 + deplacement [i]]).magnitude < 2.0f*tailleCase && occupe [pos1 + deplacement [i]] == false){
					pos1 += deplacement[i];
					Debug.DrawLine (cases[pos1], cases[pos1-deplacement[i]]);
				}
			}
		}
//===============// FIN DEBUG VISUEL //===============//
	}

	IEnumerator Move ()
	{
		// on commence les mouvement
		IsStatic = false;
		// pour chaque déplacement/rot enregistré ( rot.Count = deplacement.Count)
		for (int i = 0; i < deplacement.Count; i++) {  

			// seulment si la rotation est différente de zero, on procede a la rotation
			// sinon on passe au deplacement
			if (rot [i] != 0) {				
				// rot[i] est la rotation a effectuer
				// target est la rotation effectuée
				float target = 0.0f;
				// tant que la rotation n'est pas complete, on procede
				//le "-1" est pour etre sur de ne pas dépasser la valeur voulue
				// on s'arrete un petit peu avant la rotation tatal, on compensera apres.
				while (Mathf.Abs (rot [i]) - 1 > Mathf.Abs (target)) {
					target += rot [i] * Time.deltaTime * 3.14f;
					transform.Rotate (Vector3.forward * Time.deltaTime * rot [i] * 3.14f);
					yield return null;
				}
				// on compense le "-1" en effectuant une derniere rotation, de la différence 
				// entre celle voulue et celle deja effectuée pour bien caller la rotation du robot sur un multiple de 90 ( angle droit)
				transform.Rotate (-Vector3.forward * (target - rot [i]));
			}

			// Seulement si la case suivante est adjacente a l'actuelle, ou son index compris entre 0 et le nombre de case
			// si elle ne l'est pas, c'est qu'on sortirai du terrain, donc on ne fait pas l'action demandée => contre le mur exterieur
			// idem si elle est occupée, on passe pour pas rentrer dans le mur
			if ((pos + deplacement [i]) >= 0 && (pos + deplacement [i]) < taille * taille && (cases [pos] - cases [pos + deplacement [i]]).magnitude < 2.0f*tailleCase && occupe [pos + deplacement [i]] == false) {
				// la position suivante du robot sera sa case actuelle + le déplacement enregistré
				pos += deplacement [i];
				// tant que le robot n'est pas arrivé a destionation, on procede au deplacement
				while (transform.position != cases [pos]) {
					transform.position = Vector3.MoveTowards (transform.position, cases [pos], Time.deltaTime * 10.0f);
					yield return null;
				}
			}
			// si la case suivante n'est pas accessible
			else{
				Col = Color.red;
				yield return new WaitForSeconds(0.5f);
				Col = Color.green;
			}
		}

		// le robot a effectué toute les action enregistré, on efface la liste de déplacement et rot
		deplacement = new List<int> (); // on reset la liste de déplacement.
		rot = new List<int> ();

		//on a terminé les mouvement
		IsStatic = true;
	}
}
Allez t'as du boulot pour tout déchiffrer ::d ::d
et hésite pas si t'as des questions. l'important est de comprend un script plutôt que l'appliquer bêtement ^^

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

SidCamelot
Messages : 27
Inscription : 16 Oct 2018 23:14

Re: [DB-AL] Déplacement "Case Par Case"

Message par SidCamelot » 07 Nov 2018 16:22

:ghee:

Djulio, tu es allé bien au delà de toutes mes attentes et tu as résolu une grande partie des problèmes auxquels j'allais m'attaquer (après avoir réglé celui des déplacements), à savoir, (entre autre) les collisions. Ton script est vraiment TRES PRECIEUX et il devrai être épinglé quelque part soit carrément dans la doc d'Unity, soit dans ce forum. J'ai encore du boulot pour bien tout décortiquer et assimiler le code, mais ce n'est rien en comparaison des jours (semaines) qu'il m'aurait fallu pour y arriver par moi même. En attendant encore MERCI, Merci aussi à Boubouk. A charge de revanche, si je peux vous êtres utiles à quelque chose, je suis journaliste, photographe et graphiste... Demandez moi en MP et je me ferais vraiment un plaisir de vous donner un coup de main dans la mesure de mes capacités (oui, sur Unity je risque plutôt d'être un boulet).

:amen:

J'attend quelques jours avant de mettre le sujet en "résolu" au cas ou il y ait encore quelques questions sur ce script.

SidCamelot
Messages : 27
Inscription : 16 Oct 2018 23:14

Re: [DB-AL] Déplacement "Case Par Case"

Message par SidCamelot » 08 Nov 2018 10:46

En application, le Script de Julio :
https://youtu.be/egJC9bE5XlE

J'ai ajouté la "chauffe moteur" et l'affichage des ordres. Plus quelques obstacles "visibles". Encore une fois merci. Les script est très clair et aborde en très peu de lignes plusieurs aspects totalement différents, c'est une véritable mine d'or que je recommande à chaque débutant !

djulio74
Messages : 682
Inscription : 19 Déc 2009 22:55

Re: [DB-AL] Déplacement "Case Par Case"

Message par djulio74 » 08 Nov 2018 10:59

SidCamelot a écrit :
08 Nov 2018 10:46
En application, le Script de Djulio :
c'est vraiment pas mal tout ça, content que tu ai pu exploiter mon script. :super:
par contre du coup ton robot ne se déplace pas au milieu des case affichée par les sprite.
c'est un soucis de placement de sprite ou de case. essaye dans l'initialisation de l'array case ( void Start) :

Code : Tout sélectionner

cases [i * taille + j] = new Vector3 ( (j+0.5f)*tailleCase, (i+0.5f)*tailleCase, 0);
j'ai rajouté un +0.5f pour x et y. ça devrait te positionner ton robot au milieu des cases.
Peut être besoin de soustraire 0.5f plutôt, pour x ou y ou les deux si ton robot ne se trouve pas sur la bonne case

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

SidCamelot
Messages : 27
Inscription : 16 Oct 2018 23:14

Re: [DB-AL] Déplacement "Case Par Case"

Message par SidCamelot » 08 Nov 2018 14:48

Merci Djulio, je savais comment résoudre ça, mais je voulais juste faire une petite "démo" en vitesse pour te montrer un peu. Je peaufine encore tout ça comme tu vois sur la vidéo les ordres s'affichent au moment de l'exécution, il vont s'afficher au fur et à mesure qu'on clique. Je travaille aussi sur un "vrai" robot avec des animations de déplacement dans les 4 directions. Au niveau de décors j'avais prévu qu'il soit "fixé" mais du coup je vais quand même, utiliser le principe de dalles aléatoires qui feront apparaître des cases d'eau qui permettront au robot de refroidir quand il passe dedans... Après, j'attaque un "gros morceau". Le deuxième joueur et les interactions entre les robots. Et après, (mais là je suis peut être trèèèès présomptueux) éventuellement une IA. Si je n'arrive pas à faire une IA "compétitive" (un 3ème joueur) je ferais au moins une IA "perturbatrice" (un robot imprévisible qui fout le bordel pendant les courses).

Encore un grand merci Djulio :super:

Répondre

Revenir vers « (C#) CSharp »