原文地址::http://www.j2megame.com/html/xwzx/ty/512.html

相关网帖

1.M8SDK教程-游戏开发心得(一): 游戏程序框架----http://bbs.meizu.com/thread-957024-1-4.html

2.M8SDK教程-游戏开发心得(二): DirectDraw基础----http://bbs.meizu.com/thread-968949-1-1.html

3.M8SDK教程-游戏开发心得(三): DDraw进阶教程-简单贴图,Alpha半透明效果和Sprite动画----http://bbs.meizu.com/thread-981601-1-1.html

转载自M8开发者社区

很高兴看到M8下的原创应用开发逐渐丰富起来了.本人有幸也发布了自己的游戏:《K3.原子球》,虽然该游戏目前还未完善,不过作为M8sdn的论坛管理员,为配合我们的开发宗旨,我非常高兴将我的代码进行开源,并且给大家分享我在开发中的心得.

游戏开发是一个复杂的过程,它牵扯到程序框架,逻辑设计,绘图,素材美工等多个流程,但是游戏编程也是有趣的,它更是对一个非游戏开发人员的挑战. 我们会在这个教程系列中将我们近期通过在M8上做一些简单游戏所获得的心得和重要的技术介绍给大家.

我所写的教程中大部分都应该在K3.原子球的代码中得以体现,如果想更清楚的了解一个完整的游戏流程,欢迎参考我的代码,不过原子球是我写的第一个游戏,所以有很多很多地方写的不太好,仍需完善.如果各位高手认为我的程序有什么问题,欢迎拍砖.
K3.原子球的完整代码已经在M8sdn开发社区开放,欢迎大家参考.

正式开始:
大多数熟悉Windows编程的朋友可能都已经习惯了一个套路: 处理控件的各种消息事件,然后在这些事件的处理函数里写我们的程序逻辑.我们在使用M8 SDK开发一般软件时仍然可以使用这一模式,那是因为SDK已经将Windows程序的最基础部分都做好了.

但是游戏程序有其特殊性,我们需要做到实时处理程序逻辑,并对屏幕进行实时的刷新,并且在用户没有进行操作的时候也一样要做到,所以我们已经不能依赖Windows消息来达到这些目的,我们必须回到底层,去做一些SDK已经为我们做好了的事情.
(我在这里不介绍Windows程序的基本结构和消息循环的相关知识,如果大家对此不熟悉,请大家去下载一本叫做Programming Windows(Windows程序设计)的电子书,看完前几章你就会了解)

这些我们必须做的事情就是自己实现程序的消息循环,目前大部分流行的写法是这样:

复制内容到剪贴板

代码:

        
        for(;;) 
        {
                MSG msg;
                // 取一个消息
                while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
                {
                        // 如果是Quit消息,则退出
                        if (msg.message == WM_QUIT) 
                        {
                                goto _quit;
                        }
                        // 转换键盘消息
                        TranslateMessage(&msg);
                        // 分发消息
                        DispatchMessage(&msg);
                }
                // 进行一帧渲染
                render_frame();
        }
        _quit:

这段代码的逻辑是用 PeekMessage(这个函数与GetMessage有区别哦,请查MSDN) 来处理Windows 消息,一旦消息队列为空,就转去跑一帧游戏逻辑,这帧逻辑完成后游戏屏幕也被刷新了一帧.通过这样,我们就可以做到有消息的时候,优先处理Windows消息,没有消息的时候,就不断处理游戏逻辑并刷新屏幕.

看完上面的代码,我们可能存在以下的问题:
1. 我们为什么要自己写消息循环?为什么不能用WM_TIMER做个定时器来处理(如果大家不了解WM_TIMER请 查询 MSDN)
1. 这段代码其实是一个死循环,我们不能判断它会造成每秒钟刷新屏幕多少次,对于M8来说,刷新屏幕次数太多不但没有必要而且可能也更费电吧.
2. 这段代码如果在一个从main函数写起的程序应该是有意义的,可是在M8中有个CMzApp类,消息循环已经被封装好了,我们是根本看不到的,我们必须想办法处理.

第一个问题:  不使用WM_TIMER 的一个原因是精度不够。WM_TIMER的精度如果我没有记错的话大概也就几十毫秒,不过这个帧数在 手机 上貌似也差不多足够,但是另外一个原因则是因为它并不能完全准确的表示时刻。WM_TIMER和 WM_PAINT 一样,属于比较特别的消息:优先级低,在消息队列中同时只存在一份(即多个同样的消息可能被合并),所以,在游戏中完全依赖WM_TIMER是不保险的.

