[Résolu] Déclencher un Timer

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
JollyStone
Messages : 33
Inscription : 03 Avr 2019 15:09
Localisation : Liège (B)
Contact :

[Résolu] Déclencher un Timer

Message par JollyStone » 13 Mai 2023 11:21

Bonjour,

Un jeu 2D très simple :
Dans la partie gauche de l’écran sont placés 9 objets vides qui représentent des positions. Chaque objet possède un trigger et un identifiant (Int).
Dans la partie droite de l’écran sont instanciés à des positions aléatoire des objets (Sprites) représentant des formes géométriques et possédant un collider ainsi qu’un identifiant (Int).
Les « Sprites » doivent être déplacés à la position leur correspondant dans la partie gauche de l’écran par Drag&Drop.
Le problème qui m’occupe est celui-ci :
Lorsque le joueur clique sur un premier objet, un Timer doit démarrer pour s’arrêter lorsque tous les objets sont bien placés à gauche de l’écran.
Pour ce faire, chaque objet à déplacer passe sa variable booléenne « isTiming » à True dans sa fonction « OnMouseDown ».
Dans le script « GameManager » je crée une boucle dans la fonction « Update » qui vérifie si l’un des objets à modifié la valeur de sa variable « isTiming ». Si c’est le cas, le tirer démarre.
Mais voilà ! Ce Timer ne démarre jamais.

Extrait du GameManager :

Code : Tout sélectionner

public GameObject [] stucks;
public Text timeText;

void Start () {
		StucksSpawner ();
		Debug.Log ("Start : " + elapsedTime);
		Debug.Log (timerActived);
	}

void FixedUpdate () {
		for (int i = 0; i < nbStucks; i++) {
			if (stucks [i].GetComponent<MoveStuck> ().isTiming) {
				timerActived = true;
				Debug.Log (timerActived);
				Debug.Log ("'isTiming' est à True " + stucks [i].name + ": " + elapsedTime + " secondes.");
			}
		}

		if (timerActived) {
			StartTimer ();
		}

		timeText.text = elapsedTime.ToString ("F2");
	}

void StartTimer () {
		elapsedTime = 0.0f;
		elapsedTime += Time.fixedDeltaTime;
		Debug.Log ("StartTimer : " + elapsedTime);
	}
Extrait du script sur l’objet à déplacer :

Code : Tout sélectionner

public bool isTiming = false;
public bool isDragging = false;

void OnMouseDown () {
		isTiming = true;
		screenPoint = Camera.main.WorldToScreenPoint (transform.position);
		isDragging = true;
		//Debug.Log ("Objet cliqué et 'isTiming' à True");
	}
Il me semble évident que cette boucle ne fonctionne pas. Auriez-vous une piste ?
Dernière édition par JollyStone le 14 Mai 2023 16:19, édité 1 fois.

Avatar de l’utilisateur
jmhoubre
Messages : 856
Inscription : 05 Oct 2019 22:05

Re: Déclencher un Timer

Message par jmhoubre » 13 Mai 2023 13:22

Bonjour,
La technique qui me semble la plus appropriée est d'utiliser un event qui déclenche le timer, et un second qui l'arrête.
Le timer s'abonne aux deux event :
- sur le déclencheur, il vérifie qu'il n'est pas lancé, sinon il le lance.
- sur l'arrêt, il vérifie que tous les objets sont bien en place.

