Question

Je souhaite qu'une méthode d'une classe DrawableGameComponent soit renvoyée à ne pas être renvoyée tant qu'une condition particulière n'est pas remplie

Dites que j'ai cette classe (extrait d'une classe DrawableGameComponent ):

public override void Update(GameTime gameTime)
        {
            if (moving && pixFromLastMove <= distanceToMove)
            {
                position += velocity;
                pixFromLastMove += velocity.Length();
            }
            else
            {
                moving = false;
            }
            if (rotating)
            {
                rotation += 0.1f;
                var cRotation = MathHelper.Clamp(rotation, -MathHelper.PiOver2, angleBeforeRotation + degToRotate);
                if (cRotation != rotation)
                {
                    rotation = cRotation;
                    angleBeforeRotation = rotation;
                    rotating = false;
                }
            }
            base.Update(gameTime);
        }

public void Ahead(int pix)
        {
            moving = true;
            distanceToMove = pix;
            pixFromLastMove = 0;
            velocity = new Vector2((float) Math.Cos(rotation), (float) Math.Sin(rotation))*5.0f;

            //DO NOT RETURN UNTIL THIS ROBOT HAS MOVED TO ITS DESTINATION
            }

public void TurnLeft(int deg)
        {
            rotating = true;
            degToRotate = MathHelper.ToRadians(deg);
            angleBeforeRotation = rotation;

            //DO NOT RETURN UNTIL THIS ROBOT HAS BEEN FULLY ROTATED
        }

Cette classe est en cours de dessin ( Draw () ) dans le thread principal (car ce drawablegamecomponent est exécuté dans un thread séparé), ainsi que dans le thread principal, j'ai une liste de commandes que je souhaite à exécuter dans l'ordre ... mais actuellement, puisque la méthode Ahead est renvoyée juste après l'attribution d'une valeur à vélocité , les méthodes s'exécutent presque simultanément, ce qui exécute à son tour toutes les animations en même temps.

Alors, que pensez-vous que je devrais faire pour empêcher les méthodes qui sont des commandes ( Ahead , TurnLeft etc.) de revenir avant qu'une certaine condition ne soit remplie?

Était-ce utile?

La solution

Vous devez créer une sorte de machine à états pour votre méthode Update (). par exemple

public override void Update() { 
    if (movingRobot) {
        OnlyUpdateRobotPosition();
    }
    else {
        DoStuffPerhapsIncludingStartingRobotMove();
    }
}

Ou est-ce que je manque la question?

Autres conseils