第二个问题其实比较好处理,我从Gledplay的源代码中抄来了一个FPS类,专门用来处理此问题,可以把帧数进行精确限定.

对于第三个问题,大家是否注意到CMzApp类中有个run函数,我们只需要继承并重写这个函数,把我们写的消息循环写在这个函数里,这样问题也可以 解决 了.

好的,下面我们可以开始写一个简单的游戏程序的基本框架了:
 
来看一下我们的项目 文件 ,很简单,CMainApp.h 和CMainApp.cpp是我们的App类, CMainWnd.h和CMainWnd.cpp是我们的主窗口类,Fps.h和Fps.cpp是我们引入的帧数控制类.

来看一下具体的代码:
1. App类的Init函数:
 
这段代码创建了我们的主窗口,同时做了一些重要的辅助性工作.SetDVS这个函数是从论坛著名的OpenGl ES的演示代码中来的,目的是为了让CPU全速运行(至于怎么实现的,我也没看懂...). HideMzTopBar()这个函数也很重要,目的是为了隐藏M8的顶部工具栏以使我们可以创建一个全屏的窗口,之后我们使用GetSystemMetrics获得屏幕的大小,然后根据这个大小创建主窗口,我们这里不写成固定值是为了以后的扩展性(说不定以后M9,M10,M11的分辨率就更高了)

2. 主窗口初始化
 
这段代码没什么特殊的,重要的一句就是使用了SetScreenAlwaysOn来使屏幕一直打开同时防止自动锁屏(你不希望在游戏进行中屏幕黑掉吧)

3. App类Run函数
 
这里就是我们的消息循环了.可以看到,这里跟我们一开始提供的那段代码差不多比较特殊的地方就在于我们使用帧数控制器控制了现实帧数.同时,在执行一次游戏逻辑后,我们sleep了一下,适当释放资源给其他的线程.由于我们还没有讲到其他的内容,所以游戏的实际逻辑RenderFrame我们只是简单的使用GDI绘了一行文字,同时我们也把绘图帧数绘在了屏幕左上角,便于观察游戏执行效率.

2009-05-18更新
这里增加了当前我们的游戏窗口是否激活的判断,因为如果 电话 呼入我们的游戏窗口就会失去焦点,这时应该停止绘图并让出CPU资源,这里非常感谢 linuxlt 的提醒.
这个active变量是怎么产生的呢,请看主窗口的处理函数如下:
 
注意,在游戏窗口重新获得焦点的时候我们需要再次隐藏顶部

4.App类Done函数
 
这个函数会在程序退出时调用,所以我们在这里做退出程序时该做的事情: 恢复CPU节能状态,解除屏幕高亮以及重新显示顶部工具栏,SetScreenAutoOff函数你可能发现了,有拼写错误,看着有点别扭,所以我们这里用一个宏改过来.

好了,代码分析完成,我们来看看通过本次学习我们了解到了什么?
1. 游戏程序的基本执行流程和消息循环
2. 精确限定游戏帧数的方法
3. 在M8上创建全屏游戏窗口的方法
4. 在游戏运行时保持屏幕开启的方法
5. 在游戏运行时维持CPU全速的方法
6. 在游戏窗口失去焦点时需要停止绘图并让出cpu资源

这个范例的实际运行效果如下:
 
呵呵,只有一行字,很土鳖,而且你会发现,我们明明限定了帧数是40帧的,为什么执行结果反而还没有到40帧呢?这说明我们的绘图性能方面有问题.解决绘图性能是游戏的一个大问题.那么我们该如何解决这个问题呢?请关注本教程的下个部分:  DirectDraw绘图基础

本章范例代码:
  GameSample1.rar (16.54 KB)

M8SDN K3 studio

非常高兴上一篇教程能给大家带来帮助.同时非常感谢指出不足的朋友(没有处理WM_ACTIVATE消息),我后来已经处理了这个问题.第二个教程其实想快点写出来的,但是由于最近非常忙,所以写的比较慢,请大家谅解.本期的目的是为大家简单介绍DirectDraw(以下简称DDraw),因为对于DDraw我本人也只是略懂,所以只能将我所知道的告诉大家了.因为使用图形加速有许多性能方面的考虑,所以这一章我会写一些理论性的东西来讲一些我了解的细节,以利于大家对自己的程序进行更好的优化. DDraw的接口和函数细节请大家查MSDN,如果大家在使用中有问题,欢迎回帖讨论,我会尽可能的去解决大家使用DDraw的问题.在开始这章之前我建议不熟悉Windows GDI的朋友先去简单去了解一下Windows GDI的使用,因为GDI是Windows绘图的基础,了解GDI也会有助于学习DirectDraw.

