游戏开发中心----游戏实验室

游戏开发日志,教程,学习过程,学习经历
http://www.3vgame.com

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

开头的几句废话 RPG游戏的编程(概念) RPG游戏的编程(编程1)
RPG游戏通用开发工具 “盗亦有道” 返回上页 开头的几句废话因为我从来没有过机会,能够真正参与一个商业游戏的制作,所以我这些所谓的经验,完全来自于我自己平时对一些游戏的观察,和自己的一些尝试。对于专业人士来讲,也许无异于胡言乱语。自己有了一些所谓的经验,不好意思敝帚自珍,希望能对于也有志于游戏开发,又没有经验的朋友有一点帮助。以后我希望能够经常在这里和大家探讨各种游戏类型的编程经验,也恳请高手不吝赐教。RPG游戏的编程(概念)为什么先选择 RPG游戏来谈游戏编程的经验?因为我私下认为,在所有的游戏类型中,它是最容易编程的游戏了。他不象即时战略那样,涉及电脑的 AI 算法和多任务的处理;也不象DOOM类的游戏,要涉及复杂的三维图形算法,RPG所涉及的东西要少。一个成功的游戏,必须要有一个成功的引擎。“引擎”这个概念,就象它原本的含义一样,不管换了什么样的车身,它都能让车子跑动起来。同样,在游戏里,剧情的进行,形形色色的角色,场景的变换,都不是用编程语言一句一句的来控制的,而是制定好一种模式,一种框架,依靠数据文件来完成的。大家熟悉的《命令与征服》,《红色警报》,就是采用的同一个引擎来完成的。也就是说,一个成功的游戏引擎,可以适用于同一类型的游戏,你通过改动和美工,声音,剧情有关的数据文件,就可以构成一个全新的游戏了。
对于 RPG游戏来说,他的引擎要负责画面的显示,用户输入的接受,剧情的进展,
战斗的模式,以及每个角色,物品,武功数据的存取,和当前游戏进行状态的保存和恢复(就是玩家们常用的Load_Save大法)。
1、RPG游戏的画面:
  如果大家仔细观察一下 RPG游戏的画面,就会发现游戏的画面是由很多重复的单元所构成的。比如草地,也许你会看出来很多相同的块,很多树,建筑的组成部分,也都是一样的。也许好的美工可以掩饰掉这些重复的痕迹,但是这些已经给了我们很大的启发:RPG 游戏的画面是由一个一个小的单元(可以叫做“图块”)拼凑起来的。这样做的原因,一是为了节省图象资源所占用的空间,也给全屏幕的移动带来了方便。根据这个原则,可以想象到 RPG游戏引擎处理画面的办法:先由美工制作好每个单元的图块,包括每个场景的基本构成因素,每个角色的一系列动作,在游戏中即将出现的每一种物品,每种武功的特殊攻击效果,然后把它转化成游戏程序可以操作的文件格式(不少游戏的图块格式都是自己定义的,不同的游戏格式不同),合并到一个大文件里,并为它建立索引,以备以后为程序调用。你当然也可以不合并,如果你不觉得很多小文件是很麻烦的话。这样在以后需要时,程序就可以通过建立好的索引来查找到需要显示的图块,把它显示在应该显示的地方。现在 RPG的画面大致有两种形式:90度俯视视角和45度俯视视角,但是它们的显示原理相同,只不过45度视角要多出很多计算(好看的代价嘛)。
  在 RPG游戏里,场景的构成,是图块排列顺序的记录。首先制定一个场景构成文件的格式,在这个文件里记录构成场景所需要的图块的排列顺序,因为我们已经为每个图块建立了索引,所以只需要记录这些索引就可以了。一个场景的构成,是分成几层来完成的:地面,建筑和植物,家具摆设,和在场景中活动的人物或者物体(比如飘扬的旗帜),按照一定的顺序把它们依次显示到屏幕上,就形成了一个丰富多采的场景。我真不明白为什么现在一些讲游戏制作的文章,总是一遍又一遍,不厌其烦地重复如何在屏幕上画点的函数,其实这在游戏引擎的制作中,绝对是一个用得很少的函数。一般做法是:在内存中开辟一到两个屏幕缓存区,事先把即将显示的图象数据准备在缓存区内,然后一次性搬家:把它们传送到真正的屏幕缓冲区内。而且准备图象数据的时候,也不会逐点去画,而是一行一行的传送,来加快速度。
