1. #1
    Membre actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    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
    5 125
    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 : 5 125
    Points : 21 597
    Points
    21 597

    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 habitué Avatar de EliXirr
    Homme Profil pro
    Développeur informatique
    Inscrit en
    avril 2013
    Messages
    57
    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 : 57
    Points : 139
    Points
    139

    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 actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    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 habitué Avatar de EliXirr
    Homme Profil pro
    Développeur informatique
    Inscrit en
    avril 2013
    Messages
    57
    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 : 57
    Points : 139
    Points
    139

    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 actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    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
    89
    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 : 89
    Points : 148
    Points
    148

    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 actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    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

  9. #9
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    5 125
    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 : 5 125
    Points : 21 597
    Points
    21 597

    Par défaut

    Re,

    la co-routine semble plus propre - m'enfin ça reste plus que subjectif et tu peux très bien faire une classe helper de timer pour avoir un code du genre if (myGameplayStateUpdate.must_work()) ou carrément une classe qui gère la logique sur timer que tu appeles juste à chaque update myTimedAction.update(); -, mais c'est une fausse bonne idée pour le gameplay imo.
    Si tu regardes la doc et l'ordre d'exécution de Unity, les Update sont appelés, puis les co-routines exécutées. Et tu ne contrôles pas leur ordre. Donc si ta co-routine met à jour un état, cet état ne sera disponible que dans le prochain Update. Et si tu utilises cet état dans une autre co-routine, celle-ci peut aussi n'avoir l'état à jour qu'à son prochain appel. Donc ton code sera toujours en retard d'au moins une frame. Tu peux t'en moquer, mais à terme c'est le genre de bugs chiants à retrouver.
    Sans compter que la co-routine est spécifique à Unity, donc si tu comptes rester sur cette techno à la limite pourquoi pas - mais le problème ci-dessus subsiste -, mais une solution à base de timer est aisément transportable à n'importe quel langage et projet.
    Enfin niveau lisibilité: Pas de surprise, tout est au même endroit et appelé dans l'ordre. Pas besoin de sauter d'une fonction à l'autre, et aucune surprise dans l'ordre d'appel et exécution.

    Et pour une gestion des buffs c'est exactement ce que tu devrais faire.
    Pour l'instant c'est facile, tu as juste une regen.
    Worst case scenario : un peu de vie est manquante dans l'update : dommage, du coup le perso prend un coup et meurt pendant l'update, alors qu'il a un hot mais il ne tickera que plus tard.
    Maintenant tu ajoutes un dot de poison : dans l'update ton perso n'est pas encore mort, mais le tick le tue pendant la co-routine. Et le perso ne sera géré mort que lors du prochain update.
    Et vu que tu ne contrôles pas l'ordre des appels, le résultat est différent d'un joueur à l'autre..!

    Donc pour un système de buffs, tu crées un gestionnaire, qui a un tableau de buff et qui se comporte ainsi (pseudo-code C++ mais certainement transposable):
    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
    class Timeout {
      unsigned int mTimeoutMs;
      unsigned int mTimer{0};
    public:
      Timeout(unsigned int timeout) : mTimeoutMs(timeout) {}
      bool Update(unsigned int dt)
      {
        mTimer += dt;
        if (mTimer >= mTimeoutMs)
        {
            mTimer = 0;
            return true;
        }
        return false;
      }
    };
    class Buff {
    Timeout mTimer;
    Timeout mDuration;
    bool mOver{false};
    public:
      Buff(unsigned int tick, unsigned int duration) : mTimer(tick), mDuration(duration) {}
      void Update(unsigned int dtms)
      {
        if (mTimer.Update(dtms))
          execute(); //!< execute peut être une fonction à implémenter ou un membre type callback à initialiser dans le constructeur par exemple
        if (!mOver && mDuration.Update(dtms))
          mOver = true;
      }
      bool IsOver() const { return mOver; }
    };
    class BuffManager {
    vector<Buff*> mBuffs;
    public:
      void Update(unsigned int dtms)
      {
        for each buff in mBuffs
           buff->Update(dtms);
        RemoveFinished();
      }
      void RemoveFinished()
      {
         //  ...
      }
    };
    Ensuite tu peux complexifier en réordonnant les buffs d'après tes critères.
    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.

  10. #10
    Membre habitué Avatar de EliXirr
    Homme Profil pro
    Développeur informatique
    Inscrit en
    avril 2013
    Messages
    57
    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 : 57
    Points : 139
    Points
    139

    Par défaut

    Je me permet de compléter les propos de Bousk avec un schema de l'ordre des évènements pour une passe dans unity :

    https://docs.unity3d.com/Manual/ExecutionOrder.html

    (PS : C'était le Graal lorsque j'ai découvert ceci ^^ )

  11. #11
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    5 125
    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 : 5 125
    Points : 21 597
    Points
    21 597

    Par défaut

    Tu complètes en ajoutant un lien qui est déjà présent ?
    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.

  12. #12
    Membre actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    Par défaut

    Merci Bousk pour ton exemple
    Je vais regarder ça de plus près et en effet j'avais eu le soucis du fait de passer par une coroutine qui empêche de pouvoir gérer l'ordre d'application avec le void update
    Par contre merci pour le liens sur les ordres dans Unity, en effet je n'avais pas vu plus loin de mon nez ....le jour ou j'aurais rajouté une autre coroutine de poison par exemple.... c'est clairement plus facile de faire comme ton exemple!

    Merci de m'avoir averti sur ce genre de problème de conception

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

  13. #13
    Membre habitué Avatar de EliXirr
    Homme Profil pro
    Développeur informatique
    Inscrit en
    avril 2013
    Messages
    57
    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 : 57
    Points : 139
    Points
    139

    Par défaut

    lol j'avais mal lu ^^ DSL pour la redite du coup.

  14. #14
    Membre actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    Par défaut

    Salut Bousk,

    J'ai pris un peu de temps pour regarde ton code, je pense que je comprends l'idée générales mais décrypter toute les lignes de code c'est au delà de ma compréhension pour le moment

    Quand tu fais par exemple un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Update(unsigned int dtms)
    , c'est pour forcer l'update à une fréquence fixe nommé dtms ? c'est bien cela?


    Je vais essayer de retranscrire sous unity le code et je vais surement revenir avec plein d'autre question

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

  15. #15
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    5 125
    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 : 5 125
    Points : 21 597
    Points
    21 597

    Par défaut

    C'est juste un paramètre de deltatime de mise à jour de la simulation.
    Pour le reste du code, il n'y a rien de particulier, un débutant devrait être à-même de le lire et comprendre, y'a aucune notion de quelconque complexité, c'est strictement algorithmique avec litérallement 1 addition, 1 comparaison de valeur entière et 3 booléens.
    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.

  16. #16
    Membre actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    Par défaut

    Salut,

    Je ne suis pas habitué à la notation , n'utilisant que le C# d'unity pour le moment je dois faire la translation pour être sur de bien comprendre

    Par exemple dans la class Timeout:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Timeout {
      unsigned int mTimeoutMs;
      unsigned int mTimer{0};
    public:
      Timeout(unsigned int timeout) : mTimeoutMs(timeout) {}
    Il m'a fallut déjà comprendre pourquoi il y a un "public:" qui traine après ta déclaration de variable, à quoi sert la répétition du ": mTimeoutMs(timeout)", le temps que je fasse le lien avec ce que je connais c'était un peu du chinois hi hi



    Par contre je ne comprends pas dans la classe BUFF à quoi sert le :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bool IsOver() const { return mOver; }
    Vu que c'est appelé nulle part.
    C'est juste pour savoir facilement si le buff est finie et donc lié avec le RemoveFinished() dans le BuffManager ou l'on peut checker tous les IsOver() de chaque buff? :

    Si oui, cela ne suffirait pas de call le RemoveFinished dans la class BUFF quand on vient de checker que la duration est finie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if (!mOver && mDuration.Update(dtms))
          mOver = true;
    RemoveFinished();
    Ou il y a une utilité de le faire comme tu le fais? ou c'est pour une toute autre utilisation que j'ai raté?

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

  17. #17
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    5 125
    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 : 5 125
    Points : 21 597
    Points
    21 597

    Par défaut

    J'avais prévenu c'est du pseudo-code pseudo-C++.
    Je viens de vérifier et il semble que la portée par défaut des membres d'une classe en C# est aussi private. Donc le public il sert à repasser en public.
    Cette notation est la liste d'initialisation, si ça n'existe pas en C#, tu mets une assignation dans l'implémentation.

    C'est juste pour savoir facilement si le buff est finie et donc lié avec le RemoveFinished() dans le BuffManager ou l'on peut checker tous les IsOver() de chaque buff?
    Bien évidemment.. je vais pas non plus proposer une implémentation complète en C# d'un gestionnaire de buff, ou d'un tableau de données et leur suppression d'après critères : je n'ai clairement pas le temps, ni l'envie particulière, et je ne pratique pas vraiment le C# de toute façon qui propose sûrement ça de base (RemoveAll ?).
    Mais ceci ne devrait pas être un challenge quelconque, et un bon exercice, vu qu'il s'agit purement d'algorithmique. Typiquement le genre que je donnerais à des étudiants en fait.
    Et tu n'as pas à comprendre tout le code, tu dois juste saisir l'idée : un compteur, tu l'incrémentes, et il a une valeur maximale et t'indiques quand elle est atteinte.
    Que ton compteur soit du temps en secondes, milisecondes pour indiquer un chrono ou timer, ou une distance pour déplacer un véhicule ou le nombre de canards devant traverser la route avant de passer le feu au vert... c'est le contexte que tu lui donnes.
    Et un buff pour ma part c'est un truc avec un temps limite (ou pas s'il dure toujours) et un autre compteur de temps pour chaque tick. Bref rien d'extraordinaire il me semble..?
    Si tu réfléchis à ceci avec un bête crayon, tu dessines 3 rectangles pour tes éléments (au hasard : un perso, un gestionnaire de buffs, quelques buff), quelques flèches pour leurs intéractions (ajouter, mettre à jour, supprimer, ... autre ?) et l'implémenter devrait prendre bien moins d'une heure pour la première version. Voire à peine quelques minutes pour une toute première implémentation naïve/vide qui ne fait que l'architecture de plus haut-niveau (qui est exactement ce que j'ai fait : transfert des updates et appel d'un remove).

    Si oui, cela ne suffirait pas de call le RemoveFinished dans la class BUFF quand on vient de checker que la duration est finie :
    Et comment tu supprimes le buff dans la liste des buffs du Manager ? A fortiori pendant que tu itères dessus.
    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.

  18. #18
    Membre actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    Par défaut

    Salut Bousk,

    Alors reprenons ensemble calmement:

    J'avais prévenu c'est du pseudo-code pseudo-C++.
    On est d'accord, c'est comme je te disais du coup, il me faut le temps de tout assimiler

    Je viens de vérifier et il semble que la portée par défaut des membres d'une classe en C# est aussi private. Donc le public il sert à repasser en public.
    Cette notation est la liste d'initialisation, si ça n'existe pas en C#, tu mets une assignation dans l'implémentation.
    Yes, pour t'expliquer du coup en gros mon résonnement à la première lecture quand je vois

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    public:
      Buff(unsigned int tick, unsigned int duration) : mTimer(tick), mDuration(duration) {}
    Pour moi je ne vois pas une déclaration nommée Buff vu que les deux lignes sont dissociés mais un apppel de fonction nommé BUFF même si c'est juste un pseudo-code... donc en gros qui s’appellerait lui même ce qui n'avais pas lieux d'être mais je pensais surtout que j'avais loupé un truc sur le fonctionnement global ^^

    Bien évidemment.. je vais pas non plus proposer une implémentation complète en C# d'un gestionnaire de buff, ou d'un tableau de données et leur suppression d'après critères : je n'ai clairement pas le temps, ni l'envie particulière, et je ne pratique pas vraiment le C# de toute façon qui propose sûrement ça de base (RemoveAll ?).
    La question était sans arrière pensée, je me doute que tu ne vas pas me proposer une implémentation complète mais de la même façon je me doute que tu ne vas pas me montrer des lignes de codes inutiles donc je voulais bien cerner le pourquoi du comment tout simplement
    Pour le RemoveAll yeah ça peut être utile, je me le mets dans un coin de la tête

    Mais ceci ne devrait pas être un challenge quelconque, et un bon exercice, vu qu'il s'agit purement d'algorithmique. Typiquement le genre que je donnerais à des étudiants en fait.
    Et tu n'as pas à comprendre tout le code, tu dois juste saisir l'idée : un compteur, tu l'incrémentes, et il a une valeur maximale et t'indiques quand elle est atteinte.
    Que ton compteur soit du temps en secondes, milisecondes pour indiquer un chrono ou timer, ou une distance pour déplacer un véhicule ou le nombre de canards devant traverser la route avant de passer le feu au vert... c'est le contexte que tu lui donnes.
    Oui enfin faut pas pousser non plus, je n'ai jamais dit que je comprenais pas le
    La vous auriez eu le droit d'aller chercher du bois dans la forêt et me faire bruler sur place!
    Et tu n'as pas à comprendre tout le code, tu dois juste saisir l'idée
    C'est exactement ce que je disais justement, on est d'accord mais vu que le topic est la j'ai préféré te poser des questions pour dormir moins bête le soir

    Et un buff pour ma part c'est un truc avec un temps limite (ou pas s'il dure toujours) et un autre compteur de temps pour chaque tick. Bref rien d'extraordinaire il me semble..?
    De part mon expérience, ce dirais que n'est pas parce que la fonction n'est pas extraordinaire que le code est simpliste et tient sur 3 lignes, et inversement. Mais je peux me tromper
    Après je parlais du tick et de la duration en terme général dans le code (avant même de poser la question comme tu imagines bien avec mes "expériences de jeu", je savais un peu comment un HOT ou DOT fonctionne en globalité), je me demandais surtout à quoi cette ligne peut servir en particulier car en C# j'ai surement vu qu'une petite fraction des possibilités et je n'ai jamais été confrontés à cela
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Timeout(unsigned int timeout) : mTimeoutMs(timeout) {}
    Mais du coup c'est juste la déclaration du Buff, rien de particulier en effet donc ça roule ^^

    Et comment tu supprimes le buff dans la liste des buffs du Manager ? A fortiori pendant que tu itères dessus.
    Je crois que je vois le soucis, personnellement je l'aurais supprimé en faisant un call de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    BuffManager.RemoveFinished(string NomBuff);
    quand on est dans la condition de sortie
    if (!mOver && mDuration.Update(dtms))
    mOver = true;
    Ou directement en faisant une manipulation du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    BuffManager.mBuffs[NomBuff].delete
    ... mais cela implique une variable en plus pour stocker le nom mais cela supprime le IsOver() ... en effet tu n'es pas du tout partie sur cette option en bouclant directement dedans! intéressant

    Par contre soyons fous, si j'ai 100 buffs/Etats divers et variés d'appliqués, si j'ai qu'un seul buff qui se termine, il va call de lui même le RemoveFinished ou tout autre fonction dans ma vision de la chose. Ça sera un gain de temps plutôt que de faire 100 vérifications IsOver() de chaque buffs dans le for each (si j'ai bien suivi ton procédé) non?
    Après je vois souvent le verre à moitié vide ou à moitié plein avec mon manque d'expérience faut avouer , si par expérience tu proposes ce genre de solution c'est que cela doit avoir son utilité, j'aimerais bien avoir ton avis



    Bon au final j'ai regardé rapidement le code pour faire un retour ici car ça faisait un moment que tu as répondu, en prenant bien le temps j'aurais pu éviter de poser les questions un peu naïve en effet, désolé ^^
    Par contre les deux autres questions sur le "Bool IsOver" et le "RemoveFinsihed()" sont d'actualités.

    En résumé je conçois et comprends que j'ai l'air de ne pas comprendre "les principes de bases" selon toi et que cela peut t'énerver mais mon but premier était juste d'approfondir mes connaissances et de discuter avec toi la dessus
    Si tu préfères ne plus perdre de temps pas de soucis, dis le moi simplement; dans le cas contraire ça sera un plaisir de discuter sur la manière de comment supprimer un buff et son avantage

    Dans tous les cas, merci pour tes réponses et ton aide jusqu'ici Bousk!

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

  19. #19
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    5 125
    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 : 5 125
    Points : 21 597
    Points
    21 597

    Par défaut

    Ajouter ou supprimer des éléments d'un tableau pendant son parcours c'est surtout un coup à crasher son programme.
    Sans compter que si Buff doit aller s'enlever lui-même du Manager, tu lui ajoutes une dépendance qui ne sert à rien.. Le Manager contrôle les buffs, point barre. Architecture des plus classiques, donc j'en reviens toujours à: fais toi des dessins et si tu tombes sur une dépendance cyclique, 95% de chance que t'as fait un truc de travers.
    Et ton appel implique le Manager en global. Hors les globales sont à éviter. Simple bonne pratique.

    Pour moi je ne vois pas une déclaration nommée Buff vu que les deux lignes sont dissociés mais un apppel de fonction nommé BUFF
    Dans une class Buff, la fonction Buff, sans type de retour qui plus est, s'appelle le constructeur

    Quant à ta crainte pour les performances... Avec 100 buffs tu vas le faire rire ton processeur. Mais dans l'absolu tu as un tableau fixe ou assez grand de cache de buffs et ne le réalloues pas constamment.
    Mais bon contente toi d'avoir un truc qui marche déjà, on verra l'optimisation plus tard.
    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.

  20. #20
    Membre actif Avatar de Tonton Nico
    Homme Profil pro
    Ingénieur
    Inscrit en
    septembre 2017
    Messages
    122
    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 : 122
    Points : 226
    Points
    226

    Par défaut

    Salut,

    Encore et toujours des questions techniques de ma part désolé ^^
    Si c'est trop de question et trop de temps à consacrer à ce sujet pas de soucis surtout que c'est purement un intérêt scolaire car je n'ai pas de buff manager de prévu pour le moment toute fois un jour j'y viendrais si je continue de faire quelque pti jeu donc le sujet m'intéresse comme tu peux le voir surtout que ça me fait apprendre et comprendre des choses

    Ajouter ou supprimer des éléments d'un tableau pendant son parcours c'est surtout un coup à crasher son programme.
    Du coup dans le manager de buff tu ne supprimes pas le buff du tableau quand tu checks le IsOver() et qu'il est renvoie True dans ton For each?
    Tu fais quoi du coup? tu stock les buffs a supprimer et tu fais une seule RemoveFinished() pour tout clear d'un coup ? ou tu refais une boucle complète sur tout les buffs et supprime ceux qui sont finis dans le RemoveFinished() carrément?

    Sans compter que si Buff doit aller s'enlever lui-même du Manager, tu lui ajoutes une dépendance qui ne sert à rien.. Le Manager contrôle les buffs, point barre. Architecture des plus classiques, donc j'en reviens toujours à: fais toi des dessins et si tu tombes sur une dépendance cyclique, 95% de chance que t'as fait un truc de travers.
    Une dépendance cyclique? en autre terme une référence circulaire? je ne vois pas du tout, on doit pas avoir la même idée d'architecture en tête (enfin forcément vu nos niveaux de prog différent j'imagine). Que ce soit mon buff qui va dire au Manager "hey mec j'ai finis mon taf" ou que ce soit mon manager qui demande à chaque buff "hey mec, t'en es ou?" dans tous les cas y a un liens ou une dépendance comme tu dis ou alors comment ton manager sait que le buff est finis?

    Par contre je vois la lumière au bout du tunnel et vois l’intérêt de tout gérer via le Manager seulement en terme de simplicité et de maintenance (et donc que le buff par exemple n'est pas de dépendance ce qui est important), merci de me remettre ça en tête car je fais trop de bricolage

    Et ton appel implique le Manager en global. Hors les globales sont à éviter. Simple bonne pratique.
    je dois tout avoir en global/public dans mes script C# sous unity
    Va falloir que je fasse attention à ça alors même si en général je sais que moins il y a d’interaction moins ça a de chance de merder dans quoi que ce soit, sous unity je ne fais pas du tout attention à ça


    Pour moi je ne vois pas une déclaration nommée Buff vu que les deux lignes sont dissociés mais un apppel de fonction nommé BUFF
    Dans une class Buff, la fonction Buff, sans type de retour qui plus est, s'appelle le constructeur
    je ne savais pas, merci

    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, 14h20
  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, 12h06
  3. Réponses: 1
    Dernier message: 24/04/2013, 19h24
  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, 14h05
  5. Réponses: 2
    Dernier message: 19/10/2006, 15h29

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