views:

283

answers:

4

I want a method in a DrawableGameComponent class to not return until a particular condition is met

Say I have this class (snippet from a DrawableGameComponent class):

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
        }

This class is being drawn (Draw())in the main thread (because this drawablegamecomponent is executing in seperate thread), and also in the main thread I have a list of commands that I want to be executed in order...but currently, since the Ahead method returns just after assigning a value to velocity, the methods will run almost concurrently, which in turn just executes all the animations at the same time.

So what do you think should I do to prevent methods that are commands (Ahead,TurnLeft etc..) from returning before a certain condition is met?

+1  A: 

Although I find your design somewhat odd, best way to accomplish what you want is to use an EventWaitHandle and signal it from another thread.

Say you have an instance of the waithandle on your class

you can call waithadle.WaitOne() in your method, and signal the even from another thread using waithandle.Set() when the condition is met, at which point your method will resume from waiting.

Pop Catalin
"Although I find your design somewhat odd" - Then help me improve it please :-) I would really like suggestions, good or bad
Andreas Grech
Gladly, unfortunately I don't know enough about your code to give your my opinion on how I think it would be better, how you do it might be the best solution, I just said it was odd not that it wasn't good :). I usually prefer not to block inside methods but inside the main loop ...
Pop Catalin
+3  A: 

You need to create some kind of state machine for your Update() method. e.g.

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

Or am I missing the question?

erikkallen
+2  A: 

Ahh, two words: Cooperative multitasking. With the joy of Fibers (or your cooperative multitasking building block of choice) you could (after laying some ground work, such as this to enable fibers in C#) do something like this:

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
  }
}

In order to make this work however you need to implement a simple round-robin scheduler. C# isn't really my boat, but what I'd do is to keep it simple and create some sort of base-class that I'd call Cooperative (or something). This class would have a static list of all created fibers as well as the static methods Create() and Yield(). Create() will create a new fiber (or whatever) and Yield() will simply schedule next fiber to execute (round-robin style), in fiber-world that would include a call to SwitchToFiber(). It will also have a virtual method called Start() (or whatever) that is where the fiber will start to run.

To make it more fancy-smancy you could later keep separate lists of fibers that are either runnable or not runnable (i.e. waiting for something to happen). In that case you might be able to simplify the loop in Ahead to:

WaitFor(HasReachedDestination);

But I suggest getting your feet wet with the concept of cooperative multitasking first.

Finally some thoughts on what should be made fibers, typically your main update loop is one fiber, updating and drawing all objects and then calls Yield(). The all game objects would also be fibers (this may not be feasible if you have a lot of game objects). For your game objects you'd do something like:

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);
}
Andreas Magnusson
Hmm, this is quite interesting, thanks. I will check this out
Andreas Grech
+2  A: 

I agree with Pop Catalin: it is probably best not to block in those command functions. I think you could improve your game by thinking about the design a bit more. Let me provide some thoughts for you on how you could possibly improve your design.

First, it sounds like the problem you are describing is that you want to send a lot of move commands, in a certain order, to a game component and have it execute those commands in that certain order. As you have noticed, there is a difference in the time it takes the computer to perform the calculations (for the velocity or rotation) and the time it takes the component to actually perform the action (move or rotate).

The problem with blocking during the calculations (Ahead, TurnLeft, etc) is that the update loop that is calling that function cannot update any other components. That may work okay if there is only one component to worry about, but that's not usually the case in most games.

Now for the good part: how do we fix this problem? I think erikkallen has the right idea, but I would take it a bit further. It sounds like the game component is some kind of entity that will be moving around, so why not give it an action queue? A simple implementation would be to just have your calling function call something like:

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

Your queueAction function might look like this:

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

At the top of your Update function you could add:

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

And you'd need to add a line at the end of the Update function like:

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

Now, I definitely wouldn't call this the best solution, but it doesn't take much code to implement it. Of course if you need to move and rotate at the same time you'll have to tweak it a bit. It will also get messier when you add different types of actions.

For a more general solution, I would think about making a base Action class, and deriving specific action classes from it. Then you could just push actions to the queue, and your Update function could call the action's Update function, which would do the work the two sections of your game components Update function is doing now.

These are just some ideas to think about, I hope something here will get you started.

One last thing I wanted to mention was that I don't see you using gameTime variable that is passed to Update. The amount your component moves and rotates may need to be a function of the elapsed time since Update was last called. Meaning that the Update function would move and rotate your game component based on the amount of time that has passed, not just how many times the Update function was called. I'm not very good at explaining it, and it depends on how you'd like your game to function. Here are a couple different posts from Shawn Hargreaves (XNA expert). Also, an XNA Forum post discussing the point I was trying to make.

Venesectrix