2009-10-18 92 views
3

我正在使用C#,但这适用于开发任何语言的游戏。开发游戏 - 如何执行多个游戏循环?

大多数游戏使用一个“游戏循环”,这将是这个样子:

while (game is running) 
{ 
    UpdateEnvironment(); 
    DrawEnvironment(); 
} 

我竭力要了解的东西这将需要一个以上的游戏循环配合入方程。例如,使一个能量球从一个瓦片漂浮到另一个瓦片。或者让一个玩家移动一个图块(不是从一个图块跳转到另一个图块,而是对其进行动画处理)。

我想出了正在自上次循环所经过的时间,并将它传递给对象/方法,因此可以做它的事的最好的事情。但是,这使得它很难做的事情,如:

AI.MoveTo(10, 20); // Pathfind, then walk the path to this tile. 
Player.Shoot(); // Shoot a bullet, and detect collisions and update along the way. 

我在哪里可以找到“执行其需要超过一个游戏循环完成事件”的更多信息?

+0

请参阅http://gafferongames.com/game-physics/fix-your-timestep/ – 2009-10-18 21:09:18

回答

1

一种方式来做到这一点是存储完整的挂起操作,然后让游戏循环只执行一点点。从动作引用的游戏对象知道它们所处的状态,所以下一步要执行的操作是已知的。虽然还有一些工作要做,但是动作会被添加到下一个循环中要执行的待处理动作的队列中,并且当动作完成时,它不会再被添加回去。

因此,在你MoveTo例如,动作存储是整个游戏循环每一次转会10, 20,并且在AI移向它一点点。你的例子可能更好地描述为子弹在某个方向上行驶,然后无论它命中什么,都会决定行动是否继续。

我没有做游戏开发,所以我不知道这是否是它是如何在这一领域做了,但是这是我会做这样的事情在一个基于事件的系统的方式。

1

其实,你说的关于使用经过是相当准确的。如果你熟悉C#中,并没有这样做,我会强烈建议你看看无论是XNA或者,如果你愿意通过“TheGameCreators”花一点钱,黑暗GDK .NET。

无论如何,这个想法是,对于每个游戏循环,您使用自上次更新以来的经过时间来更新任何“实时”对象。 “生活”对象是仍然被认为是主动并需要更新的任何东西(例如,敌人,玩家,中途飞行中的子弹,还在爆炸的爆炸等)。根据经过的时间,碰撞,火灾等情况,确定每个物体应具有的下一个状态,位置,健康状况等,然后执行下一个状态。最后,你再为这些对象调用绘制过程,并将它们渲染成新的状态。

对于一些这样的球员拍摄,这里是你可以做的。请注意,这比任何东西都更伪代码。希望它给你一个想法。

//Generic Game Class 
public class MySweetGame : Game 
{ 
    Player _player = new Player(this); 
    List<Bullet> _bullets = new List<Bullet>(); 
    public List<Bullet> AllBullets() 
    { 
     get { return _bullets; } 
    } 


    public void Update() 
    { 
     //You would obviously need some keyboard/mouse class to determine if a click took place 
     if(leftMouseButtonClicked) 
     { 
      _player.Shoot(); 
     } 

     //This would be assuming you have an object collection or array to hold all the bullets currently 'live' like the above '_bullets' collection 
     //This is also assuming elapseGameTime is some built in game time management, like in XNA 
     foreach(Bullet blt in _bullets) 
     { 
      blt.Update(elapsedGameTime); 
     } 
    } 
} 

//Generic Player Class 
public class Player() 
{ 
    Vector2 _position = new Vector2(0,0); 
    int _ammunition = 50; 
    MySweetGame _game; 

    public Player(MySweetGame game) 
    { 
     _game = game; 
    } 

    public void Shoot() 
    { 
     if(_ammunition > 0){ 
      _game.AllBullets.Add(new Bullet(50, _position)); 
      _ammunition--; 
     } 
    } 
} 

//Generic Bullet Class 
public class Bullet() 
{ 
    float _metersPerSecond = 0; 
    Vector2 _position = new Vector2(0, 0); 

    public Bullet(float metersPerSecond, Vector3 position) 
    { 
     _metersPerSecond = metersPerSecond; 
     _position = position; 
    } 

    //Here is the meat you wanted 
    //We know the speed of the bullet, based on metersPerSecond - which we set when we instantiated this object 
    //We also know the elapsedGameTime since we last called update 
    //So if only .25 seconds have passed - we only moved (50 * .25) - and then update our position vector 
    public void Update(float elapsedGameTime) 
    { 
     distanceTraveled = metersPerSecond * elapsedGameTime; 
     _position.x += distanceTraveled; 
    } 
} 
-2

考虑操作系统如何允许多个程序在一个处理器上运行:

  • 程序1运行
  • 程序1被中断
  • 方案1的状态(CPU寄存器内容例如)由内核保存
  • 程序2的状态由内核加载
  • 程序2 re sumes