正式开始:
在上一个教程中,我们大致介绍了一个M8游戏的基本程序框架,同时进行了最简单的绘图.通过显示帧数,我们发现绘图方面存在瓶颈,所以我们在这一章将通过硬件加速解决这个问题.

大家知道,目前的游戏根据画面大致分为2D和3D游戏,二者在M8中的实现硬件加速的技术并不相同. M8所使用的三星6410支持DirectDraw,Direct 3D Mobile, OpenGL ES 1.1/2.0等图形加速接口. 如果打算在M8中制作2D游戏,一般可以选择DDraw. OpenGL ES其实也可以在M8里用做2D加速(在0905的SDK中可以看到相应的类库封装,但是还没验证是否可用),这个问题如果有机会我们将在以后的教程讨论(这就要看我们的教程是不是真正受到大家欢迎啦,哈哈). 本章我们重点介绍DDraw.

DDraw的背景
DDraw是DirectX的2D部分,通过DDraw,可以为开发者提供一个比GDI层次更高、功能更强、操作更有效、速度更快的应用程序图象引擎,与此同时,其保持了设备无关的优良特性. DDraw同时有一个优点,虽然我们使用了DDraw,但是我们仍然可以把DDraw和我们熟悉的GDI函数结合起来使用. 在Win32中, 8.0以上版本的DirectX,DDraw都已经不再更新,所以DDraw在Win32里已经不再作为主流的2D图形. 但是在M8(对应WinCE 6.0)中,由于图形技术比PC还要慢一拍,所以DDraw依然非常重要.

为什么使用DDraw
如果大家已经有WINCE或者WM的开发经验,可能会用过GAPI,GAPI是微软尚未在WinCE中实现DirectX功能的替代选择,所以很多很老的Windows Mobile游戏使用了GAP作为图形接口,但是到了M8目前所用的WINCE6.0, DirectX已经比较完善了,由于DirectX在PC上就基本上已经是游戏的标准库,如果使用DirectX,开发人员可以得到更多的资源和文档支持.而且目前来看,M8本身并未实现GAPI的硬件加速,所以对于2D游戏,DDraw是我们的优先选择之一.

为了理解DDraw硬件加速的方法以及了解如何正确使用DDraw,下面简单介绍一些概念:

2009-05-29
我们使用DDraw绘图与Windows绘图的区别
这里其实有一点东西要讲,结果我给漏掉了.算是回答KORY的问题.
我们平时在Windows中绘图时遵循这样的流程: 当Windows需要你的窗口重绘的时候,会给窗口发一个WM_PAINT消息,我们只需要在响应这个消息的函数写下绘图代码就可以了.在M8 SDK中的CMzWnd类中,OnPaint函数就是WM_PAINT消息的响应函数.PaintWin函数应该是优化过的绘图函数(依据绘图的效果来看,很可能已经实现了双缓冲),所以在一般应用中我们在PaintWin这个函数里写绘图代码就可以了.如果我们需要频繁主动的刷新屏幕,我们就要不断的用Invalidate或者InvalidateRect函数使窗口(或某一部分区域)失效,这样Windows就会发给我们WM_PAINT消息了.
但是在游戏中,一般都不会使用这种方式. 因为WM_PAINT产生的优先级是很低的,而且消息队列2个WM_PAINT消息是会被Windows合并掉的.所以这种方式在我们的游戏应用中是不可靠的.一般在游戏程序中我们都不依赖Windows的WM_PAINT消息,而是采用主动绘图的方式.也就是间隔一段时间我们就必须强制去绘图(请再看一下第一章教程中的游戏框架的实现,正是基于这种思想).DDraw是直接依靠硬件加速方式实现的,所以绘图根本不受窗口的限制,我们可以直接绘图到屏幕的任何位置(这一点也有点不好,因为我们必须在窗口失去焦点时停止绘图让M8处理电话等其他事件,否则即使接到电话你看到的仍然是游戏屏幕在不断的刷...).

Surface与双缓冲
我们在使用GDI绘图的时候可能有这样的经历: 我们通常在绘图的时候会得到一个当前窗口的DC(设备句柄),这个DC描述了最终的图形设备,我们使用这个DC去绘图就可以把图形绘到图形设备上. 如果绘图过程很复杂我们通常会发现这样绘图速度会很慢,典型的现象是看到屏幕闪烁.我们通常采用的优化方法是创建一个内存DC,将图形先绘制到这个DC上面,之后通过BitBlt这个函数通过位拷贝的方式将内存DC的数据拷贝到图形设备上去,由于内存拷贝的速度很快,所以我们很大程度改进了绘图效率(这种技术叫做双缓冲).

