65.9K
CodeProject 正在变化。 阅读更多。
Home

游戏编程 - 一

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (36投票s)

2008年5月8日

CPOL

5分钟阅读

viewsIcon

87351

downloadIcon

1495

介绍用于创建简单游戏的方法。

引言

这些教程的初步目的是向您展示如何从零开始创建一个简单的游戏,而无需借助 XNA 或 DirectX 等高级 API,这些 API 会为您自动化一半的过程。我们将仅使用 Windows Form 和 GDI+ 进行一些基本的绘图,并为了方便起见,使用 Form 的一些事件。

下一篇 »

背景

游戏循环和逻辑流程会存在一些奇怪之处;这是因为 Windows Forms 是基于事件的,而总体而言,游戏通常不是。我将指出一些“常规”代码与必须用于 Windows Form 的代码之间可能存在的差异。

步骤 1:设置游戏

基本循环

由于游戏通常不是事件驱动的,因此我们使用一个循环并在该循环中执行所有逻辑和绘图。因此,在每个循环中,我们将执行游戏逻辑、检查输入,并将图形绘制到屏幕上。看看下面的代码。您能发现任何问题吗?

bool runGame = true;
while(runGame)
{
  GetInput();
  PerformLogic();
  DrawGraphics();
}

如果您注意到游戏将以尽可能快的速度运行,那么恭喜您。使用此循环,游戏将以尽可能快的速度运行,速度可能从 1fps 到 1000fps 以上不等。随着敌人或对象添加到游戏中,游戏的速度可能会有所不同,玩家可能根本来不及反应。

一切关乎时机

我们需要控制游戏运行的速度;为此,我们使用一个计时器。计时器将设置一个标志,以便我们知道何时在主循环中运行逻辑;这样,游戏将以更易于管理的速度运行。

Timer MainTimer;
//The timer’s interval is in milliseconds, so we need to work out
//how long each interval should be
MainTimer.Interaval = 1000/60;
bool runGame = true;
volatile bool doStuff = false;
Main()
{
  while(runGame)
  {
    if(doStuff)
    {
      GetInput();
      PerformLogic();
      DrawGraphics();
      doStuff = false;
    }
  }
}

Timer()
{
  doStuff = true;
}

我们可以做得更好!

尽管这会将游戏速度限制在可接受的水平,但仍有更多我们可以做的事情。将图形绘制到屏幕通常是游戏中速度最慢的部分,因此如果场景渲染花费的时间太长,它将导致逻辑运行缓慢,整个游戏都会减慢。为了避免此问题,我们需要将图形绘制与其余逻辑分开,然后我们需要确保逻辑优先于场景渲染。这意味着如果逻辑落后,我们不应该在当前循环中更新图形,因此我们丢弃该帧。

这是我们将用于管理我们游戏的设置

Timer MainTimer;
//The timer’s interval is in milliseconds, so we need to work out
//how long each interval should be
MainTimer.Interaval = 1000/60;
bool runGame = true;
volatile uint speedCounter = 0;
Main()
{
  while(runGame)
  {
    if(speedCounter >0)
    {
      GetInput();
      PerformLogic();
      speedCounter--;
      if(speedCounter == 0)
        DrawGraphics();
    }
  }
}

Timer()
{
  speedCounter++;
}

在此循环中,只有当计数器大于零时,逻辑才会运行。运行完所有逻辑后,我们将计数器减一,如果计数器回到零,我们将更新场景。如果整个循环花费的时间超过一帧(计时器的间隔),那么当我们减少计数器时;它仍然会大于零,因此我们丢弃该帧,看看是否可以争取一些时间。

窗体的更改

如果在 Windows Form 上使用 `while` 循环,Windows 将永远不会更新屏幕,应用程序将挂起。因此,我们将把逻辑放在计时器的回调函数中,至少目前是这样 - 稍后,我们将把它放到自己的线程中。除此之外,一切都将保持不变。最终的代码看起来会像这样

bool drawGraphics = false
void TimerElapsed()
{
  //Increase the counter
  speedCounter++;

  GetInput();
  PerformLogic();
  speedCounter--;

  //If the counter is 0, then the logic is not running slow
  //we can therefore get on with drawing the graphics
  if(speedCounter == 0)
    drawGraphics = true;
}

我们还需要重写 `OnPaint` 和 `OnPaintBackground` 方法,以便 Windows 不会自己进行任何绘制,我们可以自己处理。请记住,我们不会调用 `base.OnPaint` 或 `base.OnPaintBackground`。

protected override void OnPaint(PaintEventArgs e)
{
   //We do our drawing here
}

protected override void OnPaintBackground(PaintEventArgs e)
{
  //Do nothing
  //base.OnPaintBackground(e);
}

GDI+

由于我们将使用它来绘制图形,我认为最好回顾一下如何使用它。要开始绘图,我们需要一些东西

  1. 一个图形对象(它将负责绘图)。
  2. 一个用于绘图的表面。
  3. 一支画笔或一支笔 - 真的。

如果我们重写窗体的 `OnPaint` 方法,我们可以使用 `PaintEventArgs` 中的 `Graphics` 对象,它已经将窗体作为表面。然后,您可以使用 `Graphics` 对象中的方法在屏幕上绘图。

override void OnPaint(PaintEventArgs e)
{
  //We can draw with e.Graphics

  //Brushes are used to fill areas
  Brush myBrush = new SolidBrush(Color.Red);
  e.Graphics.FillRectangle(myBrush,0,0,100,100);
  
  //Pens are used to draw lines
  Pen myPen = new Pen(Color.Green);
  e.Graphics.DrawLine(myPen, 50, 250, 65, 15);
}

演示

演示包含我们一直在谈论的游戏循环,以及一些绘图代码,以便我们可以看到屏幕上的内容。

什么?家庭作业!

在我撰写下一篇文章(或者在你阅读它之前),为什么不尝试编辑演示,让圆形在屏幕上移动?哦,拜托,这并不难,我知道如果你尝试,你就能做到。

即将推出

好吧,我今天就到这里。但好消息是,既然我们有了基本的游戏代码,我们就可以开始对它进行操作了。下一篇文章将介绍

  • 双缓冲
  • 获取输入
  • 设置 FPS 计数器
  • 绘制和动画精灵

您想要什么?

您可以随时发表您希望在这些文章中看到的内容。我在这里提供帮助,如果您知道如何做您所要求的,我会尝试包含它。这些文章将涉及 2D 游戏的世界,并向上延伸到 3D 领域 - 尽管我们可能需要为此切换到 C++ 和 OpenGL。我一直不太喜欢 DirectX,也从未使用 XNA 进行 3D。请记住,我不是要给您代码来制作游戏,而是向您展示如何构建可用于制作游戏的 कोड - 如果您跟上,语言就不那么重要了。

历史

  • 2008 年 5 月 8 日 - 文章提交。
© . All rights reserved.