VC++游戏编程最基础教程.pdf
第一章VC+游戏编程最基础教程学习各种高级外挂制作技术,马上去百度搜索(魔鬼作坊),点击第一个站进入,快速成为做挂达人。一动画游戏是动画的延伸,只要知道了动画的原理,在动画变化时,使它根据键盘或鼠标的输入操作而变化,就是游戏了。所以在介绍游戏编程之前,先讲讲动画。动画一般分三种形式:形变、位变、色变。如下:1 1 1 1 1 1 1 1 形变形变即几种形状的图形或位图依次转变而成。(图 1-1)图 1-1先看一个简单的实例:a a 新建工程 1_1,选择单文档,完成。b b 在 view 类加变量intituxing,并在构造函数里赋值为 ituxing=0;c c 在 OnDraw(CDC*pDC)函数上添上以下语句:void CMy1_1View:OnDraw(CDC*pDC)CMy1_1Doc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw codefor native datahere/清除当前图形CBrush mybrush1;mybrush1.CreateSolidBrush(RGB(255,255,255);CRect myrect1(0,0,1024,768);pDC-FillRect(myrect1,&mybrush1);/判断当前图形 ituxingif(ituxing=1)/ituxing=1,画矩形pDC-Rectangle(20,20,89,89);elseif(ituxing=2)/ituxing=2,画圆pDC-Ellipse(20,20,89,89);else/否则,画三角形pDC-MoveTo(20,20);pDC-LineTo(89,45);pDC-LineTo(20,89);pDC-LineTo(20,20);d d 添 加 OnTimer(UINTnIDEvent)和 OnCreate(LPCREATESTRUCTlpCreateStruct),并加上语句如下:void CMy1_1View:OnTimer(UINT nIDEvent)/TODO:Add your message handler codehere and/or call default/获取指针 pdcCDC*pDC=GetDC();/调用 OnDraw(pDC)重画OnDraw(pDC);/使 ituxing 循环ituxing=(ituxing+1)%3;CView:OnTimer(nIDEvent);intCMy1_1View:OnCreate(LPCREATESTRUCT lpCreateStruct)if(CView:OnCreate(lpCreateStruct)=-1)return-1;/TODO:Add your specialized creation codehere/设置更新时间SetTimer(1,750,NULL);return 0;e e 完成。2 2 2 2 2 2 2 2 位变位变即同一个图形或位图依次在不同的位置上出现而形成动画。(图 1-2)图 1-2见实例 1_2:a.a.复制实例 1_1 文件夹,改名为 1_2。把voidCMy1_1View:OnTimer(UINTnIDEvent)中 的ituxing=(ituxing+1)%3;改 为ituxing=(ituxing+1)%10;b.b.把 OnDraw(CDC*pDC)函数改为:void CMy1_1View:OnDraw(CDC*pDC)CMy1_1Doc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw codefor native datahere/清除当前图形CBrush mybrush1;mybrush1.CreateSolidBrush(RGB(255,255,255);CRect myrect1(0,0,1024,768);pDC-FillRect(myrect1,&mybrush1);/用当前图形位置 ituxing/画圆pDC-Ellipse(30*ituxing,30*ituxing,30*ituxing+50,30*ituxing+50);c.c.执行,OK!3 3 色变色变即利用不同颜色的转变而形成有动感的图画。(图 1-3)图 1-3实例 1_3:a.a.新建单文档工程 1_3。b.b.添加参数inticolor 并赋值为 0。c.c.添 加函 数 OnTimer(UINT nIDEvent)和 OnCreate(LPCREATESTRUCTlpCreateStruct),并添加语句如下:void CMy1_3View:OnTimer(UINT nIDEvent)/TODO:Add your message handler codehere and/or call defaulticolor+=1;/此函数使客户区失效,以致重画Invalidate();CView:OnTimer(nIDEvent);intCMy1_3View:OnCreate(LPCREATESTRUCT lpCreateStruct)if(CView:OnCreate(lpCreateStruct)=-1)return-1;/TODO:Add your specialized creation codehereSetTimer(1,750,NULL);return 0;d.d.最后在 OnDraw(CDC*pDC)加上画圆语句:void CMy1_3View:OnDraw(CDC*pDC)CMy1_3Doc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw codefor native datahere/获取 icolor 值inti=icolor;/在不同位置画四个不同颜色的圆CBrush brush1(RGB(20+(64*i)%255,140+(64*i)%255,210*(64+i)%255);CBrush*pOldBrush1=pDC-SelectObject(&brush1);pDC-Ellipse(300,200,350,250);pDC-SelectObject(pOldBrush1);CBrush brush2(RGB(84+(64*i)%255,204+(64*i)%255,24+(64*i)%255);CBrush*pOldBrush2=pDC-SelectObject(&brush2);pDC-Ellipse(350,200,400,250);pDC-SelectObject(pOldBrush2);CBrush brush3(RGB(148+(64*i)%255,14+(64*i)%255,88+(64*i)%255);CBrush*pOldBrush3=pDC-SelectObject(&brush3);pDC-Ellipse(300,250,350,300);pDC-SelectObject(pOldBrush3);CBrush brush4(RGB(212+(64*i)%255,78+(64*i)%255,152+(64*i)%255);CBrush*pOldBrush4=pDC-SelectObject(&brush4);pDC-Ellipse(350,250,400,300);pDC-SelectObject(pOldBrush4);e.e.完成。以上是三种最基本的动画。当然,具体情况不会这么简单,而往往的其中二或三种的复杂的结合。而作为游戏,它的关键在:在动画的基础上加一些主观的操作。如先画一个球,然后用方向键或鼠标移动。如实例 1_4:a.a.新建单文档工程 1_4。b.b.添加半径和圆心intir;/半径CPoint center;/圆心在 CMy1_4View()中赋值如下:CMy1_4View:CMy1_4View()/TODO:add construction codeherecenter.x=400;center.y=200;ir=50;c.c.在 OnDraw(CDC*pDC)函数中画圆void CMy1_4View:OnDraw(CDC*pDC)CMy1_4Doc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw codefor native datahere/画圆pDC-Ellipse(center.x-ir,center.y-ir,center.x+ir,center.y+ir);d.d.添加 OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags),使它按照键盘方向键的操作而移动圆的位置。如下:void CMy1_4View:OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)/TODO:Add your message handler codehere and/or call default/利用方向键左右上下移动 20switch(nChar)caseVK_LEFT:center.x-=20;break;caseVK_RIGHT:center.x+=20;break;caseVK_UP:center.y-=20;break;caseVK_DOWN:center.y+=20;break;/重画Invalidate();CView:OnKeyDown(nChar,nRepCnt,nFlags);e.e.执行,看看效果,你就可以操纵了。f.f.那么,利用鼠标又是怎样操作呢?添加 OnLButtonDown(UINT nFlags,CPointpoint)如下:void CMy1_4View:OnLButtonDown(UINT nFlags,CPoint point)/TODO:Add your message handler codehere and/or call default/把圆移动鼠标指定位置/圆心赋值为 pointcenter=point;/重画Invalidate();CView:OnLButtonDown(nFlags,point);g.g.再执行,你现在键盘鼠标都可以操作了。真正的游戏,也就是以上简单的图形或位图的复杂化和简单操作的复杂化而已。二部分图更新既然游戏是动画,就离不开“动”,而所谓“动”,就是利用画面的刷新,用新的要求的图画或位置去代替原先的图画或位置。图画储存在哪里?内存。画图需要什么?系统资源。而它们有什么关系?成正比。为什么说这些呢?因为这关系到游戏编程一个极大的问题:速度!上面那些简单的例子,我们是怎样实现的呢?不就是利用一个控制时间的函数定期地对画面进行重画吗?它们的速度快吗?快。是的,但如果你编一个大屏幕的画面,并在上面画上几十种图形,添上几十种颜色,然后再利用上面的方法试试,速度还快吗?慢了。为什么?因为这个图画太占用内存,需要太多的系统资源,我们的机器满足不了要求,变慢了。怎样解决呢?一切的计算机图形都是利用部分图形重画的原理!一种是:只保存要改变的那部分图形,然后把要改变的新图接在已保存过画面的范围内,形成新的图。接着把刚才保存的图画还原,变成原先的图画。再保存另一个地方,贴上新图,变成新图画,再还原。反复进行(如下图 1-4)。一种是:不重画,直接在要变化的位置变化。图 1-4实例 1_5:一个正方形在复杂的画面上移动。要求:先用全部重画做一次,再用部分重画做一次,对比一下。a.a.新建单文档工程,添加两个变量:intixunhuan;CBitmap m_Bitmap;b.b.添加位图 IDB_BITMAP1,即狮子图象。图 1-5c.c.在构造函数里面添加两个语句:CMy1_5View:CMy1_5View()/TODO:add construction codehere/获得位图m_Bitmap.LoadBitmap(IDB_BITMAP1);ixunhuan=0;d.d.添加虚函数 OnInitialUpdate(),初始化位图变量e.e.添 加 OnCreate(LPCREATESTRUCT lpCreateStruct)和 OnTimer(UINTnIDEvent)函数如下:intCMy1_5View:OnCreate(LPCREATESTRUCT lpCreateStruct)if(CView:OnCreate(lpCreateStruct)=-1)return-1;/TODO:Add your specialized creation codehereSetTimer(1,1200,NULL);return 0;void CMy1_5View:OnTimer(UINT nIDEvent)/TODO:Add your message handler codehere and/or call default/TODO:Add your message handler codehere and/or call default/即大于 400 后变小ixunhuan=(ixunhuan+30)%400;Invalidate();CView:OnTimer(nIDEvent);f.f.在 OnDraw(CDC*pDC)函数中添加画多图形多颜色的语句,并加上狮子位图如下:void CMy1_5View:OnDraw(CDC*pDC)CMy1_5Doc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw codefor native datahereCDC Dc;if(Dc.CreateCompatibleDC(pDC)=FALSE)AfxMessageBox(Cant create DC);/画复杂图形,有多种颜色for(int j=0;j25;j+)for(int i=0;iFillRect(myrect1,&mybrush1);/圆pDC-Ellipse(20*i,20*j,20*i+20,20*j+20);/小正方形CRect myrect2(20*i+4,20*j+4,20*i+16,20*j+16);pDC-FillRect(myrect2,&mybrush1);/小圆pDC-Ellipse(20*i+4,20*j+4,20*i+16,20*j+16);/更小正方形CRect myrect3(20*i+7,20*j+7,20*i+13,20*j+13);pDC-FillRect(myrect3,&mybrush1);/更小圆pDC-Ellipse(20*i+7,20*j+7,20*i+13,20*j+13);/最小正方形CRect myrect4(20*i+9,20*j+9,20*i+11,20*j+11);pDC-FillRect(myrect4,&mybrush1);/在不同位置显示位图Dc.SelectObject(m_Bitmap);pDC-StretchBlt(70+ixunhuan,70,160,160,&Dc,0,0,71,71,SRCCOPY);g.g.执行,你就可以看到它重画时有多慢了。h.h.现在,再用部分重画做:把 OnTimer(UINT nIDEvent)函数改为如下:void CMy1_5View:OnTimer(UINT nIDEvent)/TODO:Add your message handler codehere and/or call default/TODO:Add your message handler codehere and/or call defaultixunhuan=(ixunhuan+30)%400;/如果图象回到开头,全部重画if(ixunhuanFillRect(myrect1,&mybrush1);g.g.运行,怎么样?图象还会变大呢!四显示对称图像上面既然谈到了位图的透明显示,那么,我们经常用到的另外一种显示方式:对称,也就该接着说明一下。毕竟,无论是人,或是景,我们都讲究对称,特别是中国人;无论你向左,还是向右,它的图像一般也是讲究对称的。既然对称在我们的社会里,在我们的文化中是多么地受到人们的追求,人们是非常强烈地以对称为美,我们就必须学会怎样用程序来实现它。这也就是我把它放在前面讲的原因。下面,我们就来说明对称位图的实现。我们的程序语言里面有许多关于位图的操作,包括位图的复制,放大缩小,位图的反转甚至布尔运算。但是,我们找不到对称的操作。开始,我也不知道该怎么办,难道一边看着位图,一边对称地画另一张位图吗?事实上,我差一点就这么做了。但是,我突然发现,我可以一行一行地利用复制的方法,在原来的位图上面复制一行或一列,然后粘贴到另一张位图上面对称的地方去。我是这么做了,并且由于我的这个实践,我发现,我根本就可以利用程序的 for 循环来帮助我完成这一件事。甚至,让它能够按照我们的意愿放大缩小,按照我们的意愿在我们希望的位置显示。实例如下:添加单文档工程 1_7,添加一个位图,改变如下函数:由于下面说明清楚,不再细说。void CMy1_7View:OnDraw(CDC*pDC)CMy1_7Doc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw codefor native datahereintx,y,width,height,i;/设置反面显示位图的起点和大小/可以在别的地方设置,赋值x=350;y=30;width=240;height=200;BITMAPm_bm;CDC Dc;if(Dc.CreateCompatibleDC(pDC)=FALSE)AfxMessageBox(Cant create DC);/选入位图Dc.SelectObject(bitmap);GetObject(bitmap,sizeof(BITMAP),(LPSTR)&m_bm);/显示位图宽高POINTptSize;/取得位图的宽度ptSize.x=m_bm.bmWidth;/取得位图的该度ptSize.y=m_bm.bmHeight;/显示正面位图pDC-StretchBlt(10,30,240,200,&Dc,0,0,ptSize.x,ptSize.y,SRCCOPY);/显示反面位图/以下参数的说明:/x+(i*width/ptSize.x)表示每个循环的起点位置,/其中 width/ptSize.x 表示每次移动的大小/y 为显示的 top/height 为显示位图的高度/ptSize.x-i 为每次 SRCCOPY 的起点 x/在不同位置显示位图for(i=0;iStretchBlt(x+(i*width/ptSize.x),y,width/ptSize.x,height,&Dc,ptSize.x-i,0,1,ptSize.y,SRCCOPY);运行,效果就在第一页。五游戏的内核上面所说的,并不是游戏,它只能算是游戏中的一些元素。一个游戏,不可能只是表面图形这么简单,我们还没有谈到它的实质的东西内核。我们后面将讲到的挖雷游戏、俄罗斯方块等,都是一些简单的游戏,可是,第一次接触时,你能说出它的原理吗?它是怎么实现的吗?游戏的核心并不是我们所看到的图形,反而是我们所看不到的,游戏的算法。那么,游戏的图形和核心之间是怎么联系的呢?例如我们怎么利用键盘的方向键来控制俄罗斯方块的移动和转变呢?我们又如何利用鼠标来对挖雷游戏进行操作呢?游戏的图形和核心的联系是必然的。但我之所以要说,是因为我想告诉你:正是这个联系障碍了人们编程的思路!其实,它们的联系就像两个城市之间的那条公路一样连接着,它们事实上是两个整体,是分离的。为什么?什么是游戏?游戏本来就是一种变化过程,它是无形的,是不依奈图形而存在的。我们可以举一个例子,先编好一个游戏(如俄罗斯方块),运行它,你可以高兴地玩了。然后你再修改一下,把它的左(或右)半边用一张鲜花美女图象盖上去,然后再一次运行游戏,虽然你只能到一半游戏界面,但你依然可以玩游戏。或许说游戏依然正常运行。那么,我们把整个界面都盖上,不难想象,游戏还是会正常运行的。由此可见,游戏本来就是存在的,它是无形的,是不依奈于图形界面的。即使你编一个没有界面的俄罗斯方块游戏,它也能运行,它是一个我们只知道开始和结束的游戏,是不为人们所喜欢的。游戏可以说是不依奈于图形而存在的,如果你不想让别人玩的话。而图形的存在,就是为了别人可以玩。它是作用就是让你看清楚游戏的进程,让你更好地把握你所玩的游戏罢了。用上面的城市来说,核心城市并不依奈于图形城市,不与图形城市交流,核心城市依然能发展下去,如没有界面的俄罗斯方块游戏;而图形城市也不依奈于核心城市,如上面的鲜花美女图象。但是,我们编程,并不希望它们各自独立,我们要利用它们的联系来展开我们的游戏。于是,我们就朝着文明发展的方向为它们建了高速公路。我们究竟是怎样联系它们的呢?我们怎样把它们同步实现呢?事实上,我们所看到的同步,并不是真的!只是一种错觉!核心城市每时每刻都在发展,而图形城市并不是每时每刻都知道核心城市的发展情况的,它只是每隔一段时间才去了解一下情况,或许核心城市有情况传来时,图形城市才知道的,这时图形界面才改变给人看。游戏编程,就是你把实质的东西全部用算法在核心城市表现出来,然后再依照你的要求把那些要让人看的东西让图形城市知道,让图形城市帮你公布到界面上。学习各种高级外挂制作技术,马上去百度搜索(魔鬼作坊),点击第一个站进入,快速成为做挂达人。