DDraw中Surface的作用跟我们刚才说的例子一个道理. 对于DDraw来说,要绘图,首先要创建一个Primary Surface,这代表当前的屏幕,要实现优化,我们需要再创建一个Background Surface(这就相当于前面说的内存DC),绘图的时候,先将图形绘制到Background Surface,然后将Background Surface的内容复制到Primary Surface,这个就是DDraw实现的双缓冲.与GDI 的双缓冲不同的是,DDraw的双缓冲是使用硬件加速实现的,所以速度会更快.

全屏独占模式与窗口模式,Flip与Blt
DDraw在全屏模式和窗口模式的性能并不相同. 还以刚才提到的双缓冲为例,刚才我们讲过,在最终绘图的时候,我们需要将Background Surface中的数据复制到Primary Surface上去,在窗口模式中,为实现这个功能我们只能使用Surface对象的Blt这个函数,这个函数就是直接进行位拷贝(当然,虽然仍然是拷贝操作,但是由于有硬件优化,这个拷贝操作比GDI的BitBlt还是要快很多的).而在全屏模式中,我们则可以使用Flip这个函数,这个函数则要快的多,而且这个函数是一个完全的异步操作(这一点很重要哦),如果你的Surface是创建在显存中,它的实际操作的是直接交换2个Surface中的显存指针,这样连位拷贝都省了,效率自然最高. 经过我在M8里的简单测试,全屏模式大概比窗口模式要快十几帧左右.这就是为什么大多数游戏都要做成全屏模式了.
(补充: 忘了一点,通过Overlay可以在窗口模式下进行Flip,这样应该也可以实现和全屏类似的性能,这一点我没有试过,待验证)

更好的解决方案: 三缓冲
在大家已经理解双缓冲的基础上,我们来介绍三缓冲.顾名思义,三缓冲只不过是再多一层缓冲罢了.那么,我们为什么需要3缓冲呢?

上面的一张图来自MSDN,清楚的描述了3缓冲的Surface结构,A,B,C表示Surface中封装的显存/内存指针.可以看到,每经过一次Flip,三个Surface中的内存指针就会发生调换. 刚才我们讲过了Flip是个异步操作,即我们在调用了这个函数是可以直接返回的.这时就是Flip大显神通的时候了.

一般我们的绘图过程是这样的: 将图形绘到Bufferbuffer Surface上去,然后调用Primary的Flip函数.在每次绘图的时候我们都同样执行这样的步骤. 如图所示,我们在第一次Flip之后BackBuffer内的显存/内存指针已经被改变了,所以我们在Flip函数返回之后我们不用考虑Flip操作是否已经完成,我们已经可以用BackBuffer进行下一次绘图了.这样其实等于DDraw自动提供我们2个BackBuffer交替使用,同时省去了上一次Flip的等待时间.三缓冲比双缓冲虽然要多消耗一些资源,但是依据微软的说法,速度能再快20%左右,是一种比较好的方案.

下面,在正式看代码之前,我们简单介绍一下DDraw的编程接口,DDraw的编程接口是通过COM的方法暴露,所以我们主要关心的是以下几个主要的COM接口:

IDirectDraw 这个是DDraw的基本功能接口,其他的接口基本要从这个接口创建,同时我们使用这个接口来进行一些DDraw的最基本功能操作,这个接口要使用DirectDrawCreate这个全局函数来创建
IDirectDrawSurface 这个是最重要的了,就是是我们刚才所提到的Surface,我们大多数的绘图操作都必须依赖它来进行,这个接口由IDirectDraw 的方法来创建
IDirectDrawClipper 这是个裁剪器,当我们使用窗口模式时,这个接口比较有用,可以防止我们将图形绘到窗口外面,这个接口由IDirectDraw 的方法来创建

好的,下面我们仍然基于上一次教程的例子,加上DDraw的代码,这次我们主要目的是理解DDraw的基本概念和使用方法,所以我们仍然以绘文字为主,至于位图操作我们会在下一次讲.

InitDraw函数是我们新增加的,用来初始化DDraw的相关对象.可以看到,我们首先使用DirectDrawCreate函数创建了IDirectDraw接口,之后使用这个接口设定全屏模式并创建其他的接口.创建Surface时的步骤比较重要.我们通过制定DDSCAPS_FLIP和BackBufferCount就在创建Primary Surface的同时创建了2个支持Flip的BackBuffer Surface. DDraw会自动将他们形成一个链,不用我们去操心他们的具体关系.之后我们通过遍历Primary Surface上相关联的Surface来找到第一个BackBuffer Surface的指针,以后我们绘图用的就是这个Surface.这个回调函数的代码如下:

