1

我希望DrawableGameComponent类中的方法在满足特定条件之前不返回

假设我有这个课程(来自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
        }

这个类正在Draw()主线程中绘制()(因为这个drawablegamecomponent在单独的线程中执行),并且在主线程中我有一个我想要按顺序执行的命令列表......但是目前,因为Ahead方法在为 赋值后立即返回velocity,这些方法将几乎同时运行,而这反过来只是同时执行所有动画。

那么你认为我应该怎么做才能防止命令(等)的方法AheadTurnLeft满足特定条件之前返回?

4

4 回答 4

3

您需要为 Update() 方法创建某种状态机。例如

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

还是我错过了这个问题?

于 2009-03-16T22:41:03.717 回答
2

啊,两个字:合作多任务。借助Fibers(或您选择的协作式多任务构建块)的乐趣,您可以(在奠定了一些基础工作之后,例如在 C# 中启用纤维)执行以下操作:

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

然而,为了完成这项工作,您需要实现一个简单的循环调度程序。C# 并不是真正的我的船,但我要做的是保持简单并创建某种我称之为合作(或其他)的基类。此类将具有所有已创建纤维的静态列表以及静态方法 Create() 和 Yield()。Create() 将创建一个新的纤程(或其他),而 Yield() 将简单地安排下一个纤程执行(循环方式),在纤程世界中将包括对 SwitchToFiber() 的调用。它还将有一个名为 Start() (或其他)的虚拟方法,这是光纤开始运行的地方。

为了使它更花哨,您可以稍后保留单独的可运行或不可运行的纤程列表(即等待某些事情发生)。在这种情况下,您可以将 Ahead 中的循环简化为:

WaitFor(HasReachedDestination);

但我建议先了解合作多任务的概念。

最后,关于应该制作纤维的一些想法,通常你的主要更新循环是一根纤维,更新和绘制所有对象,然后调用 Yield()。所有游戏对象也将是纤维(如果您有很多游戏对象,这可能不可行)。对于您的游戏对象,您可以执行以下操作:

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);
}
于 2009-03-17T23:15:06.023 回答
2

我同意 Pop Catalin:最好不要阻止这些命令功能。我认为你可以通过多考虑设计来改进你的游戏。让我为您提供一些关于如何改进设计的想法。

首先,听起来您所描述的问题是您想以特定顺序向游戏组件发送大量移动命令,并让它以特定顺序执行这些命令。正如您所注意到的,计算机执行计算(速度或旋转)所花费的时间与组件实际执行动作(移动或旋转)所花费的时间是不同的。

计算期间阻塞的问题(Ahead、TurnLeft 等)是调用该函数的更新循环无法更新任何其他组件。如果只需要担心一个组件,这可能会正常工作,但在大多数游戏中通常情况并非如此。

现在是好的部分:我们如何解决这个问题?我认为 erikkallen 的想法是正确的,但我会更进一步。听起来游戏组件是某种会四处移动的实体,那么为什么不给它一个动作队列呢?一个简单的实现是让你的调用函数调用如下:

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

您的 queueAction 函数可能如下所示:

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

在更新函数的顶部,您可以添加:

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

您需要在 Update 函数的末尾添加一行,例如:

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

现在,我绝对不会称其为最佳解决方案,但实现它并不需要太多代码。当然,如果您需要同时移动和旋转,则必须对其进行一些调整。当您添加不同类型的操作时,它也会变得更加混乱。

对于更通用的解决方案,我会考虑制作一个基本 Action 类,并从中派生特定的 action 类。然后您可以将动作推送到队列中,并且您的 Update 函数可以调用动作的 Update 函数,这将完成游戏组件 Update 函数现在正在执行的两个部分的工作。

这些只是一些需要思考的想法,我希望这里的一些东西能让你开始。

我想提到的最后一件事是,我没有看到您使用传递给 Update 的 gameTime 变量。您的组件移动和旋转的量可能需要成为自上次调用 Update 以来经过的时间的函数。这意味着 Update 函数将根据经过的时间量移动和旋转您的游戏组件,而不仅仅是调用 Update 函数的次数。我不太擅长解释它,这取决于你希望你的游戏如何运作。这里有几个来自 Shawn Hargreaves(XNA 专家)的不同帖子。此外,还有一个 XNA 论坛帖子讨论了我试图提出的观点。

于 2009-03-26T22:02:26.257 回答
1

尽管我发现您的设计有些奇怪,但完成您想要的最佳方法是使用EventWaitHandle并从另一个线程发出信号。

假设您的班级有一个等待句柄的实例

您可以在您的方法中调用 waithadle.WaitOne(),并在满足条件时使用 waithandle.Set() 从另一个线程发出偶数信号,此时您的方法将从等待中恢复。

于 2009-03-16T22:25:53.910 回答