这种“中断/保存/恢复/恢复”方法对于真正难以分解成部分的任务是一种“最坏情况”选项。在某个时候(可能根据任务运行了多长时间),您可以节省所有任务需要的变量,并停止运行代码。稍后,您可以恢复状态并恢复代码。

然而,通常可以设计你的系统的方式,减少需要采取这样的事情。例如,设计动画可以一次处理一帧。

+0

这听起来像一个__horrible__想法。先发制人的多任务对于像每帧增加动画这样的简单事情来说太复杂了(例如) – 2009-10-18 08:52:53

+0

我只是通过解释的方式来使用它。对于像路径查找这样的事情,如果它发生的时间太长,你可能想要中断数字处理,这是我猜想的一种合作良率。 – Artelius 2009-10-18 08:59:58

1

你可能不会使用事件;相反,MoveTo或Shoot应该被认为是状态的变化。你的AI对象将需要包括变量,像这样的状态:

class AI 
{ 
    StateEnum State; //Idle, Moving, Attacking, Dying, etc. 
    PointF Location; 
    PointF Velocity; 
    PointF Destination; 

在你通过MoveTo方法,你就需要设置对象的状态 - 喜欢的事:

void MoveTo(x, y) 
    { 
     Destination = new PointF(x, y); 
     Velocity = new PointF(0.5, 0); 
     State = StateEnum.Moving; 
    }

然后在其更新方法,你会更新位置。

void Update() 
    { 
     switch (State) 
     { 
     case StateEnum.Moving: 
      Location.Offset(Velocity); //advance 0.5 pixels to the right 
      break; 
     default: 
      break; 
     } 
    } 
}

该方法将从游戏循环基于一些计时器(例如每秒60只蜱)被调用,因此,实际上,该对象被移动每秒30个像素。如果它具有动画帧,只需用滴答倒数并根据需要更改帧。对于寻路,要从拼块移动到拼块,可以更新每个拼块的速度,以便物体沿所需方向移动。

1

除了WesleyJohnson和Gannon的解决方案外,您还可以使用基于任务的方法。 WesleyJohnson's和Gannon的解决方案的复杂性较低,这是一件好事,尤其是当您的游戏演员的行为是静态定义的时候。就像一个简单的射击游戏。但是当你想要通过脚本动态定义行为,或者当你的演员有复杂的行为时,你可能想要外部化行为管理。 因为否则你的演员的更新功能必须有一个复杂的状态管理。

一个常见的方法是创建一个名为Task(或Process或Job)的基类 以及特定的较长时间运行的任务子类Job。例如,你可以有一个MoveActorTask,PlayAnimationTask等。使用结果代码和标记是否已完成,还可以按照一次执行一个任务的方式链接任务,等待前者完成后使用composite tasks

以下是我们使用的,稍微编辑以便更好读,剥去可能混淆其他一些高级选项:

class Task 
{ 
public: 

    /** 
    * Constructor. 
    * 
    * @param isDiscardable Set this true, if the Task's goal can be reached in a single step. 
    *   For instance if a Task is supposed to slowly close a window by fading 
    *   its alpha to 0, then it is discardable, and Task#discard will just finish 
    *   the process by closing the window. 
    * 
    * @param destroyWhenDone Set this to true, when the TaskScheduler shall delete the 
    *   Task, after execution is finished. This should usually be the case, but 
    *   sometimes it is sensible to pool a number of Jobs for reuse. 
    */ 
    Task(bool isDiscardable, 
     bool destroyWhenDone); 

    virtual ~Task(); 

    /** 
    * This is the function in which the Task is supposed to do whatever it is supposed to do. 
    * This function is called by the TaskScheduler at most once per frame. The frequency depends 
    * on the Job's priority given with TaskScheduler#addTask. 
    * @param time the time source time, since the last call of this function. 
    * @return true, when the Task is done, false else. If false is returned, the Task will be 
    * rescheduled for another execution. 
    */ 
    virtual bool execute(Time time) = 0; 

    virtual TimeSource::TimeSourceType getTimeSource() const = 0; 

    /// Returns whether the Task can be removed from the queue by the scheduler, 
    bool isDiscardable() const; 

    /// Returns true, if the Task shall be deleted, if the Job is finished. Returns false else. 
    bool destroyWhenDone() const; 

    /// Finish whatever the Task is doing. It won't get a chance to continue. 
    /// Overloaded functions must *not* call this implementation. 
    virtual void discard(); 

protected: 
    bool mIsDiscardable; 
    bool mDestroyWhenDone; 
}; 

的任务是通过的TaskScheduler管理。 TaskScheduler的每个帧都调用任务的所有任务的execute函数(Round robin),或者您可以有不同的调度策略。