我希望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
之后才返回,因此这些方法几乎会同时运行,而这些方法只会同时执行所有动画。
那么您认为我应该怎么做才能防止命令(Ahead
,TurnLeft
等)的方法在满足某个条件之前返回?
答案 0 :(得分:3)
您需要为Update()方法创建某种状态机。 e.g。
public override void Update() {
if (movingRobot) {
OnlyUpdateRobotPosition();
}
else {
DoStuffPerhapsIncludingStartingRobotMove();
}
}
或者我错过了这个问题?
答案 1 :(得分:2)
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);
}
答案 2 :(得分: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类,并从中派生特定的动作类。然后你可以将动作推送到队列,你的Update函数可以调用动作的Update函数,这将完成游戏组件Update功能的两个部分正在进行的工作。
这些只是一些想法,我希望这里有一些东西可以让你开始。
我想提到的最后一件事是我没有看到你使用传递给Update的gameTime变量。组件移动和旋转的数量可能需要是自上次调用Update以来经过的时间的函数。这意味着Update函数将根据已经过的时间量移动和旋转游戏组件,而不仅仅是调用Update函数的次数。我不太擅长解释它,这取决于你喜欢你的游戏如何运作。以下是来自Shawn Hargreaves(XNA专家)的couple不同posts。此外,XNA论坛post讨论了我试图提出的观点。
答案 3 :(得分:1)
虽然我发现你的设计有些奇怪,但是要达到你想要的最好方法是使用EventWaitHandle并从另一个线程发出信号。
假设您的班级有等待句柄的实例
你可以在你的方法中调用waithadle.WaitOne(),并在满足条件时使用waithandle.Set()从另一个线程发出信号,此时你的方法将从等待恢复。