下面我们来看具体绘图时的操作:

刚才讲到,DDraw是兼容GDI的,所以我们在使用DDraw的同时仍然可以用GDI函数.可以通过GetDC得到Surface的DC,通过ReleaseDC释放DC.由于目前M8无法对DDraw的全屏程序进行截屏,所以后面我自己写了代码进行截屏,大家可以加到自己的游戏中,可能有用.下面就是最重要的Flip,可以看到,虽然我们有2个BackBuffer Surface,但是我们根本不用指定从哪个BackBuffer Flip到Primary Surface,DDraw的链式结构会为我们搞定一切.

最后就是在程序退出时的清理动作了.由于DirectX是基于COM的,所以我们只需要把所有的DirectX对象Release即可了.

最终的运行效果如下,和上次一样,很土鳖,呵呵,不过效率提升明显哦,你可以试着把帧数限制去掉,看看我们的程序究竟能跑多快.

通过这次的介绍,不知道大家对DDraw是否已经有大概的了解了呢.因为篇幅有限,没有做过多细节的描述,关于DDraw的应用细节,最好的老师还是MSDN: http://msdn.microsoft.com/en-us/library/aa913258.aspx,这里有文档,有范例,大家在具体开发过程中可能需要经常参考.

今天的代码不太多,但是我费了很多篇幅去讲一些我理解的概念,不知道这样是不是更有助于理解呢.抱歉的是,以我现有的时间和能力,大概就能写成这样了,呵呵.哪里不对的地方,欢迎拍砖.

最后,我们来看看通过本次的学习,我们了解到了什么:
1. 了解了DDraw加速的原理
2. 了解了DDraw的初始化,简单使用和对象的最后释放
3. 了解了三缓冲的优点和使用方法
4. 通过看这次的代码,可以了解如何在DDraw全屏模式下自己截图

OK,我们已经有了图形引擎,但是丰富的游戏画面只靠这几行代码是做不出来的.请关注我们的下一个教程: DirectDraw进阶: 位图显示,半透明和精灵动画......

本次教程源代码:  GameSample1_step2.rar (26.68 KB)

M8SDN K3 Studio

前面2章的教程受到了很多朋友的欢迎,对于自己写的东西能帮助到很多朋友我感到很欣慰.
这里非常抱歉这次教程迟了一些出来.因为这次要写更多的代码,本人水平有限,且遇到了一些比较郁闷的技术问题(稍后会讲到),实在解决不了也只好仓促发布了.
闲话少叙,本次是游戏开发心得系列教材的重头戏了这次我们将会讨论到游戏场景的编程设计,贴图,alpha半透明效果,Sprite动画.这些都是真功夫哦,呵呵.
在前面2章讲的理论性的东西比较多,有些朋友看着不是很有趣,所以这一次我写了很多代码,重点进行代码实现的讲解.为了演示Sprite我还大概做了一个RPG游戏人物的行走效果,呵呵,算是RPG游戏的一点点技术原型.

下面我们正式开始,由浅入深的来看这几个例子:

游戏场景

从前面2章的例子我们可以看到我们的游戏窗口只有一个,但是我们需要在这个窗口里实现多个场景.何为场景呢?在开发者的眼中,游戏的进入菜单,游戏loading画面,RPG的人物行走,战斗,状态等等都是独立的场景.为了便于开发,我们需要将它们从逻辑上分开.因为这次我要演示好几个独立的效果,所以我也实现了一个山寨版的"场景"的架构,供大家参考.
我定义的场景类名为CMyScene,类定义如下:

我们来看代码,当主窗口创建时,我们调用了CreateScenes用于创建场景.

上面的代码中,我们创建了4个场景,同时将第一个菜单场景设为当前场景. 在主窗口的RenderFrame函数和各个处理常用消息的函数中,我们都直接调用了默认场景的对应函数,这样就把绘图和消息的控制权直接转移到了场景内部,我们可以直接在各个场景内写我们的逻辑,实现代码的清晰分离.
我们看看场景内的代码

以CMenuScene(主菜单场景)为例,可以看到,在初始化时,我们顺序创建了几个button(我自己实现的,感兴趣的请看源代码),并分别赋予了CommandID,用于按钮点击产生的事件处理.这些事件可用于场景切换和其他的特殊处理(按钮点击等的处理写在基类里了,这里就不具体说了,感兴趣的朋友可以看看).

