1. #1
    Membre habitué Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    81
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : septembre 2017
    Messages : 81
    Points : 145
    Points
    145

    Par défaut Méthode pour une boucle qui fait une action toute les X secondes

    Bonsoir tout le monde,

    Une petite question à la con, j'ai besoin de vos expériences sur Unity (et surement plus en général sur la programmation), je m'explique:

    Comme vous avez vu dans le titre, je me pose la question de la meilleur façon de faire une boucle (infinie ou non, ce n'est pas un souci) qui fait une action toutes les X secondes (comme une regen de vie par exemple dans un jeu)
    Actuellement avec mes connaissances acquises je vois deux méthodes (peut être que vous allez m'en apprendre des autres ) et j'aimerais savoir ce qui est le mieux selon vous ou carrément si une des méthodes est une très mauvaise idée/habitude!

    1. Une simple float pour compter le temps dans la void update
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    void Update() {
     
            TempsRegenVie -= Time.deltaTime;
            if (Temps <= 0.00f)
            {
                Vie += 1;
                TempsRegenVie = 1.00f;
            }
        }
    2. Une coroutine:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    void Start()
        {
            StartCoroutine("RegenVie");
        }
     
     IEnumerator RegenVie()
        {
            while (true)
            {
                Vie += 1;
                yield return new WaitForSeconds(1);
            }
     
        }
    Utiliser une variable TempsRegenVie de type float sera moins précis de la coroutine? vu que c'est mis à jour pendant le void update qui a lieu une fois par frame, au final on ne sera jamais à 1 secondes précisément.... ou cela n'a pas d'impact pour des millièmes de secondes?
    Et du coup le waitforseconds lui fonctionne indépendamment de tout ça ? ou c'est liés aussi au time.deltatime mais on ne le voit pas?

    Merci pour vos retour et conseils d'avance


    TontonNico
    On me dit souvent que je ressemble à Einstein... mais plutôt à Frank que Albert

  2. #2
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    4 837
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 4 837
    Points : 20 175
    Points
    20 175

    Par défaut

    Salut,

    je n'utilise pas Unity mais typiquement oui tu auras un timer dans ta fonction update. Quand il dépasse la valeur souhaitée (ou quand il descend sous 0 si tu le fais décrémenter), tu réalises ton action (dot, hot, ...).
    Perso je préfère faire un compteur qui s'incrémente en utilisant un type non signé, mais ça dépend généralement de comment est représenté le temps (float, unsigned int, secondes, millisecondes, ...).

    En gros en pseudo-code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    unsigned int mTimer = 0;
    unsigned int mTimeout = 1000; // 1000 ms = 1s
    void update(unsigned int dtms) // mon dt est en millisecondes
    {
      ...
      mTimer += dtms;
      if (mTimer >= mTimeout)
      {
        mTimer -= Timeout;
        onTick();
      }
      ...
    }
     
    void onTick()
    {
    }
    Au lieu d'un simple if tu peux aussi avoir une boucle while, si tu as un dt qui peut être assez gros, ou un timeout assez petit, pour que l'action doive se réaliser plusieurs fois dans la frame
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    ...
    while (mTimer >= mTimeout)
    {
      mTimer -= mTimeout;
      onTick();
    }
    ...
    La coroutine devrait être utilisée pour les trucs qui ne sont pas liées au gameplay ou doivent être threadées. Ici je ne vois pas d'intérêt particulier.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre régulier Avatar de EliXirr
    Homme Profil pro
    Développeur informatique
    Inscrit en
    avril 2013
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : avril 2013
    Messages : 46
    Points : 104
    Points
    104

    Par défaut

    Je ne suis pas vraiment d'accord avec Bousk, même si je respect son opinion et qu'elle est correcte.

    En fait tu as déjà les 2 solutions, c'est juste philosophique. Les 2 sont bonnes cependant si tu as des liens forts avec Monobehaviour alors pour moi la Coroutine semble bien plus adapté. L'avantage c'est que tu ne va pas poluer ton Update() et ton behaviour avec tout un tas de variables alors que tu peux bien isoler le méchanisme dans une coroutine. J'irais même plus loin en te disant que tu pourrais utiliser un "CoroutineController" afin de pouvoir la Stoper ou la mettre en pause.

  4. #4
    Membre habitué Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    81
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : septembre 2017
    Messages : 81
    Points : 145
    Points
    145

    Par défaut

    Merci pour vos réponses


    @Bousk, je note ta préférence pour le void update et je vais étudier le type non signé dans unity et son application, merci pour l'info

    @EliXirr (comme le jeu de carte? ), pas forcément de 3ieme solution à ce genre de chose je vois, par contre qu'entends tu par
    si tu as des liens forts avec Monobehaviour
    ? concernant le coroutinecontroller je ne connais pas, j'aurais juste fait de simple if sur des variables bool mais en terme de gestion de mémoire c'est au niveau 0 je pense ^^ je vais aussi me renseigner la dessus pour gérer les coroutines, merci pour l'info

    Bonne soirée!

    TontonNico
    On me dit souvent que je ressemble à Einstein... mais plutôt à Frank que Albert

  5. #5
    Membre régulier Avatar de EliXirr
    Homme Profil pro
    Développeur informatique
    Inscrit en
    avril 2013
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : avril 2013
    Messages : 46
    Points : 104
    Points
    104

    Par défaut

    Ce n'est pas parceque tu bosses sur unity que tu est obligé de tout faire avec des Monobehaviour. Le paradigme objet et les different design patern t'offrent ennormement de possibilités differentes pour résoudre un même problème. L'élégance de la solution va dépendre de la cohérence de ton architecture.

    Je disais ça car pour utiliser les coroutines tu dois obligatoirement être dans un Monobehaviour où l'appeler via un Monobehaviour (ex : MonMonobheaviour.StartCoroutine(MaCoroutine); ). Cependant parfois tu dois faire la même chose mais tu n'as accès à aucun monobehaviour, tu es dans une classe par exemple.

    pour la CoroutineController : c'est cadeau. Vraiment utile ce p'tit machin.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
     
    using UnityEngine;
    using System.Collections;
     
    namespace Utils
    {
        /// <summary>
        /// Control a coroutine by adding a state système
        /// </summary>
        /// /// <example> 
        /// this sample code shown an example control. BlinkerYes() is an IEnumerator put in the CoroutineController
        /// <code>
        /// CoroutineController coroutineExample = new CoroutineController(blinkerYes());
        /// StartCoroutine(coroutineExample.Start());
        /// </code>
        /// </example>
        public class CoroutineController
        {
            /// <summary>
            /// The coroutine Ienumerator wich will be manage
            /// </summary>
            private IEnumerator _routine;
     
            /// <summary>
            /// State of the coroutine.
            /// </summary>
            public CoroutineState state;
     
            /// <summary>
            /// ctor
            /// </summary>
            public CoroutineController(IEnumerator routine)
            {
                _routine = routine;
                state = CoroutineState.Ready;
            }
     
            /// <summary>
            /// Start The Coroutine
            /// </summary>
            public IEnumerator Start()
            {
                if (state != CoroutineState.Ready)
                {
                    throw new System.InvalidOperationException("Unable to start coroutine in state: " + state);
                }
     
                state = CoroutineState.Running;
                while (_routine.MoveNext())
                {
                    yield return _routine.Current;
                    while (state == CoroutineState.Paused)
                    {
                        yield return null;
                    }
                    if (state == CoroutineState.Finished)
                    {
                        yield break;
                    }
                }
     
                state = CoroutineState.Finished;
            }
     
            /// <summary>
            /// Stop the coroutine
            /// </summary>
            public void Stop()
            {
                if (state != CoroutineState.Running && state != CoroutineState.Paused)
                {
                    throw new System.InvalidOperationException("Unable to stop coroutine in state: " + state);
                }
     
                state = CoroutineState.Finished;
            }
     
            /// <summary>
            /// Pause the coroutine
            /// </summary>
            public void Pause()
            {
                if (state != CoroutineState.Running)
                {
                    throw new System.InvalidOperationException("Unable to pause coroutine in state: " + state);
                }
     
                state = CoroutineState.Paused;
            }
     
            /// <summary>
            /// resume the coroutine.
            /// </summary>
            public void Resume()
            {
                if (state != CoroutineState.Paused)
                {
                    throw new System.InvalidOperationException("Unable to resume coroutine in state: " + state);
                }
     
                state = CoroutineState.Running;
            }
        }
     
        /// <summary>
        /// State of a coroutine
        /// </summary>
        public enum CoroutineState
        {
            Ready,
            Running,
            Paused,
            Finished,
            Unknow
        }
     
    }

  6. #6
    Membre habitué Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    81
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : septembre 2017
    Messages : 81
    Points : 145
    Points
    145

    Par défaut

    Je ne fais que du Monobehavior en effet sur Unity (et je n'ai jamais rien vu d'autre dans les vidéos pour l'instant mais le coroutinecontroller est le messi ) mais je me doutais que d'autres trucs existaient mais je ne visualise pas l'impact que cela peut avoir sur la conception/architecture je dois avouer, je suis complètement largué la
    Mais pourtant je le note dans un coin de ma tête, un jour -quand je maitriserais mieux le C# je pense - il me faudra absolument découvrir tout cela ^^


    Merci pour le bout de code, cela va me faire gagner beaucoup de temps, il me reste à faire des tests et comprendre chaque étape

    TontonNico
    On me dit souvent que je ressemble à Einstein... mais plutôt à Frank que Albert

  7. #7
    Membre habitué Avatar de yaraco
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    septembre 2010
    Messages
    80
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2010
    Messages : 80
    Points : 128
    Points
    128

    Par défaut

    Même avis qu' Elixir, la co-routine est plus propre niveau organisation du code, même si je ne suis absolument pas un fan des appels de fonction via leur nom sous forme de string.

    Dans mon projet j'avais commencé à utiliser des timers maison dans le Update à la manière de Bousk mais c'est ré-inventer la roue et complexifier la chose pour rien. Maintenant que j'ai découvert les co-routines, c'est ce que j'utilise.

    A noter qu'il y a un "WaitForFixedUpdate" aussi qui existe, si jamais cela peut t'être utile. Ou que tu peux également utiliser les Timers .NET classiques. Mais tu es obligé de repasser dans le thread principal pour toute interaction avec les mono/network behaviour. Sans compter que je ne suis pas sûr que ça soit utilisable pour des plateformes mobiles.

    PS : Merci pour le script Elixir!

  8. #8
    Membre habitué Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    81
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : septembre 2017
    Messages : 81
    Points : 145
    Points
    145

    Par défaut

    Oui je vais voir ce qui est le plus simple en utilisation entre le timer ou la coroutine

    Merci beaucoup pour vos retours en tout cas ! je passe en résolu

    TontonNico
    On me dit souvent que je ressemble à Einstein... mais plutôt à Frank que Albert

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Meilleure méthode pour envoyer des variables dans une base depuis shell
    Par blachzero dans le forum Shell et commandes GNU
    Réponses: 3
    Dernier message: 17/09/2014, 15h20
  2. [11g] Quelle méthode pour calculer la taille d'une base ?
    Par zidane2012 dans le forum Administration
    Réponses: 4
    Dernier message: 11/03/2014, 13h06
  3. Réponses: 1
    Dernier message: 24/04/2013, 20h24
  4. outil/méthode pour vérifier la syntaxe d'une macro
    Par Anouschka dans le forum Linux
    Réponses: 2
    Dernier message: 16/11/2007, 15h05
  5. Réponses: 2
    Dernier message: 19/10/2006, 16h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo