51Testing软件测试论坛

标题: 游戏引擎的工作原理与应用 [打印本页]

作者: 51testing    时间: 2007-11-30 15:51
标题: 游戏引擎的工作原理与应用
做一个小巧而又好玩的游戏是每一个软件爱好者的梦想,当然我们不可能一下子做出像《仙剑》和《星际争霸》这样的游戏,因为这些游戏的开发需要非常专业的知识,也需要大量的人力及物力。目前主流游戏开发环境是Visual C++和Directx技术,开发要涉及到许多算法如:A*算法(寻找路径)等,主要技术还有Alpha光影以及人工智能技术。对于初学者来说,这些技术都不是在短时间内可以完成的。单就是Visual C++开发工具的入门也需要花很大一段时间学习,“聪明的程序员是学Delphi,真正的程序员是学习VC++的”,Delphi是一个好学易用的开发工具,用VC来开发商业游戏和用Delphi来开发,他们的制作原理都是大同小异的,我们可以用它来实现一个简单的游戏引擎,让您对游戏开发原理有一个感性认识。下面我将逐步介绍“潜艇大战”游戏的制作过程,等到您对开发过程熟悉了我们就可以用VC++和Directx技术来开发更好的游戏软件。下面是游戏的一张截图。有兴趣的读者可以发信到我的信箱索要程序或者讨论游戏制作方面的问题,我的e_mail:creativesoftfan@sina.com 主页:http://creativesoft.home.shangdu.net
                                   图1
一 游戏的工作原理
  在具体制作程序之前,我们先总的介绍一下游戏的工作原理,他是制作的总纲。懂得了原理,我们就不会对具体的编程迷惑不解了。对于一个游戏制作者来说,游戏可以分为代码部分(游戏引擎)和游戏资源两部分,代码部分其实是一个控制中枢,他是游戏的大脑,也是游戏的灵魂,它负责将游戏所需的各种资源如图像资源,声音资源等按要求调用,例如本程序中各种潜艇的运动,实际上是程序控制各种潜艇图像文件在指定区域移动。游戏资源包括图像资源,声音资源等,他是游戏的对外表现,像人的外表一样。例如在该游戏中各种潜艇的图片。当然,这些图片在编译时我已经将他们包涵在可执行文件中(即exe文件)。所以说,我们在编程前要准备一些游戏用到的图片,你可以从我做的游戏截图获得,这是游戏制作爱好者的一件法宝^-^。由于是向朋友们介绍游戏制作的基本知识,我暂时没有在引擎中加入处理声音的程序模块。

  二 游戏的具体制作
  1. 游戏的主要模块
  “潜艇大战”游戏主要分为运动模块,战斗模块,事件处理模块。运动模块主要负责我方潜艇的各种运动以及敌方各种潜艇和水母的运动。他像人们控制肢体运动的神经。战斗模块包括我方深水炸弹的攻击,敌方导弹的攻击(包括跟踪导弹的攻击),敌方中弹后的爆炸场面的显示。事件处理模块包括我方军舰获得一定的分数后游戏局数和游戏的难度的增加,例如,玩家在打过第二局后,程序启动跟踪导弹的处理程序,导弹开始跟踪军舰。其实在各种RPG(角色扮演游戏),ARPG(即时战斗角色扮演游戏)和SLG(即时战略游戏)中,运动模块,战斗模块,事件处理模块都起着极其重要的作用。
  2.编写程序

数据字典:
Image1:游戏背景图片
Image8:跟踪导弹图像
Image2:我方军舰图像
Image9:大潜艇图像
Image3:对方潜艇的图像
Image10:爆炸图像
Image4:导弹图像
Label1:用来显示游戏的分数
Image5:水母图像
Label2:用来显示船数
Image6:水雷图像
label3:用来显示游戏的轮数
Image7:深水炸弹图像


  <1>.应用程序的建立:运行Delphi,选择主菜单的File下的New Application选项,创建一个应用程序。此时会看到Form1窗体,选择主菜单的File下的New选项,在弹出的New Items对话框的New页面中选择Form,按OK按键。这时会出现Form2窗体,在该窗体中设计游戏版权等信息,如图2。

                           图2