简单贴图:
我们之前的例子都只是使用了GDI函数输出文字,非常土鳖,现在我们终于可以开始贴图了!
我记得在Win32编程的时候,载入位图一般都用LoadImage这个API,这个函数只能载入BMP,其他格式只能用第三方类库或者自己写,在绘图的时候还要用DC做好几次转换,非常麻烦.在M8里,你仍然可以使用LoadImage,但是我们已经有了更好的选择: ImageHelper. ImageHelper是基于WinCE Imaging COM接口的封装,可以支持多种图像格式.同时支持带Alpha通道的PNG图像,功能很强大.
贴图的例子写在CBlitScene场景中,我们来看初始化代码:

创建了2个按钮之后,我们使用ImageHelper的LoadImageFromRes方法从资源中直接加载PNG图像,下面是我们已经熟悉的创建缓存Surface的步骤,最后,我们从ImageHelper直接绘图到缓存Surface中.在这里我们为什么要使用一个缓存Surface呢?为什么不使用ImageHelper直接绘图到BackBuffer呢?这是因为DDraw对Surface之间的Blt是硬件加速的,但是如果我们直接从ImageHelper绘图到BackBuffer是没有硬件加速的,如果每次都这样绘图,会牺牲我们的绘图性能.所以尽可能的使用Surface来缓存位图数据是必要的选择,当然Surface容量是有限的,在复杂的游戏中,我们不可能每次在游戏开始的时候就把所有素材都加载到Surface中,但是我们可以按场景等单位加载位图,尽可能的进行优化.大家见过大部分RPG在场景切换都是要Loading的吧,呵呵,它们很可能就是在加载图像到Surface中哦.
下面来看CBlitScene的RenderFrame方法:

我在这里做的工作比较简单,计算源矩形和目标矩形,然后将位图从缓存Surface拷贝到BackBuffer Surface.这样我们就实现了每次RenderFrame都显示同一个位图.
简单贴图效果如下:

Alpha半透明
Alpha半透明是一种非常重要的效果,有了它可以做出很多绚丽的游戏画面.但是这个例子我遇到了一些难以解决的问题.其实我已经不想把这个例子加上来的,因为功能远未达到我要求的效果,不过觉得把问题拿出来大家讨论也许更好吧.我们先来看代码,然后我来说局限性.
这个例子使用CAlphaScene场景,首先来看初始化的代码:

在创建完该场景的按钮之后,我们仍然使用ImageHelper来载入位图,不同的是,最后一个参数我们改为true,用于正式支持Alpha通道.之后将图像加载到surface中.

在绘图函数这种,比较重要的是我们使用了AlphaBlt方法,这是DDraw中支持硬件加速的半透明实现方法.该方法需要传入一个DDALPHABLTFX结构体,用于填充Alpha数据.在这个范例中我们仅仅改变Alpha值来实现半透明的效果.
该范例运行效果如下:

这个范例其实存在巨大的问题:
1. 有时候效率很低下,在范例中最快的时候6ms,慢的时候100多ms,你在看代码的时候会发现绘图前先GetDC,随便绘了点文字(空的都行),这是因为我发现通过这样做,速度就会正常,否则AlphaBlt就很慢.这是什么怪原因?是否因为我没有指定该Surface在内存中呢?
2. 这个函数的最大缺陷就是不支持关键色,何为关键色?就是绘图时,你希望被抠掉的背景色.在Blt函数中关键色可以通过设置参数很好的支持,但是AlphaBlt这个函数竟然不支持设置关键色,而奇怪的是,在CE5.0的这个函数是有关键色这个参数的,竟然在CE6被取消了?(微软的人脑子也进水了?)我曾经尝试使用Alpha通道来解决,但是由于DDraw没有提供对PNG的原生支持,仅仅通过bitblt方式无法将关键色拷贝到Surface当中去.所以,我觉得不能去掉绘图时的背景色极大的限制了该函数的应用.我今后会有时间再研究下看看能否解决这个问题.

Sprite动画
Sprite动画的原理其实和动画片差不多.看我们这个范例的素材(天之痕中的陈靖仇,呵呵,别告诉我你没玩过天之痕):

这些是阿仇各个方向的行走图,通过把每个方向上的图在很短的时间内连续显示,在我们看来就基本形成动画效果了,Sprite动画可是2D游戏,尤其是RPG游戏的实现基础,应用还是很广泛的.通过这范例,我演示了Sprite动画,而且还是实现了人物在地图中的行走(当然,这里没有地图了,所以用了一个重复背景来替代).

这个范例放在CSpriteScene场景中,下面我们来看代码:

初始化代码和上面的大同小异,但是有些不同的是,在绘Sprite的时候我们必须要擦掉Sprite的背景了,这时候要用到我们刚才提到的关键色了,通过DDSetColorKey这个函数(从微软DDraw的范例代码里copy出来的),我们设置了Surface的关键色,一会在绘图的时候会用到.