2、用户输入的接受
  似乎传统的 RPG都只接受键盘的输入,智冠的《神雕侠侣》在使用鼠标操作上做
了一些尝试,但是能够接受鼠标的输入,必然会成为一种趋势。当然,在WIN/NT+DIR
-ECTX 的支持下,这些都已经不成为问题。在RPG游戏里,如何用鼠标操作角色的移动,是一个头痛的问题,当玩家给角色指明了一个目的之后,引擎必须判断角色在从原来位置走到目的地的路上,有没有什么障碍,然后选择一条最佳的路线。判断路上的障碍,可以根据场景文件中的图块索引和图块的属性来判断,而寻求最佳路径,则需要把可能的路径分段组合起来,进行比较,选择最短的一条。不然,大侠连路都不会走,岂不是成了天大的笑话!
  接受用户输入的另一个方面,就是要又合理的图形化操作界面。哪个玩家也不愿意在翻了好几层菜单后,才找到自己最经常用的功能。为了游戏画面的精彩,WINDOWS的标准图形界面是没法用的了,只好自己在消息循环中判断鼠标,键盘的输入,做一个专门的图形用户界面。还是建议大家把界面所用到的资源诸如菜单内容等等写成资源文件,为了以后好修改嘛,不然修改时又要翻阅长长的原程序,想到就要晕倒了......
3、剧情的进展
  剧情的进展,是依靠引擎对剧本文件的解释来完成的。可以定义一种文件,记录在每个场景的什么位置,什么时间(如果你的RPG游戏有时间概念的话,现在 RPG游戏里的大侠似乎都是没日没夜急着赶路的,精神实在可嘉),有可能会有事件发生。然后制定另外一种文件,在里面列举在游戏里所有可能发生的事情:可能会具体到打开一个箱子(随便乱翻别人东西的强盗!!)发现什么东西等等等等。这样当角色在场景中走动时,当位置符合,就可以查找相应的事件,判断事件是否应该发生,和应该发生什么样的事件。
  有些事件是需要玩家输入确定的信息之后,才会发生的,比如和场景中人物的对话,这样的事件可以在接受到用户的输入以后,再进行判断;有些是根据角色的移动位置,来自动判断的,比如主角走到某个地方,无缘无故招来一场杀身之祸,这就需要在游戏的进行中,时时刻刻判断角色当前的位置和状态。在一些事件发生过后,要修改事件记录文件,以避免同一事件的重复。着RPG多线索,多结局的趋势,这部分的工作量可能会越来越大的。
4、战斗的模式
  不同的游戏战斗模式肯定很少相同,从目前比较流行的游戏来看,大致有这么几种:
  回合制轮番动作:即你打我一拳,我再回敬你一剑;
  回合制同时动作:你打我一拳的同时,我也砍了你一剑;
  即时战斗:不管我是否真的想砍你一剑,你总是不断地向我打拳;
  其中当然第一种模式最简单,只需要展示各种武功的攻击效果,根据每个角色的参数来判断一次攻击所造成的伤害,然后修改生命值就搞定了。第二种当然是换汤不换药,而第三种就要麻烦得多:敌人的攻击行为必须用一个由时钟控制的例程来支配,要为敌人专门分配一个线程,涉及到了多任务的概念。
  另外也有RPG游戏的战斗模式虽然是回合制轮番动作,但是同时引入了象 SLG类型的战斗模式,这就需要为每个角色定制一套思维模式(电脑AI),这已经属于另外一类游戏的范围,以后还要和大家详细讨论。 
