爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      游戏编程之六 游戏编程的特点

      首页 知识中心 软件开发 文章详情页

      游戏编程之六 游戏编程的特点

      2024-09-25 10:13:46 阅读次数:538

      人工智能,函数,编程,面向对象

      第七章 游戏编程的特点

      第一节 概述:

      电脑游戏在计算机发展使用中可以说扮演了一个极为有趣的角色,一方面不为很多人所赞同,认为是一种浪费;而另一方面电脑游戏却是推动计算机的各项技术迅速发展的最有力的力量之一。

      这一点,可以从3d类游戏对硬件无止境的需求,游戏迷对游戏图像的质量、游戏的交互性、人机界面的友好性等方面的需求体现出来(当然游戏迷对游戏的的构思、创意的要求也是苛刻且无止境的,但这一点只有靠您自己的想象力,我们是爱莫能助了)。

      从游戏近期的发展中,我们从3d游戏的发展,可以看到从Doom到现在的古墓丽影2、雷神之锤2,3d游戏的画面从生硬单调的多边形到今天柔和复杂几进乱真的场景、道具、怪物,敌人从只会疯狂向你冲来到今天会偷袭、会审时度势地采取合适的方式方法向你进攻;游戏无论从硬件支持还是编程技术方面都有突飞猛进的进展。在游戏发展的过程中,很多技术也随之发展起来了,例如各种图形加速卡的出现和发展,directx的出现,和各个成功游戏中采用的各种优化技术都推动了计算机技术的发展。

      游戏可以说是集合了每个时期计算机行业中最先进的硬件技术和最新的编程思想,比如近期的游戏都是采用了面向对象的编程思想的基于Windows的软件,大部分图象要求高的游戏都要求或支持图形加速卡。同时游戏编程中也有自己基本的方式方法、结构和理论,在这一章的学习中我们将讨论这些问题。
      在这一章中我们将讨论下面几个问题:

      程序入口 即是游戏获取外部操作的讯息,得到下次刷新所需的新参数的手段。如同一般的SDK Windows应用程序一样,程序的入口为WINMAIN()。

      游戏初始化 包括创建标准的WINDOWS程序所需的初始化程序以及游戏内部的初始化程序,例如游戏系统初始化、游戏图形的装入、游戏声音的装入等。

      游戏内部循环: 游戏的循环入口是WINDOWS消息循环内部的一个函数调用,游戏内部循环包括刷新游戏单位、画游戏单位两部分。

      刷新游戏单位: 用于每一帧刷新游戏单位的状态,例如改变游戏单位的状态、改变游戏单位的位置、获取外部信息等。

      画游戏单位: 用于每一帧往屏幕上画游戏单位的图象,并进行特殊处理以提高速度。

      计算机人工智能: 主要用于受计算机处理的游戏单位的行为控制算法,程序部分位于刷新计算机游戏单位部分中。

      游戏内存管理:  这一部分对于优质高效的游戏软件是十分重要的,内存管理不当会导致游戏性能的降低,甚至引起死机。

      游戏交互设计: 交互设计是游戏可玩性的关键,友好的交互界面和交互方式可以使游戏增色不少。

      游戏图象底层设计: 游戏软件的主要处理时间花在处理图象和画图象上,所以游戏图象底层的设计对于游戏的最终效果是十分重要的。

      游戏多媒体设计: 主要包括图形界面设计、游戏音乐音效设计、游戏动画设计、游戏影象设计的几个方面,更广泛的说还包括游戏所有运行过程的功能设计。

      第二节 程序入口
      这个标题看起来似乎很难理解,它的意思就是当游戏被启动时,计算机从什么地方开始运行程序的。在Windows的应用程序上,Winmain()函数一般就是程序入口。游戏开始后,就调用Winmain()函数,然后再按语句的顺序或所接受到的消息调用相应的函数。

      从第三章Windows编程基础中我们了解到Winmain()函数的的结构、运行过程,现在我们就游戏编程的角度来讨论Winmain()函数的编制。

      int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
                          int nCmdShow )
      {
          MSG msg;


          while( lpCmdLine[0] == '-' || lpCmdLine[0] == '/')
          {
              lpCmdLine++;


              switch (*lpCmdLine++)
              {
              case 'e':
                  bUseEmulation = TRUE;
                  break;
              case 'w':
                  bFullscreen = FALSE;
                  break;
              case 'f':
                  bFullscreen = TRUE;
                  break;
              case '1':
                  CmdLineBufferCount = 1;
                  break;
              case '2':
              case 'd':
                  CmdLineBufferCount = 2;
                  break;
              case '3':
                  CmdLineBufferCount = 3;
                  break;
              case 's':
                  bStretch = TRUE;
                  break;
              case 'S':
                  bWantSound = FALSE;
                  break;
              case 'x':
                  bStress= TRUE;
                  break;
              case '?':
                  bHelp= TRUE;
                  bFullscreen= FALSE;  // give help in windowed mode
                  break;
              }


              while( IS_SPACE(*lpCmdLine) )
              {
                  lpCmdLine++;
              }
          }


          GameMode.cx = getint(&lpCmdLine, 640);
          GameMode.cy = getint(&lpCmdLine, 480);
          GameBPP = getint(&lpCmdLine, 8);


          /*
           * create window and other windows things
           */
          if( !initApplication(hInstance, nCmdShow) )
          {
              return FALSE;
          }


          /*
           * Give user help if asked for
           *
           * This is ugly for now because the whole screen is black
           * except for the popup box.  This could be fixed with some
           * work to get the window size right when it was created instead
           * of delaying that work. see ddraw.c
           *
           */


          if( bHelp )
          {
              MessageBox(hWndMain,
                         "F12 - Quit\n"
                         "NUMPAD 2  - crouch\n"
                         "NUMPAD 3  - apple\n"
                         "NUMPAD 4  - right\n"
                         "NUMPAD 5  - stop\n"
                         "NUMPAD 6  - left\n"
                         "NUMPAD 7  - jump\n"
                         "\n"
                         "Command line parameters\n"
                         "\n"
                          "-e   Use emulator\n"
                          "-S   No Sound\n"
                          "-1   No backbuffer\n"
                          "-2   One backbuffer\n"
                          "-4   Three backbuffers\n"
                          "-s   Use stretch\n"
                          "-x   Demo or stress mode\n",
                         OUR_APP_NAME, MB_OK );
          }


          /*
           * initialize for game play
           */


          if( !InitGame() )
          {
              return FALSE;
          }
          dwFrameTime = timeGetTime();
         
          while( 1 )
          {
              if (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE))
              {
                  if (!GetMessage( &msg, NULL, 0, 0))
                  { 
                      break;
                  }
                  TranslateMessage(&msg); 
                  DispatchMessage(&msg);
              }
              else if (!bPaused && (bIsActive || !bFullscreen))
              {
                  ProcessFox(lastInput);
                  lastInput=0;
              }
              else
              {
                  WaitMessage();
              }
          }


          if (AveFrameRateCount)
          {
              AveFrameRate = AveFrameRate / AveFrameRateCount;
              Msg("Average frame rate: %d", AveFrameRate);
          }


          return msg.wParam;


      } /* WinMain */

      我们知道在消息循环之中只有一个消息----WM_QUIT可以结束这个循环,退出WINDOWS。所以我们要在消息循环之前完成所有的工作即所有的初始化。关于初始化这个概念在下一节我们将详细讨论。在我们提供的例程(在光盘的sample目录中)中的foxbear.c里的WMain()中我们可以看到在消息循环之前先运行DDint()函数对DirectDraw进行初始化,检测命令行参数并对相关的参数进行赋值和确定显示模式,进行窗口的初始化,检测bhelp的值以确定是否显示帮助对话框,进行游戏的初始化。
      在一个游戏的消息循环中除了包含一般Windows应用程序的消息循环所应包含的部分外还应有调用有关检测游戏单位状态位置、刷新游戏单位和重画新图以及有关人工智能的函数的部分。在例程中的消息循环部分包含了一个关于检测游戏单位状态位置、刷新游戏单位和重画新图函数的调用。

      在这些调用中一般有两种方法:
      1.在消息循环中直接调用有关函数。比如说在一个RPG的游戏中每个循环都检测主角的的位置是否发生改变,若改变了则在新位置上重画主角的图。
      2.通过检测WM_TIMER消息,以决定是否调用有关函数。即是每隔一段时间(若干个时钟周期),检测一次,然后决定函数的调用与否。

      在上面的两种方法里,第一种是现在较常用的,它的缺点是CPU资源占用相对较多,但对不同的机型适应性较强,较稳定。第二种在一些较老的游戏或对速度要求不高的游戏中较常见,与第一种相比它的CPU资源占用相对较少,但在不同的机型中表现差异极大。

      在谈WinMain()的编制时,窗口函数(WINPROC)的编制是必须说的。窗口函数可以说是把功能不同的函数通过Switch-Case结构连起来组成一个复杂程序的线索。它的基本编写方法在Windows编程基础中我们就已经谈到了。仔细阅读例程中的MainWndProc()函数相信对您是有相当大的帮助的。

      /*
       * MainWndProc
       *
       * Callback for all Windows messages
       */
      long FAR PASCAL MainWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
      {
          PAINTSTRUCT ps;
          HDC         hdc;


          switch( message )
          {
          case WM_SIZE:
          case WM_MOVE:
              if (IsIconic(hWnd))
              {
                  Msg("FoxBear is minimized, pausing");
                  PauseGame();
              }


              if (bFullscreen)
              {
                  SetRect(&rcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
              }
              else
              {
                  GetClientRect(hWnd, &rcWindow);
                  ClientToScreen(hWnd, (LPPOINT)&rcWindow);
                  ClientToScreen(hWnd, (LPPOINT)&rcWindow+1);
              }
              Msg("WINDOW RECT: [%d,%d,%d,%d]", rcWindow.left, , rcWindow.right, rcWindow.bottom);
              break;


          case WM_ACTIVATEAPP:
              bIsActive = (BOOL)wParam && GetForegroundWindow() == hWnd;


              if (bIsActive)
                  Msg("FoxBear is active");
              else
                  Msg("FoxBear is not active");


              //
              // while we were not-active something bad happened that caused us
              // to pause, like a surface restore failing or we got a palette
              // changed, now that we are active try to fix things
              //
              if (bPaused && bIsActive)
              {
                  if (RestoreGame())
                  {
                      UnPauseGame();
                  }
                  else
                  {
                     if (GetForegroundWindow() == hWnd)
                     {
                          //
                          //  we are unable to restore, this can happen when
                          //  the screen resolution or bitdepth has changed
                          //  we just reload all the art again and re-create
                          //  the front and back buffers.  this is a little
                          //  overkill we could handle a screen res change by
                          //  just recreating the front and back buffers we dont
                          //  need to redo the art, but this is way easier.
                          //
                          if (InitGame())
                          {
                              UnPauseGame();
                          }
                      }
                  }
              }
              break;


          case WM_QUERYNEWPALETTE:
              //
              //  we are getting the palette focus, select our palette
              //
              if (!bFullscreen && lpPalette && lpFrontBuffer)
              {
                  HRESULT ddrval;


                  ddrval = IDirectDrawSurface_SetPalette(lpFrontBuffer,lpPalette);
                  if( ddrval == DDERR_SURFACELOST )
                  {
                      IDirectDrawSurface_Restore( lpFrontBuffer );


                      ddrval= IDirectDrawSurface_SetPalette(lpFrontBuffer,lpPalette);
                      if( ddrval == DDERR_SURFACELOST )
                      {
                         Msg("  Failed to restore palette after second try");
                      }
                  }


                  //
                  // Restore normal title if palette is ours
                  //


                  if( ddrval == DD_OK )
                  {
                      SetWindowText( hWnd, OUR_APP_NAME );
                  }
              }
              break;


          case WM_PALETTECHANGED:
              //
              //  if another app changed the palette we dont have full control
              //  of the palette. NOTE this only applies for FoxBear in a window
              //  when we are fullscreen we get all the palette all of the time.
              //
              if ((HWND)wParam != hWnd)
              {
                  if( !bFullscreen )
                  {
                      if( !bStress ) 
                      {
                          Msg("***** PALETTE CHANGED, PAUSING GAME");
                          PauseGame();
                      }
                      else
                      {
                          Msg("Lost palette but continuing");
                          SetWindowText( hWnd, OUR_APP_NAME 
                                       " - palette changed COLORS PROBABLY WRONG" );
                      }
                  }
              }
              break;


          case WM_DISPLAYCHANGE:
              break;


          case WM_CREATE:
              break;


          case WM_SETCURSOR:
              if (bFullscreen && bIsActive)
              {
                  SetCursor(NULL);
                  return TRUE;
              }
              break;


          case WM_SYSKEYUP:
              switch( wParam )
              {
              // handle ALT+ENTER (fullscreen)
              case VK_RETURN:
                  bFullscreen = !bFullscreen;
                  ExitGame();
                  DDDisable(TRUE);        // destroy DirectDraw object
                  GameMode.cx = 320;
                  GameMode.cy = 200;
                  InitGame();
                  break;
              }
              break;
       
          case WM_KEYDOWN:
              switch( wParam )
              {
              case VK_NUMPAD5:
                  lastInput=KEY_STOP;
                  break;
              case VK_DOWN:
              case VK_NUMPAD2:
                  lastInput=KEY_DOWN;
                  break;
              case VK_LEFT:
              case VK_NUMPAD4:
                  lastInput=KEY_LEFT;
                  break;
              case VK_RIGHT:
              case VK_NUMPAD6:
                  lastInput=KEY_RIGHT;
                  break;
              case VK_UP:
              case VK_NUMPAD8:
                  lastInput=KEY_UP;
                  break;
              case VK_HOME:
              case VK_NUMPAD7:
                  lastInput=KEY_JUMP;
                  break;
              case VK_NUMPAD3:
                  lastInput=KEY_THROW;
                  break;
              case VK_F5:
                  bShowFrameCount = !bShowFrameCount;
                  if( bShowFrameCount )
                  {
                    dwFrameCount = 0;
                    dwFrameTime = timeGetTime();
                  }
              break;


              case VK_F6:
                  {
                  static i;
                  //
                  // find our current mode in the mode list
                  //
                  if(bFullscreen)
                  {
                      for (i=0; i<NumModes; i++)
                      {
                          if (ModeList[i].bpp == (int)GameBPP &&
                              ModeList[i].w   == GameSize.cx &&
                              ModeList[i].h   == GameSize.cy)
                          {
                              break;
                          }
                      }
                  }else
                  {
                      for (i=0; i<NumModes; i++)
                      {
                          if (ModeList[i].w   == GameSize.cx &&
                              ModeList[i].h   == GameSize.cy)
                          {
                              break;
                          }
                      }
                  }
                  //
                  // now step to the next mode, wrapping to the first one.
                  //
                  if (++i >= NumModes)
                  {
                      i = 0;
                  }
                  Msg("ModeList %d %d",i,NumModes);
                  GameMode.cx = ModeList[i].w;
                  GameMode.cy = ModeList[i].h;
                  GameBPP     = ModeList[i].bpp;
                  bStretch    = FALSE;
                  InitGame();
                  }
                  break;
              case VK_F7:
                  GameBPP = GameBPP == 8 ? 16 : 8;
                  InitGame();
                  break;


              case VK_F8:
                  if (bFullscreen)
                  {
                      bStretch = !bStretch;
                      InitGame();
                  }
                  else
                  {
                      RECT rc;


                      GetClientRect(hWnd, &rc);


                      bStretch = (rc.right  != GameSize.cx) ||
                                 (rc.bottom != GameSize.cy);


                      if (bStretch = !bStretch)
                          SetRect(&rc, 0, 0, GameMode.cx*2, GameMode.cy*2);
                      else
                          SetRect(&rc, 0, 0, GameMode.cx, GameMode.cy);


                      AdjustWindowRectEx(&rc,
                          GetWindowStyle(hWnd),
                          GetMenu(hWnd) != NULL,
                          GetWindowExStyle(hWnd));


                      SetWindowPos(hWnd, NULL, 0, 0, rc.right-rc.left, ,
                          SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
                  }
                  break;
              case VK_F9:           DevIndex ++;
                  bUseEmulation = FALSE;
                  if (DevIndex >= MaxDevIndex)
                      DevIndex = 0;


                  ExitGame();
                  DDDisable(TRUE);        // destroy DirectDraw object
                  InitGame();
                  break;
              case VK_F4:
                  // treat F4 like ALT+ENTER (fullscreen)
                  PostMessage(hWnd, WM_SYSKEYUP, VK_RETURN, 0);
                  break;


              case VK_F3:
                  bPaused = !bPaused;
                  break;


              case VK_ESCAPE:
              case VK_F12:
                  PostMessage(hWnd, WM_CLOSE, 0, 0);
                  return 0;
              }
              break;


          case WM_PAINT:
              hdc = BeginPaint( hWnd, &ps );
              if (bPaused)
              {
                  char *sz = "Game is paused, this is not a bug.";
                  TextOut(ps.hdc, 0, 0, sz, lstrlen(sz));
              }
              EndPaint( hWnd, &ps );
              return 1;


          case WM_DESTROY:
              hWndMain = NULL;
              lastInput=0;
              DestroyGame();          // end of game
              DDDisable(TRUE);        // destroy DirectDraw object
              PostQuitMessage( 0 );
              break;
          }


          return DefWindowProc(hWnd, message, wParam, lParam);


      } /* MainWndProc */


      第三节 游戏初始化

      游戏初始化包括三部分:
      1.Windows的初始化。
      2.游戏工具的初始化。
      3.游戏的初始化。

      在这三部分中Windows的初始化,也就是对窗口的注册、定义和初始化。我们在Win- dows编程基础中已经谈过,这里就不再详述了。

      游戏工具的初始化,是指对游戏程序中用到的工具进行初始化。对于一个游戏而言我们需要针对游戏的需要使用一些对图形或声音管理绘制或播放的以及其他功能的系统,这些系统就是我们所说的游戏工具(有时人们也称之为游戏引擎)。这些工具有时是由一些游戏公司提供的,比如MICROSOFT的DirectX5 SDK,有时是自己针对游戏需要编制的或使用上一部作品中用过的系统。在本例程中是指对Directdraw和DirectSound进行初始化,您可以通过阅读DDinit()和  InitSound()以及InitGame()函数的一部分的原代码以及阅读我们提供有关Directdraw和DirectSound的章节来理解。

      DDinit()和  InitSound()以及InitGame()函数的代码:
      /*
       * InitGame
       *
       * Initializing current game
       */
      BOOL InitGame( void )
      {
          ExitGame();


          GameSize = GameMode;


          /*
           * initialize sound
           */
          InitSound( hWndMain );


          /*
           * init DirectDraw, set mode, ...
           * NOTE GameMode might be set to 640x480 if we cant get the asked for mode.
           */
          if( !PreInitializeGame() )
          {
              return FALSE;
          }


          if (bStretch && bFullscreen)
          {
              GameSize.cx     = GameMode.cx / 2;
              GameSize.cy     = GameMode.cy / 2;
              GameRect.left   = GameMode.cx - GameSize.cx;
                 = GameMode.cy - GameSize.cy;
              GameRect.right  = GameMode.cx;
              GameRect.bottom = GameMode.cy;


              if (lpStretchBuffer)
                  Msg("Stretching using a system-memory stretch buffer");
              else
                  Msg("Stretching using a VRAM->VRAM blt");
          }
          else
          {
              GameRect.left   = (GameMode.cx - GameSize.cx) / 2;
                 = (GameMode.cy - GameSize.cy) / 2;
              GameRect.right  = GameRect.left + GameSize.cx;
              GameRect.bottom = + GameSize.cy;
          }


          /*
           * setup our palette
           */
          if( GameBPP == 8 )
          {
              lpPalette = ReadPalFile( NULL );        // create a 332 palette


              if( lpPalette == NULL )
              {
                  Msg( "Palette create failed" );
                  return FALSE;
              }


              IDirectDrawSurface_SetPalette( lpFrontBuffer, lpPalette );
          }


          /*
           *  load all the art and things.
           */
          if( !InitializeGame() )
          {
              return FALSE;
          }


          /*
           * init our code to draw the FPS
           */
          makeFontStuff();


          /*
           * spew some stats
           */
          {
              DDCAPS ddcaps;
              ddcaps.dwSize = sizeof( ddcaps );
              IDirectDraw_GetCaps( lpDD, &ddcaps, NULL );
              Msg( "Total=%ld, Free VRAM=%ld", ddcaps.dwVidMemTotal, ddcaps.dwVidMemFree );
              Msg( "Used = %ld", ddcaps.dwVidMemTotal- ddcaps.dwVidMemFree );   }


          return TRUE;


      } /* InitGame */


      /*
       * InitSound
       *
       * Sets up the DirectSound object and loads all sounds into secondary
       * DirectSound buffers.  Returns FALSE on error, or TRUE if successful
       */
      BOOL InitSound( HWND hwndOwner )
      {
          int idx;
          DSBUFFERDESC dsBD;
          IDirectSoundBuffer *lpPrimary;


          DSEnable(hwndOwner);


          if (lpDS == NULL)
              return TRUE;


          /*
           * Load all sounds -- any that can't load for some reason will have NULL
           * pointers instead of valid SOUNDEFFECT data, and we will know not to
           * play them later on.
           */
          for( idx = 0; idx < NUM_SOUND_EFFECTS; idx++ )
          {
              if (SoundLoadEffect((EFFECT)idx))
              {
                  DSBCAPS  caps;


                  caps.dwSize = sizeof(caps);
                  IDirectSoundBuffer_GetCaps(lpSoundEffects[idx], &caps);


                  if (caps.dwFlags & DSBCAPS_LOCHARDWARE)
                      Msg( "Sound effect %s in hardware", szSoundEffects[idx]);
                  else
                      Msg( "Sound effect %s in software", szSoundEffects[idx]);
              }
              else
              {
                  Msg( "cant load sound effect %s", szSoundEffects[idx]);
              }
          }


          /*
           * get the primary buffer and start it playing
           *
           * by playing the primary buffer, DirectSound knows to keep the
           * mixer active, even though we are not making any noise.
           */


          ZeroMemory( &dsBD, sizeof(DSBUFFERDESC) );
          dsBD.dwSize = sizeof(dsBD);
          dsBD.dwFlags = DSBCAPS_PRIMARYBUFFER;


          if (SUCCEEDED(IDirectSound_CreateSoundBuffer(lpDS, &dsBD, &lpPrimary, NULL)))
          {
              if (!SUCCEEDED(IDirectSoundBuffer_Play(lpPrimary, 0, 0, DSBPLAY_LOOPING)))
              {
                  Msg("Unable to play Primary sound buffer");
              }


              IDirectSoundBuffer_Release(lpPrimary);
          }
          else
          {
              Msg("Unable to create Primary sound buffer");
          }


          return TRUE;


      } /* InitSound */




      /*
       * DDInit
       */
      BOOL DDInit( void )
      {
          DirectDrawEnumerate(&DDEnumCallback,NULL);
          DDEnumCallback((GUID *)DDCREATE_EMULATIONONLY, "Hardware Emulation Layer", "", NULL);
          return TRUE;
      }




      游戏的初始化是指调入游戏中的图象、声音等资源和游戏中的角色、道具的属性、
      初始位置、状态等并画出初始画面的图象以及游戏的系统、操作方法的定义、游戏的规则等。比如说在一个RPG游戏之中,在游戏开始时内存中就应装入主角的图象组(比如走时的几幅图,状态对话框中的图)、状态(级别、HP、MP、DP等等)、属性(性别、职业等)等,描述整个游戏世界的图,NPC的各种属性、游戏的规则(各种攻击方式的效果、升级所需的经验值等)等等,总之要装入您所设计的游戏世界的一切。在例程的InitGame()中调用的函数InitializeGame()就完成了这个任务。


      InitializeGame()的代码:


      /*
       * InitializeGame
       */
      BOOL InitializeGame ( void )
      {
          Splash();


          hBitmapList = LoadBitmaps();
          if( hBitmapList == NULL )
          {
              return FALSE;
          }


          InitTiles( &hTileList, hBitmapList, C_TILETOTAL );
              
          InitPlane( &hForeground, &hForePosList, "FORELIST", C_FORE_W, C_FORE_H, C_FORE_DENOM );
          TilePlane( hForeground, hTileList, hForePosList );


          InitPlane( &hMidground, &hMidPosList, "MIDLIST", C_MID_W, C_MID_H, C_MID_DENOM );
          TilePlane( hMidground, hTileList, hMidPosList );


          InitPlane( &hBackground, &hBackPosList, "BACKLIST", C_BACK_W, C_BACK_H, C_BACK_DENOM );
          TilePlane( hBackground, hTileList, hBackPosList );


          InitSurface( &hSurfaceList, "SURFLIST", C_FORE_W, C_FORE_H );
          SurfacePlane( hForeground, hSurfaceList );


          InitFox( &hFox, hBitmapList );
          InitBear( &hBear, hBitmapList );
          InitApple( &hApple, hBitmapList );


          DDClear();      // clear all the backbuffers.


          return TRUE;


      } /* InitializeGame */




      在现在的大部分游戏中游戏世界中的每个组成部分通常是用结构或类分别定义储存的。比如一个即时战略游戏中,各种建筑物放在一个类中,而每个建筑物的属性就放在该类的一个子类中;各种武器放在一个类中,每种武器放在该类的一个子类中。


      class Weapon
      {
      WEAPON_TYPE Type;
      char Name;
      DWORD Id;
      WORD Defend;
      WORD Attack;
      ...
      };


      第四节 游戏内部循环


      游戏内部循环包括刷新游戏单位、画游戏单位两部分。它的实现过程是这样的:检测状态,作出判断,绘出新图。看起来这并不是一个循环,对吗?是的,游戏内部循环并不是一个真正的循环,它实际上是由消息循环完成循环作用的。让我们从例程中看看这是如何实现的吧!


      在消息循环中的第一个else if语句是这样的
      else if (!bPaused && (bIsActive || !bFullscreen))
              {
                  ProcessFox(lastInput);
                  lastInput=0;
              }


      if 后的表达式的含义是:当游戏没有被暂停时(bPause为FLASE)或以窗口模式显示(bFullscreen为FLASE)且窗口处于活动状态(bIsActive为TRUE)时执行
             {
                  ProcessFox(lastInput);
                  lastInput=0;
              }
      语句段。而函数ProcessFox(lastInput)通过调用ProcessInput()和NewGameFrame( )达成刷新游戏单元和重画新图的功能。(这三个函数的原代码见例程foxbear.c和gameproc.c两文件)。


      ProcessFox(lastInput):


      /*
       * ProcessFox
       */
      BOOL ProcessFox(SHORT sInput)
      {
          if ((lpFrontBuffer && IDirectDrawSurface_IsLost(lpFrontBuffer) == DDERR_SURFACELOST) ||
              (lpBackBuffer && IDirectDrawSurface_IsLost(lpBackBuffer) == DDERR_SURFACELOST))
          {
              if (!RestoreGame())
              {
                  PauseGame();
                  return FALSE;
              }
          }




          ProcessInput(sInput);
          NewGameFrame();
          return TRUE;


      } /* ProcessFox */


      static HFONT hFont;


      DWORD   dwFrameCount;
      DWORD dwFrameTime;
      DWORD dwFrames;
      DWORD   dwFramesLast;
      SIZE    sizeFPS;
      SIZE sizeINFO;
      int FrameRateX;
      char szFPS[]   = "FPS %02d";
      char    szINFO[]  = "%dx%dx%d%s     F6=mode F8=x2 ALT+ENTER=Window";
      char    szINFOW[] = "%dx%dx%d%s     F6=mode F8=x2 ALT+ENTER=Fullscreen";


      char szFrameRate[128];
      char szInfo[128];


      COLORREF InfoColor      = RGB(0,152,245);
      COLORREF FrameRateColor = RGB(255,255,0);
      COLORREF BackColor = RGB(255,255,255);


      /*
       * initNumSurface
       */
      void initNumSurface( void )
      {
          HDC        hdc;
          RECT        rc;
          int         len;


          dwFramesLast = 0;


          len = wsprintf(szFrameRate, szFPS, 0, 0);


          if( lpFrameRate && IDirectDrawSurface_GetDC(lpFrameRate, &hdc ) == DD_OK )
          {
              SelectObject(hdc, hFont);
              SetTextColor(hdc, FrameRateColor);
              SetBkColor(hdc, BackColor);
              SetBkMode(hdc, OPAQUE);
              SetRect(&rc, 0, 0, 10000, 10000);
              ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, szFrameRate, len, NULL);
              GetTextExtentPoint(hdc, szFrameRate, 4, &sizeFPS);
              FrameRateX = sizeFPS.cx;
              GetTextExtentPoint(hdc, szFrameRate, len, &sizeFPS);


              IDirectDrawSurface_ReleaseDC(lpFrameRate, hdc);
          }


          if (bFullscreen)
              len = wsprintf(szInfo, szINFO,
                             GameSize.cx, GameSize.cy, GameBPP,bStretch ? " x2" : "");
          else
              len = wsprintf(szInfo, szINFOW,
                             GameSize.cx, GameSize.cy, GameBPP,bStretch ? " x2" : "");


          if( lpInfo && IDirectDrawSurface_GetDC(lpInfo, &hdc ) == DD_OK )
          {
              SelectObject(hdc, hFont);
              SetTextColor(hdc, InfoColor);
              SetBkColor(hdc, BackColor);
              SetBkMode(hdc, OPAQUE);
              SetRect(&rc, 0, 0, 10000, 10000);
              ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, szInfo, len, NULL);
              GetTextExtentPoint(hdc, szInfo, len, &sizeINFO);


              IDirectDrawSurface_ReleaseDC(lpInfo, hdc);
          }


      } /* initNumSurface */


      NewGameFrame( ):


      /*
       * NewGameFrame
       */
      int NewGameFrame( void )
      {


          SetSpriteX( hFox, 0, P_AUTOMATIC );
          SetSpriteY( hFox, 0, P_AUTOMATIC );
                                          
          SetPlaneVelX( hBackground, GetSpriteVelX(hFox), P_ABSOLUTE );
          SetPlaneVelX( hMidground,  GetSpriteVelX(hFox), P_ABSOLUTE );
          SetPlaneVelX( hForeground, GetSpriteVelX(hFox), P_ABSOLUTE );


          SetPlaneX( hBackground, 0, P_AUTOMATIC );
          SetPlaneX( hMidground,  0, P_AUTOMATIC );
          SetPlaneX( hForeground, 0, P_AUTOMATIC );


          SetSpriteX( hBear, 0, P_AUTOMATIC );
          SetSpriteX( hApple, 0, P_AUTOMATIC );
          SetSpriteY( hApple, 0, P_AUTOMATIC );


          /*
           * once all sprites are processed, display them
           *
           * If we are using destination transparency instead of source
           * transparency, we need to paint the background with the color key
           * and then paint our sprites and planes in reverse order.
           *
           * Since destination transparency will allow you to only write pixels
           * on the destination if the transparent color is present, reversing
           * the order (so that the topmost bitmaps are drawn first instead of
           * list) causes everything to come out ok.
           */
          if( bTransDest )
          {
              gfxFillBack( dwColorKey );


              DisplayFrameRate();


              DisplaySprite( hBuffer, hApple, GetPlaneX(hForeground) );
              DisplaySprite( hBuffer, hBear,  GetPlaneX(hForeground) );
              DisplaySprite( hBuffer, hFox,   GetPlaneX(hForeground) );


              DisplayPlane( hBuffer, hForeground );
              DisplayPlane( hBuffer, hMidground );
              DisplayPlane( hBuffer, hBackground );
          }
          else
          {
              DisplayPlane( hBuffer, hBackground );
              DisplayPlane( hBuffer, hMidground );
              DisplayPlane( hBuffer, hForeground );
          
              DisplaySprite( hBuffer, hFox,   GetPlaneX(hForeground) );
              DisplaySprite( hBuffer, hBear,  GetPlaneX(hForeground) );
              DisplaySprite( hBuffer, hApple, GetPlaneX(hForeground) );


              DisplayFrameRate();
          }
                                                            
          gfxSwapBuffers();


          return 0;


      } /* NewGameFrame */




      第五节 刷新游戏单元
      刷新游戏单位的作用是在每一桢刷新游戏单位的状态。请您先阅读一下下面的ProcessInput()函数的部分代码,然后再看看下面这两个例子。


      ProcessInput()函数的部分代码:


      /*
       * ProcessInput
       */
      BOOL ProcessInput( SHORT input )
      {
          static BOOL fBearPlaying = FALSE;
          LONG      foxSpeedX;
          LONG      foxSpeedY;
          LONG      foxX;
          LONG      foxY;
          LONG      bearX;
          LONG      bearY;
          LONG      appleX;
          LONG      appleY;
          ACTION    foxAction;
          DIRECTION foxDir;
          BOOL      cont = TRUE;


          foxSpeedX = GetSpriteVelX( hFox );
          foxAction = GetSpriteAction( hFox );
          foxDir    = GetSpriteDirection( hFox );


          if( (GetSpriteActive(hFox) == FALSE) && (input != 4209) )
          {
              input = 0;
          }
          switch( input )
          {
          case KEY_DOWN:
              if( foxAction == STOP )
              {
                  break;
              }
              else if( foxAction == STILL )
              {
                  SetSpriteAction( hFox, CROUCH, SAME );
              }
              else if( foxAction == WALK )
              {
                  SetSpriteAction( hFox, CROUCHWALK, SAME );
              }
              break;


          case KEY_LEFT:
              if( foxAction == STOP )
              {
                  break;
              }
              else if( foxSpeedX == 0 )
              {
                  if( foxAction == STILL )
                  {
                    if( foxDir == RIGHT )
                    {
                        ChangeSpriteDirection( hFox );
                        SetPlaneSlideX( hForeground, -C_BOUNDDIF, P_RELATIVE );
                        SetPlaneSlideX( hMidground, -C_BOUNDDIF, P_RELATIVE );
                        SetPlaneSlideX( hBackground, -C_BOUNDDIF, P_RELATIVE );
                        SetPlaneIncremX( hForeground, C_BOUNDINCREM, P_ABSOLUTE );
                        SetPlaneIncremX( hBackground, C_BOUNDINCREM, P_ABSOLUTE );
                        SetPlaneIncremX( hMidground, C_BOUNDINCREM, P_ABSOLUTE );
                    }
                    else
                    {
                        SetSpriteAction( hFox, WALK, LEFT );
                        SetSpriteSwitch( hFox, C_FOX_WALKSWITCH, P_ABSOLUTE );
                        SetSpriteVelX( hFox, -C_FOX_XMOVE, P_RELATIVE );
                    }
                  }
                  else if( foxAction == CROUCH )
                  {
                    if( foxDir == RIGHT )
                    {
                        ChangeSpriteDirection( hFox );
                        SetPlaneSlideX( hForeground, -C_BOUNDDIF, P_RELATIVE );
                        SetPlaneSlideX( hMidground, -C_BOUNDDIF, P_RELATIVE );
                        SetPlaneSlideX( hBackground, -C_BOUNDDIF, P_RELATIVE );
                        SetPlaneIncremX( hForeground, C_BOUNDINCREM, P_ABSOLUTE );
                        SetPlaneIncremX( hBackground, C_BOUNDINCREM, P_ABSOLUTE );
                        SetPlaneIncremX( hMidground, C_BOUNDINCREM, P_ABSOLUTE );
                    }
                    else
                    {
                        SetSpriteAction( hFox, CROUCHWALK, LEFT );
                        SetSpriteSwitch( hFox, C_FOX_WALKSWITCH, P_ABSOLUTE );
                        SetSpriteVelX( hFox, -C_FOX_XMOVE, P_RELATIVE );
                    }
                  }
                  else
                  {
                    SetSpriteVelX( hFox, -C_FOX_XMOVE, P_RELATIVE );
                  }
              } else {
                  SetSpriteVelX( hFox, -C_FOX_XMOVE, P_RELATIVE );
              }
              break;


          case KEY_RIGHT:
              .
              .
              .
          case KEY_STOP:
              if( foxAction == STOP )
              {
                  break;
              }
              else if( (foxAction == RUN) || (foxAction == BLURR) )
              {
                  SetSpriteAction( hFox, STOP, SAME );
                  SetSpriteAccX( hFox, -foxSpeedX / 25, P_ABSOLUTE );
                  SoundPlayEffect( SOUND_STOP );
              } else {
                  SetSpriteVelX( hFox, 0, P_ABSOLUTE );
              }
              break;


          case KEY_UP: 
              if( foxAction == STOP )
              {
                  break;
              }
              else if( foxAction == CROUCH )
              {
                  SetSpriteAction( hFox, STILL, SAME );
              }
              else if( foxAction == CROUCHWALK )
              {
                  SetSpriteAction( hFox, WALK, SAME );
              }
              break;


          case KEY_JUMP:
              if( foxAction == STOP )
              {
                  break;
              }
              else
              if( (foxAction == STILL) || (foxAction == WALK) ||
                  (foxAction == RUN) || (foxAction == CROUCH) ||
                  (foxAction == CROUCHWALK) )
              {
                  SetSpriteAction( hFox, JUMP, SAME );
                  SetSpriteSwitchType( hFox, TIME );
                  SetSpriteSwitch( hFox, C_FOX_JUMPSWITCH, P_ABSOLUTE );
                  SetSpriteVelY( hFox, -C_FOX_JUMPMOVE, P_ABSOLUTE );
                  SetSpriteAccY( hFox, C_UNIT / 2, P_ABSOLUTE );
                  SoundPlayEffect( SOUND_JUMP );
              }
              break;


          case KEY_THROW:
              if( foxAction == STOP )
              {
                  break;
              }
              else if( (foxAction == STILL) || (foxAction == WALK) ||
                     (foxAction == RUN) || (foxAction == CROUCH) ||
                     (foxAction == CROUCHWALK) )
              {
                  SetSpriteAction( hFox, THROW, SAME );
                  SetSpriteSwitch( hFox, C_FOX_THROWSWITCH, P_ABSOLUTE );
                  SetSpriteVelX( hFox, 0, P_ABSOLUTE );
                  SetSpriteSwitchType( hFox, TIME );
              }
              else if( foxAction == JUMP )
              {
                  SetSpriteAccY( hFox, 0, P_ABSOLUTE );
                  SetSpriteSwitch( hFox, C_FOX_THROWSWITCH, P_ABSOLUTE );
                  SetSpriteAction( hFox, JUMPTHROW, SAME );
                  SetSpriteVelY( hFox, 0, P_ABSOLUTE );
                  SetSpriteSwitchDone( hFox, FALSE );
                  SetSpriteSwitchForward( hFox, TRUE );
              }
              break;


          default: 
              break;
          }


          /*
           * Fox actions follow...
           */
          if( GetSpriteActive(hFox) == FALSE )
          {
              goto bearActions;
          }


          if( abs(GetSpriteVelX( hFox )) < C_FOX_XMOVE )
          {
              SetSpriteVelX( hFox, 0, P_ABSOLUTE );
          }


          foxAction = GetSpriteAction( hFox );


          if( GetSpriteVelY(hFox) == 0 )
          {
              if( GetSurface( hForeground, hFox ) == FALSE )
              {
                  if( (foxAction == WALK) || (foxAction == RUN) ||
                  (foxAction == CROUCHWALK) )
                  {
                    SetSpriteAccY( hFox, C_UNIT / 2, P_ABSOLUTE );
                  }
                  else if( foxAction == STOP )
                  {
                    SetSpriteAccY( hFox, C_UNIT / 2, P_ABSOLUTE );
                    SetSpriteAccX( hFox, 0, P_ABSOLUTE );
                  }
              }
          }
          else if( GetSpriteVelY(hFox) > 2 * C_UNIT )
          {
              if( (foxAction == WALK) || (foxAction == RUN) ||
                  (foxAction == CROUCHWALK) )
              {
                  SetSpriteSwitchForward( hFox, FALSE );
                  SetSpriteAction( hFox, JUMP, SAME );
                  SetSpriteSwitchType( hFox, TIME );
                  SetSpriteSwitch( hFox, C_FOX_JUMPSWITCH, P_ABSOLUTE );
              }
              if( foxAction == STOP )
              {
                  SetSpriteAction( hFox, STUNNED, SAME );
                  SetSpriteAccX( hFox, -GetSpriteVelX(hFox) / 25, P_ABSOLUTE );
                  SoundPlayEffect( SOUND_STUNNED );
              }
          }
          
          foxSpeedX = GetSpriteVelX( hFox );
          foxSpeedY = GetSpriteVelY( hFox );
          foxAction = GetSpriteAction( hFox );
          foxDir    = GetSpriteDirection( hFox );


          switch( foxAction ) {
          case STUNNED:
              if( (GetSpriteVelY(hFox) >= 0) &&
                  (!GetSurface( hForeground, hFox ) == FALSE) )
              {
                  SetSpriteAccY( hFox, 0, P_ABSOLUTE );
                  SetSpriteAction( hFox, STOP, SAME );
                  SetSpriteVelY( hFox, 0, P_ABSOLUTE );
                  SetSpriteAccX( hFox, -foxSpeedX / 25, P_ABSOLUTE );
                  // SetSurface( hForeground, hFox );
                  SoundPlayEffect( SOUND_STOP );
              }
              break;


          case CROUCHWALK:
              if( foxSpeedX == 0 )
              {
                  SetSpriteAction( hFox, CROUCH, SAME );
              }
              else if( foxSpeedX > C_FOX_WALKMOVE )
              {
                  SetSpriteVelX( hFox, C_FOX_WALKMOVE, P_ABSOLUTE );
              }
              else if( foxSpeedX < -C_FOX_WALKMOVE )
              {
                  SetSpriteVelX( hFox, -C_FOX_WALKMOVE, P_ABSOLUTE );
              }
              break;
              
          case STOP:  
              if( foxSpeedX == 0 )
              {
                  SetSpriteAction( hFox, STILL, SAME );
                  SetSpriteAccX( hFox, 0, P_ABSOLUTE );
              }
              break;
              
          case RUN:
              if( (foxSpeedX < C_FOX_WALKTORUN ) && (foxSpeedX > 0) )
              {
                  SetSpriteAction( hFox, WALK, RIGHT );
                  SetSpriteSwitch( hFox, C_FOX_WALKSWITCH, P_ABSOLUTE );
              }
              else if( foxSpeedX > C_FOX_RUNTOBLURR )
              {
                  SetSpriteAction( hFox, BLURR, RIGHT );
                  SetSpriteSwitch( hFox, C_FOX_BLURRSWITCH, P_ABSOLUTE );
              }
              else if( (foxSpeedX > -C_FOX_WALKTORUN ) && (foxSpeedX < 0) )
              {
                  SetSpriteAction( hFox, WALK, LEFT );
                  SetSpriteSwitch( hFox, C_FOX_WALKSWITCH, P_ABSOLUTE );
              }
              else if( foxSpeedX < -C_FOX_RUNTOBLURR )
              {
                  SetSpriteAction( hFox, BLURR, LEFT );
                  SetSpriteSwitch( hFox, C_FOX_BLURRSWITCH, P_ABSOLUTE );
              }
              break;


          case WALK:
              if( foxSpeedX == 0 )
              {
                  SetSpriteAction( hFox, STILL, SAME );
              }
              else if( foxSpeedX > C_FOX_WALKTORUN )
              {
                  SetSpriteAction( hFox, RUN, RIGHT );
                  SetSpriteSwitch( hFox, C_FOX_RUNSWITCH, P_ABSOLUTE );
              }
              else if( foxSpeedX < -C_FOX_WALKTORUN )
              {
                  SetSpriteAction( hFox, RUN, LEFT );
                  SetSpriteSwitch( hFox, C_FOX_RUNSWITCH, P_ABSOLUTE );
              }
              break;


          case BLURR:
              if( (foxSpeedX < C_FOX_RUNTOBLURR ) && (foxSpeedX > C_FOX_WALKTORUN) )
              {
                  SetSpriteAction( hFox, RUN, RIGHT );
                  SetSpriteSwitch( hFox, C_FOX_RUNSWITCH, P_ABSOLUTE );
              }
              else if( (foxSpeedX > -C_FOX_RUNTOBLURR ) && (foxSpeedX < -C_FOX_WALKTORUN) )
              {
                  SetSpriteAction( hFox, RUN, LEFT );
                  SetSpriteSwitch( hFox, C_FOX_RUNSWITCH, P_ABSOLUTE );
              }
              break;


          case JUMPTHROW:
              if( !GetSpriteSwitchDone(hFox) == FALSE )
              {
                  SetSpriteSwitchForward( hFox, FALSE );
                  SetSpriteAction( hFox, JUMP, SAME );
                  SetSpriteSwitch( hFox, C_FOX_JUMPSWITCH, P_ABSOLUTE );
                  SetSpriteSwitchDone( hFox, FALSE );
                  SetSpriteAccY( hFox, C_UNIT / 2, P_ABSOLUTE );
                  SoundPlayEffect( SOUND_THROW );
              }
              else
              if( (GetSpriteBitmap(hFox) == 1) &&
                  (GetSpriteDirection(hFox) == RIGHT) )
              {
                  SetSpriteActive( hApple, TRUE );
                  SetSpriteX( hApple, GetSpriteX(hFox) + 60 * C_UNIT, P_ABSOLUTE );
                  SetSpriteY( hApple, GetSpriteY(hFox) + 30 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelX( hApple, 8 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelY( hApple, -4 * C_UNIT, P_ABSOLUTE );
                  SetSpriteAccX( hApple, 0, P_ABSOLUTE );
                  SetSpriteAccY( hApple, C_UNIT / 4, P_ABSOLUTE );
              }
              else if( (GetSpriteBitmap(hFox) == 1) &&
                       (GetSpriteDirection(hFox) == LEFT) )
              {
                  SetSpriteActive( hApple, TRUE );
                  SetSpriteX( hApple, GetSpriteX(hFox) + 15 * C_UNIT, P_ABSOLUTE );
                  SetSpriteY( hApple, GetSpriteY(hFox) + 30 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelX( hApple, -8 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelY( hApple, -4 * C_UNIT, P_ABSOLUTE );
                  SetSpriteAccX( hApple, 0, P_ABSOLUTE );
                  SetSpriteAccY( hApple, C_UNIT / 4, P_ABSOLUTE );
              }
              break;


          case THROW:
              if( !GetSpriteSwitchDone(hFox) == FALSE )
              {
                  SetSpriteAction( hFox, STILL, SAME );
                  SetSpriteSwitchType( hFox, HOR );
                  SetSpriteSwitch( hFox, 0, P_ABSOLUTE );
                  SetSpriteSwitchDone( hFox, FALSE );
                  SoundPlayEffect( SOUND_THROW );
              }
              else if( (GetSpriteBitmap(hFox) == 1) &&
                       (GetSpriteDirection(hFox) == RIGHT) )
              {
                  SetSpriteActive( hApple, TRUE );
                  SetSpriteX( hApple, GetSpriteX(hFox) + 60 * C_UNIT, P_ABSOLUTE );
                  SetSpriteY( hApple, GetSpriteY(hFox) + 50 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelX( hApple, 8 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelY( hApple, -4 * C_UNIT, P_ABSOLUTE );
                  SetSpriteAccX( hApple, 0, P_ABSOLUTE );
                  SetSpriteAccY( hApple, C_UNIT / 4, P_ABSOLUTE );
              }
              else if( (GetSpriteBitmap(hFox) == 1) &&
                       (GetSpriteDirection(hFox) == LEFT) )
              {
                  SetSpriteActive( hApple, TRUE );
                  SetSpriteX( hApple, GetSpriteX(hFox) + 20 * C_UNIT, P_ABSOLUTE );
                  SetSpriteY( hApple, GetSpriteY(hFox) + 50 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelX( hApple, -8 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelY( hApple, -4 * C_UNIT, P_ABSOLUTE );
                  SetSpriteAccX( hApple, 0, P_ABSOLUTE );
                  SetSpriteAccY( hApple, C_UNIT / 4, P_ABSOLUTE );
              }
              break;


          case JUMP:
              if( (foxSpeedY >= 0) && (!GetSpriteSwitchForward( hFox ) == FALSE) )
              {
                  SetSpriteSwitchForward( hFox, FALSE );
              }
              else if( GetSpriteSwitchForward( hFox ) == FALSE )
              {
                  if( (!GetSurface( hForeground, hFox ) == FALSE) ||
                  (!GetSurface( hForeground, hFox ) == FALSE) )
                  {
                    if( foxSpeedX >= C_FOX_RUNMOVE )
                    {
                        SetSpriteAction( hFox, RUN, SAME );
                        SetSpriteSwitch( hFox, C_FOX_RUNSWITCH, P_ABSOLUTE );
                    }
                    else if( foxSpeedX == 0 )
                    {
                        SetSpriteAction( hFox, STILL, SAME );
                        SetSpriteSwitch( hFox, C_FOX_WALKSWITCH, P_ABSOLUTE );
                    }
                    else
                    {
                        SetSpriteAction( hFox, WALK, SAME );
                        SetSpriteSwitch( hFox, C_FOX_WALKSWITCH, P_ABSOLUTE );
                    }


                    SetSpriteAccY( hFox, 0, P_ABSOLUTE );
                    SetSpriteVelY( hFox, 0, P_ABSOLUTE );
                    SetSpriteSwitchType( hFox, HOR );     
                    SetSpriteSwitchForward( hFox, TRUE );
      // SetSurface( hForeground, hFox );
                    SetSpriteSwitchDone( hFox, FALSE );
                  }
              }
              break;


          }


          /*
           * Bear Actions
           */
          bearActions:


          foxX   = GetSpriteX( hFox );
          foxY   = GetSpriteY( hFox );
          bearX  = GetSpriteX( hBear );
          bearY  = GetSpriteY( hBear );
          appleX = GetSpriteX( hApple );
          appleY = GetSpriteY( hApple );


          switch( GetSpriteAction( hBear ) ) {
          case STRIKE:
              if( GetSpriteBitmap( hBear ) == 2 )
              {
                  if( (bearX > foxX - C_UNIT * 30) && (bearX < foxX + C_UNIT * 40) &&
                      (bearY < foxY + C_UNIT * 60) )
                  {
                    SetSpriteActive( hFox, FALSE );
                      if( !fBearPlaying )
                      {
                          SoundPlayEffect( SOUND_BEARSTRIKE );
                          fBearPlaying = TRUE;
                      }
                  }
                  else
                  {
                    SetSpriteAction( hBear, MISS, SAME );
                    SetSpriteSwitch( hBear, C_BEAR_MISSSWITCH, P_ABSOLUTE );
                    SetSpriteSwitchDone( hBear, FALSE );
                  }
              }
              else if( !GetSpriteSwitchDone( hBear ) == FALSE )
              {
                  SetSpriteAction( hBear, CHEW, SAME );
                  SetSpriteSwitchDone( hBear, FALSE );
                  chewCount = 0;
                  fBearPlaying = FALSE;
              }
              break;


          case MISS:
              if( !fBearPlaying )
              {
                  SoundPlayEffect( SOUND_BEARMISS );
                  fBearPlaying = TRUE;
              }
              if( !GetSpriteSwitchDone( hBear ) == FALSE )
              {
                  SetSpriteAction( hBear, WALK, SAME );
                  SetSpriteVelX( hBear, -C_BEAR_WALKMOVE, P_ABSOLUTE );
                  SetSpriteSwitch( hBear, C_BEAR_WALKSWITCH, P_ABSOLUTE );
                  SetSpriteSwitchType( hBear, HOR );
                  fBearPlaying = FALSE;
              }
              break;


          case WALK:
              if( (!GetSpriteActive(hApple) == FALSE) && (appleX > bearX) &&
                  (appleX > bearX + 80 * C_UNIT) && (appleY > bearY + 30 * C_UNIT) )
              {
                  SetSpriteAction( hBear, STRIKE, SAME );
                  SetSpriteVelX( hBear, 0, P_ABSOLUTE );
                  SetSpriteSwitchType( hBear, TIME );
                  SetSpriteSwitch( hBear, C_BEAR_STRIKESWITCH, P_ABSOLUTE );           SetSpriteSwitchDone( hBear, FALSE );
              }
              else if( (bearX > foxX - C_UNIT * 30) &&
                       (bearX < foxX + C_UNIT * 30) &&
                     (bearY < foxY + C_UNIT * 60) )
              {
                  SetSpriteAction( hBear, STRIKE, SAME );
                  SetSpriteVelX( hBear, 0, P_ABSOLUTE );
                  SetSpriteSwitchType( hBear, TIME );
                  SetSpriteSwitch( hBear, C_BEAR_STRIKESWITCH, P_ABSOLUTE );
                  SetSpriteSwitchDone( hBear, FALSE );
              }
              break;


          case CHEW:
              ++chewCount;
              if( chewCount >= 200 )
              {
                  SetSpriteAction( hBear, STRIKE, SAME );
                  SetSpriteSwitch( hBear, C_BEAR_STRIKESWITCH, P_ABSOLUTE );
                  SetSpriteVelX( hBear, 0, P_ABSOLUTE );
                  SetSpriteSwitchDone( hBear, FALSE );


                  if( GetSpriteDirection(hFox) == RIGHT )
                  {
                    SetPlaneSlideX( hForeground, -C_BOUNDDIF, P_RELATIVE );
                    SetPlaneSlideX( hMidground,  -C_BOUNDDIF, P_RELATIVE );
                    SetPlaneSlideX( hBackground, -C_BOUNDDIF, P_RELATIVE );
                  }


                  chewDif = GetSpriteX(hFox);


                  SetSpriteActive( hFox, TRUE );
                  SetSpriteAction( hFox, STUNNED, LEFT );
                  SetSpriteX( hFox, GetSpriteX(hBear), P_ABSOLUTE );
                  SetSpriteY( hFox, GetSpriteY(hBear), P_ABSOLUTE );
                  SetSpriteAccX( hFox, 0, P_ABSOLUTE );
                  SetSpriteAccY( hFox, C_UNIT / 2, P_ABSOLUTE );
                  SetSpriteVelX( hFox, -8 * C_UNIT, P_ABSOLUTE );
                  SetSpriteVelY( hFox, -10 * C_UNIT, P_ABSOLUTE );
                  SetSpriteSwitch( hFox, 0, P_ABSOLUTE );
                  SoundPlayEffect( SOUND_STUNNED );


                  chewDif -= GetSpriteX(hFox);


                  SetPlaneSlideX( hForeground, -chewDif, P_RELATIVE );
                  SetPlaneSlideX( hMidground, -chewDif, P_RELATIVE );
                  SetPlaneSlideX( hBackground, -chewDif, P_RELATIVE );
                  SetPlaneIncremX( hForeground, C_BOUNDINCREM, P_ABSOLUTE );
                  SetPlaneIncremX( hMidground,  C_BOUNDINCREM, P_ABSOLUTE );
                  SetPlaneIncremX( hBackground, C_BOUNDINCREM, P_ABSOLUTE );
              }
              break;
          }


          /*
           * Apple actions...
           */
          if( (GetSpriteVelY(hApple) != 0) && (GetSpriteY(hApple) >= 420 * C_UNIT) )
          {
              SetSpriteX( hApple, 0, P_ABSOLUTE );
              SetSpriteY( hApple, 0, P_ABSOLUTE );
              SetSpriteAccX( hApple, 0, P_ABSOLUTE );
              SetSpriteAccY( hApple, 0, P_ABSOLUTE );
              SetSpriteVelX( hApple, 0, P_ABSOLUTE );
              SetSpriteVelY( hApple, 0, P_ABSOLUTE );
              SetSpriteActive( hApple, FALSE );
          }


          return cont;


      } /* ProcessInput */






      在射击游戏中的子弹的发射,每一帧都要检测上一帧时子弹的位置a然后确定当前帧子弹的位置b然后将该位置传给重画游戏单元的部分,在当前帧b的位置贴上子弹的图象。


      在即使战略游戏中两军对战时,程序在每一帧都要根据上一帧每个战斗单位的位置和该战斗单位移动的目的、到该目的之间的障碍物的位置以及一定的路径算法确定在当前帧该战斗单位的新位置;还有要取得在上一帧时该战斗单位的生命值和所受的打击次数及强度,以确定该战斗单位的生命值。


      通过阅读ProcessInput()函数的代码,我想您一定已理解了刷新游戏单元的概念。而从上面的两个例子中,您也一定发现用例程的方法很难实现这两类游戏的要求。我们不可能对每一颗子弹,每一个战斗单位进行操作,而且我们并不知道游戏者会一次发射多少颗子弹也不知道游戏者会造多少个战斗单位。我们应该怎么办呢?


      考虑到每一个战斗单位(或每一颗子弹)都有相似(或相同)的属性,那么我们可以采用结构数组来储存每一个战斗单位的位置和状态。这个办法好象可行!但是仔细想想,我们又遇到了上面谈到的问题我们并不知道游戏者会一次发射多少颗子弹也不知道游戏者会造多少个战斗单位。当然我们可以采用Age of Empire的方式----限制单位的数量(我并不是说Age of Empire采用的是这种办法)。但是这意味什么呢!意味着,如果我们限定数量为50的话,在游戏者只有一个士兵时,计算机却需要为这个士兵分配50倍的内存!而且游戏者还不一定造出50个士兵。显然这并不是一个好办法!


      我们应该怎么办呢?链表!链表能满足我们的要求。


      class Node
      {
      //双向链表的指针。
      Node* Next;
      Node* Pre;

      //节点数据。
      NODE_DATA data;
      ...
      };


      链表是一种结构体的集合。在链表中的每一个结构体都包含了一个元素或指针,它指向链表中的另一个结构体。这个指针用作两个结构体之间的联系。这个概念与数组有些相似,但它允许链表的动态增长。现在的游戏中凡是遇到这种问题的一般都是采用链表的。关于链表的更多的信息请阅读有关的资料。




      第六节 画游戏单元
      画游戏单位的作用是在每一桢往屏幕上画游戏单位的图象。


      这就是本例程中画游戏单元的主函数:
      /*
       * NewGameFrame
       */
      int NewGameFrame( void )
      {
      //这里是设置游戏单元的位置:
          SetSpriteX( hFox, 0, P_AUTOMATIC );
          SetSpriteY( hFox, 0, P_AUTOMATIC );
                                          
          SetPlaneVelX( hBackground, GetSpriteVelX(hFox), P_ABSOLUTE );
          SetPlaneVelX( hMidground,  GetSpriteVelX(hFox), P_ABSOLUTE );
          SetPlaneVelX( hForeground, GetSpriteVelX(hFox), P_ABSOLUTE );


          SetPlaneX( hBackground, 0, P_AUTOMATIC );
          SetPlaneX( hMidground,  0, P_AUTOMATIC );
          SetPlaneX( hForeground, 0, P_AUTOMATIC );


          SetSpriteX( hBear, 0, P_AUTOMATIC );
          SetSpriteX( hApple, 0, P_AUTOMATIC );
          SetSpriteY( hApple, 0, P_AUTOMATIC );


      //将游戏单元的图形贴到BackBuffer上:
          if( bTransDest )
          {
              gfxFillBack( dwColorKey );


              DisplayFrameRate();


              DisplaySprite( hBuffer, hApple, GetPlaneX(hForeground) );
              DisplaySprite( hBuffer, hBear,  GetPlaneX(hForeground) );
              DisplaySprite( hBuffer, hFox,   GetPlaneX(hForeground) );


              DisplayPlane( hBuffer, hForeground );
              DisplayPlane( hBuffer, hMidground );
              DisplayPlane( hBuffer, hBackground );
          }
          else
          {
              DisplayPlane( hBuffer, hBackground );
              DisplayPlane( hBuffer, hMidground );
              DisplayPlane( hBuffer, hForeground );
          
              DisplaySprite( hBuffer, hFox,   GetPlaneX(hForeground) );
              DisplaySprite( hBuffer, hBear,  GetPlaneX(hForeground) );
              DisplaySprite( hBuffer, hApple, GetPlaneX(hForeground) );


              DisplayFrameRate();
          }


      //更新前景:                                                      
          gfxSwapBuffers();


          return 0;


      } /* NewGameFrame */


      画游戏单元的顺序为:
      1。清BackBuffer;


      这是清BackBuffer的函数:
      /*
       * gfxFillBack
       */
      void gfxFillBack( DWORD dwColor )
      {
          DDBLTFX ddbltfx;


          ddbltfx.dwSize = sizeof( ddbltfx );
          ddbltfx.dwFillColor = dwColor;


          IDirectDrawSurface_Blt(
      lpBackBuffer, // dest surface
      NULL, // dest rect
      NULL, // src surface
      NULL, // src rect
                              DDBLT_COLORFILL | DDBLT_WAIT,
                              &ddbltfx);


      } /* gfxFillBack */


      2。检查游戏单元图形的Surface是否丢失;


      这是检查游戏单元图形的Surface是否丢失的函数:
      /*
       * gfxRestoreAll
       *
       * restore the art when one or more surfaces are lost
       */
      BOOL gfxRestoreAll()
      {
          GFX_BITMAP  *curr;
          HWND hwndF = GetForegroundWindow();


          Splash();


          for( curr = lpVRAM; curr != NULL; curr = curr->link)
          {
      if (curr->lpSurface &&
         (fForceRestore || IDirectDrawSurface_IsLost(curr->lpSurface) == DDERR_SURFACELOST))
              {
                  if( !gfxRestore(curr) )
                  {
                      Msg( "gfxRestoreAll: ************ Restore FAILED!" );
                      return FALSE;
                  }
              }
          }


          DDClear();
          fForceRestore = FALSE;
          return TRUE;


      } /* gfxRestoreAll */


      3。将游戏单元的图形画到BackBuffer中;


      这是画游戏单元图形的函数之一:
      /*
       * DisplayPlane
       */
      BOOL DisplayPlane ( GFX_HBM  hBuffer, HPLANE *hPlane )
      {
          USHORT      n;
          USHORT      i;
          USHORT      j;
          USHORT      x1;
          USHORT      y1;
          USHORT      x2;
          USHORT      y2;
          USHORT      xmod;
          USHORT      ymod;
          POINT       src;
          RECT        dst;




          x1 = (hPlane->x >> 16) / C_TILE_W;          
          y1 = (hPlane->y >> 16) / C_TILE_H;
          x2 = x1 + C_SCREEN_W / C_TILE_W;
          y2 = y1 + C_SCREEN_H / C_TILE_H;
          xmod = (hPlane->x >> 16) % C_TILE_W;
          ymod = (hPlane->y >> 16) % C_TILE_H;


          for( j = y1; j < y2; ++j )
          {
      for( i = x1; i <= x2; ++i )
      {
         n = (i % hPlane->width) + j * hPlane->width;
         if( hPlane->hBM[n] != NULL )
         {
      if( i == x1 )
      {
         dst.left  = 0;
         dst.right = dst.left + C_TILE_W - xmod;
         src.x     = xmod;
      }
      else if( i == x2 )
      {
         dst.left  = (i - x1) * C_TILE_W - xmod;
         dst.right = dst.left + xmod;
         src.x     = 0;
      } else {
         dst.left  = (i - x1) * C_TILE_W - xmod;
         dst.right = dst.left + C_TILE_W;
         src.x     = 0;
      }
          
      if( j == y1 )
      {
             = 0;
         dst.bottom = + C_TILE_H - ymod;
         src.y      = ymod;
      }
      else if( j == y2 )
      {
             = (j - y1) * C_TILE_H - ymod;
         dst.bottom = + ymod;
         src.y      = 0;
      } else {
             = (j - y1) * C_TILE_H - ymod;
         dst.bottom = + C_TILE_H;
         src.y      = 0;
      }
       
      gfxBlt(&dst,hPlane->hBM[n],&src);
         }
      }
          }


          return TRUE;


      } /* DisplayPlane */


      4。将BackBuffer和FrontBuffer进行翻转;


      这是全屏幕模式下的页面翻转函数:
      /*
       * gfxFlip
       */
      BOOL gfxFlip( void )
      {
          HRESULT     ddrval;


          ddrval = IDirectDrawSurface_Flip( lpFrontBuffer, NULL, DDFLIP_WAIT );
          if( ddrval != DD_OK )
          {
      Msg( "Flip FAILED, rc=%08lx", ddrval );
      return FALSE;
          }
          return TRUE;


      } /* gfxFlip */


      这是窗口模式下的页面翻转函数:
      /*
       * gfxUpdateWindow
       */
      BOOL gfxUpdateWindow()
      {
          HRESULT     ddrval;


          ddrval = IDirectDrawSurface_Blt(
                          lpFrontBuffer,          // dest surface
                          &rcWindow,              // dest rect
                          lpBackBuffer,           // src surface
                          NULL,                   // src rect (all of it)
                          DDBLT_WAIT,
                          NULL);


          return ddrval == DD_OK;


      } /* gfxUpdateWindow */




      第七节 计算机人工智能


      计算机人工智能


      记得吗?在第五节刷新游戏单元中我们谈到在刷新游戏单元时,说到在取得游戏单位的的位置后要经过一些算法的判断再确定游戏单位的新位置。包含这些算法的部
      分就是游戏中实现人工智能的部分。


      对于游戏中的人工智能,我比较赞同下面这个定义:一个非游戏者控制的对象在基于各种复杂因素时的决策行为就象时由真正的人作出的,这是通过使用  一个决策算法来完成的,这个决策算法根据设计者确定的规则和提供给程序的信息进行处理。


      现在在大部分的游戏中采用的的方式主要有以下几种:


      检索
      许多人工智能的算法中都涉及到对所有可能性的检索。这个算法的实现方式是这样的,首先您应让您的程序列一个选项表,例如一个士兵到目的之间所有可能的路径。然后再使用其他的人工智能技术如排除法等来找一个最优的选择。


      排序
      排序与检索都是基本的人工智能技术,您可以用排序来确定最佳的决策次序。比        如,在战略游戏中计算机对手不断地根据当前的环境修改行动的优先级。


      专家系统
      专家系统是指运用“if then”语句的逻辑表达式来表示所有的基本规则,然后计算机根据这些规则作出智能决策。比如,在制作一个足球游戏时,就可以请一个足球专家,记下他的的足球经验,他会说明在各种情况下,他采取的踢球方式。根据这些信息,建立一套规则库,在游戏中计算机就可以按照这些规则作出决策。


      其他的方式还有:机器学习和和神经网络系统,这两种方式的效果相当不错。但是却很不容易掌握,这里我们就不再详述了。




      第八节 游戏内存管理


      游戏内存管理


      这一部分对于优质高效的游戏软件是十分重要的,内存管理不当会导致游戏性能的降低,甚至引起死机。现在的很多游戏都使用了大量的图象和复杂的规则,需要大量的内存。这就需要我们对游戏者所用机器的内存进行精心的分配和组织了。首先,我们应当调查一下现在的主流机型的内存是多少,再与达到游戏的设计目标所需的内存量之间权衡一下,然后确定一个粗略的分配方案。


      这个方案一般可以这样指定:


      1.这个游戏从情节上可以分为几个部分,在开始时将每个部分所共有的资料调入,然后根据情节的发展将该部分不用的资料所占用的内存释放再调入该部分所特有的资料。比如说可以分为几关或者RPG游戏中的“世界”的地图可以分成几个场景。然后在游戏者到达某一关或进入某一场景时再调入相应的图象或相应的资料。


      2.在每个部分中有很多并不常用而且调用时没有严格的速度限制同时调用时并不需要太多时间(通常1秒左右即可完成)的资料,也可以在使用时调用。比如角色从大地图上走入一个城市,这个城市的图象和游戏规则等资料就可以在走入这个城市时调入。


      在完成这个方案后,我们就完成了内存和硬盘之间数据交换的规划了,接下来就应考虑运行时内存内部的管理了。


      在这一步中主要应注意两个问题:


      1.迅速释放存放无效资料的内存;

      例如:


      描述GAMEWORLD的指针temp在初始化时分配了内存空间。
      GAMEWORLD *temp=new GAMEWORLD(Init value);
      。。。


      在程序结束时要释放内存空间。
      delete temp;


      2.严禁使用空指针(这样会导致系统错误,甚至死机)。这里没有什么技巧,只有靠您自己的认真和仔细了。


      例如:
      当在程序中已经释放了temp;
      下面的调用就可能导致死机:
      temp->Function();


      这两个问题的解决方法为:


      GAMEWORLD *temp=new GAMEWORLD(Init value);
      ...


      if(temp)
      delete temp;
      temp=NULL;
      ...


      if(temp)
      {
      temp->Function();
      ...
      }
      else
      {
      提示temp为空指针。
      }




      第九节 游戏交互设计


      游戏交互设计


      交互设计,实际上就是您想让游戏者怎么去操纵游戏的发展。说简单了交互设计就是游戏者怎样去控制游戏角色的行动,在例程中对键盘的设置——用小建盘上的“456237”控制狐狸的行为就是一种简单的交互设计。说复杂了呢!就是您提供了一种什么样的方式让游戏者进入游戏之中成为游戏中的一员——他就是游戏中英勇无敌、侠肝义胆的剑客,他就是游戏中足智多谋、威震天下的将军……这就是好游戏的一个重要的要素——好的交互性。


      交互性就是设计者创造的一个诱使人去玩的游戏所拥有的提供故事线索、情绪感染、真实的声音和其他创造性的媒介所给予的特性。交互设计的目的就是让游戏者进入“幻觉状态”,幻觉状态是游戏界的一个术语,它的意思是指游戏者的意识融入到游戏世界中,这样,他或她就不是在玩游戏,而是在体验另一个世界。


      怎样作到这一点呢?作为编程人员应考虑的是:


      第一步考虑输入设备问题,设备即是游戏者控制游戏的手段,也就是输入设备的选择和设置的问题。在这一步中应该考虑是选择键盘、鼠标、游戏杆还是几种结合的方式,或是其他类型的输入设备。然后是设置各种操作所代表的含义(就象例程中
      小键盘的“4”代表左行,“5”代表停止等等,或是鼠标单击、双击某个区域及拖动时代表的含义)这些设置主要是考虑一个操作的方便性的问题。


      typedef enum enum_ACTION {
          NONE,
          STILL,
          WALK,
          RUN,
          JUMP,
          THROW,
          CROUCH,
          STOP,
          STUNNED,
          JUMPTHROW,
          CROUCHWALK,
          BLURR,
          STRIKE,
          MISS,
          CHEW,
      } ACTION;




      WinMainProc中:


          case WM_KEYDOWN:
              switch( wParam )
              {
              case VK_NUMPAD5:
                  lastInput=KEY_STOP;
                  break;
              case VK_DOWN:
              case VK_NUMPAD2:
                  lastInput=KEY_DOWN;
                  break;
              case VK_LEFT:
              case VK_NUMPAD4:
                  lastInput=KEY_LEFT;
                  break;
              case VK_RIGHT:
              case VK_NUMPAD6:
                  lastInput=KEY_RIGHT;
                  break;
              case VK_UP:
              case VK_NUMPAD8:
                  lastInput=KEY_UP;
                  break;
              case VK_HOME:
              case VK_NUMPAD7:
                  lastInput=KEY_JUMP;
                  break;
              case VK_NUMPAD3:
                  lastInput=KEY_THROW;
                  break;
              case VK_F5:
                  bShowFrameCount = !bShowFrameCount;
                  if( bShowFrameCount )
                  {
                    dwFrameCount = 0;
                    dwFrameTime = timeGetTime();
                  }
              break;


      第二步考虑信息返回的问题,这里主要是一个界面的设计的问题。这个问题我们在
      第一章第六节游戏的界面设计中已经讨论过了,这里就不详述了。




      第十节 游戏图形底层设计


      在游戏中,计算机主要花时间在处理图象和画图象上,所以我们应尽力使这些操作适合主流机型的硬件水平或尽量少占用系统资源,这就是游戏图形底层设计的目的。在前面讲的DirectDraw和DirectX5 SDK中的Direct3D都是图形底层,还有ID在 QUAKE发行后提供的QUAKE C也是一种不错的图形底层。建立一套游戏图形底层需要大量的关于图形编程的知识和很多的时间精力,而且效果不一定好,同时在市场上也有很多图形底层可供选择。所以对于一般的游戏开发者来说,只要作的游戏使用的图象并没有使计算机不负重荷或并没有使用现有的底层所不支持的特性,我建议还是使用现有的底层。


      本例程的图形底层十分简单,采用DirectDraw提供的IDirectDrawSurface_BltFast和IDirectDrawSurface_Blt函数:


          if (pbm->lpSurface)
          {
              if( pbm->bTrans )
                  bltflags = bTransDest ? DDBLTFAST_DESTCOLORKEY : DDBLTFAST_SRCCOLORKEY;
              else
                  bltflags = bTransDest ? DDBLTFAST_DESTCOLORKEY : DDBLTFAST_NOCOLORKEY;
              ddrval = IDirectDrawSurface_BltFast(
                              lpBackBuffer, x, y,
                              pbm->lpSurface, &rc, bltflags | DDBLTFAST_WAIT);


              if (ddrval != DD_OK)
              {
                  Msg("BltFast failed err=%d", ddrval);
              }
          }
          else
          {
              DDBLTFX     ddbltfx;


              rc.left   = x;
                 = y;
              rc.right  = rc.left + dx;
              rc.bottom =  + dy;


              ddbltfx.dwSize = sizeof( ddbltfx );
              ddbltfx.dwFillColor = pbm->dwColor;


              ddrval = IDirectDrawSurface_Blt(
                              lpBackBuffer,           // dest surface
                              &rc,                    // dest rect
                              NULL,                   // src surface
                              NULL,                   // src rect
                              DDBLT_COLORFILL | DDBLT_WAIT,
                              &ddbltfx);
          }


      由于DirectDraw的通用性要求,所以虽然它提供的函数的速度很快,但是对特效的支持比较少。深入的图形底层应包括大量的高效的特效处理功能,所以我们应该能够直接对显存操作。


      DirectDraw的DirectSurface提供了这个入口,它的DDSURFACEDESC结构中的变量
      lpSurface就是显存映射的入口指针。


      typedef struct _DDSURFACEDESC {
          DWORD          dwSize;
          DWORD          dwFlags;
          DWORD          dwHeight;
          DWORD          dwWidth;
          union
          {
              LONG       lPitch;
              DWORD      dwLinearSize;
          };
          DWORD          dwBackBufferCount;
          union
          {
              DWORD      dwMipMapCount;
              DWORD      dwZBufferBitDepth;
              DWORD      dwRefreshRate;
          };
          DWORD          dwAlphaBitDepth;
          DWORD          dwReserved;


          LPVOID         lpSurface;
          DDCOLORKEY     ddckCKDestOverlay;
          DDCOLORKEY     ddckCKDestBlt;
          DDCOLORKEY     ddckCKSrcOverlay;
          DDCOLORKEY     ddckCKSrcBlt;
          DDPIXELFORMAT  ddpfPixelFormat;
          DDSCAPS        ddsCaps;
      } DDSURFACEDESC; 


      但是使用它之前,必须调用DirectDrawSurface3::Lock函数将此图形内存锁住,在处理后,调用DirectDrawSurface3::Unlock函数将此内存交还给系统。





      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.51cto.com/teayear/3186978,作者:跟张哥学编程,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:Spring5参考指南:AspectJ注解

      下一篇:Java生产环境下性能监控与调优详解

      相关文章

      2025-05-19 09:04:44

      js小题2:构造函数介绍与普通函数对比

      js小题2:构造函数介绍与普通函数对比

      2025-05-19 09:04:44
      new , 关键字 , 函数 , 对象 , 构造函数
      2025-05-19 09:04:30

      【Canvas技法】辐射式多道光影的实现

      【Canvas技法】辐射式多道光影的实现

      2025-05-19 09:04:30
      代码 , 函数 , 实现
      2025-05-19 09:04:22

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      2025-05-19 09:04:22
      使用 , 函数 , 初始化 , 定义 , 对象
      2025-05-19 09:04:14

      C语言字符函数和字符串函数--(超全超详细)

      C语言字符函数和字符串函数--(超全超详细)

      2025-05-19 09:04:14
      函数 , 字符 , 字符串
      2025-05-16 09:15:24

      如何将一串数字用函数的方法倒过来(C语言)

      如何将一串数字用函数的方法倒过来(C语言)

      2025-05-16 09:15:24
      函数 , 数字 , 数组
      2025-05-14 10:33:31

      【数据结构】第一章——绪论(2)

      【数据结构】第一章——绪论(2)

      2025-05-14 10:33:31
      函数 , 实现 , 打印 , 理解 , 算法 , 输入 , 输出
      2025-05-14 10:33:31

      计算机小白的成长历程——习题演练(函数篇)

      计算机小白的成长历程——习题演练(函数篇)

      2025-05-14 10:33:31
      函数 , 字符串 , 数组 , 知识点 , 编写 , 迭代 , 递归
      2025-05-14 10:33:25

      30天拿下Rust之高级类型

      Rust作为一门系统编程语言,以其独特的内存管理方式和强大的类型系统著称。其中,高级类型的应用,为Rust的开发者提供了丰富的编程工具和手段,使得开发者可以更加灵活和高效地进行编程。

      2025-05-14 10:33:25
      Rust , type , 代码 , 函数 , 类型 , 返回
      2025-05-14 10:33:25

      30天拿下Rust之网络编程

      在现代软件开发中,网络编程无处不在。无论是构建高性能的服务器、实时通信应用,还是实现复杂的分布式系统,对网络编程技术的掌握都至关重要。Rust语言以其卓越的安全性、高性能和优秀的并发模型,为网络编程提供了坚实的基础。

      2025-05-14 10:33:25
      Rust , TCP , 使用 , 客户端 , 异步 , 编程
      2025-05-14 10:33:16

      C++ 11新特性之tuple

      在C++编程语言的发展历程中,C++ 11标准引入了许多开创性的新特性,极大地提升了开发效率与代码质量。其中,tuple(元组)作为一种强大的容器类型,为处理多个不同类型的值提供了便捷的手段。

      2025-05-14 10:33:16
      std , 元素 , 函数 , 初始化 , 模板 , 类型
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5224851

      查看更多

      最新文章

      【Canvas技法】辐射式多道光影的实现

      2025-05-19 09:04:30

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      2025-05-19 09:04:22

      C语言字符函数和字符串函数--(超全超详细)

      2025-05-19 09:04:14

      如何将一串数字用函数的方法倒过来(C语言)

      2025-05-16 09:15:24

      30天拿下Rust之高级类型

      2025-05-14 10:33:25

      C++ 11新特性之tuple

      2025-05-14 10:33:16

      查看更多

      热门文章

      python学习——面向对象编程

      2023-04-25 10:20:57

      7、awk编程语言

      2023-03-31 06:14:11

      Python 函数调用父类详解

      2023-04-23 09:44:31

      leetcode刷题三十八

      2023-03-21 10:30:17

      数据结构与算法之七 栈

      2022-11-17 12:37:20

      游戏编程之十 图像引擎DirectXDraw

      2022-11-14 02:56:39

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      算法题:剑指 Offer 30. 包含min函数的栈(题目+思路+代码+注释)时空 O(1) O(1) 0ms击败94%、69%用户

      Python编程实战营:四款实用小项目助你快速入门,从零开始打造你的个人项目集!

      每日学习一个数据结构-布隆过滤器Bloom Filter

      R语言使用技巧全面指南

      【linux】linux C 程序 注册信号处理函数

      python中类型转换函数和eval内置函数

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号