Sinon, pourquoi FixedUpdate et pas Update (je n'ai pas vu de physique dans ta description) ?
Dans ta fonction Update tu pourrais mettre la boucle dans un bloc if (timerActived == false).
- dans for (int i = 0; i < nbStucks; i++) nbStucks est initialisé où ? Met plutôt stucks.Length, c'est plus lisible.

Je ne vois pas pourquoi le timer ne démarre pas :
- le Debug.Log ("Objet cliqué et 'isTiming' à True"); s'affichait-il ?
- Debug.Log ("'isTiming' est à True " + stucks .name + ": " + elapsedTime + " secondes."); s'affiche-t-il ?

JollyStone
Messages : 33
Inscription : 03 Avr 2019 15:09
Localisation : Liège (B)
Contact :

Re: Déclencher un Timer

Message par JollyStone » 13 Mai 2023 14:32

Merci pour ta réponse jmhoubre !

Je ne vois pas comment utiliser un Event. Est-ce un Component « Event Trigger » à ajouter aux objets qui sont des Préfabs instanciés ?
Je viens d’essayer mais la fonction « StartTimer » n’est pas listée, alors que je l’ai mise en public.
Sinon, pourquoi FixedUpdate et pas Update (je n'ai pas vu de physique dans ta description) ?
J’ai juste essayé Update et FixedUpdate pour voir si ça changeait quelque chose. :lol:
- dans for (int i = 0; i < nbStucks; i++) nbStucks est initialisé où ? Met plutôt stucks.Length, c'est plus lisible.
Oui, nbStucks est initialisé, j’ai juste oublié cette ligne dans l’extrait de code. Mais tu as raison, je vais changer ça.
- le Debug.Log ("Objet cliqué et 'isTiming' à True"); s'affichait-il ?
- Debug.Log ("'isTiming' est à True " + stucks .name + ": " + elapsedTime + " secondes."); s'affiche-t-il ?
Non, ces logs ne s’affichent pas. C’est bien pour cette raison que je pense que cette boucle ne fonctionne pas, ou du moins que le problème est situé là. En tout cas, la case « isTiming » est bien cochée dans l’inspector lors du clic sur l’objet.

Bon, je replace ici l’entièreté du script « GameManager » :

Code : Tout sélectionner

using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour {

	public GameObject [] stucks;
	public float areaMargin;
	public Text timeText;

	int nbStucks = 9;
	bool timerActived = false;
	float elapsedTime = 0f;

	void Start () {
		StucksSpawner ();
	}

	void Update () {
		if (timerActived == false) {
			for (int i = 0; i < stucks.Length; i++) {
				if (stucks [i].GetComponent<MoveStuck> ().isTiming) {
					timerActived = true;
					Debug.Log (timerActived);
					Debug.Log ("'isTiming' est à True " + stucks [i].name + ": " + elapsedTime + " secondes.");
				}
			}
		}

		if (timerActived) {
			StartTimer ();
		}

		timeText.text = elapsedTime.ToString ("F2");
	}

	public void StartTimer () {
		elapsedTime = 0.0f;
		elapsedTime += Time.fixedDeltaTime;
		Debug.Log ("StartTimer : " + elapsedTime);
	}

	void StopTimer () {
		Debug.Log ("StopTimer : " + elapsedTime);
	}

	void StucksSpawner () {
		float screenHeight = Camera.main.orthographicSize;
		float screenWidth = screenHeight * Camera.main.aspect;
		float stuckAreaHeight = screenHeight - areaMargin * 2f;
		float stuckAreaWidth = screenWidth / 2f - areaMargin * 2f;

		for (int i = 0; i < nbStucks; i++) {
			Vector2 spawnPosition = new Vector2 (
				Random.Range (transform.position.x + areaMargin, transform.position.x + stuckAreaWidth + stuckAreaWidth) + 0.5f,
				Random.Range (transform.position.y - stuckAreaHeight / 2f, transform.position.y + stuckAreaHeight / 2f));
			Instantiate (stucks [i], spawnPosition, Quaternion.identity);
		}
	}
}

Avatar de l’utilisateur
jmhoubre
Messages : 856
Inscription : 05 Oct 2019 22:05

Re: Déclencher un Timer

Message par jmhoubre » 13 Mai 2023 20:28

Je souhaite tester tes scripts dans un projet et je reviens vers toi. Ta boucle devrait afficher quelque chose. Poste le script complet des objets.

Je suis allé sur ta présentation, j'imagine que depuis avril 2019 tu as progressé. Je te demande pardon de poser cette question, mais j'ai l'impression que les objets à droite (ObjectToMove pour moi) sont en place dans la scène, et qu'au Start du jeu tu les instancie. Tu devrais obtenir le double d'objets non ?

Avatar de l’utilisateur
jmhoubre
Messages : 856
Inscription : 05 Oct 2019 22:05

Re: Déclencher un Timer

Message par jmhoubre » 13 Mai 2023 21:13

J'ai un peu avancé. Quand on clique sur un des objets à bouger, Le debug.Log s'affiche et isTiming passe à true.

Pour le reste, on doit avoir 2 tableaux :
  • Le premier contient les prefab des formes à générer. Nommons-le Transform objectsToSpawn. Il est renseigné dans l'inspecteur.
  • Le second contient les objets instanciés. Disons Transform objectsToMove. Il est privé et renseigné dynamiquement à chaque spawn.
Je modifie, teste et commente les scripts.

Avatar de l’utilisateur
jmhoubre
Messages : 856
Inscription : 05 Oct 2019 22:05

Re: Déclencher un Timer

Message par jmhoubre » 13 Mai 2023 21:37

Bon cela fonctionne. Mes sprites sont des formes Unity (triangle), mais tout sprite devrait fonctionner.

Code : Tout sélectionner

using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
	[SerializeField] private Transform[] objectsToSpawn;
	[SerializeField] private int nbObjectToSpawn = 5;
	[SerializeField] private float areaMargin;
	[SerializeField] private Text timeText;

	private Transform[] objectsToMove;
	private bool timerActived = false;
	private float elapsedTime = 0f;
	// public GameObject[] stucks; ne sert plus.

	private void Start ()
	{
		objectsToMove = new Transform[nbObjectToSpawn];
		StucksSpawner ();
	}

	private void Update ()
	{
		if (timerActived == false)
		{
			for (int i = 0; i < objectsToMove.Length; i++)
			{
				if (objectsToMove[i].GetComponent<ObjectToMove> ().isTiming)
				{
					timerActived = true;
					// J'ai enlevé la fin du Debug : elapsedTime sera toujours à 0 puisque le timer n'a pas démarré.
					Debug.Log ("isTiming est à True " + objectsToMove[i].name);
				}
			}
		}

		if (timerActived)
		{
			StartTimer ();
		}

		timeText.text = elapsedTime.ToString ("F2");
	}

	// Cette fonction n'a pas besoin d'être publique.
	private void StartTimer ()
	{
		//elapsedTime = 0.0f; si tu la met à 0 à chaque passage dans la fonction, cette variable est inutile.
		elapsedTime += Time.fixedDeltaTime;
		//Debug.Log ("StartTimer : " + elapsedTime);
	}

	private void StopTimer ()
	{
		Debug.Log ("StopTimer : " + elapsedTime);
	}

	private void StucksSpawner ()
	{
		float screenHeight = Camera.main.orthographicSize;
		float screenWidth = screenHeight * Camera.main.aspect;
		float stuckAreaHeight = screenHeight - areaMargin * 2f;
		float stuckAreaWidth = screenWidth / 2f - areaMargin * 2f;

		for (int i = 0; i < nbObjectToSpawn; i++)
		{
			// x 0.5 est plus rapide que / 2.
			Vector2 spawnPosition = new Vector2 (
				Random.Range (transform.position.x + areaMargin, transform.position.x + stuckAreaWidth + stuckAreaWidth) + 0.5f,
				Random.Range (transform.position.y - stuckAreaHeight * 0.5f, transform.position.y + stuckAreaHeight * 0.5f));

			// On choisit la forme à instancier.
			Transform randomPrefab = objectsToSpawn[Random.Range(0, objectsToSpawn.Length)];
			// On stocke la forme dans les objets à bouger.
			objectsToMove[i] = Instantiate (randomPrefab, spawnPosition, Quaternion.identity);
		}
	}
}

Code : Tout sélectionner

using UnityEngine;

public class ObjectToMove : MonoBehaviour
{
	public bool isTiming = false;
	public bool isDragging = false;

	// J'ai juste ajouté le type de screenPoint qui doit être ailleurs.
	void OnMouseDown ()
	{
		isTiming = true;
		Vector3 screenPoint = Camera.main.WorldToScreenPoint (transform.position);
		isDragging = true;
		Debug.Log ("Objet cliqué et 'isTiming' à True");
	}
}

Avatar de l’utilisateur
jmhoubre
Messages : 856
Inscription : 05 Oct 2019 22:05

Re: Déclencher un Timer

Message par jmhoubre » 13 Mai 2023 22:06

Image
J'ai ajouté une couleur aléatoire et j'ai modifié le calcul du vector2 de position.

Code : Tout sélectionner

// class ObjectToMove
private void Awake ()
{
	GetComponent<SpriteRenderer> ().color = Random.ColorHSV (0f, 1f, 1f, 1f, 0.5f, 1f, 0.5f, 1f);
}

Code : Tout sélectionner

private void StucksSpawner ()
{
	// Taille du 1/2 écran avec une marge de 10%.
	float screenHeight = Camera.main.orthographicSize * 0.9f;
	float screenWidth = screenHeight * Camera.main.aspect;

	for (int i = 0; i < nbObjectToSpawn; i++)
	{
		Vector2 spawnPosition = new Vector2 (
			Random.Range (screenWidth * 0.5f, screenWidth),
			Random.Range (-screenHeight, screenHeight)
			);

		// On choisit la forme à instancier.
		Transform randomPrefab = objectsToSpawn[Random.Range (0, objectsToSpawn.Length)];
		// On stocke la forme dans les objets à bouger.
		objectsToMove[i] = Instantiate (randomPrefab, spawnPosition, Quaternion.identity);
	}
}

JollyStone
Messages : 33
Inscription : 03 Avr 2019 15:09
Localisation : Liège (B)
Contact :

Re: Déclencher un Timer

Message par JollyStone » 14 Mai 2023 16:19

J’ai résolu le problème en modifiant la logique.
Un nouveau script « Timer » tout simple :

Script « Timer » placé sur l'objet GameManager dans la scène :

Code : Tout sélectionner

using UnityEngine;

public class Timer : MonoBehaviour {

	float startTime;
	public float elapseTime;

	void Start () {
		startTime = Time.time;
	}

	void Update () {
		elapseTime = Time.time - startTime;
	}
}
Le script « MoveStuck » placé sur chaque objet instanciés au démarrage :

Code : Tout sélectionner

using DG.Tweening;
using UnityEngine;

public class MoveStuck : MonoBehaviour {

	public int stuckID;
	public bool isDragging = false;

	Vector2 screenPoint, targetPos;
	GameManager gm;
	Case caseScript;

	void Start () {
		gm = FindObjectOfType<GameManager> ();
	}

	void OnMouseDown () {
		screenPoint = Camera.main.WorldToScreenPoint (transform.position);
		isDragging = true;
		gm.timer.enabled = true;
	}

	void OnMouseDrag () {
		Vector2 currentScreenPoint = new Vector2 (Input.mousePosition.x, Input.mousePosition.y);
		Vector2 currentPosition = Camera.main.ScreenToWorldPoint (currentScreenPoint);
		if (isDragging) {
			transform.position = new Vector2 (currentPosition.x, currentPosition.y);
		}
	}

	void OnMouseUp () {
		isDragging = false;
	}

	void OnTriggerEnter2D (Collider2D other) {
		caseScript = other.GetComponent<Case> ();
		if (caseScript.caseID == stuckID) {
			targetPos = caseScript.objectPosition;
			transform.DOMove (targetPos, 0.5f);
			isDragging = false;
			gm.nbStucksPlaced++;
		}
	}
}
Le script « GameManager » placé sur l'objet GameManager dans la scène :

Code : Tout sélectionner

using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour {

	public GameObject [] stucks;
	public float areaMargin;
	public Text timeText;
	public int nbStucksPlaced = 0;
	public Timer timer;

	void Start () {
		timer.enabled = false;
		StucksSpawner ();
	}

	void Update () {

		if (nbStucksPlaced == 9) {
			timer.enabled = false;
			Debug.Log ("Partie terminée !");
		}

		timeText.text = timer.elapseTime.ToString ("F2");
	}

	void StucksSpawner () {
		float screenHeight = Camera.main.orthographicSize * 0.9f;
		float screenWidth = screenHeight * Camera.main.aspect;
		float stuckAreaHeight = screenHeight - areaMargin * 2f;
		float stuckAreaWidth = screenWidth / 2f - areaMargin * 2f;

		for (int i = 0; i < stucks.Length; i++) {
			Vector2 spawnPosition = new Vector2 (
				Random.Range (transform.position.x + areaMargin, transform.position.x + stuckAreaWidth + stuckAreaWidth) + 0.5f,
				Random.Range (transform.position.y - stuckAreaHeight / 2f, transform.position.y + stuckAreaHeight * 0.5f));
			Instantiate (stucks [i], spawnPosition, Quaternion.identity);
		}
	}
}
Le script « Case » placé sur chaque Collider déterminant l'emplacement où les objets doivent être placés :

Code : Tout sélectionner

using UnityEngine;

public class Case : MonoBehaviour {

	public int caseID;
	public Vector2 objectPosition;	

	void Start () {
		objectPosition = new Vector2 (transform.position.x, transform.position.y);
	}
}

Avatar de l’utilisateur
jmhoubre
Messages : 856
Inscription : 05 Oct 2019 22:05

Re: [Résolu] Déclencher un Timer

Message par jmhoubre » 15 Mai 2023 00:58

Bonsoir,
nickel. Le prochain conseil que je voulais te donner, c'est de bien séparer les fonctionnalités, en particulier faire une classe Timer.

Répondre

Revenir vers « Scripting »