RPG游戏的编程(编程一)
 -- 熟悉CDX类库

  在我们了解了 RPG游戏引擎的基本概念后,就可以开始自己动手来尝试编写一个比较完整的 RPG游戏引擎了。
★■在我们动手之前,先来准备一下我们需要用到的工具:
   1、Microsoft Visual C/C++ 5.0 编写这个引擎的主力--编译器 
   2、CDX 类库 一套 C++ 封装过的 DirectX 5.0 类库("开发工具"提供了下载)
   3、DirectX SDK 5.0 如果你还没有找到或者嫌微软的太大,到我这里来下载简化版。
   4、<金庸群侠传>游戏光盘 因为我们暂时还没有找到美工,只有先偷点了!:-)
   5、PhotoShop 4.0 用来做一些必要的图象处理。
   6、随便找点你自己听着顺耳的MIDI曲子和WAV文件来做实验。
   准备工作完成了!!让我们动手吧!
★■考虑到在网上的时间很宝贵,请直接下载打包的工程文件,只需要重新设置相关路
径,并请仔细阅读里面的程序注释。也可以把本页保存在自己的硬盘上慢慢阅读。
----请在这里直接下载----
演示程序的工程压缩文件  cdxgames.zip 86  kb
★■建立工程:Microsoft Visual C/C++ 5.0 中的一些设置 
  以下的设置在你下载到的工程里已经做好。但是考虑到有可能会出现绝对路径的错误(我没有实验过在其他路径能不能编译正常),如果出现错误,请按以下步骤重新设置。
  1、 建立一个新的工程文件。选择 File-> New -> Projects,选Win32Application, 输入工程项目名称。因为没有用到 MFC 的框架,所以不选用 MFC AppWizard (EXE)。
  2、 选择 Tools-> Options-> Directories->在 Include Files的目录中增加 DirectX SDK 5.0的头文件包含目录,CDX 类库的头文件包含目录。为了防止编译器错误地找到老版本的DIRECTX,请把新增加的路径调整到最上面。
  3、 选择 Tools-> Options-> Directories->在 Libary Files的目录中增加 DirectX SDK 5.0 的库文件包含目录,CDX 类库的库文件包含目录。为了防止编译器错误地找到老版本的DIRECTX,请把新增加的路径调整到最上面。
  4、在 Workspace 的 FileView 里面,增加连接所需要的库文件。
CDX.Lib      CDX 类库的库文件
CDXMStrm.lib    我自己扩充的用MIDI流构造的MIDI播放器的类库
comctl32.lib
Winmm.lib     Windows 播放MIDI必需的库文件
ddraw.lib
dinput.lib
dsound.lib
dxguid.lib    DirectX SDK 自己带的库文件)注意,一定要是 5.0
         的版本的库,不要错误地把 VC 里自己带的老版本的
         库加入。
  5、在 Workspace 的 FileView 里面,增加连接所需要的源程序文件。
★■程序所演示的内容 
  1、显示游戏的开始画面,并发出一个来自 WAV文件的声音,开始演奏一段来自一个MIDI文件的乐曲。当接受到用户的输入,或者过了一段时间后,音乐停止,开始画面淡出,进入游戏状态。
  2、进入游戏状态。(这次先显示一幅代表游戏进行的画面,游戏真正的进行态,可是下节课的内容了哦!一定要记得经常来我这里看看新课开始了没有哦!)当接受到用户的输入时,游戏结束......(如果哪个玩家买到这样的游戏,鼻子一定气歪了,嘿嘿。
~@)@~!!!!)
  3、显示游戏的结束画面,道理和开始画面相同。
