admin 管理员组文章数量: 887021
2024年3月1日发(作者:numpy库文档)
一 游戏编程基础
1-概论
1.游戏的组成
游戏由剧情、图形图像、声音、文本等资源组成。
2.游戏设计与制作
设计与制作过程大致分为策划,美工,音效,程序,测试五部分。
策划:负责设计游戏的剧情、类别、玩法等,是游戏最重要的部分,直接决定了游戏的成功与否。
美工:负责绘制游戏中所需图形图像资源。
音效:负责制作游戏中所需的声音资源。
程序:负责将多媒体资源按照策划规定的方式组合起来,制作成最终产品-游戏。
测试:负责测试程序的稳定性、游戏的难度等。
我之前看过一本书,书中有这么一个比喻:如果拿游戏与人来类比的话,策划就是心脏,程序是骨骼,美工是皮肤,音效是衣服。
游戏编程,就是游戏设计与制作的程序部分。在我的整个笔记中,所探讨的核心内容,就是游戏的程序实现。
2-游戏程序组成
1.组成
主要由逻辑更新和画面渲染两部分组成,也可以说游戏程序就只干这两件事情。
逻辑更新:接收玩家的输入,更新敌人、玩家、世界等数据。
画面渲染:将游戏内容以图像的方式呈现出来。
2.程序流程
初始化数据-更新-渲染-释放资源。
3-Windows程序设计基础
我所使用到的技术都是基于windows操作系统的,在2D游戏编程方面,使用GDI(图像开发接口)来处理图形图像,虽然GDI的执行效率较低,但是相对于其他的开发包来说,它比较容易学习和理解,在我们学习阶段使用它没问题。当我们对游戏编程思想有了更深的了解的时候,可以使用其他开发包来处理图形图像,如DirectX 3D,OpenGL等。
1.程序入口WinMain
一个简单的windows程序。
[cpp] view plaincopy
1. #include
2. int WINAPI WinMain(HINSTANCE hInstance,
3. HINSTANCE hPrevInstance,
4. LPSTR lpCmdLine,
5. int nShowCmd )
6. {
7. MessageBox(NULL,
8. L"这是一个简单的windows应用程序!",
9. L"这是标题",
10. MB_OKCANCEL|MB_ICONINFORMATION);
11. return 0;
12. }
WinMain和C语言的main函数类似,都是程序的入口函数,由系统调用。
[cpp] view plaincopy
1. int WINAPI WinMain(
2. HINSTANCE hInstance, // handle to current instance
3. HINSTANCE hPrevInstance, // handle to previous instance
4. LPSTR lpCmdLine, // command line
5. int nCmdShow // show state
6. );
详细参数可以参看MSDN或去百科上看,写程序时,保持这个结构不变即可。最重要的参数是hInstance,为应用程序实例句柄,标识了当前应用程序的资源地址。在游戏编程中,经常会用到它,所以,我们经常会将该值保存起来,方便后面使用。
2.创建windows应用程序的流程
主函数(WinMain)->注册窗口类(RegisterClassEx)->创建窗口(CreateWindowEx) ->消息循环(MainLoop),处理窗口过程(WinProc)。其中,窗口过程在消息循环中被反复调用。以下是算法伪代码:
[cpp] view plaincopy
1. WinMain()
2. {
3. RegisterClassEx()
4. CreateWindowEx()
5. MainLoop()
6. }
7. MainLoop()
8. {
9. while(true)
10. {
11. WinProc()
12. }
13. }
3.注册窗口类RegisterClassEx
窗口类,即窗口的类型,它并不是指C++中的类(class)。告诉操作系统即将创建什么样的窗口。
[cpp] view plaincopy
1. ATOM RegisterClassEx(
2. CONST WNDCLASSEX *lpwcx // class data
3. );
4. typedef struct _WNDCLASSEX { //窗口类数据结构
5. UINT cbSize; //本结构大小
6. UINT style; //窗口类的样式
7. WNDPROC lpfnWndProc; //窗口过程函数指针
8. int cbClsExtra; //附加参数
9. int cbWndExtra; //附加参数
10. HINSTANCE hInstance; //应用程序实例句柄
11. HICON hIcon; //窗口图标
12. HCURSOR hCursor; //窗口光标
13. HBRUSH hbrBackground; //背景画刷
14. LPCTSTR lpszMenuName; //菜单名称
15. LPCTSTR lpszClassName; //窗口类名称
16. HICON hIconSm; //窗口小图标
17. } WNDCLASSEX, *PWNDCLASSEX;
4.创建窗口CreateWindowEx
[cpp] view plaincopy
1. HWND CreateWindowEx(
2. DWORD dwExStyle, // extended window style扩展窗口样式
3. LPCTSTR lpClassName, // registered class name已注册的窗口类名称
4. LPCTSTR lpWindowName,// window name窗口标题
5. DWORD dwStyle, // window style窗口样式
6. int x, // horizontal position of window坐标x
7. int y, // vertical position of window坐标y
8. int nWidth, // window width窗口宽度
9. int nHeight, // window height高度
10. HWND hWndParent, // handle to parent or owner window父窗口
11. HMENU hMenu, // menu handle or child identifier菜单句柄
12. HINSTANCE hInstance, // handle to application instance实例句柄
13. LPVOID lpParam // window-creation data附加参数
14. );
返回值是一个窗口句柄,句柄好比就是一个地址,标识了这个窗口的资源位置。
5.消息循环MainLoop
Windows程序都是基于消息机制的,所有的通信都是经过消息传递实现。
1) 消息MSG:
[cpp] view plaincopy
1. typedef struct tagMSG {
2. HWND hwnd; //窗口句柄
3. UINT message; //消息
4. WPARAM wParam; //参数
5. LPARAM lParam; //附加参数
6. DWORD time; //消息产生时间
7. POINT pt; //消息产生时的鼠标坐标
8. } MSG, *PMSG;
2) 获取消息,GetMessage与PeekMessage:
[cpp] view plaincopy
1. BOOL GetMessage(
2. LPMSG lpMsg, // message information
3. HWND hWnd, // handle to window
4. UINT wMsgFilterMin, // first message
5. UINT wMsgFilterMax // last message
6. );
7. BOOL PeekMessage(
8. LPMSG lpMsg, // message information
9. HWND hWnd, // handle to window
10. UINT wMsgFilterMin, // first message
11. UINT wMsgFilterMax, // last message
12. UINT wRemoveMsg // removal options
13. );
两者都是从消息队列中取消息,不同的是当消息队列为空时,两者的处理方式不一样,前者是等待,后者是继续执行。由于我们的游戏需要不断的更新和重绘,而不能等待,所以我们要选择后者。
3)翻译消息TranslateMessage
将消息翻译成可处理的格式。
[cpp] view plaincopy
1. BOOL TranslateMessage(
2. CONST MSG *lpMsg // message information
3. );
4)转发消息DispatchMessage
将消息转发给窗口过程。
[cpp] view plaincopy
1. LRESULT DispatchMessage(
2. CONST MSG *lpmsg // message information
3. );
6.窗口过程WinProc
用户处理消息的函数,该函数在消息循环中被系统函数所调用。该函数的结构必须与下面这个函数类型相同!名称可以不同。
[cpp] view plaincopy
1. LRESULT CALLBACK WindowProc(
2. HWND hwnd, // handle to window
3. UINT uMsg, // message identifier
4. WPARAM wParam, // first message parameter
5. LPARAM lParam // second message parameter
6. );
7.例:创建窗口
[cpp] view plaincopy
1. #include
2. //窗口过程
3. LRESULT CALLBACK WndProc(
4. HWND hwnd, // handle to window
5. UINT uMsg, // message identifier
6. WPARAM wParam, // first message parameter
7. LPARAM lParam // second message parameter
8. )
9. {
10. switch(uMsg)
11. {
12. case WM_DESTROY://窗口销毁消息。按下窗口的叉时会产生。
13. PostQuitMessage(0);//发送退出程序消息WM_QUIT。
14. break;
15. case WM_LBUTTONDOWN:
16. MessageBox(hwnd,L"正处理鼠标左键单击消息",L"这是标题",MB_OK);
17. break;
18. default:
19. return DefWindowProc(hwnd,uMsg,wParam,lParam);//调用默认窗口过程
20. }
21. return 0;
22. }
23. //主函数
24. int WINAPI WinMain(
25. HINSTANCE hInstance, // handle to current instance
26. HINSTANCE hPrevInstance, // handle to previous instance
27. LPSTR lpCmdLine, // command line
28. int nCmdShow // show state
29. )
30. {
31. //MessageBox(NULL,L"这是消息框",L"这是标题",MB_OKCANCEL);
32. WNDCLASSEX wcx;//窗口类
33. memset(&wcx,0,sizeof(WNDCLASSEX));
34. = sizeof(WNDCLASSEX);//窗口类大小
35. = CS_CLASSDC;//窗口类风格
36. kground = (HBRUSH)GetStockObject(WHITE_BRUSH);//获得系统画刷(白色)
37. r = LoadCursor(NULL,IDC_HAND);//加载系统光标
38. m = = LoadIcon(NULL,IDI_APPLICATION);//加载系统图标
39. nce = hInstance;//应用程序实例句柄
40. /*字符串前面‘L’的意思是,该字符串为Unicode编码格式,
41. 不是默认的ASCII格式。如果要改成ASII格式,可以修改项目属性。*/
42. assName = L"WndClass";//窗口类名称
43. dProc = (WNDPROC)WndProc;//窗口过程
44. //注册窗口类
45. RegisterClassEx( &wcx );
46. //窗口过程
47. HWND hWnd = CreateWindowEx(0,L"WndClass",L"这是窗口标题",
48. WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
49. 0,0,640,480,NULL,NULL,hInstance,NULL);
50. //显示窗口
51. ShowWindow(hWnd,SW_SHOWNORMAL);
52. //更新窗口,即发送重绘消息
53. UpdateWindow(hWnd);
54. MSG message;//消息结构
55. while(true)
56. {
57. //从消息队列中取消息
58. if(PeekMessage(&message,NULL,0,0,PM_REMOVE))
59. {
60. if (e == WM_QUIT)//跳出循环,退出程序。
61. {
62. break;
63. }
64. //翻译消息
65. TranslateMessage(&message);
66. //发送给窗口过程
67. DispatchMessage(&message);
68. }
69. Sleep(1);//暂停ms,即放弃CPU时间片ms,避免浪费CPU。
70. }
71. return 0;
72. }
8.使用VS2008-VC9创建Win32应用程序
菜单:文件->新建->项目,在弹出的对话框中选Win32项目,截图如下:
输入名称,选择好路径后,点确定,然后再点下一步,来到如下步骤,
点选windows应用程序,勾选空项目,然后完成。这样一个空的Win32应用程序框架就建好了,接着给框架添加C++源文件(cpp文件)。写代码,调试,运行,OK。
如果使用的是VC6.0,操作方法跟这个也很类似,但是VC6中的默认编码格式是ASCII,所以字符串前面不需要加„L‟。
注意,不管是VS还是VC6,都要建立win32应用程序项目,不要创建控制台应用程序!否则无法通过链接。
修改编码:在解决方案上点右键->属性,在弹出来的对话框中,将字符集改为“使用多字节字符集”,之后程序的编码将会变成ASCII格式。如果是初学者的话,建议将字符集改为“使用多字节字符集”。
9.小节
创建窗口的过程比较麻烦,但是大部分代码是固定不变的。所以,我们可以将不变的部分封装起来,今后使用到的时候,直接拿过来使用,而不必再写一遍,可以达到一劳永逸的效果!
WindowsAPI比较多,对于这些API,我们只用知道它的用法就可以了,具体的参数我们可以去MSDN(微软软件开发文档)查,所以建议大家一定要装个MSDN不管哪个版本,都必须要有一个。
刚开始学习的时候,新概念会有很多,如果不能理解的话,可以先忽略它,把它当一个黑盒子使用,用的时间长了,就可以理解了。
二 游戏编程起步
1.一个简单的游戏-贪吃蛇
1.贪吃蛇游戏剖析
1)游戏的目标。在不被撞死的前提下,吃掉奖子增加自己的长度,来完成升级。
2)游戏中的物体。蛇,墙壁,奖子。
3)动作。蛇移动,蛇吃奖子,蛇增加长度。
2.数据结构与算法分析
1)数据结构。简单起见,所有物体都用方块拼接。
则蛇可以使用一个一维数组描述,数组的每个单元描述了蛇块的状态,如方向;可使用一个二维数组来描述地面情况,不可通过的地方为墙壁设置为1,可通过的地方设置为0;奖子,就是一个特殊的方块。
2)算法。
注意观察蛇的特点。将蛇肢解为方块,则会发现每个方块的移动都依赖于它前面方块上一次的移动状态(第一个方块由玩家控制)。
如图,1是蛇头,4是尾,蓝色箭头是原来移动方向,红色箭头是玩家控制的方向(按了下键)。移动方向从1~4。(a)->(b) 下右右右,(b)->(c)下下右右。
根据这个规律,我们可以总结出一个基本算法:用数组来存贮蛇块,每个蛇块结点包含的信息有当前的移动方向,那么更新蛇的状态时,从尾部到头部进行处理。
[cpp] view plaincopy
1. for i=n-1 to 1
2. {
3. dir(i) = dir(i-1)
4. pos(i) += dir(i)
5. }
6. if dirKeyDown
7. {
8. dir(0) = k
9. }
10. pos(0) += dir(0)
这样,最复杂的部分就解决了。当然这不是最好的算法,如果我们再仔细观察下,就会发现这样的规律,蛇每次移动的时候,都只是头和尾发生了变化,那么,每次更新蛇的时候,我们只需要将尾部的蛇块移到头部相应的位置,不就更简单吗?答案是肯定的。这就是算法的魅力!只要我们勤于动脑筋,总会发现一些更好的解决办法。
[cpp] view plaincopy
1. pos(n-1) = pos(0)+dir
2. insert(n-1) before pos(0)
算法的复杂度立即从O(n)变到了O(1)!而且我们还会发现,我们只需要记录一个方向就可以了,则空间复杂度也因此降低了。
3)地图。描述了地面信息。
我们的贪吃蛇游戏地图信息很简单,总共有3类物体会站到地面上:墙壁,蛇,奖子。在每次更新的时候,我们将3类物体的信息按类别填充到地图中。如,墙壁的位置填1,奖子的位置填2,蛇的位置填3(每个蛇块都填),没有东西的地方填0。然后,将这个填满0,1,2,3的二维数组,交给渲染系统。
到此,我们的幕后操作就算基本完成,剩下的就是些细节,等到编码的时候在详细处理。
3.渲染地图数据描述
渲染贪吃蛇游戏其实也很简单,把地图中1的部分涂成蓝色,2的部分涂成红色,3的部分涂成绿色,这将会是一个什么样的效果呢?看下图:
怎么样,有贪吃蛇游戏的感觉吗?再看看下图:
简直是完美!
游戏编程笔记-起步(二)
2.在窗口上绘图
看了我上面的分析,我想,很多朋友都迫不及待的想学这部分知识,原因是上面的知识实在太简单了,只要稍懂点算法的同胞们都可以看懂。关键是大家苦于英雄无勇武之地啊!学了n年的算法,做了n到算法题,却写不出一个如此简单的小游戏。
那好,让我们开始我们真正的游戏编程之旅吧!
1.概述
在Windows上绘图方式,跟美术大师绘图差不多。美术绘画,首先要具备以下工具:画板,画布,画笔,画刷。同样,Windows上也有相关的概念。绘图设备DeviceContext(DC),位图Bitmap,画笔Pen,画刷brush。他们一一对应。
2.画板
在Windows中被称作设备上下文(Device Context,DC),我习惯称之为绘图设备。但是Windows的“画板”与美术大师手中的“画板”不一样,Windows中的“画板”实质上是一个工具的集合体,将画布、画笔、画刷、绘画方式全部综合管理起来,然后,所有的绘画操作都将在这上面进行。
1)系统绘图设备。系统有默认的绘图,我们可以直接使用它进行一系列的绘图操作。
获取系统绘图设备:
[cpp] view plaincopy
1. HDC GetDC(
2. HWND hWnd // 窗口句柄。即指定获得那个窗口的绘图设备。
3. );
如:
[cpp] view plaincopy
1. HDC hDC = GetDC(g_hWnd);
2. Rectangle(hDC,0,0,100,100);//绘制矩形
3. ....
使用完毕后要归还给窗口:
[cpp] view plaincopy
1. int ReleaseDC(
2. HWND hWnd, // handle to window
3. HDC hDC // handle to DC
4. );
2)创建画板。通常我们不直接使用系统画板,因为系统画板直接和显示器关联,在上面一边画,就会一边显示到窗口,由于在绘制的时候,中间有时间差(我们可能经过若干次绘制,才绘制完一个地图),而游戏程序需要反复的擦除、重绘,所以常常会造成画面剧烈闪烁。为了解决这个问题,我们会创建一个辅助画板,在幕后绘制完毕后,一次性的将它显示到屏幕上,因为显示的时间非常快,所以就看不到闪烁。
创建辅助绘图设备:
[cpp] view plaincopy
1. HDC CreateCompatibleDC(
2. HDC hdc // 指向一个已经存在的DC,如果该值传入0,则系统会创建一个存贮DC。
3. );
存贮DC(辅助绘图设备),在创建完毕后,它的大小只有一个像素,我们没办法直接往他上面直接绘图,我们还需要往它上面贴一张画布。
使用完毕后记得要释放资源:
[cpp] view plaincopy
1. BOOL DeleteDC(
2. HDC hdc // handle to DC
3. );
例:
[cpp] view plaincopy
1. HDC memDC = CreateCompatibleDC(0); //创建辅助绘图设备
2. SelectObject(memDC,hBitmap); //将画布贴到绘图设备上
3. Rectangle(memDC,x1,y1,x2,y2); //绘制矩形
4. HDC hDC = GetDC(g_hWnd); //获得系统绘图设备
5. copy memDC to hDC //复制到系统设备上显示
6. ReleaseDC(g_hWnd,hDC); //归还系统绘图设备
7. DeleteDC(memDC); //释放辅助绘图设备
3.画布
画布其实就是位图。位图的作用非常强大,在此我只介绍它的普通用法——充当画布。其他作用,后面再介绍。
创建掩码位图(画布):
[cpp] view plaincopy
1. HBITMAP CreateCompatibleBitmap(
2. HDC hdc, //指向绘图设备,最好是系统的绘图设备
3. int nWidth, // 位图宽度
4. int nHeight // 位图高度
5. );
释放资源:
[cpp] view plaincopy
1. BOOL DeleteObject(
2. HGDIOBJ hObject // handle to graphic object
3. );
4.画笔
画笔就不用解释了,Windows的画笔和美术大师的差不多。
创建画笔:
[cpp] view plaincopy
1. HPEN CreatePen(
2. int fnPenStyle, // 画笔风格。直线:PS_SOLID,点线:PS_DOT
3. int nWidth, // 画笔宽度。决定了画出来的线条粗细
4. COLORREF crColor // 画笔颜色
5. );
释放资源:
[cpp] view plaincopy
1. BOOL DeleteObject(
2. HGDIOBJ hObject // handle to graphic object
3. );
颜色:可由红绿蓝三种颜色按程度混合起来构成。
如COLORREF cr = RGB(255,0,255),此时cr是粉红色(红色+蓝色)。COLORREF实际上是无符号长整型的别名,RGB将红绿蓝三个数值整合成一个无符号长整型数。
[cpp] view plaincopy
1. COLORREF RGB(
2. BYTE byRed, //红色分量0~255,值越大表面程度越深。
3. BYTE byGreen, // 绿色分量0~255
4. BYTE byBlue // 蓝色分量0~255
5. );
5.画刷
[cpp] view plaincopy
1. 创建画刷:
2. HBRUSH CreateSolidBrush(
3. COLORREF crColor // brush color value
4. );
5. 释放资源:
6. BOOL DeleteObject(
7. HGDIOBJ hObject // handle to graphic object
8. );
6.绘画
画布、画笔、画刷创建好之后,要是分别使用API,将它们选入绘图设备。当然,绘图设备,有自己的默认画刷和画笔。
[cpp] view plaincopy
1. HGDIOBJ SelectObject(
2. HDC hdc, // 直线绘图设备
3. HGDIOBJ hgdiobj // 指向GDI对象。就是画刷、画笔、位图等。
4. );
返回值是旧有的相关内容。
一些常用绘图API:
1)绘制矩形。
[cpp] view plaincopy
1. BOOL Rectangle(
2. HDC hdc, // 绘图设备
3. int nLeftRect, // 矩形区域左上角x坐标
4. int nTopRect, // 矩形区域左上角y坐标
5. int nRightRect, //矩形区域右下角x坐标
6. int nBottomRect // 矩形区域右下角y坐标
7. );
2)填充区域。用画刷来填充区域,经常用来擦除整个窗口。
[cpp] view plaincopy
1. int FillRect(
2. HDC hDC, // 绘图设备
3. CONST RECT *lprc, // 矩形区域
4. HBRUSH hbr // 填充画刷
5. );
[cpp] view plaincopy
1. 区域结构:
2. typedef struct _RECT {
3. LONG left; //矩形左边(左上角x坐标)
4. LONG top; //矩形顶边(左上角y坐标)
5. LONG right; //矩形右边(右下角x坐标)
6. LONG bottom; //矩形底边(右下角y坐标)
7. } RECT, *PRECT;
[cpp] view plaincopy
1. 获得窗口客户区区域:
2. BOOL GetClientRect(
3. HWND hWnd, // handle to window
4. LPRECT lpRect // client coordinates
5. );
3)绘图设备之间的拷贝。
[cpp] view plaincopy
1. BOOL BitBlt(
2. HDC hdcDest,//目标绘图设备
3. int nXDest, // 目标区域左上角x坐标
4. int nYDest, //目标区域左上角y坐标
5. int nWidth, // 目标区域宽度
6. int nHeight,// 目标区域高度
7. HDC hdcSrc, // 源绘图设备
8. int nXSrc, // 源区域左上角x坐标
9. int nYSrc, // 源区域左上角y坐标
10. DWORD dwRop // 操作码。常用SRCCOPY(复制)。
11. );
可以简单的理解这个API:将源绘图设备拷贝给目标绘图设备。从幕后画布拷贝给前景画布。从幕后绘图设备拷贝给显示器。拷贝的区域,就是上面指定的。
如:BitBlt(hDC,0,0,g_nWidth,g_nHeight,memDC,0,0,SRCCOPY);
4)其他常用API
文字处理
创建字体:CreateFont,CreatePointFont,SelectObject(.,font)。
显示文本:Textout,DrawText
设置文本颜色:SetTextColor ,GetTextColor
设置背景:SetBkColor,SetBkMode(设置背景模式,TRANSPARENT为透明)
绘制
点:SetPixel,SetPixelV,GetPixel。
线:MoveToEx-LinTo。
圆:Ellipse
多边形:Polygon
弧:Arc
位图处理
从文件加载位图/图标/光标:LoadImage
从资源加载位图:LoadBitmap
获得位图信息:GetObject
绘图设备间的拷贝复制
直接模式:BitBlt
拉伸拷贝:StretchBlt
透明处理:TranparentBlt,需要加 库。
半透明处理(Alpha混合):AlpaBlend
其他
获得系统属性:GetSystemMetrics,一些很重要的信息。
获得鼠标屏幕坐标:GetCursorPos,需使用坐标转换转换为窗口坐标
坐标转换:ClientToScreen,ScreenToClient
获得键盘键状态:GetAsyncKeyState
获得整个键盘信息:GetKeyboardState
消息框:MessageBox
7.样例:绘制地图网格
我将上第一章的,创建窗口代码用C语言格式稍微封装了下。核心代码如下:
[cpp] view plaincopy
1. #include "app.h"
2.
3. int g_nWidth = 600;//窗口宽度
4. int g_nHeight = 480;//窗口高度
5.
6. void init()//游戏初始化
7. {
8. }
9.
10. void update()//逻辑更新
11. {
12. }
13.
14. void render()//画面渲染
15. {
16. HDC hDC = GetDC(getHWnd()); //获得系统绘图设备
17.
18. HDC memDC = CreateCompatibleDC(0); //创建辅助绘图设备
19.
20. HBITMAP bmpBack = CreateCompatibleBitmap(hDC,g_nWidth,g_nHeight);//创建掩码位图(画布)
21. SelectObject(memDC,bmpBack); //将画布贴到绘图设备上
22.
23. HPEN penBack = CreatePen(PS_SOLID,1,RGB(255,0,255));//创建画笔
24. SelectObject(memDC,penBack); //将画笔选到绘图设备上
25.
26. HBRUSH brushBack = CreateSolidBrush(RGB(255,255,255));//创建画刷
27. SelectObject(memDC,brushBack); //将画刷选到绘图设备上
28.
29. //擦除背景
30. RECT rcClient;//区域结构
31. GetClientRect(getHWnd(),&rcClient);//获得客户区域
32. HBRUSH brushTemp = (HBRUSH)GetStockObject(WHITE_BRUSH);//获得库存物体,白色画刷。
33. FillRect(memDC,&rcClient,brushTemp);//填充客户区域。
34. //////////////////////////////////////////////////////////////////////////
35. HBRUSH brushObj = CreateSolidBrush(RGB(0,255,0));//创建物体画刷
36. //绘制维网格,矩形画法。
37. int dw = 30;
38. int rows = g_nHeight/dw;
39. int cols = g_nWidth/dw;
40. for (int r=0; r 41. { 42. for (int c=0; c 43. { 44. if (r == c) 45. { 46. SelectObject(memDC,brushObj); 47. } 48. else 49. { 50. SelectObject(memDC,brushBack); 51. } 52. Rectangle(memDC,c*dw,r*dw,(c+1)*dw,(r+1)*dw); 53. } 54. } 55. 56. DeleteObject(brushObj); 57. ////////////////////////////////////////////////////////////////////////// 58. BitBlt(hDC,0,0,g_nWidth,g_nHeight,memDC,0,0,SRCCOPY);//复制到系统设备上显示 59. DeleteObject(penBack); //释放画笔资源 60. DeleteObject(brushBack);//释放画刷资源 61. DeleteObject(bmpBack); //释放位图资源 62. DeleteDC(memDC); //释放辅助绘图设备 63. ReleaseDC(getHWnd(),hDC); //归还系统绘图设备 64. Sleep(1); 65. } 66. 67. void clear()//资源释放 68. { 69. } 70. 71. 72. //主函数 73. int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE , LPSTR , int ) 74. { 75. if(!initApp(hInstance,L"贪吃蛇游戏",g_nWidth,g_nHeight)) 76. { 77. return 0; 78. } 79. 80. //初始化游戏 81. init(); 82. 83. //游戏循环 84. mainLoop(); 85. 86. //释放资源 87. clear(); 88. 89. return 0; 90. } 3.贪吃蛇游戏实现 代码下载:/detail/you_lan_hai/3738025 核心代码实现如下,算法描述见 “起步(一)”: [cpp] view plaincopy 1. #include "app.h" 2. #include 3. #include 4. 5. int g_map[100][100];//2维地图 6. 7. int g_nWidth = 610; //窗口宽度 8. int g_nHeight = 514;//窗口高度 9. 10. int g_rows = 0; //地图行数 11. int g_cols = 0; //地图列数 12. int g_nSize = 30; //地图方格尺寸 13. 14. struct Point//二维点 15. { 16. int r;//行 17. int c;//列 18. }; 19. 20. Point g_snake[1000]; //蛇 21. int g_nLength = 0; //蛇的长度 22. int g_nSpeed; 23. bool g_bLive = true; //是否存活 24. int g_nSnakeDir = 0; //蛇的当前移动方向。0-左,1-右,2-上,3-下 25. 26. const Point g_direction[4] = {{0,-1},{0,1},{-1,0},{1,0}};//四个方向运动增量。 27. 28. Point g_prizePos; //奖子坐标 29. 30. bool g_bPause = true; //是否暂停 31. 32. HBRUSH g_brushs[4]; //背景画刷 33. 34. 35. void setSnakeDir(int dir)//设置蛇的运动方向。 36. { 37. if ((dir==0 && g_nSnakeDir==1) 38. || (dir==1 && g_nSnakeDir==0) 39. || (dir==2 && g_nSnakeDir==3) 40. || (dir==3 && g_nSnakeDir==2))//反方向移动则令他暂停 41. { 42. g_bPause = true; 43. return;//直接返回,什么也不做。 44. } 45. else 46. { 47. g_bPause = false; 48. g_nSnakeDir = dir; 49. } 50. } 51. 52. void resetLevel()//重置关卡 53. { 54. /*初始化地图。将四边初始为障碍,即不可通过。 55. * 0-可通过,1-障碍,2-奖子,3-蛇 56. */ 57. memset(g_map,0,sizeof(g_map)); 58. for (int r=0; r 59. { 60. g_map[r][0] = 1; 61. g_map[r][g_cols-1] = 1; 62. } 63. for (int c=0; c 64. { 65. g_map[0][c] = 1; 66. g_map[g_rows-1][c] = 1; 67. } 68. 69. g_nSpeed = 500; 70. 71. g_bPause = true;//暂停 72. g_nSnakeDir = 1;//向右。 73. g_bLive = true; 74. g_nLength = 3;//蛇初始有3个结点 75. g_snake[0].r = 1;//蛇头 76. g_snake[0].c = 3; 77. g_snake[1].r = 1; 78. g_snake[1].c = 2; 79. g_snake[2].r = 1; 80. g_snake[2].c = 1; 81. //把初始状态写入地图 82. for (int i=0; i 83. { 84. g_map[g_snake[i].r][g_snake[i].c] = 3; 85. } 86. 87. //初始化奖子坐标 88. g_prizePos.r = 1; 89. g_prizePos.c = g_cols-2; 90. } 91. 92. void init()//游戏初始化 93. { 94. g_brushs[0] = CreateSolidBrush(RGB(255,255,255));//白色画刷 95. g_brushs[1] = CreateSolidBrush(RGB(0,0,255));//绘制障碍画刷 96. g_brushs[2] = CreateSolidBrush(RGB(255,0,0));//绘制奖子画刷 97. g_brushs[3] = CreateSolidBrush(RGB(0,255,0));//绘制蛇画刷 98. 99. srand(GetTickCount()); 100. 101. //矫正窗口高度、宽度数据 102. RECT rc; 103. GetClientRect(getHWnd(),&rc); 104. g_nWidth = - ; 105. g_nHeight = - ; 106. 107. //计算行列值 108. g_rows = g_nHeight / g_nSize; 109. g_cols = g_nWidth / g_nSize; 110. 111. resetLevel(); 112. } 113. 114. void update()//逻辑更新 115. { 116. //时钟控制 117. static int oldTime = 0; 118. int curTime = GetTickCount(); 119. if (curTime - oldTime < g_nSpeed) 120. { 121. return ; 122. } 123. oldTime = curTime; 124. ////////////////////////////////////////////////////////////////////////// 125. static int lastOcurTime = 0;//奖子上次出现的时间 126. 127. if(curTime - lastOcurTime > 10000)//每隔10s更新一次位置。 128. { 129. //lastOcurTime不为-1表示,没有被吃掉,则将原来位置的奖子从地图擦掉。 130. if (lastOcurTime != -1) 131. { 132. g_map[g_prizePos.r][g_prizePos.c] = 0; 133. } 134. lastOcurTime = curTime; 135. 136. //随机产生奖子位置。 137. bool flag = true; 138. while(flag) 139. { 140. g_prizePos.r = rand()%(g_rows-2)+1; 141. g_prizePos.c = rand()%(g_cols-2)+1; 142. if (g_map[g_prizePos.r][g_prizePos.c] == 0)//符合条件 143. { 144. flag = false; 145. g_map[g_prizePos.r][g_prizePos.c] = 2; 146. } 147. } 148. } 149. 150. if (!g_bLive || g_bPause)//死亡或暂停 151. { 152. return ; 153. } 154. 155. //先判断是否可走,即是否撞死。 156. Point newHead; 157. newHead.r = g_snake[0].r + g_direction[g_nSnakeDir].r; 158. newHead.c = g_snake[0].c + g_direction[g_nSnakeDir].c; 159. if (g_map[newHead.r][newHead.c]==1 || g_map[newHead.r][newHead.c]==3) 160. { 161. g_bLive = false; 162. if(IDYES == MessageBox(getHWnd(),L"你撞死啦!是否重新来过?", 163. L"撞死提示",MB_YESNO)) 164. { 165. resetLevel(); 166. } 167. else 168. { 169. PostQuitMessage(0); 170. } 171. return ; 172. } 173. else if (g_map[newHead.r][newHead.c] == 2)//遇到奖子 174. { 175. ++g_nLength ; 176. lastOcurTime = -1; 177. g_nSpeed -= 10; 178. } 179. else 180. { 181. //撤销尾部在地图上遗留数据 182. g_map[g_snake[g_nLength-1].r][g_snake[g_nLength-1].c] = 0; 183. } 184. 185. memmove(g_snake+1,g_snake,sizeof(Point)*(g_nLength-1));//移动蛇的中间数据 186. g_snake[0].r = newHead.r; 187. g_snake[0].c = newHead.c; 188. 189. //添加蛇头数据 190. g_map[g_snake[0].r][g_snake[0].c] = 3; 191. ////////////////////////////////////////////////////////////////////////// 192. } 193. 194. void render()//画面渲染 195. { 196. HDC hDC = GetDC(getHWnd()); //获得系统绘图设备 197. 198. HDC memDC = CreateCompatibleDC(0); //创建辅助绘图设备 199. 200. HBITMAP bmpBack = CreateCompatibleBitmap(hDC,g_nWidth,g_nHeight);//创建掩码位图(画布) 201. SelectObject(memDC,bmpBack); //将画布贴到绘图设备上 202. 203. HPEN penBack = CreatePen(PS_SOLID,1,RGB(255,0,255));//创建画笔 204. SelectObject(memDC,penBack); //将画笔选到绘图设备上 205. 206. //擦除背景 207. RECT rcClient;//区域结构 208. GetClientRect(getHWnd(),&rcClient);//获得客户区域 209. HBRUSH brushTemp = (HBRUSH)GetStockObject(WHITE_BRUSH);//获得库存物体,白色画刷。 210. FillRect(memDC,&rcClient,brushTemp);//填充客户区域。 211. ////////////////////////////////////////////////////////////////////////// 212. 213. //绘制2维网格,矩形画法。 214. for (int r=0; r 215. { 216. for (int c=0; c 217. { 218. SelectObject(memDC,g_brushs[g_map[r][c]]); 219. Rectangle(memDC,c*g_nSize,r*g_nSize,(c+1)*g_nSize,(r+1)*g_nSize); 220. } 221. } 222. 223. ////////////////////////////////////////////////////////////////////////// 224. BitBlt(hDC,0,0,g_nWidth,g_nHeight,memDC,0,0,SRCCOPY);//复制到系统设备上显示 225. DeleteObject(penBack); //释放画笔资源 226. DeleteObject(bmpBack); //释放位图资源 227. DeleteDC(memDC); //释放辅助绘图设备 228. ReleaseDC(getHWnd(),hDC); //归还系统绘图设备 229. 230. Sleep(10); 231. } 232. 233. void clear()//资源释放 234. { 235. //释放画刷资源 236. for (int i=0; i<4; ++i) 237. { 238. if(g_brushs != NULL) 239. { 240. DeleteObject(g_brushs[i]); 241. g_brushs[i] = NULL; 242. } 243. } 244. } 245. 246. /*窗口过程。如果没有处理消息请返回0,否则返回1。*/ 247. LRESULT wndProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam ) 248. { 249. switch(uMsg) 250. { 251. case WM_KEYDOWN: 252. { 253. switch(wParam) 254. { 255. case VK_LEFT: setSnakeDir(0); break; //向左 256. case VK_RIGHT: setSnakeDir(1); break; //向右 257. case VK_UP : setSnakeDir(2); break; //向上 258. case VK_DOWN: setSnakeDir(3); break; //向下 259. case VK_ESCAPE: DestroyWindow(hwnd);break; 260. } 261. } 262. break; 263. default: 264. return 0; 265. } 266. return 1; 267. } 268. 269. //主函数 270. int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE , LPSTR , int ) 271. { 272. if(!initApp(hInstance,L"贪吃蛇游戏",g_nWidth,g_nHeight)) 273. { 274. return 0; 275. } 276. 277. //初始化游戏 278. init(); 279. 280. //游戏循环 281. mainLoop(); 282. 283. //释放资源 284. clear(); 285. 286. return 0; 287. }
版权声明:本文标题:简单游戏编程 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1709282644h541920.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论