3D游戏图形二维图像游戏基础

2020-03-01 207浏览

  • 1.二维图像游戏基础 1
  • 2.主要内容 图像文件结构 ( 以 BMP 为例) 图像显示(例程) 典型图像操作 典型的图像特效原理 二维游戏简单框架 二维游戏(例程) 2
  • 3.课程目标 理解图像概念 图像操作的实现 简单的图像特效 简单的二维游戏 3
  • 4.什么是图像 空间的离散的数字化的描述 二维区域 (r0,g0,b0) (r1,g1,b1) (r2,g2,b2) (r3,g3,b3) (r4,g4,b4) (r5,g5,b5) (r6,g6,b6) (r7,g7,b7) 4
  • 5.图像表示 文件格式 ◦ BMP, TGA, TIFF, GIF, JPEG 等 定义 (Bitmap) ◦ 位图图像是一块由彩色点集组成的矩形区域。 DIB(Device Independent bitmap) ◦ 设备无关位图 DDB(Device-Dependent Bitmaps) ◦ 设备有关位图:老的 Windows 系统。 5
  • 6.BMP 结构 BMP 文件头 ◦ 比较简单的头信息 位图信息 ◦ 关于数据尺寸的详细信息 调色板 表 (optional) :相当于一个查找 ◦ RGB 四元组 位图数据 ◦ RGB 象素值 ◦ 索引值 ( 如果有调色板 ) 6
  • 7.BMP 文件头 typedef struct { WORD bfType; //“BM”, 表示该文件为位图 DWORD bfSize; // 文件大小 ( 以 byte 计 ) WORD bfReserved1; // 保留位 WORD bfReserved2; DWORD bfOffbits; // 实 实 位实 实 实 实起始位置相 实 实 实 实 实 实于文件 实 实 实 实的偏移量 实 } BITMAPFILEHEADER 7
  • 8.位图信息 typedef struct { DWORD biSize; DWORD biWidth; DWORD niHeight; //= sizeof(BITMAPINFOHEADER) // 位图宽度 ( 以象素计 ) // 正数,左下角为起始点,从下向上 // 负数,左上角为起点,从上向下 WORD biplanes; //color plane 数,恒等于 1 WORD biBitCount; // 1, 4, 8, 24, 32 DWORD biCompression; // 通常,以下域可以忽略,置实0 即可 DWORD biSizeImage; DWORD biXPelsPerMeter; DWORD biYPelsPerMeter; DWORD biClrUsed; // 位图使用的颜色 DWORD biClrImportant; // 重要实实色数 实 } BITMAPINFOHEADER 8
  • 9.RGB 四元组 typedef struct { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD 9
  • 10.BMP 图像显示 从 BMP 文件中载入图像数据 ◦ 分配内存缓冲区 ◦ 基于文件结构提取图像数据 在屏幕上显示图像:将图像数据发送至显 卡 ◦ Bitblt() :块拷贝图像数据 ◦ OpenGL/DirectX StretchBlt :将一个保存在 DIB 中的图像 数据拷贝到另外一个矩形区域,带缩放。 ----Demo 演示 10
  • 11.图像操作  全局操作 ◦ 对图像上的所有象素作同样的操作 如:傅立叶变变变 、直方 变 变 变变变 计、 变 变变 拷变变 、灰度 变 变 变变变 、变  局部操作 ◦ 操作只与象素及其周围邻居的值有关 如:滤波、边缘检测等 11
  • 12.直方图 表示图像中象素颜色值的分布 直方图上每一点   ◦ ◦ 横坐变变 :变变 色(亮度) 变变变变变 纵坐标:图像中具有该颜色(亮度)值的 象素的数目 12
  • 13.亮度增强 亮度  ◦ 图像象素颜色平均值 用直方变变变 行亮度增 变变 变 变  ◦ ◦ 将每一个象素灰度值加上一个常数 得到的直方图是原始直方图向正轴方向的 平移 13
  • 14.提高对比度 每一象素点 (x,y) 上的图像对比度 C 定义为 ◦ I(x,y) - 该点象素值 ◦ I - 背景亮度平均值 ◦ Imax - 图像灰度最大值 ◦ 0 – 图像灰度最小值 通过直方图提高对比度 ◦ 将原始图像直方图的取值范围 [Xmin,Xmax] 通 过线性变换扩大至 [0,Ymax] 14
  • 15.算术运算 加 : 减 : 除 : 与 : 或 : 异或 : 15
  • 16.几何运算 平移 ◦ 将图像沿坐标轴移动若干偏移量 缩放 ◦ 整数倍放大 ◦ 整数倍缩小 ◦ 一般情况:缩放系数非整数的情况 旋转 ◦ 旋变 矩 变 变 R 及其逆矩变 R-1 16
  • 17.图像滤波算子  本质上实现的时候都是将某个象素的新的 值用邻域象素值的加权平均计算而得。 例如:边界增强算子 垂直方向算子 水平方向算子 17
  • 18.形态算子 一组空间滤波操作 用于改变二值区域的形状 ◦ 腐蚀:减少物体边界的象素数 ◦ 膨胀:增加物体边界的象素数 ◦ 变 合 方法  开:腐蚀,然后膨胀  闭:膨胀,然后腐蚀 18
  • 19.Original Image
  • 20.膨胀与腐蚀( Dilation, Erosion ) 数学形态学里面最重要的操作 腐蚀将图像的尺寸减少 膨胀增加图像的尺寸 可以用来消除图像上小的亮斑噪声和 不规则的边 20
  • 21.腐蚀(续) 定义:物体的颜色是白,背景是黑 定义腐蚀模板为 ◦1 ◦1 ◦1 1 1 1 1 1 1 将模板与图像进行加操作 如果有,则结果为 1 ,否则为 0 21
  • 22.腐蚀(续) 模板的效果相当于去掉物体边界处的单个象素 4 种情况 : ◦ 当前处理象素为 1 ,邻域象素为 1 -》 1 ◦ 当前处理象素为 0 ,邻域象素为 1 -》 0 ◦ 当前处理象素为 0 ,邻域象素为 1 、 0 的混合-》 0 ◦ 当前处理象素为 1 ,邻域象素为 1 、 0 的混合 -》 1 22
  • 23.腐蚀(续) 原始图像 腐蚀两次 腐蚀后的图像 23
  • 24.膨胀 膨胀是腐蚀的逆操作 模板文件是 ◦0 ◦0 ◦0 0 0 0 0 0 0 其效果相当于在物体的边界添加单个 象素 24
  • 25.膨胀(续)  4 种情况  当前处理象素为 0 ,邻域象素为 0 -》 0  当前处理象素为 1 ,邻域象素为 1 -》 1  当前处理象素为 1 ,邻域象素为 1 、 0 的混合 -》 1  当前处理象素为 0 ,邻域象素为 1 、 0 的混合 -》 1  逻辑操作算子是 Or 25
  • 26.膨胀(续) 原始图像 膨胀多次后的图像 膨胀图像 26
  • 27.开操作  开操作相当于先做腐蚀操作,再做膨胀操作  效果相当于去掉单个象素,但是保留原来的 形状何尺寸。 原始图像 腐蚀两次,然后膨胀两次(开操作) 27
  • 28.变操 作  闭操作是开操作的相互操作  先膨胀,然后腐蚀  它可以用来填补一些小洞 原始图像 闭操作结果 28
  • 29.轮廓抽取  先做腐蚀操作, 再将腐蚀结果图像减去原 始图像 29
  • 30.图像特效工具 例如 : Adobe Premiere 或者 Avid ◦ 提供了丰富的特效 ◦ 非常方便和简单 ! 电影广告中的特效经常以这种方式完 成 Hollywood 由于资金不缺,经常是手 工完成 ◦ 但是现在的趋势是编程实现 30
  • 31.淡入淡出  最简单的图像特效 ◦ 图像从一个黑色背景中出现  令透明度 ALPHA 从 0 变到 1 ◦ ALPHA = 0 图像为黑色 ◦ ALPHA = 1 原始图像 ◦ Alpha 改变变 的速度决定了 变 变 变 变 变 变变 像的溶解速度 变变变变变  如果让 ALPHA 从 1 变到 0 ,就得到 淡出的效果 31
  • 32.Morphing (变形) 图像处理中最有意思的效果 ◦ 某个物体伸展到另外一个物体 ◦ 通常利用网格辅助 ◦ 也涉及很多计算机变变变 的知 变变 图像溶解 图像变形
  • 33.Morphing (变形) 电影《黑与白》中应用的技术 蜘蛛网格演示
  • 34.模糊( blur ) 本质上相当于一个信号处理中的反走 样滤波 将每个象素用其周围邻域象素值的加 权平均值替代 效果一般 34
  • 35.
  • 36.图像量化 改变图像中颜色的数目或者灰度的层 次 效果非常有趣 36
  • 37.
  • 38.风格化图像 保留尖锐的边缘 其他部分用纹理来代替 下图是 Intel 公司网变变 上的一个例子 变 变变 变变 38
  • 39.风格化图像(续) 用多种合成滤波方法产生油画风格 39
  • 40.风格化图像(续) 利用类比的方法产生更多风格各异的 图像 40
  • 41.风格化图像(续) 纹理合成+类比的方法 41
  • 42.二维游戏技术 二维游戏概览 地变变 的变变 建与 变 变变 示 颜色混合与半透明 精灵动画 碰撞检测 游戏循环概念及实例解析 42
  • 43.二维游戏 早期的游戏都是二维的  ◦ ◦  如 Diablo( 暗黑破坏神 ) 只有两个轴(上下,左右) 很多 RPG 游戏是固定视角的二维半 43
  • 44.二维游戏(续)  二维游戏对现在的编程仍然 有意义(特别是现在的手机游 戏):如内存、分辨率  本质上视频游戏是一个连续 的循环,执行逻辑指令,并 将图像输出到屏幕。这和电 影的播放非常类似,但是这 个电影是用户指定的。 44
  • 45.地图的创建与显示 为实现一个基本的二维游戏框架,首 先要实现游戏地图的各种加载和编辑操 作,为角色提供游戏环境。 不同的游戏对图像有不同的要求,有 些要求地图尽量接近现实,有真实感; 有的游戏则只需要一个简单的背景图片 。 45
  • 46.地图的创建与显示 地图很大时会耗费很大内存资源,可 将大的地图区域拆分成小块,每块对应 一个小块地图 (tile), 这些小块地图可 以重复使用,并且可以拼合在一起形成 大的地图。 种通用地图实现的方法:固定地图、 滚屏地图、多层次地图、菱形地图 4 46
  • 47.固定地图  使用固定的背景作为地图  将屏幕切割成棋盘状的一系列小块  在内存中保持一个二维数组,保存每个小块对应的 编号  绘制时根据数组提供的信息,在每个小块画上相应 图块 47
  • 48.固定地图拼接算法实现 for (yi = 0; yi49.滚屏地图 是固定地变 的 变 变 一步 变变 变变 展,可以 变 变变 变变 示 远大于固定地图的图像 根据玩家所在位置,确定显示的地图 部分 完整地图 屏幕可见范围 滚屏地图 4950.滚屏地图算法实现 变量设置: playerx, playery 为人物相对于完整地图左上角的坐标; screen_wide, screen_high 为屏幕的宽和高; xtile 为屏幕上 x 轴上可显示的小地图个数; ytile 为屏幕上 y 轴上可显示的小地图个数; tileplayerx = playerx / tile_wide 为人物所在格 x 轴下标; tileplayery = playery / tile_high 为人物所在格 y 轴下标; 应该绘制的地图范围是: x 轴: 由 tileplayerx - xtile/2 至 tileplayerx + xtile/2 ; y 轴: 由 tileplayery - ytile/2 至 tileplayery + ytile/2 ; 当人物在屏幕正中央时,地图到屏幕的位置变化公式为: screenx = xi * tile_wide – playerx + 0.5 * screen_wide screeny = yi * tile_high – playery + 0.5 * screen_high 5051.滚屏地图算法实现(续) 例程: int beginx = tileplayerx - xtile/2 int endx = tileplayerx + xtile/2 int beginy = tileplayery - ytile/2 int endy = tileplayery + ytile/2 tileplayerx = playerx / tile_wide tileplayery = playery / tile_high for (yi = beginy; yi52.多层次地图  以下列情况,可以考虑使用多层次地图。 ◦ 需要小地图能重叠或者有层次关系; ◦ 在背景上有多个物体运动; ◦ 需要模拟物体远近不同的透视关系;  多层次地图的实现思想并不复杂 , 在滚屏地图的基础上 设置多个层次的地图即可。不妨设从底往上分别为 0 层 , 1 层,…把地图数据数组改为三维数组。  可以使每个图层以不同的速度运动 , 模拟景物远近不同 的层次感。这种技术,又称视差卷轴( Parallax Scrollers )。 5253.菱形地图  除了视差卷轴技术可以模拟三维效果外,菱形地 图是在二维画面上表现三维场景的常用技术  拼接所使用的小地图是菱形,而不是前面用到的 矩形地图,因此关于贴图位置的计算比较复杂 5354.菱形地图 通常前一个坐标轴为 X 坐标,后一个坐标轴为 Y 坐 标  当 X 坐标值增大时,对应的菱形水平位置右移 n* 菱 形小地图长度 /2, 竖直位置下移 n* 菱形小地图高 度 /2  当 Y 坐标值增大时,对应的菱形水平位置左移 n* 菱 形小地图长度 /2, 竖直位置下移 n* 菱形小地图高 度 /2  5455.菱形地图算法实现 int MapDraw(HDC hdc){ int i,j; int lim=MAXSCREENX/TILEWIDE; // 所需绘制地图的范围 for (i=-lim; i0) && (sy+TILEHIGH>0) && (playerx+i<100 && playerx+i>=0) && (playery+j<100 && playery+j>=0) ){ // 边界判断 TransparentBlt(hdcMem,sx,sy,TILEWIDE,TILEHIGH, hdcTiles[Data[playerx+i][playery+j]], 0,0,TILEWIDE,TILEHIGH,RGB(0,255,0)); // 贴图 } } } BitBlt(hdc,0,0,MAXSCREENX,MAXSCREENY,hdcMem,0,0,SRCCOPY);// 画到窗口 return 0; } 5556.图像的颜色混合与半透明操作  二维游戏开发中,颜色的混合与半透明效果处理是最常 见的二维画面的视觉效果生成方法之一  如需要将多个图像重叠显示,但又不是完全覆盖,就需 用到半透明技术 +  每种颜色都由红绿蓝 3 种基本色彩(三原色)组合而成 ;  三原色中每一种颜色的亮度用一个 8 位的二进制数来表 示  半透明图色彩 = 源图像色彩 × ( 100% - 透明度) + 背景图像色彩 × 透明度 5657.图像的颜色混合与半透明操作  Windows API 函数: AlphaBlend 专门对带有 Alpha 值的图像进行透明和混合处理  三种方法 用全局透明度忽略各像素的 Alpha 值 忽略全局透明度将源图像中间的透明通道 Alpha 设为 0 每个像素的 RGB 自行乘上透明度 既使用全局透明度又使用各像素的 Alpha 值 1. 2. 3. 5758.精灵动画 (1) 基于精灵的人物表现 ghosts, 精灵 sprites, 骑士 knights 精灵:前景是图像,背景是透明的 精灵动画:将上一帧中精灵出现的地方用 背景填充,并在新的指定地点绘制精灵 鬼怪 透明区域 5859.图像镂空 (2)  将掩码图和背景图案进行按位 AND ,使得原始图像的 对应位置变空。  将原始图像和上一步处理结果按位 OR 。  这样,原始图像贴到背景上并遮盖背景,其余部分(掩 码图中白色部分)没有贴到背景上。 原始图像 掩码图 5960.图像镂空 (2) 其原理: 根据 C 语言里的位运算,可以知道任 何颜色 RGB 值和黑色按位与,得到黑 色;和白色按位与,结果不变。同样的 ,任何颜色和黑色按位或,不变;白色 按位或,得到白色。 6061.精灵动画 (3)  英文为 sprite animation ◦ 一幅背景图 ◦ 一组模板图( mask ) ◦ 人物的连续显示方式  双缓冲机制  不要在窗口中直接贴图 ,避免闪烁  建立一个内存 DC ,然后把所 有的变 变 变 变 作都在 变变变变 个 DC 上 进行,最后把结果显示到操作 窗口中。 6162.精灵动画( 4 ) 对动画序列中的每一帧 ◦ Load 背景图 ◦ 确定 sprite 绘画的位置 ◦ 将某一掩码图与背景图作 AND 运算 ◦ 将对应的人物图与背景图作 OR 运算 ◦ 更新 sprite 绘画的位置 6263.碰撞检测 对运动物体的碰撞判断是许多游戏程 序中不可或缺的要素 常见的碰撞检测方法 ◦ 区域检测 ◦ 碰撞点检测 ◦ 变色变变变: 变变变精确, 变 变 相对耗时 区域检测 碰撞点检测 6364.区域检测 采用某种规则形状 逼近物体 物体之间的碰撞检 测转化为规则形状之 间的检测 6465.碰撞点检测 本质是区域检测的一种 一般在两个运动物体中 的一个物体上设置碰撞 点,在另一个物体上设 置检测区域,运行时逐 个判断碰撞点是否在检 测区域中。 6566.颜色检测  为树林做一张掩码图,将树林用黑色填充。要产生汽车驶入 树林后面的效果,先在背景上贴上汽车的图像,然后在上面 用镂空图技术画上树林。然后,判断汽车图像在树林图像上 的相对位置,将汽车图像上的点和掩码图上相应位置的点做 按位 AND 操作,检查结果中是否有黑色点( RGB 值为 0 )存在。任何颜色的 RGB 值与黑色图形进行按位 AND 运 算,将得到黑色。如果存在黑色点,表明有碰撞。 6667.物体运动模拟 需要模拟现实世界中的物体运动时, 可依据物理公式,计算运动物体的坐标 并在屏幕上现实。 如:实现一个简单小球的运动画面, 若想达到小球在窗口的每条边上反弹的 效果,只要判断出小球与窗口边缘的碰 撞,并在碰撞时候速度取反即可。 6768.游戏循环的基本步骤 1. 2. 3. 4. 5. 6. 7. 初始化 初始化 进入游戏循环 查询用户输入状态 执行游戏逻辑和 AI 判断 绘制图像 循环 退出 过程循环 按键处理 图像绘制 AI 逻辑 退出游戏 6869.Game Init Game Menu Game Starting Game Restart Game Run Game Exit 6970.// defines for game loop states #define GAME_INIT #define GAME_MENU #define GAME_STARTING #define GAME_RUN #define GAME_RESTART #define GAME_EXIT 1 2 3 4 5 6 // the game is initializing // the game is in the menu // the game is about to run // the game is now running // the game is going to restart // the game is exiting // game globals int game_state = GAME_INIT; // start off in this state Int error // used to send errors back to OS = 0; // main begins here Void main( ) { // implementation of main game loop 7071.While (game_state != GAME_EXIT) { // implementation of main game loop switch(game_state) { case GAME_INIT:{ // the game is initializing // allocate all memory and resources Init( ); game_state = GAME_MENU; } break; 7172.case GAME_MENU:{ // the game is in the menu // call the main menu function and let it switch states game_state = Menu( ); //note:we could force a RUN state here } break; case GAME_STARTING:{ // the game is about to run // this state is optional, but usually used to set things up right // before the game is run you might do a little more housekeeping Setup_For_Run( ); // switch to run state game_state = GAME_RUN; } break; 7273.case GAME_RUN:{ // the game is now running // this section contains the entire game logic loop Clear( ); // clear the display Get_Input( ); // get the input Do_Logic( ); // perform logic and AI Render_Frame( ); // display the next frame of animation Wait( ); // synchronize the display // the only way that state can be changed is thru user interaction // in the input section or by maybe losing the game. } break; 7374.case GAME_RESTART:// the game is restarting { // this section is a cleanup state used to fix up any loose ends // before running again Fixup( ); // switch states back to the menu game_state = GAME_MENU; } break; 7475.case GAME_EXIT:{ // the game is exiting // if the game is in this state then it’s time to bail, kill everything // and cross your fingers Release_And_Cleanup( ); error =0; // set the error word to whatever //note:we do not have to switch states since we are already in this state // on the next loop iteration the code will fall out of the main while and // exit back to the OS } break;default:break; } // end switch return(error); // return error code to operating system } // end main 7576.二维潜艇游戏分析 具变 了最基本的游 变 变 变变变 变变 变 要素:目 变变 变变 变 性、计分 变变 变 系统、竞技性等。 主要利用的技术: Windows GDI 、二维 图像77.二维潜艇游戏分析 (续) CChildView CSubmarine CMyShip CMyObject CBomb CExplosion CTorpedo CScore78.二维潜艇游戏分析 (续) CMyObject 所有物体的基类 CPoint GetPos() ; CMyObject virtual CRect GetRect(); 虚函数,获得物 体的矩形坐标 virtual bool Draw(CDC* pDC, bool bPause); 在 pDC 上绘制当前物体图像 bool IsSubmarine(); 判断当前物体是否是 潜艇79.二维潜艇游戏分析 (续) CmyShip 我方战舰,由 CmyObject 继承 static void DeleteImage(); CMyShip 释放内存 static BOOL LoadImage(); 图像初始化 bool Draw(CDC* pDC, bool bPause); 绘制 void SetMotion(int motion) 水平移动, motion 是移动距离 int GetMotion() 获取战舰移动方面 CPoint GetPos() 获取战舰绘制坐标 CRect GetRect() 获得物体的矩形坐标80.二维潜艇游戏分析 (续) CSubmarine Csubmarine 变 方 潜水艇 static void DeleteImage(); 释放内存 static BOOL LoadImage(); 图像初始化 CRect GetRect() 获得物体的矩形坐标 int GetType() 获得潜艇类型,(绿色、黄色) bool Draw(CDC* pDC, bool bPause); void SetFireFlag() 艇多次开火 bool GetFireFlag() 变 置 绘制 已 变 变 开火 变变 变变 志,以防潜 变 变变 判断潜艇是否开火81.二维潜艇游戏分析 (续) Cexplosion 我方炸弹 CRect GetRect() int GetMulti() 的潜艇个数) 获得物体的矩形坐标 获取 m_nMulti (标记连锁爆炸 void SetMulti(int Multi) { m_nMulti = Multi; } 设置 m_nMulti bool Draw(CDC* pDC, bool bPause); 绘制82.二维潜艇游戏分析 (续) CScore Cscore 分数显示 bool Draw(CDC* pDC, bool bPause); 绘制 CRect GetRect() (返回 NULL ,无效函数) static int GetTotalScore() 获得当前总得分 { return m_nTotalScore; }83.课后作业 1. 熟练掌握一种图像处理工具,推荐 ACDSee, Photoshop8.0 ; 2. 详细读码:潜艇游戏 3. 熟练掌握 Windows 图像操作; 4. 变变一 个精灵 变变 变画; 5. 参考 VC++ 数字变变 像变变 理代 变 变变 ,变变变 任意一种 变 变 变 变变 像变变 波 8384.课后预读 1. 三维向量代数与矩阵运算 2. 基本的三维变换矩阵 84
  • 49.滚屏地图 是固定地变 的 变 变 一步 变变 变变 展,可以 变 变变 变变 示 远大于固定地图的图像 根据玩家所在位置,确定显示的地图 部分 完整地图 屏幕可见范围 滚屏地图 49
  • 50.滚屏地图算法实现 变量设置: playerx, playery 为人物相对于完整地图左上角的坐标; screen_wide, screen_high 为屏幕的宽和高; xtile 为屏幕上 x 轴上可显示的小地图个数; ytile 为屏幕上 y 轴上可显示的小地图个数; tileplayerx = playerx / tile_wide 为人物所在格 x 轴下标; tileplayery = playery / tile_high 为人物所在格 y 轴下标; 应该绘制的地图范围是: x 轴: 由 tileplayerx - xtile/2 至 tileplayerx + xtile/2 ; y 轴: 由 tileplayery - ytile/2 至 tileplayery + ytile/2 ; 当人物在屏幕正中央时,地图到屏幕的位置变化公式为: screenx = xi * tile_wide – playerx + 0.5 * screen_wide screeny = yi * tile_high – playery + 0.5 * screen_high 50
  • 51.滚屏地图算法实现(续) 例程: int beginx = tileplayerx - xtile/2 int endx = tileplayerx + xtile/2 int beginy = tileplayery - ytile/2 int endy = tileplayery + ytile/2 tileplayerx = playerx / tile_wide tileplayery = playery / tile_high for (yi = beginy; yi52.多层次地图  以下列情况,可以考虑使用多层次地图。 ◦ 需要小地图能重叠或者有层次关系; ◦ 在背景上有多个物体运动; ◦ 需要模拟物体远近不同的透视关系;  多层次地图的实现思想并不复杂 , 在滚屏地图的基础上 设置多个层次的地图即可。不妨设从底往上分别为 0 层 , 1 层,…把地图数据数组改为三维数组。  可以使每个图层以不同的速度运动 , 模拟景物远近不同 的层次感。这种技术,又称视差卷轴( Parallax Scrollers )。 5253.菱形地图  除了视差卷轴技术可以模拟三维效果外,菱形地 图是在二维画面上表现三维场景的常用技术  拼接所使用的小地图是菱形,而不是前面用到的 矩形地图,因此关于贴图位置的计算比较复杂 5354.菱形地图 通常前一个坐标轴为 X 坐标,后一个坐标轴为 Y 坐 标  当 X 坐标值增大时,对应的菱形水平位置右移 n* 菱 形小地图长度 /2, 竖直位置下移 n* 菱形小地图高 度 /2  当 Y 坐标值增大时,对应的菱形水平位置左移 n* 菱 形小地图长度 /2, 竖直位置下移 n* 菱形小地图高 度 /2  5455.菱形地图算法实现 int MapDraw(HDC hdc){ int i,j; int lim=MAXSCREENX/TILEWIDE; // 所需绘制地图的范围 for (i=-lim; i0) && (sy+TILEHIGH>0) && (playerx+i<100 && playerx+i>=0) && (playery+j<100 && playery+j>=0) ){ // 边界判断 TransparentBlt(hdcMem,sx,sy,TILEWIDE,TILEHIGH, hdcTiles[Data[playerx+i][playery+j]], 0,0,TILEWIDE,TILEHIGH,RGB(0,255,0)); // 贴图 } } } BitBlt(hdc,0,0,MAXSCREENX,MAXSCREENY,hdcMem,0,0,SRCCOPY);// 画到窗口 return 0; } 5556.图像的颜色混合与半透明操作  二维游戏开发中,颜色的混合与半透明效果处理是最常 见的二维画面的视觉效果生成方法之一  如需要将多个图像重叠显示,但又不是完全覆盖,就需 用到半透明技术 +  每种颜色都由红绿蓝 3 种基本色彩(三原色)组合而成 ;  三原色中每一种颜色的亮度用一个 8 位的二进制数来表 示  半透明图色彩 = 源图像色彩 × ( 100% - 透明度) + 背景图像色彩 × 透明度 5657.图像的颜色混合与半透明操作  Windows API 函数: AlphaBlend 专门对带有 Alpha 值的图像进行透明和混合处理  三种方法 用全局透明度忽略各像素的 Alpha 值 忽略全局透明度将源图像中间的透明通道 Alpha 设为 0 每个像素的 RGB 自行乘上透明度 既使用全局透明度又使用各像素的 Alpha 值 1. 2. 3. 5758.精灵动画 (1) 基于精灵的人物表现 ghosts, 精灵 sprites, 骑士 knights 精灵:前景是图像,背景是透明的 精灵动画:将上一帧中精灵出现的地方用 背景填充,并在新的指定地点绘制精灵 鬼怪 透明区域 5859.图像镂空 (2)  将掩码图和背景图案进行按位 AND ,使得原始图像的 对应位置变空。  将原始图像和上一步处理结果按位 OR 。  这样,原始图像贴到背景上并遮盖背景,其余部分(掩 码图中白色部分)没有贴到背景上。 原始图像 掩码图 5960.图像镂空 (2) 其原理: 根据 C 语言里的位运算,可以知道任 何颜色 RGB 值和黑色按位与,得到黑 色;和白色按位与,结果不变。同样的 ,任何颜色和黑色按位或,不变;白色 按位或,得到白色。 6061.精灵动画 (3)  英文为 sprite animation ◦ 一幅背景图 ◦ 一组模板图( mask ) ◦ 人物的连续显示方式  双缓冲机制  不要在窗口中直接贴图 ,避免闪烁  建立一个内存 DC ,然后把所 有的变 变 变 变 作都在 变变变变 个 DC 上 进行,最后把结果显示到操作 窗口中。 6162.精灵动画( 4 ) 对动画序列中的每一帧 ◦ Load 背景图 ◦ 确定 sprite 绘画的位置 ◦ 将某一掩码图与背景图作 AND 运算 ◦ 将对应的人物图与背景图作 OR 运算 ◦ 更新 sprite 绘画的位置 6263.碰撞检测 对运动物体的碰撞判断是许多游戏程 序中不可或缺的要素 常见的碰撞检测方法 ◦ 区域检测 ◦ 碰撞点检测 ◦ 变色变变变: 变变变精确, 变 变 相对耗时 区域检测 碰撞点检测 6364.区域检测 采用某种规则形状 逼近物体 物体之间的碰撞检 测转化为规则形状之 间的检测 6465.碰撞点检测 本质是区域检测的一种 一般在两个运动物体中 的一个物体上设置碰撞 点,在另一个物体上设 置检测区域,运行时逐 个判断碰撞点是否在检 测区域中。 6566.颜色检测  为树林做一张掩码图,将树林用黑色填充。要产生汽车驶入 树林后面的效果,先在背景上贴上汽车的图像,然后在上面 用镂空图技术画上树林。然后,判断汽车图像在树林图像上 的相对位置,将汽车图像上的点和掩码图上相应位置的点做 按位 AND 操作,检查结果中是否有黑色点( RGB 值为 0 )存在。任何颜色的 RGB 值与黑色图形进行按位 AND 运 算,将得到黑色。如果存在黑色点,表明有碰撞。 6667.物体运动模拟 需要模拟现实世界中的物体运动时, 可依据物理公式,计算运动物体的坐标 并在屏幕上现实。 如:实现一个简单小球的运动画面, 若想达到小球在窗口的每条边上反弹的 效果,只要判断出小球与窗口边缘的碰 撞,并在碰撞时候速度取反即可。 6768.游戏循环的基本步骤 1. 2. 3. 4. 5. 6. 7. 初始化 初始化 进入游戏循环 查询用户输入状态 执行游戏逻辑和 AI 判断 绘制图像 循环 退出 过程循环 按键处理 图像绘制 AI 逻辑 退出游戏 6869.Game Init Game Menu Game Starting Game Restart Game Run Game Exit 6970.// defines for game loop states #define GAME_INIT #define GAME_MENU #define GAME_STARTING #define GAME_RUN #define GAME_RESTART #define GAME_EXIT 1 2 3 4 5 6 // the game is initializing // the game is in the menu // the game is about to run // the game is now running // the game is going to restart // the game is exiting // game globals int game_state = GAME_INIT; // start off in this state Int error // used to send errors back to OS = 0; // main begins here Void main( ) { // implementation of main game loop 7071.While (game_state != GAME_EXIT) { // implementation of main game loop switch(game_state) { case GAME_INIT:{ // the game is initializing // allocate all memory and resources Init( ); game_state = GAME_MENU; } break; 7172.case GAME_MENU:{ // the game is in the menu // call the main menu function and let it switch states game_state = Menu( ); //note:we could force a RUN state here } break; case GAME_STARTING:{ // the game is about to run // this state is optional, but usually used to set things up right // before the game is run you might do a little more housekeeping Setup_For_Run( ); // switch to run state game_state = GAME_RUN; } break; 7273.case GAME_RUN:{ // the game is now running // this section contains the entire game logic loop Clear( ); // clear the display Get_Input( ); // get the input Do_Logic( ); // perform logic and AI Render_Frame( ); // display the next frame of animation Wait( ); // synchronize the display // the only way that state can be changed is thru user interaction // in the input section or by maybe losing the game. } break; 7374.case GAME_RESTART:// the game is restarting { // this section is a cleanup state used to fix up any loose ends // before running again Fixup( ); // switch states back to the menu game_state = GAME_MENU; } break; 7475.case GAME_EXIT:{ // the game is exiting // if the game is in this state then it’s time to bail, kill everything // and cross your fingers Release_And_Cleanup( ); error =0; // set the error word to whatever //note:we do not have to switch states since we are already in this state // on the next loop iteration the code will fall out of the main while and // exit back to the OS } break;default:break; } // end switch return(error); // return error code to operating system } // end main 7576.二维潜艇游戏分析 具变 了最基本的游 变 变 变变变 变变 变 要素:目 变变 变变 变 性、计分 变变 变 系统、竞技性等。 主要利用的技术: Windows GDI 、二维 图像77.二维潜艇游戏分析 (续) CChildView CSubmarine CMyShip CMyObject CBomb CExplosion CTorpedo CScore78.二维潜艇游戏分析 (续) CMyObject 所有物体的基类 CPoint GetPos() ; CMyObject virtual CRect GetRect(); 虚函数,获得物 体的矩形坐标 virtual bool Draw(CDC* pDC, bool bPause); 在 pDC 上绘制当前物体图像 bool IsSubmarine(); 判断当前物体是否是 潜艇79.二维潜艇游戏分析 (续) CmyShip 我方战舰,由 CmyObject 继承 static void DeleteImage(); CMyShip 释放内存 static BOOL LoadImage(); 图像初始化 bool Draw(CDC* pDC, bool bPause); 绘制 void SetMotion(int motion) 水平移动, motion 是移动距离 int GetMotion() 获取战舰移动方面 CPoint GetPos() 获取战舰绘制坐标 CRect GetRect() 获得物体的矩形坐标80.二维潜艇游戏分析 (续) CSubmarine Csubmarine 变 方 潜水艇 static void DeleteImage(); 释放内存 static BOOL LoadImage(); 图像初始化 CRect GetRect() 获得物体的矩形坐标 int GetType() 获得潜艇类型,(绿色、黄色) bool Draw(CDC* pDC, bool bPause); void SetFireFlag() 艇多次开火 bool GetFireFlag() 变 置 绘制 已 变 变 开火 变变 变变 志,以防潜 变 变变 判断潜艇是否开火81.二维潜艇游戏分析 (续) Cexplosion 我方炸弹 CRect GetRect() int GetMulti() 的潜艇个数) 获得物体的矩形坐标 获取 m_nMulti (标记连锁爆炸 void SetMulti(int Multi) { m_nMulti = Multi; } 设置 m_nMulti bool Draw(CDC* pDC, bool bPause); 绘制82.二维潜艇游戏分析 (续) CScore Cscore 分数显示 bool Draw(CDC* pDC, bool bPause); 绘制 CRect GetRect() (返回 NULL ,无效函数) static int GetTotalScore() 获得当前总得分 { return m_nTotalScore; }83.课后作业 1. 熟练掌握一种图像处理工具,推荐 ACDSee, Photoshop8.0 ; 2. 详细读码:潜艇游戏 3. 熟练掌握 Windows 图像操作; 4. 变变一 个精灵 变变 变画; 5. 参考 VC++ 数字变变 像变变 理代 变 变变 ,变变变 任意一种 变 变 变 变变 像变变 波 8384.课后预读 1. 三维向量代数与矩阵运算 2. 基本的三维变换矩阵 84
  • 52.多层次地图  以下列情况,可以考虑使用多层次地图。 ◦ 需要小地图能重叠或者有层次关系; ◦ 在背景上有多个物体运动; ◦ 需要模拟物体远近不同的透视关系;  多层次地图的实现思想并不复杂 , 在滚屏地图的基础上 设置多个层次的地图即可。不妨设从底往上分别为 0 层 , 1 层,…把地图数据数组改为三维数组。  可以使每个图层以不同的速度运动 , 模拟景物远近不同 的层次感。这种技术,又称视差卷轴( Parallax Scrollers )。 52
  • 53.菱形地图  除了视差卷轴技术可以模拟三维效果外,菱形地 图是在二维画面上表现三维场景的常用技术  拼接所使用的小地图是菱形,而不是前面用到的 矩形地图,因此关于贴图位置的计算比较复杂 53
  • 54.菱形地图 通常前一个坐标轴为 X 坐标,后一个坐标轴为 Y 坐 标  当 X 坐标值增大时,对应的菱形水平位置右移 n* 菱 形小地图长度 /2, 竖直位置下移 n* 菱形小地图高 度 /2  当 Y 坐标值增大时,对应的菱形水平位置左移 n* 菱 形小地图长度 /2, 竖直位置下移 n* 菱形小地图高 度 /2  54
  • 55.菱形地图算法实现 int MapDraw(HDC hdc){ int i,j; int lim=MAXSCREENX/TILEWIDE; // 所需绘制地图的范围 for (i=-lim; i0) && (sy+TILEHIGH>0) && (playerx+i<100 && playerx+i>=0) && (playery+j<100 && playery+j>=0) ){ // 边界判断 TransparentBlt(hdcMem,sx,sy,TILEWIDE,TILEHIGH, hdcTiles[Data[playerx+i][playery+j]], 0,0,TILEWIDE,TILEHIGH,RGB(0,255,0)); // 贴图 } } } BitBlt(hdc,0,0,MAXSCREENX,MAXSCREENY,hdcMem,0,0,SRCCOPY);// 画到窗口 return 0; } 55
  • 56.图像的颜色混合与半透明操作  二维游戏开发中,颜色的混合与半透明效果处理是最常 见的二维画面的视觉效果生成方法之一  如需要将多个图像重叠显示,但又不是完全覆盖,就需 用到半透明技术 +  每种颜色都由红绿蓝 3 种基本色彩(三原色)组合而成 ;  三原色中每一种颜色的亮度用一个 8 位的二进制数来表 示  半透明图色彩 = 源图像色彩 × ( 100% - 透明度) + 背景图像色彩 × 透明度 56
  • 57.图像的颜色混合与半透明操作  Windows API 函数: AlphaBlend 专门对带有 Alpha 值的图像进行透明和混合处理  三种方法 用全局透明度忽略各像素的 Alpha 值 忽略全局透明度将源图像中间的透明通道 Alpha 设为 0 每个像素的 RGB 自行乘上透明度 既使用全局透明度又使用各像素的 Alpha 值 1. 2. 3. 57
  • 58.精灵动画 (1) 基于精灵的人物表现 ghosts, 精灵 sprites, 骑士 knights 精灵:前景是图像,背景是透明的 精灵动画:将上一帧中精灵出现的地方用 背景填充,并在新的指定地点绘制精灵 鬼怪 透明区域 58
  • 59.图像镂空 (2)  将掩码图和背景图案进行按位 AND ,使得原始图像的 对应位置变空。  将原始图像和上一步处理结果按位 OR 。  这样,原始图像贴到背景上并遮盖背景,其余部分(掩 码图中白色部分)没有贴到背景上。 原始图像 掩码图 59
  • 60.图像镂空 (2) 其原理: 根据 C 语言里的位运算,可以知道任 何颜色 RGB 值和黑色按位与,得到黑 色;和白色按位与,结果不变。同样的 ,任何颜色和黑色按位或,不变;白色 按位或,得到白色。 60
  • 61.精灵动画 (3)  英文为 sprite animation ◦ 一幅背景图 ◦ 一组模板图( mask ) ◦ 人物的连续显示方式  双缓冲机制  不要在窗口中直接贴图 ,避免闪烁  建立一个内存 DC ,然后把所 有的变 变 变 变 作都在 变变变变 个 DC 上 进行,最后把结果显示到操作 窗口中。 61
  • 62.精灵动画( 4 ) 对动画序列中的每一帧 ◦ Load 背景图 ◦ 确定 sprite 绘画的位置 ◦ 将某一掩码图与背景图作 AND 运算 ◦ 将对应的人物图与背景图作 OR 运算 ◦ 更新 sprite 绘画的位置 62
  • 63.碰撞检测 对运动物体的碰撞判断是许多游戏程 序中不可或缺的要素 常见的碰撞检测方法 ◦ 区域检测 ◦ 碰撞点检测 ◦ 变色变变变: 变变变精确, 变 变 相对耗时 区域检测 碰撞点检测 63
  • 64.区域检测 采用某种规则形状 逼近物体 物体之间的碰撞检 测转化为规则形状之 间的检测 64
  • 65.碰撞点检测 本质是区域检测的一种 一般在两个运动物体中 的一个物体上设置碰撞 点,在另一个物体上设 置检测区域,运行时逐 个判断碰撞点是否在检 测区域中。 65
  • 66.颜色检测  为树林做一张掩码图,将树林用黑色填充。要产生汽车驶入 树林后面的效果,先在背景上贴上汽车的图像,然后在上面 用镂空图技术画上树林。然后,判断汽车图像在树林图像上 的相对位置,将汽车图像上的点和掩码图上相应位置的点做 按位 AND 操作,检查结果中是否有黑色点( RGB 值为 0 )存在。任何颜色的 RGB 值与黑色图形进行按位 AND 运 算,将得到黑色。如果存在黑色点,表明有碰撞。 66
  • 67.物体运动模拟 需要模拟现实世界中的物体运动时, 可依据物理公式,计算运动物体的坐标 并在屏幕上现实。 如:实现一个简单小球的运动画面, 若想达到小球在窗口的每条边上反弹的 效果,只要判断出小球与窗口边缘的碰 撞,并在碰撞时候速度取反即可。 67
  • 68.游戏循环的基本步骤 1. 2. 3. 4. 5. 6. 7. 初始化 初始化 进入游戏循环 查询用户输入状态 执行游戏逻辑和 AI 判断 绘制图像 循环 退出 过程循环 按键处理 图像绘制 AI 逻辑 退出游戏 68
  • 69.Game Init Game Menu Game Starting Game Restart Game Run Game Exit 69
  • 70.// defines for game loop states #define GAME_INIT #define GAME_MENU #define GAME_STARTING #define GAME_RUN #define GAME_RESTART #define GAME_EXIT 1 2 3 4 5 6 // the game is initializing // the game is in the menu // the game is about to run // the game is now running // the game is going to restart // the game is exiting // game globals int game_state = GAME_INIT; // start off in this state Int error // used to send errors back to OS = 0; // main begins here Void main( ) { // implementation of main game loop 70
  • 71.While (game_state != GAME_EXIT) { // implementation of main game loop switch(game_state) { case GAME_INIT:{ // the game is initializing // allocate all memory and resources Init( ); game_state = GAME_MENU; } break; 71
  • 72.case GAME_MENU:{ // the game is in the menu // call the main menu function and let it switch states game_state = Menu( ); //note:we could force a RUN state here } break; case GAME_STARTING:{ // the game is about to run // this state is optional, but usually used to set things up right // before the game is run you might do a little more housekeeping Setup_For_Run( ); // switch to run state game_state = GAME_RUN; } break; 72
  • 73.case GAME_RUN:{ // the game is now running // this section contains the entire game logic loop Clear( ); // clear the display Get_Input( ); // get the input Do_Logic( ); // perform logic and AI Render_Frame( ); // display the next frame of animation Wait( ); // synchronize the display // the only way that state can be changed is thru user interaction // in the input section or by maybe losing the game. } break; 73
  • 74.case GAME_RESTART:// the game is restarting { // this section is a cleanup state used to fix up any loose ends // before running again Fixup( ); // switch states back to the menu game_state = GAME_MENU; } break; 74
  • 75.case GAME_EXIT:{ // the game is exiting // if the game is in this state then it’s time to bail, kill everything // and cross your fingers Release_And_Cleanup( ); error =0; // set the error word to whatever //note:we do not have to switch states since we are already in this state // on the next loop iteration the code will fall out of the main while and // exit back to the OS } break;default:break; } // end switch return(error); // return error code to operating system } // end main 75
  • 76.二维潜艇游戏分析 具变 了最基本的游 变 变 变变变 变变 变 要素:目 变变 变变 变 性、计分 变变 变 系统、竞技性等。 主要利用的技术: Windows GDI 、二维 图像
  • 77.二维潜艇游戏分析 (续) CChildView CSubmarine CMyShip CMyObject CBomb CExplosion CTorpedo CScore
  • 78.二维潜艇游戏分析 (续) CMyObject 所有物体的基类 CPoint GetPos() ; CMyObject virtual CRect GetRect(); 虚函数,获得物 体的矩形坐标 virtual bool Draw(CDC* pDC, bool bPause); 在 pDC 上绘制当前物体图像 bool IsSubmarine(); 判断当前物体是否是 潜艇
  • 79.二维潜艇游戏分析 (续) CmyShip 我方战舰,由 CmyObject 继承 static void DeleteImage(); CMyShip 释放内存 static BOOL LoadImage(); 图像初始化 bool Draw(CDC* pDC, bool bPause); 绘制 void SetMotion(int motion) 水平移动, motion 是移动距离 int GetMotion() 获取战舰移动方面 CPoint GetPos() 获取战舰绘制坐标 CRect GetRect() 获得物体的矩形坐标
  • 80.二维潜艇游戏分析 (续) CSubmarine Csubmarine 变 方 潜水艇 static void DeleteImage(); 释放内存 static BOOL LoadImage(); 图像初始化 CRect GetRect() 获得物体的矩形坐标 int GetType() 获得潜艇类型,(绿色、黄色) bool Draw(CDC* pDC, bool bPause); void SetFireFlag() 艇多次开火 bool GetFireFlag() 变 置 绘制 已 变 变 开火 变变 变变 志,以防潜 变 变变 判断潜艇是否开火
  • 81.二维潜艇游戏分析 (续) Cexplosion 我方炸弹 CRect GetRect() int GetMulti() 的潜艇个数) 获得物体的矩形坐标 获取 m_nMulti (标记连锁爆炸 void SetMulti(int Multi) { m_nMulti = Multi; } 设置 m_nMulti bool Draw(CDC* pDC, bool bPause); 绘制
  • 82.二维潜艇游戏分析 (续) CScore Cscore 分数显示 bool Draw(CDC* pDC, bool bPause); 绘制 CRect GetRect() (返回 NULL ,无效函数) static int GetTotalScore() 获得当前总得分 { return m_nTotalScore; }
  • 83.课后作业 1. 熟练掌握一种图像处理工具,推荐 ACDSee, Photoshop8.0 ; 2. 详细读码:潜艇游戏 3. 熟练掌握 Windows 图像操作; 4. 变变一 个精灵 变变 变画; 5. 参考 VC++ 数字变变 像变变 理代 变 变变 ,变变变 任意一种 变 变 变 变变 像变变 波 83
  • 84.课后预读 1. 三维向量代数与矩阵运算 2. 基本的三维变换矩阵 84