<2>.主菜单的建立:在控件栏的Standard中按下MainMenu选项,在Form1中单击鼠标左键,这时会在Form1中会看到一个MainMenu控件,双击该控件,在Object Inspector 窗体中选择Caption选项,输入菜单的名称,依此建立如图3,图4所示的菜单选项。

图3

图4
  <3>.游戏资源的加入:因为程序中未用到声音文件,这里所说的游戏资源主要指的是图像文件。单击控件栏的Addtional选项,选择Image控件,在Form1上单击鼠标左键,在Form1上出现一个Image控件,双击该控件,在弹出的Picture Editor对话框中按下"Load..."按钮,在Load Picture对话框中选择游戏的背景图片,将Object Inspector 的Image1的AutoSize属性改为True,调整Form1的宽度和高度,使游戏背景图片占满整个窗体,如图5。

图5
然后按照上述方法加入各种潜艇和武器的图像。如图6。
图6
我们可以在以后编写的程序中控制这些图像文件。当然,游戏中用到的图象文件也可以直接存放在磁盘上,由我们编写的引擎来调用,在各种大型商业游戏中大多采用这种方法,因为该游戏用到的图像文件较少,所以我将它们包含在可执行文件中了。
  <4>.游戏参数的显示:游戏中要显示游戏轮数,游戏分数,玩家拥有的军舰数目等信息,这写信息的数值由游戏引擎计算和控制。信息的内容可以由控件栏Standard选项中的Label控件的Caption属性值来显示。添加该控件的方法同上。更改相应Label控件的Caption属性值即可即时显示相关信息。见图7。
                               图7  
