前腐后继 发表于 2007-1-8 10:47:00

游戏入门基础]在DirectX中贴瓷砖:Part 2

<p><font face="Tahoma"><font size="4">绪论</font><font size="2">朋友们,大家好,欢迎来到新的章节!在第二个部分我将在第一部分的基础上继续讨论贴瓷砖这个话题。由于缺乏时间在本文中我仅仅讨论平滑滚动的问题。这样可能会使你们当中的某些人失望,但是请理解我,我是学生,我有自己的要做的事,我的时间日益减少,对不起了。</font></font>
        </p><p><strong><font face="Tahoma" size="4">平滑滚动</font>
                </strong></p><p><font face="Tahoma" size="2"><strong>你是否记得第一部分的内容,我们设置了一个简单的瓷砖引擎,其中使用了一个2维的数组,并用DirectDraw的BltFast()将瓷砖贴到屏幕。我们不能仅此而已!我们即将一个大地图用滚动效果加入你的游戏。不要认为这步是令人畏缩的。一旦你理解了这个原理,其实还是非常容易的。</strong></font></p><p><font face="Tahoma" size="2"><strong>因为我们中的有些人不懂“平滑滚动”,所以我要谈一谈一个基于瓷砖的背景在精灵背后的移动这一效果的实施。尽管主角(或在你游戏中所有你想要控制的东西)正要从一个格子运动到另一个格子,但它能被画到屏幕的同一位置。知道超级马丽吗?,它就是一个很好的平滑滚动的例子,想不想实现它?</strong></font></p><h1><font face="Tahoma" size="4">原理</font></h1><p><font face="Tahoma" size="2"><strong>滚动的关键是保存大地图的当前轨迹这样就可以计算出哪些瓷砖要被画到屏幕。举个例子,假设按下键盘的右方向键,游戏的精灵将向右移动两个瓷砖。在你的画瓷砖的函数中,你可以认可这个改变,并且画出被切换了两个瓷砖的一系列瓷砖,或者说将游戏背景向右移动64像素。结果你的精灵看上去就好像改变过了一样。如果这个解释很难理解的话,我很报慊,但是我希望你能清晰的理解,以便我们能继续。</strong></font></p><p><font face="Tahoma" size="2"><strong>接下来我们将创建两个变量来存储地图中的改变。这两个变量将用在最后版本的Draw_tiles()函数中以决定哪些瓷砖要被画到屏幕。 原理就这些了,如果你有一些主意关于你的游戏,赶快加入这些技术到你的代码中吧。</strong></font></p><h1><font face="Tahoma" size="4">设置</font></h1><p><font face="Tahoma" size="2"><strong>在我们重写draw_tiles()这个函数前,我们必须定义一些常量。</strong></font></p><blockquote><div class="code"><font face="Tahoma"><br/></font><font face="Fixedsys"><strong>#define TILE_SIZE 32<br/>#define WORLD_SIZEX 20<br/>#define WORLD_SIZEY 20<br/>#define SCREEN_SIZEX 12<br/>#define SCREEN_SIZEY 12<br/></strong></font></div></blockquote><p><font face="Tahoma" size="2"><strong>在这儿我们定义了瓷砖的尺寸是32像素,地图的大小为20*20个瓷砖,屏幕大小为12*12个瓷砖。这样我们就有12*12(144)个瓷砖能够被同时显示在屏幕上,于此同时有20*20个瓷砖在我的我们的数组中。我们必须建立一个比屏幕可视区域更大的地图这样我们就能鉴证滚动的行为了。注意:你设置DirectX应用的决定,将使得你可能不能看到整个被瓷砖填充的屏幕。</strong></font></p><p><font face="Fixedsys"><strong>int world_camerax = 0;</strong></font></p><p><font face="Fixedsys"><strong>int world_cameray = 0;</strong></font></p><p><font face="Tahoma" size="2"><strong>我在原理那节中提到的两个变量 <i>world_camerax</i> 和 <i>world_cameray ,</i>是为了跟踪地图中当前的位置。它们将决定哪些瓷砖要被画到屏幕上。现在,我们初始化这两个变量为0,因此我们的当前位置是(0,0),或者说在地图/数组的最左上方。<i>world_camerax</i> 将存储X位置,或者说与0点的X轴距离。<i>world_cameray</i> 将存储Y的位置,或者说与0点的Y轴距离。如果你有问题请回顾原理一节。如果你仍然有困难,那么那可能是我的过错了。:) 最后要做的是定义新的地图格子(20*20个瓷砖),这要比向导一中的地图大多了。</strong></font></p><blockquote><div class="code"><strong><font face="Fixedsys">char map = {<br/>{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2},<br/>{2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2},<br/>{2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2},<br/>{2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2},<br/>{2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2},<br/>{2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2},<br/>{2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2},<br/>{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, };</font>
                        </strong></div></blockquote><p><font face="Tahoma" size="2"><strong>漂亮的地图格子就在这,随同一个小消息。:)</strong></font></p><h1><font face="Tahoma" size="4">平滑的画出这些瓷砖</font></h1><p><font face="Tahoma" size="2"><strong>现在我们重写 draw_tiles() 这个历程</strong></font></p><blockquote><div class="code"><strong><font face="Fixedsys">void draw_tiles(void)<br/>{<br/>&nbsp;&nbsp;&nbsp; int tile;<br/>&nbsp;&nbsp;&nbsp; int x, y;<br/>&nbsp;&nbsp;&nbsp; int scroll_x, scroll_y; // (NEW)<br/>&nbsp;&nbsp;&nbsp; int offset_x, offset_y; // (NEW)<br/><br/>&nbsp;&nbsp;&nbsp; RECT tile_src;</font>
                        </strong></div></blockquote><p><font face="Tahoma"><strong><font size="2"><i>tile</i>用来决定map值,例如,当我们通过遍历地图数组时,map将有一个ID#=1或2。x,y这两个变量决定在确切的位置画出瓷砖。RECT tile_src用来指定每一个瓷砖对应的图像的位置,指向屏幕的偏移。<i>scroll_x</i> and <i>scroll_y</i> 变量将服务于函数就好比向导一中x,y变量服务于draw_tiles()函数一样。你也许会问“为什么不再用x,y?”问得好。答案是对地图的一些计算必须用到x,y变量,就如下一步所示,计算的结果将被保存在<i>scroll_x</i> 和 <i>scroll_y </i>中,这样我们就能够使用x,y的原始值来画出瓷砖。 <i>offset_x</i> 和 <i>offset_y</i> 用来决定画出瓷砖的精确位置。这两个变量是平滑移动的核心变量。</font><br/></strong></font></p><blockquote><div class="code"><font face="Fixedsys"><strong>for (y = 0; y &lt; SCREEN_SIZEY; y++)<br/>{<br/>&nbsp;&nbsp;&nbsp; for (x = 0; x &lt; SCREEN_SIZEX; x++)<br/>&nbsp;&nbsp;&nbsp; {<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // (NEW)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scroll_x = x + (world_camerax / TILE_SIZE);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scroll_y = y + (world_cameray / TILE_SIZE);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // (NEW)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset_x = world_camerax &amp; (TILE_SIZE - 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset_y = world_cameray &amp; (TILE_SIZE - 1);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tile = map;<br/></strong></font></div></blockquote><p><font face="Tahoma"><strong><font size="2">ok,新代码在这。首先,用一个嵌套的双重循环来遍历我们的地图获得每一个瓷砖的ID#值。 接下来,分别设置<i> scroll_x</i> ,<i>scroll_y</i> 值为x,y加上目前为止已经滚过的瓷砖个数。 然后设置绘图偏移量,我们要做的就是,将瓷砖的最后位置减去偏移量。</font><br/></strong></font></p><blockquote><div class="code"><strong><font face="Fixedsys">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tile_src.left = (tile - 1) * TILE_SIZE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tile_src.top = 0;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tile_src.right = tile * TILE_SIZE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tile_src.bottom = TILE_SIZE;</font>
                        </strong></div></blockquote><p><font face="Tahoma"><strong><font size="2"><i>tile_src</i> RECT 的设置依赖于存储在map中瓷砖的ID。</font><br/></strong></font></p><blockquote><div class="code"><strong><font face="Fixedsys">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // (MODIFIED)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BltFast((x * TILE_SIZE) - offset_x, (y * TILE_SIZE) - offset_y, lpddsoffscreen, &amp;tile_src, NULL);<br/><br/>&nbsp;&nbsp;&nbsp; }<br/>}<br/>}</font>
                        </strong></div></blockquote><p><font face="Tahoma" size="2"><strong>现在我们将瓷砖画到屏幕确切的位置,用x,y的值乘以32再减去偏移量。我使用 lpddsoffscreen 作为DirectX 表面的名字,你的表面可以用不同的名字。</strong></font></p><h1><font face="Tahoma" size="4">结束语</font></h1><p><font face="Tahoma" size="2"><strong>我们已经有了一个平滑滚动的引擎, 那么我们怎么把他用在游戏中呢? 那非常容易. 你只要作如下的事情:</strong></font></p><ol><blockquote><div class="code"><font face="Tahoma"><br/></font><strong><font face="Fixedsys">if (keypress_right)<br/>{<br/>&nbsp;&nbsp;&nbsp; world_camerax += 8;<br/>}<br/><br/>if (keypress_up)<br/>{<br/>&nbsp;&nbsp;&nbsp; world_cameray += 8;<br/>}</font>
                                </strong></div></blockquote></ol><p><font face="Tahoma" size="2"><strong>现在玩家可以用键盘控制地图的滚动。当然地图和玩家的运作还有许多工作要做,但我确信你一定能解决。:) 注意:在这的kepress_right等,你可以使用DirectInput或者Win32 API 输入函数来得到。如果你在文章中发现一些错误或bugs ,请告诉我。</strong></font></p>
页: [1]
查看完整版本: 游戏入门基础]在DirectX中贴瓷砖:Part 2