Il était demandé d’avoir des déclencheurs dans Unity avec des cannons et un colonne qui tombe. Comme d’habitude, j’ai voulu tout recréer de A à Z.
Voici la colonne :
et le cannon
Les déclencheurs dans Unity et la colonne.
Le code pour exécuter les déclencheurs dans Unity est assez simple mais il y a une petite subtilité. En fait quand on passe sur le zone de déclanchement, on veut activer différents objets qui auront chaqu’un des réactions uniques. Mais on ne sera pas le nom des scripts. Par exemple, on aura la colonne qui va s’effondrer. Mais on aura aussi le canon qui va tirer. Comment on peut appeler un script sans savoir son nom, ni la fonction publique à appeler.
Simplement en créant des Interfaces :
https://unity3d.com/fr/learn/tutorials/topics/scripting/interfaces
On va créer nos scripts en temps qu’interface d’une classe parent qui aura la définition de la fonction a exécuter.
Voici le fichier TriggerableObject :
1 2 3 4 |
public interface TriggerableObject { void runTrigger(); } |
Et notre script pour détruire la colonne.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class DestroyColonne : MonoBehaviour, TriggerableObject { public GameObject destroyedColonne; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKeyDown("r")) { Destroy(gameObject); Instantiate(destroyedColonne, transform.position, transform.rotation); } } public void runTrigger() { Destroy(gameObject); Instantiate(destroyedColonne, transform.position, transform.rotation); } } |
Pour faire bref sur la colonne et comment elle se casse, J’ai tout simplement deux prefabs. Un avec la colonne en entier, et la deuxième avec la colonne en plusieurs morceaux. Je détruis la colonne complète et je la remplace par les morceaux avec un instantiate. Le moteur physique fait le reste du boulot …. (Et j’ai un déclencheur si on appuie sur le bouton “r” pour les tests…)
Sinon, pour le déclanchement, il faut surtout noter la déclaration de la classe : public class DestroyColonne : MonoBehaviour, TriggerableObject.
On utilise MonoBehaviour ET notre tout nouveau TriggerableObject. Ceci nous OBLIGE à avoir une fonction runTrigger() comme défini dans notre fichier interface. Cela permet surtout de récupérer les scripts de type “TriggerableObject” et exécuter la fonction runTrigger :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
public class TriggerObjects : MonoBehaviour { public List<GameObject> triggerObjects; private AudioSource clickSound; // Use this for initialization void Start () { clickSound = GetComponent<AudioSource>(); } // Update is called once per frame void Update () { } private void OnTriggerEnter(Collider col) { if (col.tag == "Player") { clickSound.Play(); foreach (GameObject trigger in triggerObjects) { if(trigger != null) //check if the object hasn't been destroyed { //could add a paticle flash on each object to visualy show the awake state TriggerableObject scriptToTrigger = trigger.GetComponent<TriggerableObject>(); scriptToTrigger.runTrigger(); } } } } //add some lines to the creation screen for ease of dev void OnDrawGizmosSelected() { if(triggerObjects.Count > 0) //no need to draw lines until we have an actual list { Gizmos.color = Color.green; foreach (GameObject trigger in triggerObjects) { if(trigger != null) { Gizmos.DrawLine(transform.position, trigger.transform.position); } } } } } |
Ceux qui est intéressant ici est qu’on propose une liste d’objets peuvent être déclenchés grâce à “public List<GameObject> triggerObjects;”
Ensuite, au déclenchement par le joueur, on récupère le script de type triggerObjects sur chaque élément et on exécute la fonction runTrigger();
Petit bonus, on exécute un “void OnDrawGizmosSelected()” sur chaque élément. Ceci permet d’afficher les lignes vertes entre le déclencheur et les objets dans la vue création d’Unity.
Et pour le canon ?
Un peu le même principe sauf que l’objet est un peu plus complexe.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Canon : MonoBehaviour, TriggerableObject { [Header("Instatiate Cannon Ball")] public Transform firePoint; public GameObject canonBall; private Animator Anim; private ParticleSystem particles; public float speed = 50f; public float canonBallLifetime = 10f; private AudioSource canonAudio; private GameObject firedBall; [Space] [Header("Options for trigger")] public float fireRate = 1f; public int numberOfShots = 4; private bool isShooting = false; // Use this for initialization void Start () { Anim = GetComponent<Animator>(); particles = GetComponentInChildren<ParticleSystem>(); //smoke particle system canonAudio = GetComponent<AudioSource>(); } // Update is called once per frame void Update () { if (Input.GetKeyDown("f")) { //Debug.Log("fire"); shoot(); } } private void shoot() { canonAudio.Play(); Anim.SetTrigger("FireCanon"); firedBall = Instantiate(canonBall, firePoint.position, firePoint.rotation); StartCoroutine(firedBall.GetComponent<CanonBall>().destroyBallAfterTime(canonBallLifetime)); //destroy the canonBall after fixed time firedBall.GetComponent<Rigidbody>().velocity = firePoint.transform.forward * speed; /:add a velocity to the canonBalls particles.Emit(20); //smoke } private IEnumerator fireSequance(float fireRate, int numberOfShots) { isShooting = true; while (numberOfShots > 0) { shoot(); yield return new WaitForSeconds(fireRate); numberOfShots -= 1; } isShooting = false; } public void runTrigger() { if (!isShooting) { StartCoroutine (fireSequance(fireRate, numberOfShots)); } } } |
On a un Transform pour l’endroit où les boulets de canon vont apparaitre, un GameObject pour les boulets de canon (qui sont en Prefab). Il y a également un Animator qui gère un petit animation lorsque le canon tire. Des particules pour faire l’effet de fumée et diverses variables pour la vitesse de tire, le délai entre les tirs et la durée de vie des boulets de canon (on ne veut pas 10.000 éléments qui trainent dans notre environnement).
Un élément important est le bool isShooting qui va gérer si le canon est en train de tirer ou pas. On ne veut pas plusieurs déclanchements en même temps.
Lors du déclanchement du trigger, on exécute la coRoutine fireSequance(). Ceci permet de tirer plusieurs coups à une vitesse définie.
Tout le code du tir se trouve fans le fonction shoot(). On Instantiate le boulet de canon et on émet la fumée. Tout le reste se passe sur le boulet de canon directement. Que nous virons dans un prochain poste.
On retrouve notamment cette ligne très importante :
StartCoroutine(firedBall.GetComponent<CanonBall>().destroyBallAfterTime(canonBallLifetime));
qui appel la CoRoutine dans le script du boulet qu’on retrouve ici