<5>.运动模块的编程
      游戏中的物体应是运动的,对此我们需要一个连续控制物体运动的监测和控制器。在Delphi中,我们可以用Timer控件来实现。对于每一个图片,它都有Left和Top属性,在Timer控件的每一个时间间隔内根据引擎或用户按键来更改图片的Left和Top属性就可以调整图像在显示区域的位置,从而实现物体的运动。   

     (1)对方潜艇的控制:首先选择控件栏System页面下的Timer控件,在Form1上单击鼠标左键,建立一个Timer控件,在Object Inspector 窗体中将Timer控件的Interval的值改为10,双击Timer控件,在弹出的代码窗体中输入如下代码:

    if image3.Left<=2 then  //到图片最左边后,潜艇停下来并消失
        image3.Left:=430  //到图片最左边后,潜艇到最右边
    else
        image3.Left:=image3.Left-3;  //不到最左边,则潜艇向左移

    image3是对方潜艇的图像控件的名称,其它各种潜艇的运动部分代码同上。

    (2)我方军舰的控制:主要代码如下:

     if (chr(Key) in ['J','j']) and (image2.Left>10 )then
         image2.Left:=image2.Left-20;
     if  (chr(Key)in ['L','l'])and(image2.Left<420) then
         image2.Left:=image2.Left+20;

      其中image2是我方军舰图像控件的名称,chr(Key) in ['J','j']用于判断用户是否按下键盘的'J','j'按键,当用户按下'J','j'按键后当image2的位置不在窗体最左边时image2向左移动20个单位,chr(Key) in ['L','l']用于判断用户是否按下键盘的'L','l'按键,当用户按下'L','l'按键后当image2的位置不在窗体最右边时image2向右移动20个单位。

     (3)导弹的控制:由于导弹从水下向水面上方运动,只要改动相应图像的Top属性即可,代码如下:

     if image4.top>=50 then    //控制导弹不浮出水面
       begin
         image4.top:=image4.top-6;  //导弹徐徐上升
         image4.Visible:=True  //显示导弹
       end
     else
        image4.Visible:=False;  //隐藏导弹

     image4是导弹图像控件的名称,当导弹浮出水面时,导弹应消失,所以image4.top<50时,隐藏导弹。

     第二局开始后会出现跟踪导弹,这时只需在一定时间间隔内调整导弹位置,使导弹竖直方位和军舰竖直方 位一致即可。
    if image8.Visible=True then  //image8是跟踪导弹图像控件的名称
       image8.Left:=image2.Left; //导弹自动跟踪船体

     当然我们还可以设计一个调整导弹位置的程序模块,逐渐调整导弹位置,这样程序运行起来会更好看。

    (4)水母的控制:水母从水面向上运动的程序原理同上,代码如下:

     if image5.top>=50 then
       begin
         image5.top:=image5.top-10;
         image5.Visible:=True
       end
    else if image5.top<=48 then
       begin
         image5.top:=48;
         //控制水母向右运动
         if (image5.left>10)and (image5.Left<460) then
             image5.Left:=image5.Left+10;
       end;
     image5是水母图像控件的名称,当水母浮出水面时,水母会向左方或右方移动,因此当image5.top<=48,即水母已经浮出水面,我们可以更改其图像控件的Left属性来控制它向右运动。

    (5)水雷的控制:

    if image6.top>=50 then
     begin
       image6.top:=image6.top-6;
       image6.Visible:=True
     end
    else
       image6.Visible:=False;

     程序原理同上。

   (6)深水炸弹的控制:image7是深水炸弹图像控件的名称

      //当用户按下'K','k'按键后,显示深水炸弹
    if (Key in ['K','k']) and (image7.Visible=False) then
       begin
         image7.Visible:=True; //显示深水炸弹
         image7.Top:=56;    //显示深水炸弹的位置
         image7.Left:=image2.left+20;
       end;
      //由程序控制深水炸弹的运动
   if image7.top<=320 then
      image7.top:=image7.top+10;
   if image7.top>=320 then
      image7.Visible:=False;

  <6>.战斗模块的编程
      战斗模块可以概括为两大类,一是我方军舰发射的深水炸弹击中敌方潜艇或水母,另外是敌方潜艇发射的导弹或水雷击中军舰。如何判断深水炸弹或导弹是否击中目标呢?我们可以通过判断深水炸弹或导弹的位置是否在要攻击目标图像控件的矩型区域即可。
     击中的条件如下:
     要攻击目标图像控件的Left属性的数值<=深水炸弹或导弹的Left属性的数值<=要攻击目标图像控件的Left属性的数值+要攻击目标图像控件的Width属性的数值      
     并且
     要攻击目标图像控件的Top属性的数值<=深水炸弹或导弹的Top属性的数值<=要攻击目标图像控件的Top属性的数值+要攻击目标图像控件的Height属性的数值      
     (1)我方军舰发射的深水炸弹击中敌方潜艇或水母

       //判断敌方潜艇是否被深水炸弹击中
     if(image7.visible=True)and(90<=image7.Top)and(image7.Top<=120)and                                  (image7.Left>=image3.Left)and(image7.left<=(image3.left+image3.Width)) then
       begin
         image3.visible:=False;  //显示敌方潜艇被击中后的情景
         image10.Left:=image3.Left;  //显示敌方潜艇被击中后爆炸图像
         image10.Top:=image3.Top;  //使敌方潜艇的高度与爆炸体的高度一致
         image10.visible:=True;
         label1.Caption:=IntToStr(StrToInt(label1.Caption)+10);  //分数加十
         image3.Left:=435;  //敌方潜艇被击中后,敌方潜艇从最右边出现
         image7.visible:=False;  //第一枚深水炸弹爆炸掉了
       end;

    其中image3是对方潜艇的图像控件的名称,image7是深水炸弹图像控件的名称。其它潜艇和水母被深水炸弹击中的程序模块同上。

     (2)敌方潜艇发射的导弹或水雷击中军舰

        //判断船是否被导弹击中
     if(image4.visible=True)and(45<=image4.Top)and(image4.Top<=50)and                                   (image2.Left<=image4.Left)and(image4.left<=(image2.left+image2.Width)) then
       begin
         image2.visible:=False;  //显示船被击中后的情景
         image10.visible:=True;  //显示敌方潜艇被击中后爆炸图像
         image10.Left:=image2.Left; //确定爆炸图像显示的位置
         image10.Top:=image2.Top;
       end;

    其中image2是我方军舰图像控件的名称,image4是导弹图像控件的名称。判断船是否被水雷击中的程序只要将导弹图像换为水雷图像即可。

   <7>事件处理模块的编程
        游戏中的事件处理主要有:游戏的开始及结束的控制,游戏的分数记录,游戏局数和轮数的增加,游戏的难易程度的控制等。下面我将这些事件处理的编程技巧与方法逐一介绍给大家。

     (1)游戏的开始及结束的控制:
        游戏的开始一般要对游戏的各种参数进行初试化,即对游戏的各种参数赋予开始值,代码如下:

     timer1.Enabled:=True;  //开始控制船及各种潜艇的运动
     image2.Visible:=True;  //显示船体
     image7.visible:=False;  //隐藏深水炸弹,image7是深水炸弹图像控件的名称
     label2.Caption:=inttostr(2);  //船数置2,label2用于显示船数
     label1.Caption:='0';  //从0从新开始记分,label1用于显示分数
          //隐藏水母
     image5.Visible:=False;  //image5是水母图像控件的名称
     label3.Caption:='0';  //游戏从第一轮开始

     (2)游戏的分数记录:
        游戏分数记录只需更改相应label控件的Caption值即可,当我方发射的深水炸弹击中对方潜艇时调用以下程序即可。读者朋友可看战斗模块的编程(1)我方军舰发射的深水炸弹击中敌方潜艇或水母部分的程序。

         //label1用于显示分数
       label1.Caption:=IntToStr(StrToInt(label1.Caption)+10);//分数加十
     (3)游戏局数和轮数的增加:
        游戏局数和轮数是随着程序用户获得的分值的增加或运行游戏的时间来增加,在该程序中,我主要根据用户获得的分值来改变游戏的局数和轮数。代码如下:

     score:=StrToInt(label1.Caption);  //score是用户获得的分值
     if (score>=150)and(score<=170)and(image2.Visible=True) then  //第二局开始
       begin
         label3.Caption:='第二局开始';
         image8.Visible:=True;  //image8是跟踪导弹图像控件的名称
       end;
     if (score>=270)and(score<=290)and(image2.Visible=True) then  //第三局开始
       begin                     //出现水母
         image5.Visible:=True;  //image5是水母图像控件的名称   
         label3.Caption:='第三局开始';
      end;
     if (score>=380)and(score<=400)and(image2.Visible=True) then  //第四局开始
       begin
         image9.Left:=110;      //image9是大潜艇图像控件的名称
         image9.Visible:=True;  //出现大潜艇
         label1.Caption:='第四局开始';
      end;
     if score>=550   then             //下一轮第一局开始
      begin
        label1.Caption:='0';  //从0从新开始记分
          //隐藏水母
        image5.Visible:=False;
        image9.Visible:=False;  //隐藏大潜艇
         //label3用来显示游戏的轮数
        label3.Caption:=IntToStr(StrToInt(label3.Caption)+1);  //游戏轮数加一
        label2.Caption:=IntToStr(StrToInt(label2.Caption)+3);  //奖船三艘
      end;
    (4)游戏的难易程度的控制:
       游戏的难易程度的控制可以通过变动敌方潜艇的数量和敌方潜艇发射的导弹和水雷的数量和快慢来控制。在该程序中,随着游戏局数的增加会出现水母,大潜艇来增加游戏的难度。程序的实现见上面“游戏局数和轮数的增加”部分的代码。当然,也可以改动敌方潜艇的属性来增加游戏的难度。

    <8>游戏中的其它部分程序
       游戏的主菜单各选项代码可参考以上对应部分的代码,比如游戏重新开始,可以参考“事件处理模块的编程”的“游戏的开始及结束的控制”部分的程序,这里不在叙述。
       最后将游戏成绩存储在磁盘上,只需将用户输入的名称和用来显示游戏分数的label1的Caption属性值,用来显示游戏的轮数的label3的Caption属性值及游戏运行日期存储到文件中即可。
通过上述介绍,我想大家会对游戏的开发有一个完整的认识,其实用VC++或Delphi开发游戏本质上都是一样的。用VC++和Directx技术来开发游戏需要对Windows操作系统有更深入的了解,要用到大量的系统函数,数据结构和算法。但是只要您对设计原理理解了,用VC++和Directx技术来开发游戏也会容易许多。
作者: 板砖    时间: 2007-11-30 17:26
太长了......
作者: muyang327    时间: 2007-11-30 17:57
看得眼花,不过还是看完了,谢谢。




欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2