10 votes

Ne sait pas comment utiliser les coroutines dans Unity3D

Dans Unity3D, voici mon code :

void ActivateBuff1(){
    gun.equippedGun.msPerShot /= 2;
    gun.equippedGun.shotsLeftInMag += 10;
    StartCoroutine (WaitRage ());
}

void ActivateBuff2(){
    player.speedModifier *= 1.5f;
    StartCoroutine (WaitSpeed ());
}

IEnumerator WaitRage(){
    yield return new WaitForSeconds(powerUpDuration);
    gun.equippedGun.msPerShot *= 2;
}

IEnumerator WaitSpeed(){
    yield return new WaitForSeconds(powerUpDuration);
    player.speedModifier /= 1.5f;
}

À chaque fois qu'un joueur tombe sur un power up, l'une des méthodes ActivateBuff est appelée. Évidemment, les effets des power ups ne durent pas éternellement, c'est pourquoi j'ai utilisé des IEnumerators pour annuler les effets de ma méthode initiale après une attente de quelques secondes. Cependant, pour une raison inconnue, le code à l'intérieur des IEnumerators n'est jamais appelé. Veuillez m'aider... (et suggérer éventuellement une autre manière de coder ceci car je sais que ce n'est pas très propre)

12voto

Serlite Points 3098

Dans des circonstances normales, le code que vous avez fourni devrait fonctionner correctement. Cependant, comme indiqué dans les commentaires, il y a une mise en garde - si le Gameobject appelant la coroutine est désactivé/détruit avant que le délai de WaitForSeconds() soit écoulé, la coroutine sera arrêtée et le code restant ne sera pas appelé du tout. Vous devrez soit attendre que la coroutine se termine avant de détruire le Gameobject, soit avoir un autre Gameobject appeler la coroutine.

Vous avez mentionné que vous cherchiez également des alternatives qui pourraient simplifier votre code - vous pourriez envisager Invoke(), qui vous permet d'appeler une méthode après un délai spécifié. (Tant que vous n'appelez pas cela très souvent, le surcoût de la réflexion n'aura pas d'effet appréciable sur vos performances.) Ainsi, votre code pourrait être réécrit pour être un peu plus court :

void ActivateBuff1(){
    gun.equippedGun.msPerShot /= 2;
    gun.equippedGun.shotsLeftInMag += 10;
    Invoke("ResetPlayerRage", powerUpDuration);
}

void ActivateBuff2(){
    player.speedModifier *= 1.5f;
    Invoke("ResetPlayerSpeed", powerUpDuration);
}

void ResetPlayerRage(){
    gun.equippedGun.msPerShot *= 2;
}

void ResetPlayerSpeed(){
    player.speedModifier /= 1.5f;
}

Malheureusement, Invoke() sera également annulé si le Gameobject est détruit - mais contrairement à une coroutine, il ne sera pas annulé si le Gameobject est désactivé. Vous pourriez donc d'abord désactiver le Gameobject (pour le rendre invisible et ne pas interagir avec quoi que ce soit), puis le détruire seulement après l'exécution de la méthode retardée :

void ActivateBuff1(){
    gun.equippedGun.msPerShot /= 2;
    gun.equippedGun.shotsLeftInMag += 10;
    gameObject.SetActive(false);
    Invoke("ResetPlayerRage", powerUpDuration);
}

void ResetPlayerRage(){
    gun.equippedGun.msPerShot *= 2;
    Destroy(gameObject);
}

Voici un résumé de savoir si Invoke() et les coroutines seront arrêtés en fonction de la manipulation du composant de script ou du Gameobject entier :

..........................................................................
:                                  :                     :               :
:          Est-ce que ça s'arrête?  :   InvokeRepeating   :   Coroutine   :
:                                  :                     :               :
:..................................:.....................:...............:
:                                  :                     :               :
:   Désactiver le composant de script:        Non         :      Non       :
:                                  :                     :               :
:..................................:.....................:...............:
:                                  :                     :               :
:   Détruire le composant de script:        Oui         :      Oui      :
:                                  :                     :               :
:..................................:.....................:...............:
:                                  :                     :               :
:   Désactiver le Gameobject        :         Non         :      Oui      :
:                                  :                     :               :
:..................................:.....................:...............:
:                                  :                     :               :
:   Détruire le Gameobject          :         Oui         :      Oui      :
:                                  :                     :               :
:..................................:.....................:...............:

2voto

Rafael Costa Points 616

Homme, Serlite a tout dit.. Cependant j'aurais abordé ce genre de situation différemment.

