C# 开发游戏-如何执行需要多个游戏循环的事情?

C# 开发游戏-如何执行需要多个游戏循环的事情?,c#,language-agnostic,events,loops,C#,Language Agnostic,Events,Loops,我使用的是C#,但这适用于用任何语言开发游戏 大多数游戏都使用“游戏循环”,看起来像这样: while (game is running) { UpdateEnvironment(); DrawEnvironment(); } class AI { StateEnum State; //Idle, Moving, Attacking, Dying, etc. PointF Location; PointF Velocity; PointF Destination

我使用的是C#,但这适用于用任何语言开发游戏

大多数游戏都使用“游戏循环”,看起来像这样:

while (game is running)
{
  UpdateEnvironment();
  DrawEnvironment();
}
class AI
{
   StateEnum State; //Idle, Moving, Attacking, Dying, etc.
   PointF Location;
   PointF Velocity;
   PointF Destination;
我很难理解,需要不止一个游戏循环的事情是如何符合这个等式的。例如,使一个能量球从一个瓷砖漂浮到另一个瓷砖。或者让玩家移动一个瓷砖(不是从一个瓷砖跳到另一个瓷砖,而是为其设置动画)

我想到的最好的办法是计算自上次循环以来经过的时间,并将其传递给对象/方法,以便它能够完成它的任务。但这使得我们很难做以下事情:

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.

在哪里可以找到有关“执行需要多个游戏循环才能完成的事件”的更多信息?

一种方法是存储完整的挂起操作,然后让游戏循环只执行一点点。从动作中引用的游戏对象知道它们所处的状态,因此下一个要执行的位是已知的。虽然还有一些事情要做,但该操作会被添加回待在下一个循环中执行的挂起操作队列中,并且当该操作完成时,它不再被添加回队列中

因此,在您的
MoveTo
示例中,要存储的操作是移动到
10,20
,并且每次在游戏循环中,
AI
都会向它移动一点。您的
shot
示例最好描述为朝某个方向移动的子弹,然后无论它击中什么,都会决定动作是否继续


我没有做过游戏开发,所以我不知道在这个领域是否是这样做的,但这是我在基于事件的系统中做类似事情的方式。

事实上,你所说的使用事件时间是相当准确的。如果你熟悉C#,并且还没有这样做,我强烈建议你去看看XNA,或者如果你愿意花点钱,看看“游戏创造者”的Dark 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;
    }
}
//通用游戏类
公共类MysweetName:游戏
{
玩家_Player=新玩家(此);
列表_项目符号=新列表();
公共列表所有项目符号()
{
获取{return\u bullets;}
}
公共无效更新()
{
//显然,您需要一些键盘/鼠标类来确定是否发生了单击
如果(左鼠标按钮单击)
{
_player.Shoot();
}
//这将假设您有一个对象集合或数组来保存当前“活动”的所有项目符号,就像上面的“项目符号”集合一样
//这也是假设elapseGameTime是一些内置的游戏时间管理,比如XNA
foreach(子弹blt in_子弹)
{
blt.更新(elapsedGameTime);
}
}
}
//通用玩家类
公共类玩家()
{
向量2_位置=新向量2(0,0);
国际单位弹药=50;
MySweetGame_游戏;
公共玩家(MySweetGame游戏)
{
_游戏=游戏;
}
公开射击()
{
如果(_>0){
_游戏。所有子弹。添加(新子弹(50,_位置));
_弹药--;
}
}
}
//通用项目符号类
公共类项目符号()
{
浮点数_metersPerSecond=0;
向量2_位置=新向量2(0,0);
公共项目符号(浮动米秒,矢量3位置)
{
_metersPerSecond=metersPerSecond;
_位置=位置;
}
//这是你要的肉
//我们知道子弹的速度,基于metersPerSecond——我们在实例化这个对象时设置的
//我们还知道自上次调用update以来的elapsedGameTime
//因此,如果只经过了.25秒-我们只移动了(50*.25)-然后更新我们的位置向量
公共无效更新(浮动elapsedGameTime)
{
行驶距离=米/秒*elapsedGameTime;
_位置x+=行驶距离;
}
}

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

  • 程序1正在运行
  • 程序1被中断
  • 程序1的状态(CPU寄存器等的内容)由内核保存
  • 程序2的状态由内核加载
  • 程序2恢复
这种“中断/保存/恢复/恢复”方法是一种“最坏情况”选项,适用于很难分解为多个部分的任务。在某一时刻(可能基于任务运行的时间),保存任务所需的所有变量,并停止运行代码。稍后,您可以恢复状态并恢复代码


然而,通常可以通过某种方式设计您的系统,以减少求助于这种方式的需要。例如,设计动画以便一次只能处理一帧。

您可能不会使用事件;相反,MoveTo或Shot应该被视为状态的变化。您的AI对象需要一个由以下变量组成的状态:

while (game is running)
{
  UpdateEnvironment();
  DrawEnvironment();
}
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次)从游戏循环中调用,因此在e