★■程序结构的简单讲解(以下的代码只为讲解程序结构,不能直接编译,更准确的代码, 请参照源程序)
1、WinMain( )     //Windows 程序的主函数。一切的工作从这里开始。
 {
   initApp( );   //所有关于Windows窗口类,程序,游戏状态的初始化
   while(1)    //Windows 程序的主循环
  {
    if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
    //Windows程序的消息循环,在当前的例子里面,只接受ESC键的退出
    //消息。
    {
      if(!GetMessage(&msg, NULL, 0, 0 ))
       return msg.wParam;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
     else if(bActive) //这里才是我们游戏进行的主循环,在这里处理
             //游戏的输入,画面更新
    {
      UpdateGame();
     }
     else
      WaitMessage();
   }
 }
 注意:我们没有把游戏的主要循环放在Windows的消息循环中,而是放在另外
    的一个 循环里面。
2、InitApp( ) //所有关于Windows窗口类,程序,游戏状态的初始化
 {
   RegisterClass(&WndClass);//登记窗口类
   CreateWindowEx( );       //创建窗口
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   InitGame();       //在这里初始化屏幕,声音,MIDI,状态变量
   cdxObjs.Input.Create(hInst, hWnd);    //初始化游戏的输入接口
  }
3、InitGame( ) // 初始化游戏所需要的DirectX对象。
  /*1.初始化频率计数器 Performance Counter
    2.装载*.ini文件,初始化options选项
   3.如果Debug开关是打开的,则打开调试文件
   4.初始化屏幕,音乐,和声音对象
   5.设置程序状态为封面状态
   6.用当前时间设置一个随机数发生器
   7.reset 游戏*/
 {
   QueryPerformanceFrequency(
     (LARGE_INTEGER*)&timeVars.freqPerformance );
   LoadINIVars();
   SaveINIVars();
   if (options.DebugFlag == 1)
      debugVars.fdebug = fopen("debug.txt","a+");

   #ifdef _USES_DDRAW
    cdxObjs.Screen = new CDXScreen(hWnd, 640, 480, 8);
   #endif
   cdxObjs.Music = new CDXMusicStrm( );
   cdxObjs.Sound = new CDXSound;
   cdxObjs.Sound->Create(hWnd);

   ProgramState = PS_SPLASH;
   srand((unsigned)time(NULL));
   ResetGame();
  }
4、UpdateGame(void)  //我们游戏进行的主循环
  {
     switch(ProgramState)
     {
       case PS_SPLASH:
            SplashScreen();
            break;
       case PS_ACTIVE:
            Update();
            break;
       case PS_GAMEOVER:
            GameOver();
            break;
     }
  }

5、void SplashScreen(void)//显示游戏的初始画面
  {
     // 只显示一次并且确认 WaitTime 被清除.
     /* 因为这是在WinMain循环中执行的,用
        DisplayIt 来保证初始化屏幕只被显示了一次*/
     if (DisplayIt == FALSE)
     {
        DisplaySplashScreen();
        DisplayIt = TRUE;
        WaitTime = 0;
        // 强迫 lastTick 做新的设置
        GetTick();
        lastTick = timeVars.firstTick;
     }

     // 设置程序状态到激活
     GetTick();  // 将要设置 firstTick 和 freqAdjust
 
     diffTick = timeVars.firstTick - lastTick;
     lastTick = timeVars.firstTick;
 
     WaitTime += diffTick;   //由这次进来的 firstTick 和上次的
     //lastTick的差 diffTick决定两次进来
     //的时间差,来计算已经等待了多长时间
     cdxObjs.Input.Update(); //接受输入

     if (WaitTime > ((15000*timeVars.freqPerformance)/timeVars.freqAdjust)
       || cdxObjs.Input.Keys[DIK_SPACE]
         || cdxObjs.Input.Keys[DIK_RETURN]
           || cdxObjs.Input.Keys[DIK_LCONTROL]
             || cdxObjs.Input.MouseLB)
     { //如果超过等待时间或者用户有输入
        WaitTime = 0;
        DisplayIt = FALSE;
 
        CloseSplashScreen( );
        // 等待结束,接下来做什么?设置游戏的状态。
        ProgramState = PS_NEXTLEVEL;
     }
 }
★■如果有任何疑问,请Email我:lighter@nease.net 或者 llighter@usa.net

posted on 2006-04-10 23:44  3vgame  阅读(1135)  评论(0编辑  收藏  举报