下面我们来看看"阿仇快跑"的实现逻辑.
我们可以设想一下,RPG游戏人物的移动一般都是人物在屏幕中间摆出移动的动作,但是人物并不做实际的移动,反而是游戏背景进行滚动,这样你会发现,游戏主角始终在屏幕中间,但是看起来人物也移动了.
所以按照此原理我们只需要在每次屏幕点击时,计算点击坐标和屏幕中心坐标的偏差,就可以确定移动方位了.在不断播放Sprite动画的同时,将背景一点一点沿人物移动的反方向移动就可以实现我们需要的效果了.
来看代码:

首先用一个enumn来定义了行走方向,这个定义和素材图的方向顺序保持一致,一会贴图的时候便于我们的计算.

然后我们看看屏幕点击事件的处理:

在这里,我们通过计算x和y左边的偏差来判断阿仇移动的方向,我们设计了8个方向: 上下左右,左上,坐下,右上,右下,通过计算可以比较容易的计算方向,并且记录阿仇需要移动的x,y轴步长.设置m_bStartMove为true作为阿仇开始跑步的状态,最后把这一次的步长记录下来备用.

我们来看我们每次的绘图函数都做了什么:

首先根据我们移动的步长绘背景(为了偷懒,背景采用的是无缝连接的循环绘图方法,这里不是重点,大家自己看看代码好了). 之后,我们修正绘图帧数,开始画Sprite(一会分析具体代码),之后的代码判断跑步是否结束,如果结束则将帧数恢复到0(站立状态),如果屏幕依然处于依然被按下状态,则说明阿仇需要持续不断的跑,我们取出刚才记录的步长,继续进行跑步动作.如果跑步没有结束,则需要对路径方向做修正,这是为什么呢?这是因为我们只设计了8个移动方向,但是真实的移动方向则可能是任意度,所以我们需要随时判断是否x,y轴一个方向已经和终点一致,如果是则需要改变移动的方向.下面的代码就是进行每一次的移动了,同时,我们在一定间隔更行一个frame来绘图,这样阿仇的步伐看上去就比较舒服,不快也不慢.

来看看我们Sprite绘图的核心:

这里的代码其实很简单,我们通过移动方向就确定了画sprite的哪一行,通过刚才函数的计算,就确定了画哪一帧,由此我们直接计算出相应的Sprite位置,把这个矩形区域拷贝到BackBuffer上.注意这里启用源图关键色的方法,设置DDBLT_KEYSRC.

下面来看看我们处理移动步长的代码:

可以看到,在这个函数.每进行一次绘图,我们就把记录的x,y轴移动步长规律递减.同时背景坐标进行反方向的加减.

大概的逻辑就是这样了,是不是还忘记了处理什么事件呢?对了,是移动事件,在屏幕上按下之后并且移动后我们是需要更改阿仇跑步的方向的:

很简单,再重新调用一下StartMove即可.

好了,大概流程就是这样,也不知道讲清楚了没有.好在有源代码,大家没懂的可以具体看看代码.这个例子的具体效果如下,看上去是不是有点意思了:

通过这一章的教程,我已经基本上把我目前所知道的DDraw知识都讲完了.前面有朋友提到过图像旋转的问题,经过我的研究,DDraw不支持硬件加速的图像旋转,所以这方面只能自己写算法实现了.
本人的DDraw功力尚浅,所以能讲的大概也就这些了.由于时间很仓促,代码里难免有考虑步骤的地方或者性能问题以及Bug,所以权当参考.

最后奉上本章源代码:
 GameSample1_step3.rar (271.97 KB)

本章要点:
1. 游戏场景框架的初步搭建
2. 使用DDraw进行贴图的方法
3. 使用DDraw进行Alpha半透明贴图的方法
4. 使用DDRaw实现Sprite动画的方法
5. RPG游戏中人物活动的技术原型

DDraw的部分我们基本告一段落,下一次我们来看一些轻松点的话题:游戏声音处理.

M8SDN K3 工作室