Ahh, deux mots: multitâche coopératif. Avec la joie de Fibres (ou de votre coopérative (vous avez le choix entre plusieurs éléments de construction multitâches) (après avoir effectué quelques travaux préparatoires, tels que ceci pour activer les fibres en C # ), procédez comme suit:

public void Ahead(int pix)
{
  moving = true;
  distanceToMove = pix;
  pixFromLastMove = 0;
  velocity = new Vector2((float) Math.Cos(rotation), (float) Math.Sin(rotation))*5.0f;

  //DO NOT RETURN UNTIL THIS ROBOT HAS MOVED TO ITS DESTINATION
  while(!HasReachedDestination())
  {
    Yield(); // let another fiber run
  }
}

Pour que cela fonctionne, vous devez toutefois implémenter un simple ordonnanceur round robin. C # n'est pas vraiment mon bateau, mais ce que je ferais, c'est de garder les choses simples et de créer une sorte de classe de base que j'appellerais Cooperative (ou quelque chose du genre). Cette classe aurait une liste statique de toutes les fibres créées ainsi que les méthodes statiques Create () et Yield (). Create () créera une nouvelle fibre (ou autre) et Yield () planifiera simplement l'exécution de la fibre suivante (style round-robin), dans le monde de la fibre, qui inclurait un appel à SwitchToFiber (). Il aura également une méthode virtuelle appelée Start () (ou quoi que ce soit) à partir de laquelle la fibre commencera à s'exécuter.

Pour rendre les choses plus sophistiquées, vous pouvez ultérieurement conserver des listes séparées de fibres pouvant être exécutées ou non (c'est-à-dire attendre que quelque chose se produise). Dans ce cas, vous pourrez peut-être simplifier la boucle dans Ahead pour:

WaitFor(HasReachedDestination);

Mais je vous suggère de commencer par vous familiariser avec le concept de multitâche coopératif.

Enfin, certaines réflexions sur ce qu’il convient de faire par fibres, généralement votre boucle de mise à jour principale est une fibre, qui met à jour et dessine tous les objets, puis appelle Yield (). Tous les objets du jeu seraient également des fibres (cela peut ne pas être faisable si vous avez beaucoup d'objets du jeu). Pour vos objets de jeu, vous feriez quelque chose comme:

public override Start()
{
  do
  {
    if(EnemyToTheLeft())
    {
      TurnLeft(90); // this will call Yield and return when we have finished turning
      Shoot();
    }
    Yield(); // always yield
  }while(!dead);
}

Je suis d'accord avec Pop Catalin: il est probablement préférable de ne pas bloquer ces fonctions de commande. Je pense que vous pourriez améliorer votre jeu en pensant un peu plus au design. Laissez-moi vous donner quelques idées sur la manière dont vous pourriez améliorer votre conception.

Tout d’abord, il semble que le problème que vous décrivez soit que vous souhaitiez envoyer un grand nombre de commandes de déplacement, dans un certain ordre, à un composant de jeu et qu’il exécute ces commandes dans cet ordre particulier. Comme vous l'avez remarqué, il existe une différence entre le temps qu'il faut à l'ordinateur pour effectuer les calculs (pour la vitesse ou la rotation) et le temps nécessaire au composant pour exécuter l'action (déplacer ou faire pivoter).

Le problème avec le blocage lors des calculs (Ahead, TurnLeft, etc.) est que la boucle de mise à jour qui appelle cette fonction ne peut mettre à jour aucun autre composant. Cela peut fonctionner sans problème avec un seul composant, mais ce n'est généralement pas le cas dans la plupart des jeux.

Maintenant pour la bonne partie: comment résoudre ce problème? Je pense qu'erikkallen a la bonne idée, mais je voudrais aller un peu plus loin. On dirait que le composant de jeu est une sorte d’entité qui va se déplacer, alors pourquoi ne pas lui donner une file d’action? Une implémentation simple consisterait simplement à ce que votre fonction d’appel appelle quelque chose comme:

gameComponent.queueAction( (MethodInvoker)delegate() 
                           { gameComponent.Ahead(10); });

Votre fonction queueAction pourrait ressembler à ceci:

public void queueAction(MethodInvoker action)
{
    queue.Enqueue(action);
}

Au sommet de votre fonction de mise à jour, vous pouvez ajouter:

if(noCurrentAction && queue.Count > 0)
{
    ((MethodInvoker)queue.Dequeue()).Invoke();
    noCurrentAction = false;
}

Et vous auriez besoin d'ajouter une ligne à la fin de la fonction de mise à jour comme suit:

if(!moving && !rotating)
    noCurrentAction = true;

Maintenant, je n'appellerais certainement pas cela la meilleure solution, mais sa mise en oeuvre ne nécessite pas beaucoup de code. Bien sûr, si vous devez déplacer et faire pivoter en même temps, vous devrez le modifier un peu. Vous obtiendrez également un désordre lorsque vous ajoutez différents types d’actions.

Pour une solution plus générale, je penserais à créer une classe d'action de base et à en dériver des classes d'action spécifiques. Ensuite, vous pouvez simplement pousser des actions dans la file d’attente et votre fonction de mise à jour peut appeler la fonction de mise à jour de l’action, qui effectue le travail effectué par les deux sections de la fonction de mise à jour de vos composants de jeu.

Ce ne sont là que quelques idées sur lesquelles réfléchir. J'espère que quelque chose va vous aider à démarrer.

Une dernière chose que je voulais mentionner est que je ne vous vois pas utiliser la variable gameTime transmise à Update. La quantité de mouvement et de rotation de votre composant peut être fonction du temps écoulé depuis le dernier appel de Update. Cela signifie que la fonction de mise à jour déplacerait et ferait pivoter votre composant de jeu en fonction du temps écoulé, et non du nombre de fois où la fonction de mise à jour a été appelée. Je ne suis pas très doué pour l'expliquer, et cela dépend de la manière dont vous souhaitez que votre jeu fonctionne. Voici un couple différent messages de Shawn Hargreaves (expert XNA). En outre, un message sur le forum XNA abordant le sujet que j'essayais faire.

Bien que votre conception me semble un peu étrange, la meilleure façon de réaliser ce que vous voulez est d'utiliser un EventWaitHandle et le signaler depuis un autre thread.

Supposons que vous ayez une instance de l'attente sur votre classe

vous pouvez appeler waithadle.WaitOne () dans votre méthode et signaler le même d'un autre thread à l'aide de waithandle.Set () lorsque la condition est remplie, moment auquel votre méthode reprendra son attente.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top