Si j'ai bien compris, tu as défini cette fonction ActivateBuff dans le script attaché à une sorte d'objet qui définit un modificateur dans le pistolet équipé et qui s'éteint par la suite. Au lieu de faire cela, je créerais simplement une fonction Buff dans le script du pistolet équipé (en passant le modificateur et le temps en paramètres) et laisserais le pistolet lui-même gérer cela.

Étant donné que le pistolet équipé ne disparaîtra pas, cela résoudrait ton problème et idéalement pourrait même être une solution plus générique. Ainsi, tu pourrais passer par une autre amélioration qui pourrait ensuite générer un comportement inattendu (car il y aurait de nombreux scripts buffant et debuffant le pistolet).

1voto

Bizz Points 1637

Jetez un coup d'œil à mon approche. Il utilise la méthode FixedUpdate pour gérer les timings et ne nécessite pas de Coroutines. J'ai également utilisé le modèle Singleton dans mes buffers pour permettre un accès facile.

J'ai un script BufferBase où je gère le début/fin des buffers. Je peux avoir autant de buffers que je veux et les dériver de cette classe.

BufferBase a

  • deux membres : _isBufferActive et _bufferRemainingTime.

  • Une méthode nommée FixedUpdateBuffer que je dois appeler dans FixedUpdate de mes buffers. Elle est responsable du timing du buffer et appelle EndBuffer() lorsque le temps est écoulé.

  • Et 3 méthodes virtuelles que je peux remplacer dans mes classes de buffer.

Voici le code :

public class BufferBase : MonoBehaviour
{
    /// 
    /// Indique si le buffer est activé
    /// 
    protected bool _isBufferActive = false;
    /// 
    /// Temps restant jusqu'à la fin du buffer
    /// 
    protected float _bufferRemainingTime = 0f;

    protected void FixedUpdateBuffer()
    {
        if (_isBufferActive)
        {
            _bufferRemainingTime -= Time.fixedDeltaTime;
            if (_bufferRemainingTime <= 0)
            {
                EndBuffer();
            }
        }
    }

    /// 
    /// Réinitialise le buffer
    /// 
    protected void ResetBuffer()
    {
        _isBufferActive = false;
        _bufferRemainingTime = 0;
    }

    /// 
    /// Marque le début du buffer
    /// 
    /// 
    protected virtual void StartOrExtendBuffer(float value)
    {
        //set buffer values
        _isBufferActive = true;
        _bufferRemainingTime = value;

        gameObject.SetActive(true);
    }

    /// 
    /// Marque la fin du buffer
    /// 
    protected virtual void EndBuffer()
    {
        _bufferRemainingTime = 0;
        _isBufferActive = false;

        gameObject.SetActive(false);
    }
}

Maintenant pour le buffer réel. J'ai plusieurs scripts dérivés de BufferBase. Tous ont ces méthodes virtuelles implémentées en eux.

Je peux facilement :

  1. vérifier si un type spécifique de buffer est actif ou non avec RageController.IsActive

  2. activer un buffer en utilisant RageController.AddRage(t) où t spécifie la durée. (sa durée sera réinitialisée à t chaque fois que AddRage est appelé)

  3. désactiver un buffer en utilisant RageController.Reset()

Voici un exemple de script de buffer :

public class RageController : BufferBase
{
    public static RageController instance;

    public static bool IsActive { get { return instance._isBufferActive; } }

    #region Méthodes statiques
    internal static void AddRage(float value)
    {
        instance.StartOrExtendBuffer(value);
    }

    internal static void Reset()
    {
        instance.ResetBuffer();
    }
    #endregion

    #region Méthodes remplacées
    protected override void StartOrExtendBuffer(float value)
    {
        base.StartOrExtendBuffer(value);

        //----
        //ajouter de la vitesse etc..
        //----
    }

    protected override void EndBuffer()
    {
        base.EndBuffer();

        //----
        //supprimer de la vitesse etc..
        //----
    }
    #endregion   

    #region Méthodes Unity
    void Awake()
    {
        instance = this;
    }
    void FixedUpdate()
    {
        FixedUpdateBuffer();

        if (_isBufferActive)
        {
            //----
            //tout ce qui change avec le temps
            //----
        }
    }
    #endregion
}

Remarquez qu'à la fin de RageController dans la méthode FixedUpdate, vous pouvez doucement modifier vos valeurs souhaitées ou activer le buffer avec un délai ou autre en lisant la valeur de _bufferRemainingTime

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X