一篇关于魅族M82D游戏开发的帖子,详细介绍了M8上DDraw的使用,DDraw同时适用于WinCE系统相关推荐

  1. 游戏开发制作流程详细介绍

    一.计划阶段:首先,是项目计划阶段. 1.创意管理:第一步,是召开个会议,在会议中最常见的方法就是采取"头脑风暴法".每个人都必须拿出自己的建议和想法,之后大家一起进行讨论.另外在 ...

  2. Cocos2dx游戏开发系列笔记9:android手机上运行《战神传说》,并解决横竖屏即分辨率自适应问题

    转载:http://blog.csdn.net/iamlazybone/article/details/17191539 懒骨头(http://blog.csdn.net/iamlazybone  Q ...

  3. Cocos2dx游戏开发系列笔记9:android手机上运行《战神传说》,并解决横竖屏即分辨率自适应...

    2019独角兽企业重金招聘Python工程师标准>>> 上节说到cygwin下成功编译出so文件,下面我们要把游戏运行在android上. 开始干活! 其实步骤可以参考 Cocos2 ...

  4. 游戏开发中的物理介绍

    游戏开发中的物理介绍 碰撞对象 物理过程回调 碰撞层和蒙版 GUI示例 代码示例 Area2D StaticBody2D RigidBody2D KinematicBody2D 在游戏开发中,您通常需 ...

  5. 限时游戏开发大赛——Ludum Dare介绍

    Ludum Dare https://ldjam.com/ Ludum Dare 是我了解的第一个游戏开发比赛,简称LD,是国外知名的独立游戏开发竞赛,首届比赛举办于2002年4月,迄今已走过足足35 ...

  6. 游戏开发常用引擎工具介绍对比区别(UE4,Unity,Cocos,LayaAir,[egret白鹭])

    UE4(即虚幻4) 是一套为开发实时技术而存在的引擎工具.目前广泛应用于3D建模渲染.游戏开发中.它完善的工具套件以及简易的工作流程能够使开发者快速修改或查看成果,对于代码的依赖性很低.而完整公开的源 ...

  7. unity超级马里奥2d游戏开发课程-01课程介绍

    大家好,我是小兵,今天给大家带来Unity开发超级马里奥2d游戏的第1课,课程介绍,你可以在我的个人博客geekape.net上找到完整的课程. 我之前主要是做前端开发工作的,并不是做游戏开发的,但由 ...

  8. 《Unity 3.x游戏开发实例》——2.13节适合上千款游戏的机制

    本节书摘来自异步社区<Unity 3.x游戏开发实例>一书中的第2章,第2.13节适合上千款游戏的机制,作者 [加]Ryan Henson Creighton,更多章节内容可以访问云栖社区 ...

  9. 基础实验篇 | RflySim底层飞行控制算法开发系列课程总体介绍

    本讲主要介绍多旋翼的特点及选用多旋翼作为实验平台的原因.对于无人系统教育的一些新需求.RflySim平台对于飞控的底层控制算法的开发优势.本期平台课程的设置.以及如何开发自驾仪系统. 相较于固定翼和直 ...

最新文章

  1. 原生 遍历_前端原生写js代码还是用vue等框架写项目?
  2. springboot-文件上传xls及POI操作Excel
  3. python xpath提取转码_python-xpath获取html文档的部分内容
  4. Ubuntu18.04安装VCS、Verdi、dve全套教程亲测(有成功截图)
  5. bzoj2721樱花——质因数分解
  6. html右侧分为两个框架,html – 两个框架一个滚动条
  7. 奖学金(洛谷-P1093)
  8. webkit内核的浏览器
  9. 屏幕提词器Presentation Prompter for Mac
  10. 2020,ToB 生态全景解读
  11. 告别硬编码,MySQL 如何实现按某字段的不同取值进行统计?
  12. python统计单词出现次数 青少年编程电子学会python编程等级考试二级真题解析2022年3月
  13. Win11快捷复制粘贴不能用怎么办?Win11快捷复制粘贴不能用
  14. fpm工作流程(转)--写的很完整很明白
  15. 人工智能革命,是创造就业还是摧毁就业?
  16. 求水仙花数C语言实现
  17. Micropython开发之TPYBoard制作声光控开关教程实例
  18. 视网膜数据集(3)STARE
  19. android 实现视频播放功能,android开发之简单视频播放器(VideoView)
  20. 湖北省黄石市谷歌高清卫星地图下载

热门文章

  1. 浙江:副省长茅临生专程祝贺台州临海市涌泉镇“忘不了”农村资金互助社开业发来的贺信
  2. 软件换肤(界面美化)—和360皮肤差不多那种
  3. 实验十五:数据恢复原理实验
  4. Layui Table 自动合并行
  5. php单页程序,动态php单页站群源码,泛解析单页循环暴力域名站群系统
  6. 游戏界的Fomo3D? “链上炉石”交易额已破800万
  7. html判断input的状态,HTML实现检测输入已完成功能
  8. Glide加载图片设置圆角和占位图片(加载失败)
  9. 一些知识概念,十进制、八进制和二进制转换
  10. win10将硬盘作为存储池删除读不到盘符_请将磁盘插入驱动器