超級瑪麗

MARIO CODE INSIDE html

超級瑪麗製做揭祕 c++

製做 programking  博客 http://blog.csdn.net/programking程序員

 

 

  算法

 

1、       超級瑪麗製做揭祕1工程開始... 2 編程

2、       超級瑪麗製做揭祕2圖片基類MYBITMAP. 4 api

3、       超級瑪麗製做揭祕3遊戲背景 MYBKSKY. 7 數組

4、       超級瑪麗製做揭祕4圖片顯示 MYANIOBJ. 9 框架

5、       超級瑪麗製做揭祕5魔法攻擊 MYANIMAGIC. 13 ide

6、       超級瑪麗製做揭祕6時鐘控制 MYCLOCK. 14 函數

7、       超級瑪麗製做揭祕7字體管理 MYFONT. 19

8、       超級瑪麗製做揭祕8跟蹤打印 FILEREPORT. 22

9、       超級瑪麗製做揭祕9精靈結構struct ROLE. 24

10、       超級瑪麗製做揭祕10子彈的顯示和幀的刷新... 26

11、       超級瑪麗製做揭祕11子彈運動和打怪... 27

12、       超級瑪麗製做揭祕12旋風攻擊,小怪運動,火圈... 29

十3、       超級瑪麗製做揭祕13小怪和火圈,模板... 34

十4、       超級瑪麗製做揭祕14爆炸效果,金幣... 37

十5、       超級瑪麗製做揭祕15金幣提示,攻擊提示... 41

十6、       超級瑪麗製做揭祕16攻擊方式切換... 43

十7、       超級瑪麗製做揭祕17地圖物品... 44

十8、       超級瑪麗製做揭祕18背景物品... 47

十9、       超級瑪麗製做揭祕19視圖... 48

二10、       超級瑪麗製做揭祕20地圖切換... 50

二11、       超級瑪麗製做揭祕21遊戲數據管理... 53

二12、       超級瑪麗製做揭祕22玩家角色類MYROLE. 58

二十3、       超級瑪麗製做揭祕23玩家動做控制... 63

二十4、       超級瑪麗製做揭祕24角色動畫... 69

二十5、       超級瑪麗製做揭祕25GAMEMAP 全局變量... 72

二十6、       超級瑪麗製做揭祕26菜單控制 窗口縮放... 76

二十7、       超級瑪麗製做揭祕27程序框架WinProc. 80

二十8、       InitInstance函數說明... 85

二十9、       後記... 87

 


 

類結構

 

圖像層:

圖像基類MYBITMAP

       遊戲背景MYBKSKYàMYBITMAP

       遊戲圖片MYANIOBJàMYBITMAP

       魔法攻擊MYANIMAGICàMYBITMAP

 

邏輯層:

遊戲邏輯GAMEMAP

時鐘處理MYCLOCK

字體處理MYFONT

跟蹤打印FILEREPORT

玩家控制MYROLEàMYBITMAP

 

結構和表:

精靈結構ROLE

物品結構MapObject

地圖信息表MAPINFO

 

 

1、            超級瑪麗製做揭祕1工程開始

兩個版本的超級瑪麗下載量已超過5000次,謝謝你們支持。誰沒法下載,請告訴我郵箱,我直接發。如今反映兩個問題,一沒有幫助文檔,二代碼註釋太少。今天起,我揭祕製做過程,保證講解到每一行代碼,每個變量。

 

代碼我已經發布,可見作這樣一個遊戲並不難。今天講準備工做,也就是所須要的開發工具。代碼編寫調試:VC 6.0,美術工具:Windows自帶的畫圖(開始-程序-附件-畫圖)。這是最簡陋的開發工具,但已足夠。最好再有Photoshop,記事本或UltraEdit等等你喜歡的文本編輯工具。

 

遊戲代碼分兩部分,圖像部分,邏輯部分。先說圖像部分:圖像分兩種,矩形圖片和不規則圖片。工程中的PIC文件夾下,能夠看到全部圖像資源

 

矩形圖片有:

地面、磚塊、水管、血條、血條背景。

 

不規則圖片有:

蘑菇(玩家,敵人1,敵人2),子彈、旋風、爆炸效果、金幣、撞擊金幣後的得分、攻擊武器(那個從魂鬥羅裏摳來的東東)、火圈1、火圈2、箭頭(用於開始菜單選擇)、樹木、河流、WIN標誌、背景圖片(遊戲背景和菜單背景)。

 

全部圖片都分紅幾個位圖BMP文件存儲。一個文件中,每種圖片,都縱向排列。每種圖片可能有多幀。好比,金幣須要4幀圖像,才能構成一個旋轉的動畫效果,那麼,各幀圖像橫向排列。

 

圖像層的結構就這樣簡單,邏輯層只須要肯定「哪一個圖像,哪一幀」這兩個參數,就能在屏幕上繪製出全部圖片。

 

圖像層的基類是:

 

class MYBITMAP

 

 void Init(HINSTANCE hInstance,int iResource,int row,int col);

 void SetDevice(HDC hdest,HDC hsrc,int wwin,int hwin);

 void Draw(DWORD dwRop);

 

 HBITMAP hBm;

//按照行列平均分紅幾個

 int inum;

 int jnum;

 

 int width;

 int height;

 

 HDC hdcdest;

 HDC hdcsrc;

 

這只是一個基類,上面是幾個重要的數據成員和函數。它所描述的圖片,是一個mn列構成的m*n個圖片,每一個圖片大小一致,都是矩形。顯然,這並不能知足上面的設計要求,怎麼解決呢?派生,提供更多的功能。可是,這個基類封裝了足夠的物理層信息:設備上下文HDC,和位圖句柄HBITMAP。矩形圖片的顯示、不規則圖片的顯示、圖片組織排列信息,這些功能交給它的派生類MYANIOBJ

 

還有,咱們最關心的問題是圖片座標,好比,不一樣位置的磚塊、精靈、金幣,這些由邏輯層處理,之後再講,先到這裏吧。

 

2、            超級瑪麗製做揭祕2圖片基類MYBITMAP

先說一下代碼風格,你們都說看不懂,這就對了。整套代碼約有3000行,並不都是針對這個遊戲寫的。我想把代碼寫成一個容易擴展、容易維護、功能全面的「框架」,須要什麼功能,就從這個框架中取出相應功能,若是是一個新的功能,好比新的圖像顯示、新的運動控制,我也能方便地實現。因此,這個遊戲的代碼,是在前幾個遊戲的基礎上擴充起來的。部分函數,部分變量在這款遊戲中,根本不用,但要保留,要爲下一款遊戲做準備。只要理解了各個類,就理解了整個框架。

 

今天先講最基礎的圖像類 MYBITMAP

成員函數功能列表:

//功能 根據一個位圖文件,初始化圖像

//入參 應用程序實例句柄 資源ID 橫向位圖個數 縱向位圖個數

void Init(HINSTANCE hInstance,int iResource,int row,int col);

//功能 設置環境信息

//入參 目的DC(要繪製圖像的DC),臨時DC,要繪製區域的寬

void SetDevice(HDC hdest,HDC hsrc,int wwin,int hwin);

//功能 設置圖片位置

//入參 設置方法 橫縱座標

void SetPos(int istyle,int x,int y);

//功能 圖片顯示

//入參 圖片顯示方式

void Draw(DWORD dwRop);

//功能 圖片縮放顯示

//入參 橫縱方向縮放比例

void Stretch(int x,int y);

//功能 圖片縮放顯示

//入參 橫縱方向縮放比例 縮放圖像ID(縱向第幾個)

void Stretch(int x,int y,int id);

//功能 在指定位置顯示圖片

//入參 橫縱座標

void Show(int x,int y);

//功能 橫向居中顯示圖片

//入參 縱座標

void ShowCenter(int y);

//功能 將某個圖片平鋪在一個區域內

//入參 左上右下邊界的座標 圖片ID(橫向第幾個)

void ShowLoop(int left,int top,int right,int bottom,int iframe);

//功能 不規則圖片顯示

//入參 橫縱座標 圖片ID(橫向第幾個)

void ShowNoBack(int x,int y,int iFrame);

//功能 不規則圖片橫向平鋪

//入參 橫縱座標 圖片ID(橫向第幾個) 平鋪個數

void ShowNoBackLoop(int x,int y,int iFrame,int iNum);

 

//動畫播放

//功能 自動播放該圖片的全部幀,函數沒有實現,但之後確定要用:)

//入參

void ShowAni();

//功能 設置動畫座標

//入參 橫縱座標

void SetAni(int x,int y);

 

成員數據

//跟蹤打印類

//     FILEREPORT f;

 

//圖像句柄

       HBITMAP hBm;

 

//按照行列平均分紅幾個

       int inum;

       int jnum;

 

//按行列分割後,每一個圖片的寬高(顯然各個圖片大小一致,派生後,這裏的寬高已沒有使用意義)

       int width;

       int height;

 

//屏幕寬高

       int screenwidth;

       int screenheight;

 

//要繪製圖片的dc

       HDC hdcdest;

 

//用來選擇圖片的臨時dc

       HDC hdcsrc; 

 

//當前位置

       int xpos;

       int ypos;

 

//是否處於動畫播放中(功能沒有實現)

       int iStartAni;

 

這個基類的部分函數和變量,在這個遊戲中沒有使用,是從前幾個遊戲中保留下來的,因此看起來有些零亂.這個遊戲的主要圖像功能,由它的派生類完成.因爲基類封裝了物理層信息(dc和句柄),派生類的編寫就容易一些,可讓我專一於邏輯含義.

基類的函數實現上,很簡單,主要是如下幾點:

1.圖片初始化:

//根據程序實例句柄,位圖文件的資源ID,導入該位圖,獲得位圖句柄

       hBm=LoadBitmap(hInstance,MAKEINTRESOURCE(iResource));

//獲取該位圖文件的相關信息

       GetObject(hBm,sizeof(BITMAP),&bm);

//根據橫縱方向的圖片個數,計算出每一個圖片的寬高(對於超級瑪麗,寬高信息由派生類處理)

       width=bm.bmWidth/inum;

       height=bm.bmHeight/jnum;

 

2.圖片顯示

各個圖片的顯示函數,大同小異,都要先選入一個臨時DC,bitblt到要繪製的dc.矩形圖片,能夠直接用SRCCOPY的方式繪製.不規則圖片,須要先用黑白圖與目的區域相""(SRCAND),再用""的方法顯示圖像(SRCPAINT),這是一種簡單的"去背"方法.

例以下面這個函數:

void MYBITMAP::ShowNoBack(int x,int y,int iFrame)

{

       xpos=x;

       ypos=y;

       SelectObject(hdcsrc,hBm);

       BitBlt(hdcdest,xpos,ypos,width,height/2,hdcsrc,iFrame*width,height/2,SRCAND); 

       BitBlt(hdcdest,xpos,ypos,width,height/2,hdcsrc,iFrame*width,0,SRCPAINT);        

}

 

 

3.圖片縮放

StretchBlt的方法實現

void MYBITMAP::Stretch(int x,int y,int id)

{

       SelectObject(hdcsrc,hBm);

       StretchBlt(hdcdest,xpos,ypos,width*x,height*y,

              hdcsrc,0,id*height,

              width,height,

              SRCCOPY);  

}

 

在超級瑪麗中的使用

在這個遊戲中,哪些圖像的處理是通關這個基類呢?只有一個:

MYBITMAP bmPre;

因爲這個基類只能處理幾個大小均等的圖片,只有這些圖片大小一致,且都是矩形:遊戲開始前的菜單背景,操做信息的背景,每一關開始前的背景(此時顯示LIFE x WORLD x),通關或遊戲結束時顯示的圖片.5,將這5個圖片,放在一個位圖文件中,因而,這些圖片的操做就作完了,代碼以下:

 

//初始設置,InitInstance函數中

bmPre.Init(hInstance,IDB_BITMAP_PRE1,1,5);

bmPre.SetDevice(hscreen,hmem,GAMEW*32,GAMEH*32);

bmPre.SetPos(BM_USER,0,0);

 

//圖片繪製,WndProc,前兩個參數指橫縱方向擴大2倍顯示.

bmPre.Stretch(2,2,0);

bmPre.Stretch(2,2,4);

bmPre.Stretch(2,2,2);   

bmPre.Stretch(2,2,1);   

bmPre.Stretch(2,2,3);

 

圖像控制部分,基類就講到這裏,欲知後事,下回分解.

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

3、            超級瑪麗製做揭祕3遊戲背景 MYBKSKY

類說明:這是一個專門處理遊戲背景的類。在橫版遊戲或射擊遊戲中,都有一個背景畫面,如山、天空、雲、星空等等。這些圖片通常只有12倍屏幕寬度,而後像一個卷軸同樣循環移動,連成一片,感受上像一張很長的圖片。這個類就是專門處理這個背景的。在超級瑪麗加強版中,主要關卡是3關,各有一張背景圖片;從水管進去,有兩關,都用一張全黑圖片。共四張圖。這四張圖大小一致,縱向排列在一個位圖文件中。MYBKSKY這個類,派生於MYBITMAP。因爲背景圖片只須要完成循環移動的效果,只須要實現一個功能,而無需關心其餘任何問題(例如句柄、dc)。編碼起來很簡單,再次反映出面向對象的好處。

 

技術原理:

怎樣讓一張圖片像卷軸同樣不停移動呢?很簡單,假設有一條垂直分割線,把圖片分紅左右兩部分。先顯示右邊部分,再把左邊部分接到圖片末尾。不停移動向右移動分割線,圖片就會循環地顯示。

 

成員函數功能列表:

class MYBKSKY:public MYBITMAP

{

public:

       MYBKSKY();

       ~MYBKSKY();

 

       //show

       //功能 顯示一個背景.

       //入參

       void DrawRoll(); //循環補空

       //功能 顯示一個背景,並縮放圖片

       //入參 橫縱方向縮放比例

       void DrawRollStretch(int x,int y);

       //功能 指定顯示某一個背景,並縮放圖片,遊戲中用的就是這個函數

       //入參 橫縱方向縮放比例 背景圖片ID(縱向第幾個)

       void DrawRollStretch(int x,int y,int id);

       //功能 設置圖片位置

       //入參 新的橫縱座標

       void MoveTo(int x,int y);

       //功能 循環移動分割線

       //入參 分割線移動的距離

       void MoveRoll(int x);

 

       //data

       //分割線橫座標

       int xseparate;

};

 

函數具體實現都很簡單,例如:

void MYBKSKY::DrawRollStretch(int x,int y, int id)

{

       //選入句柄

       SelectObject(hdcsrc,hBm);

      

       //將分割線右邊部分顯示在當前位置

       StretchBlt(hdcdest,

              xpos,ypos,      //當前位置

              (width-xseparate)*x,height*y,              //縮放比例

              hdcsrc,

              xseparate,id*height,       //右邊部分的座標

              width-xseparate,height,  //右邊部分的寬高

              SRCCOPY);

      

       //將分割線左邊部分接在圖片末尾

       StretchBlt(hdcdest,xpos+(width-xseparate)*x,ypos,

              xseparate*x,height*y,

              hdcsrc,0,id*height,

              xseparate,height,

              SRCCOPY);  

}

 

使用舉例:

定義 MYBKSKY bmSky;

初始化

mario01/mario01.cpp(234):   bmSky.Init(hInstance,IDB_BITMAP_MAP_SKY,1,4);

mario01/mario01.cpp(235):   bmSky.SetDevice(hscreen,hmem,GAMEW*32*MAX_PAGE,GAMEH*32);

mario01/mario01.cpp(236):   bmSky.SetPos(BM_USER,0,0);

遊戲過程當中顯示

mario01/mario01.cpp(366):                        bmSky.DrawRollStretch(2,2,gamemap.mapinfo.iBackBmp);

每隔必定時間,移動分割線

mario01/mario01.cpp(428):                               bmSky.MoveRoll(SKY_SPEED);//雲彩移動

 

如下兩處與玩家角色有關:

當玩家切換到一張新地圖時,刷新背景圖片的座標

mario01/gamemap.cpp(314):        bmSky.SetPos(BM_USER,viewx,0);

當玩家向右移動時,刷新背景圖片的座標

mario01/gamemap.cpp(473): bmSky.SetPos(BM_USER,viewx,0);

 

至此,遊戲背景圖片的功能就作完了。

 

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

4、            超級瑪麗製做揭祕4圖片顯示 MYANIOBJ

類說明:這個類負責遊戲中的圖片顯示。菜單背景、通關和遊戲結束的提示圖片,由MYBITMAP處理(大小一致的靜態圖片)。遊戲背景由MYBKSKY處理。其他圖片,也就是遊戲過程當中的全部圖片,都是MYANIOBJ處理。

 

技術原理:遊戲中的圖片大小不一致,具體在超級瑪麗中,能夠分紅兩類:矩形圖片和不規則圖片。在位圖文件中,都是縱向排列各個圖片,橫向排列各幀。用兩個數組存儲各個圖片的寬和高。爲了方便顯示某一個圖片,用一個數組存儲各個圖片的縱座標(即位圖文件中左上角的位置)。使用時,由邏輯層指定「哪一個圖片」的「哪一幀」,顯示在「什麼位置」。這樣圖片的顯示功能就實現了。

 

成員函數功能列表:

class MYANIOBJ:public MYBITMAP

{

public:

       MYANIOBJ();

       ~MYANIOBJ();

 

       //init list

       //功能 初始化寬度數組 高度數組 縱座標數組 是否有黑白圖

       //入參 寬度數組地址 高度數組地址 圖片數量 是否有黑白圖(0 沒有, 1 )

       //(圖片縱座標信息由函數計算得出)

       void InitAniList(int *pw,int *ph,int inum,int ismask);

 

       //功能 初始化一些特殊的位圖,例如各圖片大小一致,或者有其餘規律

       //入參 初始化方式 參數1 參數2

       //(留做之後擴展, 目的是爲了省去寬高數組的麻煩)

       void InitAniList(int style,int a,int b);

 

       //show

       //功能 顯示圖片(不規則圖片)

       //入參 橫縱座標(要顯示的位置) 圖片id(縱向第幾個), 圖片幀(橫向第幾個)

       void DrawItem(int x,int y,int id,int iframe);

       //功能 顯示圖片(矩形圖片)

       //入參 橫縱座標(要顯示的位置) 圖片id(縱向第幾個), 圖片幀(橫向第幾個)

       void DrawItemNoMask(int x,int y,int id,int iframe);

       //功能 指定寬度, 顯示圖片的一部分(矩形圖片)

       //入參 橫縱座標(要顯示的位置) 圖片id(縱向第幾個), 顯示寬度 圖片幀(橫向第幾個)

       void DrawItemNoMaskWidth(int x,int y,int id,int w,int iframe);

       //功能 播放一個動畫 即循環顯示各幀

       //入參 橫縱座標(要顯示的位置) 圖片id(縱向第幾個)

       void PlayItem(int x,int y,int id);

 

       //寬度數組 最多支持20個圖片

       int wlist[20];

       //高度數組 最多支持20個圖片

       int hlist[20];

       //縱座標數組 最多支持20個圖片

       int ylist[20];

 

       //動畫播放時的當前幀

       int iframeplay;

};

 

函數實現上也很簡單。構造函數中,全部成員數據清零;初始化時,將各圖片的高度累加,即獲得各圖片的縱座標。顯示圖片的方法如前所述。

使用舉例:

遊戲圖片分紅三類:地圖物品、地圖背景物體、精靈(即全部不規則圖片)

MYANIOBJ bmMap;

MYANIOBJ bmMapBkObj;

MYANIOBJ bmAniObj;

初始化寬高信息

程序中定義一個二維數組,例如:

int mapani[2][10]={

{32,32,64,32,32,52,64,32,64,32},

{32,32,64,32,32,25,64,32,64,32},

};

第一維mapani[0]存儲10個圖片的寬度,第二維mapani[1]存儲10個圖片的高度,初始化時,將mapani[0]mapani[1]傳給初始化函數便可。

 

1.     地圖物品的顯示:

定義

mario01/mario01.cpp(82):MYANIOBJ bmMap;

初始化

這一步加載位圖

mario01/mario01.cpp(238):   bmMap.Init(hInstance,IDB_BITMAP_MAP,1,1);

這一步初始化DC

mario01/mario01.cpp(239):   bmMap.SetDevice(hscreen,hmem,GAMEW*32*MAX_PAGE,GAMEH*32);

這一步設置寬高信息, 圖片爲矩形

mario01/mario01.cpp(240):   bmMap.InitAniList(mapsolid[0],mapsolid[1], sizeof(mapsolid[0])/sizeof(int),0);

對象做爲參數傳給邏輯層, 顯示地圖物品

mario01/mario01.cpp(368):                        gamemap.Show(bmMap);

2.     血條的顯示:

打怪時,屏幕上方要顯示血條。因爲一樣是矩形圖片,也一併放在了地圖物品的位圖中。

變量聲明

mario01/gamemap.cpp(11):extern MYANIOBJ bmMap;

顯示血條背景,指定圖片寬度:最大生命值*單位生命值對應血條寬度

mario01/gamemap.cpp(522):        bmMap.DrawItemNoMaskWidth(xstart-1, ATTACK_TO_Y-1,ID_MAP_HEALTH_BK,

顯示怪物血條,指定圖片寬度:當前生命值*單位生命值對應血條寬度

mario01/gamemap.cpp(525):        bmMap.DrawItemNoMaskWidth(xstart, ATTACK_TO_Y,ID_MAP_HEALTH,

 

3.     地圖背景物體的顯示

背景物體包括草、河流、樹木、目的地標誌。這些物體都不參與任何邏輯處理,只須要顯示到屏幕上。圖片放在一個位圖文件中,都是不規則形狀。

定義

mario01.cpp(83):MYANIOBJ bmMapBkObj;

初始化並加載位圖

mario01/mario01.cpp(242):   bmMapBkObj.Init(hInstance,IDB_BITMAP_MAP_BK,1,1);

設置dc

mario01/mario01.cpp(243):   bmMapBkObj.SetDevice(hscreen,hmem,GAMEW*32*MAX_PAGE,GAMEH*32);

設置各圖片寬高信息

mario01/mario01.cpp(244):   bmMapBkObj.InitAniList(mapanibk[0],mapanibk[1],sizeof(mapanibk[0])/sizeof(int),1);

對象做爲參數傳給邏輯層, 顯示地圖背景物體

mario01/mario01.cpp(367):                        gamemap.ShowBkObj(bmMapBkObj);

4.     精靈的顯示

精靈包括:蘑菇(玩家,敵人1,敵人2),子彈、旋風、爆炸效果、金幣、撞擊金幣後的得分、攻擊武器(那個從魂鬥羅裏摳來的東東)、火圈1、火圈2、箭頭(用於開始菜單選擇)。

定義

mario01.cpp(84):MYANIOBJ bmAniObj;

初始化加載位圖

mario01/mario01.cpp(246):   bmAniObj.Init(hInstance,IDB_BITMAP_ANI,1,1);

設置dc

mario01/mario01.cpp(247):   bmAniObj.SetDevice(hscreen,hmem,GAMEW*32*MAX_PAGE,GAMEH*32);

設置寬高信息

mario01/mario01.cpp(248):   bmAniObj.InitAniList(mapani[0],mapani[1],sizeof(mapani[0])/sizeof(int),1);

菜單顯示(即菜單文字左邊的箭頭)

mario01/mario01.cpp(341):                        gamemap.ShowMenu(bmAniObj);

對象做爲參數傳給邏輯層, 顯示各個精靈

mario01/mario01.cpp(369):                        gamemap.ShowAniObj(bmAniObj);

 

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

 

5、            超級瑪麗製做揭祕5魔法攻擊 MYANIMAGIC

類說明:玩家有兩種攻擊方式:普通攻擊(子彈),魔法攻擊(旋風)。這個類是專門處理旋風的。我最初的想法是用一些特殊的bitblt方法制造特效,例如或、與、異或。試了幾回,都失敗了。最後只能用「先與後或」的老方法。這個類可當作MYANIOBJ的一個簡化版,只支持不規則圖片的顯示。

 

成員函數功能列表:

class MYANIMAGIC:public MYBITMAP

{

public:

       MYANIMAGIC();

       ~MYANIMAGIC();

 

       //init list

       //功能 初始化寬度數組 高度數組 縱座標數組(必須有黑白圖)

       //入參 寬度數組地址 高度數組地址 圖片數量

       //(圖片縱座標信息由函數計算得出)

       void InitAniList(int *pw,int *ph,int inum);

       //功能 設置dc

       //入參 顯示dc 臨時dc(用於圖片句柄選擇) 臨時dc(用於特效實現)

       void SetDevice(HDC hdest,HDC hsrc,HDC htemp);

 

       //show

       //功能 顯示某個圖片的某幀

       //入參 橫縱座標(顯示位置) 圖片id(縱向第幾個) (橫向第幾個)

       void DrawItem(int x,int y,int id,int iframe);

 

       //寬度數組

       int wlist[20];

       //高度數組

       int hlist[20];

       //縱座標數組

       int ylist[20];

 

       //用於特效的臨時dc, 功能沒有實現L

       HDC hdctemp;

};

函數具體實現很簡單, 可參照MYANIOBJ.

 

使用舉例

定義

mario01/mario01.cpp(87):MYANIMAGIC bmMagic;

初始化加載位圖

mario01/mario01.cpp(250):   bmMagic.Init(hInstance,IDB_BITMAP_MAGIC,1,1);

設置dc

mario01/mario01.cpp(251):   bmMagic.SetDevice(hscreen,hmem, hmem2);

初始化寬高信息

mario01/mario01.cpp(252):   bmMagic.InitAniList(mapanimagic[0],mapanimagic[1],sizeof(mapanimagic[0])/sizeof(int));

 

變量聲明

gamemap.cpp(22):extern MYANIMAGIC bmMagic;

在邏輯層中, 顯示旋風圖片

mario01/gamemap.cpp(568):                      bmMagic.DrawItem(xstart,ystart, 0, FireArray[i].iframe);

 

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

6、            超級瑪麗製做揭祕6時鐘控制 MYCLOCK

類說明:時間就是生命。這對於遊戲來講,最爲準確。遊戲程序只作兩件事:顯示圖片、處理邏輯。更準確的說法是:每隔一段時間顯示圖片並處理邏輯。程序中,要設置一個定時器。這個定時器會每隔一段時間發出一個WM_TIMER消息。在該消息的處理中,先邏輯處理。邏輯處理完畢,經過InvalidateRect函數發出WM_PAINT消息,顯示各類圖片。遊戲就不停地運行下去,直至程序結束。

 

時間表示:用一個整數iNum表示當前時間,遊戲中的時間是1,2,3, … , n, 1,2,3, … ,n 不停循環.假設1秒內須要25WM_TIMER消息(40毫秒1),n=25. 也能夠用一個變量,統計過了幾秒。

 

控制事件頻率的方法:

1. 一秒內發生屢次

以遊戲背景圖片爲例, 每秒移動5, 能夠在iNum5,10,15,20,255個時間點上移動.iNum能夠被5整除時,修改背景圖片的座標.

2. 一秒內發生一次

例如火圈, 每秒產生一個新的蘑菇兵. 能夠隨便指定一個時間點,20. iNum等於20時,生成一個蘑菇兵。

3. 多秒內發生一次

須要一個輔助變量iNumShow,統計時間過了幾秒。每隔一秒iNumShow1,當iNumShow等於0時處理邏輯。

 

成員函數功能列表:(全部函數都是內聯函數)

class MYCLOCK

{

public:

       //構造函數 初始化全部變量

       MYCLOCK()

       {

              iNum=0;//時間點

              iIsActive=0;//是否已經開始計時

              iNumShow=0;//計時秒數

              iElapse=100;//默認每100ms發一個WM_TIMER消息

              ishow=0; //是否顯示時間

       }

       //析構函數 銷燬計時器

       ~MYCLOCK()

       {

              Destroy();

       }

 

       //功能 開始計時, 產生WM_TIEMR消息的時間間隔爲elapse.

       //     設置計時秒數(timetotal).

       //入參 窗口句柄 時間間隔 計時秒數

       void Begin(HWND hw,int elapse,int timetotal)

       {

              if(iIsActive)

                     return;//已經啓動了,直接返回

 

              hWnd=hw;

              iElapse=elapse;

 

              SetTimer(hWnd,1,iElapse,NULL);

              iNum=1000/iElapse;//一秒鐘的時間消息數量

              iNumShow=timetotal;

              iIsActive=1;

       }

       //功能 銷燬計時器.

       //入參

       void Destroy()

       {

              if(iIsActive)

              {

                     iIsActive=0;

                     KillTimer(hWnd,1);

              }

       }

 

       //功能 重置計時秒數

       //入參 秒數

       void ReStart(int timetotal)

       {

              iNumShow=timetotal;   

              iNum=1000/iElapse;

              ishow=1;

       }

 

       //////////////////////////// 顯示部分

       //功能 設置顯示dc (在超級瑪麗加強版中不顯示時間)

       //入參 顯示dc

       void SetDevice(HDC h)

       {

              hDC=h;

       }

       //功能 顯示時間, TIME 秒數

       //入參 顯示座標

       void Show(int x,int y)

       {

              char temp[20]={0};

 

              if(!ishow)

                     return;

 

              //設置顯示文本

              sprintf(temp,"TIME: %d  ",iNumShow);

              TextOut(hDC,x, y, temp,strlen(temp));

       }

 

       //功能 時間點減一

       //     若是到了計時秒數, 函數返回1, 不然返回0.

       //入參

       int DecCount()

       {

              iNum--;

              if(iNum==0)

              {

                     //過了一秒

                     iNum=1000/iElapse;

                     iNumShow--;

                     if(iNumShow<=0)

                     {

                            //不銷燬計時器

                            return 1;

                     }

              }

              return 0;

       }

 

       //功能 時間點減一

       //     若是到了計時秒數, 函數返回1並銷燬計時器, 不然返回0.

       //入參

       int Dec()

       {

              iNum--;

              if(iNum<=0)

              {

                     //過了一秒

                     iNum=1000/iElapse;

                     iNumShow--;

                     if(iNumShow<=0)

                     {

                            iNumShow=0;

                            Destroy();

                            return 1;

                     }

              }

              return 0;

       }

      

       //功能 設置是否顯示

       //入參 1,顯示; 0, 不顯示

       void SetShow(int i)

       {

              ishow=i;

       }

 

public:

       //窗口句柄

       HWND hWnd;

       //顯示dc

       HDC hDC;

 

       //時間點

       int iNum;

       //計時秒數

       int iNumShow;

       //消息時間間隔

       int iElapse;

       //是否開始計時

       int iIsActive;

       //是否顯示

       int ishow;

};

具體函數實現很簡單, 如上所述。

使用舉例:

定義

mario01.cpp(75):MYCLOCK c1;

設置顯示dc

mario01/mario01.cpp(270):   c1.SetDevice(hscreen);

開始計時(計時秒數無效)

mario01/mario01.cpp(271):   c1.Begin(hWnd, GAME_TIME_CLIP ,-1);

選擇遊戲菜單,每隔必定時間,重繪屏幕,實現箭頭閃爍

mario01/mario01.cpp(407):                        c1.DecCount();

mario01/mario01.cpp(408):                        if(0 == c1.iNum%MENU_ARROW_TIME)

屏幕提示LIFE,WORLD,若是達到計時秒數,進入遊戲。

mario01/mario01.cpp(415):                        if(c1.DecCount())

進入遊戲,計時300秒(無心義,在超級瑪麗加強版中取消時間限制)

mario01/mario01.cpp(418):                               c1.ReStart(TIME_GAME_IN);                  

在遊戲過程當中,每隔必定時間,處理遊戲邏輯

mario01/mario01.cpp(425):                        c1.DecCount();

mario01/mario01.cpp(426):                        if(0 == c1.iNum%SKY_TIME)

mario01/mario01.cpp(430):                        gamemap.ChangeFrame(c1.iNum);//幀控制

mario01/mario01.cpp(434):                        gamemap.CheckAni(c1.iNum);//邏輯數據檢測

玩家過關後,等待必定時間。

mario01/mario01.cpp(440):                        if(c1.DecCount())

玩家進入水管,等待必定時間。

mario01/mario01.cpp(448):                        if(c1.DecCount())

mario01/mario01.cpp(452):                               c1.ReStart(TIME_GAME_IN);                  

玩家失敗後,等待必定時間。

mario01/mario01.cpp(459):                        if(c1.DecCount())

玩家通關後,等待必定時間。

mario01/mario01.cpp(466):                        if(c1.DecCount())

玩家生命值爲0,遊戲結束,等待必定時間。

mario01/mario01.cpp(474):                        if(c1.DecCount())

程序結束(窗口關閉),銷燬計時器

mario01/mario01.cpp(518):                 c1.Destroy();

變量聲明

gamemap.cpp(20):extern MYCLOCK c1;

遊戲菜單中,選擇「開始遊戲」,顯示LIFE,WORLD提示,計時兩秒

mario01/gamemap.cpp(333):                      c1.ReStart(TIME_GAME_IN_PRE); //停頓兩秒

進入水管,等待,計時兩秒

mario01/gamemap.cpp(407):                                    c1.ReStart(TIME_GAME_PUMP_WAIT);

玩家過關,等待,計時兩秒

mario01/gamemap.cpp(1083):              c1.ReStart(TIME_GAME_WIN_WAIT);

生命值爲0,遊戲結束,等待,計時三秒

mario01/gamemap.cpp(1116):              c1.ReStart(TIME_GAME_END); 

玩家失敗,顯示LIFE,WORLD提示,計時兩秒

mario01/gamemap.cpp(1121):              c1.ReStart(TIME_GAME_IN_PRE);   

玩家失敗,等待,計時兩秒

mario01/gamemap.cpp(1140):       c1.ReStart(TIME_GAME_FAIL_WAIT);

 

至此,全部的時間消息控制、時間計時都已處理完畢。

 

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

7、            超級瑪麗製做揭祕7字體管理 MYFONT

類說明:遊戲固然少不了文字。在超級瑪麗中,文字內容是比較少的,分兩類:遊戲菜單中的文字,遊戲過程當中的文字。

菜單中的文字包括:

       "製做: programking 20088",

       "操做:   Z:子彈   X:   方向鍵移動  W:默認窗口大小",

       "地圖文件錯誤,請修正錯誤後從新啓動程序。",

       "(上下鍵選擇菜單,回車鍵確認)",

       "開始遊戲",

       "操做說明",

       "博客: http://blog.csdn.net/programking",

       "(回車鍵返回主菜單)"

這幾個字符串存儲在一個指針數組中(全局變量),通關數組下標使用各個字符串。

遊戲中的文字只有兩個:’LIFE’,’WORLD’

其餘的文字其實都是位圖,例如「通關」、「gameover」以及碰到金幣後的「+10」。這些都是位圖圖片,在pic文件夾裏一看便知。

 

成員函數功能列表:

class MYFONT

{

public:

       //構造函數,初始化字體表」,5個字體句柄構成的數組,字體大小依次遞增.

       MYFONT();

       ~MYFONT();

 

       //功能 設置顯示文字的dc

       //入參 顯示文字的dc句柄

       void SetDevice(HDC h);

       //功能 設置當前顯示的字體

       //入參 字體表下標

       void SelectFont(int i);

       //功能 設置當前字體爲默認字體

       //入參

       void SelectOldFont();

       //功能 在指定座標顯示字符串

       //入參 橫縱座標 字符串指針

       void ShowText(int x,int y,char *p);

       //功能 設置文字背景顏色,文字顏色

       //入參 文字背景顏色 文字顏色

       void SetColor(COLORREF cbk, COLORREF ctext);

       //功能 設置文字背景顏色,文字顏色

       //入參 文字背景顏色 文字顏色

       void SelectColor(COLORREF cbk, COLORREF ctext);

 

       //顯示文字的dc

       HDC hdc;

       //字體表,包含5個字體句柄,字體大小依次是0,10,20,30,40

       HFONT hf[5];

       //默認字體

       HFONT oldhf;

       //color

       COLORREF c1;//字體背景色

       COLORREF c2;//字體顏色

};

技術原理:要在屏幕上顯示一個字符串,分如下幾步:將字體句柄選入dc,設置文字背景色,設置文字顏色,最後用TextOut完成顯示。這個類就是將整個過程封裝了一下。顯示dc,背景色,文字顏色,字體句柄都對應各個成員數據。函數具體實現很簡單,一看便知。

 

使用舉例:

定義

mario01/mario01.cpp(89):MYFONT myfont;

初始化設置顯示dc

mario01/mario01.cpp(258):   myfont.SetDevice(hscreen);

地圖文件錯誤:設置顏色,設置字體,顯示提示文字

mario01/mario01.cpp(327):                        myfont.SelectColor(TC_WHITE,TC_BLACK);

mario01/mario01.cpp(328):                        myfont.SelectFont(0);

mario01/mario01.cpp(329):                        myfont.ShowText(150,290,pPreText[3]);

遊戲開始菜單:設置字體,設置顏色,顯示三行菜單文字

mario01/mario01.cpp(336):                        myfont.SelectFont(0);

mario01/mario01.cpp(337):                        myfont.SelectColor(TC_BLACK, TC_YELLOW_0);

mario01/mario01.cpp(338):                        myfont.ShowText(150,260,pPreText[4]);

mario01/mario01.cpp(339):                        myfont.ShowText(150,290,pPreText[5]);

mario01/mario01.cpp(340):                        myfont.ShowText(150,320,pPreText[6]);

遊戲操做說明菜單:設置字體,設置顏色,顯示四行說明文字

mario01/mario01.cpp(348):                        myfont.SelectFont(0);

mario01/mario01.cpp(349):                        myfont.SelectColor(TC_BLACK, TC_YELLOW_0);

mario01/mario01.cpp(350):                        myfont.ShowText(150,230,pPreText[8]);

mario01/mario01.cpp(351):                        myfont.ShowText(50,260,pPreText[1]);

mario01/mario01.cpp(352):                        myfont.ShowText(50,290,pPreText[0]);

mario01/mario01.cpp(353):                        myfont.ShowText(50,320,pPreText[7]);

 

這個類的使用就這些。這個類只是負責菜單文字的顯示,那麼,遊戲中的LIFE,WORLD的提示,是在哪裏完成的呢?函數以下:

void GAMEMAP::ShowInfo(HDC h)

{

       char temp[50]={0};

 

       SetTextColor(h, TC_WHITE);

       SetBkColor(h, TC_BLACK);

 

       sprintf(temp, "LIFE  : %d",iLife);

       TextOut(h, 220,100,temp,strlen(temp));

 

       sprintf(temp, "WORLD : %d",iMatch+1);

       TextOut(h, 220,130,temp,strlen(temp));

}

這個函數很簡單。要說明的是,它並無設置字體,由於在顯示菜單的時候已經設置過了。

至此,全部文字的處理所有實現。

 

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

8、            超級瑪麗製做揭祕8跟蹤打印 FILEREPORT

前面介紹了圖片顯示、時鐘控制、字體管理幾項基本技術。這是全部遊戲都通用的基本技術。剩下的問題就是遊戲邏輯,例如益智類、運動類、射擊類、格鬥類等等。固然,不一樣的遊戲須要針對自身作一些優化,好比益智類遊戲的時鐘控制、畫面刷新都更簡單,而格鬥遊戲,畫面的質量要更酷、更炫。下面要介紹整個遊戲的核心層:邏輯控制。地圖怎樣繪製的?物品的座標怎麼存儲?人物怎樣移動?遊戲流程是什麼樣的?

在介紹這些內容前,先打斷一下思路,說程序是怎樣寫出來的,即「調試」。

程序就是一堆代碼,了無祕密。初學時,dos下一個猜數字的程序,只須要十幾行。一個紙牌遊戲,一千多行,而超級瑪麗加強版,近三千行。怎樣讓這麼一堆程序從無到有並且運行正確?開發不是靠設計的巧妙或者笨拙,而是靠反覆調試。在三千行的代碼中,增長一千行,仍然運行正確,這是編程的基本要求。這個最基本的要求,靠設計作不到,只能靠調試。正如公司裏的測試部,人力規模,工做壓力,絲絕不比開發部差。即便如此,仍是能讓一些簡單bug流入最終產品。老闆只能先問測試部:「這麼簡單的bug,怎麼沒測出來?」再問開發部:「這麼明顯的錯誤,你怎麼寫出來的?」總之,程序是調出來的。

怎麼調?vc提供了很全面的調試方法,打斷點、單步跟蹤、看變量。這些方法對遊戲不適用。一個bug,一般發生在某種狀況下,好比超級瑪麗,玩家在水管上,按方向鍵「下」,新的地圖顯示不出來,屏幕上亂七八糟。請問,bug在哪裏?玩家座標出問題、按鍵響應出問題、地圖加載出問題、圖片顯示出問題?打斷點,無處下手。

解決方法是:程序中,建立一個文本文件,在「可能有問題」的地方,添加代碼,向這個文件寫入提示信息或變量內容(稱爲跟蹤打印)。這個文本文件,就成了代碼運行的日誌。看日誌,就知道代碼中發生了什麼事情。最終,找到bug

FILEREPORT,就是對日誌文件建立、寫入等操做的封裝。

成員函數功能列表:

class FILEREPORT

{

public:

       //功能 默認構造函數,建立日誌trace.txt

       //入參

       FILEREPORT();

       //功能 指定日誌文件名稱

       //入參 日誌文件名稱

       FILEREPORT(char *p);

       //功能 析構函數,關閉文件

       //入參

       ~FILEREPORT();

      

       //功能 向日志文件寫入字符串

       //入參 要寫入的字符串

       void put(char *p);

       //功能 向日志文件寫入一個字符串,兩個整數

       //入參 字符串 整數a 整數b

       void put(char *p,int a,int b);

       //功能 計數器計數, 並寫入一個提示字符串

       //入參 計時器id 字符串

       void putnum(int i,char *p);

      

       //功能 判斷一個dc是否爲null, 若是是,寫入提示信息

       //入參 dc句柄 字符串

       void CheckDC(HDC h,char *p);

 

       //功能 設置顯示跟蹤信息的dc和文本座標

       //入參 顯示dc 橫縱座標

       void SetDevice(HDC h,int x,int y);

       //功能 設置要顯示的跟蹤信息

       //功能 提示字符串 整數a 整數b

       void Output(char *p,int a,int b);

       //功能 在屏幕上顯示當前的跟蹤信息

       void Show();

 

 

private:

       //跟蹤文件指針

       FILE *fp;

 

       //計數器組

       int num[5];

 

       //顯示dc

       HDC hshow;

       //跟蹤文本顯示座標

       int xpos;

       int ypos;

       //當前跟蹤信息

       char info[50];

 

};

函數具體實現很簡單,只是簡單的文件寫入。要說明的是兩部分,一:計數功能,有時要統計某個事情發生多少次,因此用一個整數數組,經過putnum讓指定數字累加。二:顯示功能,讓跟蹤信息,馬上顯示在屏幕上。

使用舉例:

沒有使用。程序最終完成,全部的跟蹤打印都已刪除。

 

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

9、            超級瑪麗製做揭祕9精靈結構struct ROLE

今天開始講邏輯層:struct ROLE

這個結構用來存儲兩種精靈:敵人(各類小怪)和子彈(攻擊方式)。敵人包括兩種蘑菇兵和兩種火圈。子彈包括火球和旋風。遊戲中,精靈的結構很簡單:

struct ROLE

{

 int x;//橫座標

 int y;//縱座標

 int w;//圖片寬度

 int h;//圖片高度

 int id;//精靈id

 int iframe;//圖片當前幀

 int iframemax;//圖片最大幀數

 

 //移動部分

 int xleft;//水平運動的左界限

 int xright;//水平運動的右界限

 int movex;//水平運動的速度

 

 //人物屬性

 int health;//精靈的生命值

 

 int show; //精靈是否顯示

};

 

遊戲中的子彈處理很是簡單,包括存儲、生成、銷燬。

子彈的存儲:全部的子彈存儲在一個數組中,以下:

 struct ROLE FireArray[MAX_MAP_OBJECT];

 

其實,全部的動態元素都有從生成到銷燬的過程。看一會兒彈是怎樣產生的。

 

首先,玩家按下z鍵:發出子彈,調用函數:

 

int GAMEMAP::KeyProc(int iKey)

 

  case KEY_Z: //FIRE

   if(iBeginFire)

    break;

   iTimeFire=0;

   iBeginFire=1; 

 

break;

 

這段代碼的意思是:若是正在發子彈,代碼結束。不然,設置iBeginFire1,表示開始發子彈。

子彈是在哪裏發出的呢?

思路:用一個函數不停地檢測iBeginFire,若是它爲1,則生成一個子彈。函數以下:

int GAMEMAP::CheckAni(int itimeclip)

發子彈的部分:

//發子彈

 if(iBeginFire)

 {

    //發子彈的時間到了(連續兩個子彈要間隔必定時間)

  if(0 == iTimeFire )

  {

    //設置子彈屬性:  可見, 動畫起始幀:0

   FireArray[iFireNum].show=1;

   FireArray[iFireNum].iframe = 0;

   //子彈方向

//若是人物朝右

   if(0==rmain.idirec)

   {

//子彈向右

    FireArray[iFireNum].movex=1;

   }

   else

   {

//子彈向左

    FireArray[iFireNum].movex=-1;

   }

//區分攻擊種類: 子彈,旋風

   switch(iAttack)

   {

//普通攻擊: 子彈

   case ATTACK_NORMAL:

//精靈ID: 子彈

    FireArray[iFireNum].id=ID_ANI_FIRE;

//設置子彈座標

    FireArray[iFireNum].x=rmain.xpos;

    FireArray[iFireNum].y=rmain.ypos;

 

//設置子彈寬高

    FireArray[iFireNum].w=FIREW;

    FireArray[iFireNum].h=FIREH;

 

//設置子彈速度: 方向向量乘以移動速度

    FireArray[iFireNum].movex*=FIRE_SPEED;

    break;

最後,移動數組的遊標iFireNum.這個名字沒起好, 應該寫成cursor.遊標表示當前往數組中存儲元素的位置.

//移動數組遊標

   iFireNum=(iFireNum+1)%MAX_MAP_OBJECT;

至此, 遊戲中已經生成了一個子彈. 由圖像層,經過子彈的id,座標在屏幕上繪製出來.

子彈已經顯示在屏幕上, 接下來, 就是讓它移動, 碰撞, 銷燬. 且聽下會分解.

 

10、            超級瑪麗製做揭祕10子彈的顯示和幀的刷新

感謝你們的支持,這些代碼有很大優化的餘地,有些代碼甚至笨拙。我儘可能講清楚我寫時的思路。今天講子彈的顯示和動畫幀的刷新,這個思路,能夠應用的其餘精靈上。

上次講全部的子彈存儲到一個數組裏。用一個遊標(數組下標)表示新生產的子彈存儲的位置。設數組爲a,長度爲n。遊戲開始,一個子彈存儲在a0,而後是a1a2...,a(n-1).而後遊標又回到0,繼續從a0位置存儲.數組長度30,保存屏幕上全部的子彈足夠了.

 

子彈的顯示功能由圖像層完成.如同圖像處理中講的.顯示一個子彈(全部圖片都是如此),只須要子彈座標,子彈圖片id,圖片幀. 函數以下:

void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj)

代碼部分:

//顯示子彈,魔法攻擊

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

              if (FireArray[i].show)

              {

                     ystart=FireArray[i].y;

                     xstart=FireArray[i].x;

                    

                     switch(FireArray[i].id)

                     {

                     case ID_ANI_FIRE:

                            bmobj.DrawItem(xstart,ystart,FireArray[i].id,FireArray[i].iframe);

                            break;

子彈圖片顯示完成.

遊戲中,子彈是兩幀圖片構成的動畫. 動畫幀是哪裏改變的呢?

刷新幀的函數是void GAMEMAP::ChangeFrame(int itimeclip)

遊戲中,不停地調用這個函數,刷新各類動畫的當前幀.其中子彈部分的代碼:

//子彈,攻擊控制

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

              if(FireArray[i].show)

              {

                     switch(FireArray[i].id)

                     {

              default:

                            FireArray[i].iframe=1-FireArray[i].iframe;

                            break;

                     }

              }    

       }

子彈的動畫只有兩幀.因此iframe只是0,1交替變化.至此,子彈在屏幕上顯示,而且兩幀圖片不停播放.

子彈和小怪碰撞, 是遊戲中的關鍵邏輯.網遊裏也是主要平常工做, 打怪. 消滅小怪, 也是這個遊戲的所有樂趣, 固然,這是在我下一個版本的遊戲沒有開發出來的時候.我預計要加入更多的動態元素, 更大的地圖. 只是這個計劃被如今的工做擱置了. 那麼, 這個關鍵的碰撞檢測, 以及碰撞檢測後的邏輯處理, 是怎樣的呢? 且聽下回分解.

 

11、           超級瑪麗製做揭祕11子彈運動和打怪

感謝你們支持。書接上回。玩家按攻擊鍵,生成子彈,存儲在數組中,顯示,接下來:

子彈運動,打怪。先說子彈是怎樣運動的。思路:用一個函數不停地檢測子彈數組,若是子彈可見,刷新子彈的座標。

實現以下:

函數:int GAMEMAP::CheckAni(int itimeclip)

代碼部分:

       //子彈移動

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

       //判斷子彈是否可見

              if (FireArray[i].show)

              {

       //根據子彈的移動速度movex,修改子彈座標.

       //(movex爲正,向右移動;爲負,向左移動,).

                     FireArray[i].x+=FireArray[i].movex;

                    

       //判斷子彈是否超出了屏幕範圍,若是超出,子彈消失(設置爲不可見)

                     if( FireArray[i].x > viewx+VIEWW || FireArray[i].x<viewx-FIRE_MAGIC_MAX_W)

                     {

                            FireArray[i].show = 0;

                     }

              }

       }

至此, 子彈在屏幕上不停地運動.

打怪是怎樣實現的呢?碰撞檢測.思路:用一個函數不停地檢測全部子彈,若是某個子彈碰到了小怪,小怪消失,子彈消失.

實現以下:

函數: int GAMEMAP::CheckAni(int itimeclip)

代碼部分:

       //檢測子彈和敵人的碰撞(包括魔法攻擊)

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

       //判斷小怪是否可見

              if(MapEnemyArray[i].show)

              {

              //檢測全部子彈

                     for(j=0;j<MAX_MAP_OBJECT;j++)

                     {

                     //判斷子彈是否可見

                            if (FireArray[j].show)

                            {

                            //判斷子彈和小怪是否"碰撞"

                                   if(RECT_HIT_RECT(FireArray[j].x+FIRE_XOFF,

                                          FireArray[j].y,

                                          FireArray[j].w,

                                          FireArray[j].h,

                                          MapEnemyArray[i].x,

                                          MapEnemyArray[i].y,

                                          MapEnemyArray[i].w,

                                          MapEnemyArray[i].h)

                                          )

                                   {

                                   //若是碰撞,小怪消滅

                                          ClearEnemy(i);

 

                                          switch(iAttack)

                                          {

                                          case ATTACK_NORMAL:

                                                 //子彈消失

                                                 FireArray[j].show=0;

(說明:若是是旋風,在旋風動畫幀結束後消失)

碰撞檢測說明:

子彈和小怪,都被看做是矩形.檢測碰撞就是判斷兩個矩形是否相交.之前,有網友說,碰撞檢測有不少優化算法.我仍是想不出來,只寫成了這樣:

//矩形與矩形碰撞

#define RECT_HIT_RECT(x,y,w,h,x1,y1,w1,h1) ( (y)+(h)>(y1) && (y)<(y1)+(h1) && (x)+(w)>(x1) && (x)<(x1)+(w1) )

小怪的消失

函數: void GAMEMAP::ClearEnemy(int i)

代碼部分:

       //小怪的生命值減一

       MapEnemyArray[i].health--;

 

       //若是小怪的生命值減到0, 小怪消失(設置爲不可見)

       if(MapEnemyArray[i].health<=0)

       {

              MapEnemyArray[i].show=0;

       }

 

至此, 玩家按下攻擊鍵,子彈生成,顯示,運動,碰到小怪,子彈消失,小怪消失.這些功能所有完成.若是隻作成這樣,不算本事.攻擊方式分兩種:子彈,旋風.小怪包括:兩種蘑菇兵,兩種火圈.同時,火圈能產生兩種蘑菇兵.而旋風的攻擊效果明顯高於普通子彈.是否是很複雜?其實,了無祕密.這是怎樣作到的呢?且聽下回分解.

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

12、           超級瑪麗製做揭祕12旋風攻擊,小怪運動,火圈

接上回。前面介紹了子彈的生成、顯示、運動、碰撞、消失的過程。這個過程能夠推廣到其餘精靈上。今天講旋風、蘑菇兵、火圈。

做爲魔法攻擊方式的旋風,和子彈大同小異。

旋風的存儲與子彈同存儲在一個數組中,以下:

struct ROLE FireArray[MAX_MAP_OBJECT];

使用時,用id區分。

旋風生成函數:int GAMEMAP::CheckAni(int itimeclip)

代碼部分:

       //發子彈

       if(iBeginFire)

       {

              if(0 == iTimeFire )

              {

                     FireArray[iFireNum].show=1;

                     FireArray[iFireNum].iframe = 0;

 

                     //子彈方向

                     if(0==rmain.idirec)

                     {

                            FireArray[iFireNum].movex=1;

                     }

                     else

                     {

                            FireArray[iFireNum].movex=-1;

                     }

 

                     switch(iAttack)

                     {

                     case ATTACK_MAGIC:

                            FireArray[iFireNum].id=ID_ANI_FIRE_MAGIC;

                            FireArray[iFireNum].x=rmain.xpos-ID_ANI_FIRE_MAGIC_XOFF;

                            FireArray[iFireNum].y=rmain.ypos-ID_ANI_FIRE_MAGIC_YOFF;

                            FireArray[iFireNum].w=FIRE_MAGIC_W;

                            FireArray[iFireNum].h=FIRE_MAGIC_H;

                            FireArray[iFireNum].movex=0;

                            break;    

                     }

                     //移動數組遊標

                     iFireNum=(iFireNum+1)%MAX_MAP_OBJECT;

              }

              iTimeFire=(iTimeFire+1)%TIME_FIRE_BETWEEN;

       }

這和子彈生成的處理相同。惟一區別是旋風不移動,因此movex屬性最後設置爲0

旋風的顯示:

旋風在屏幕上的繪製和子彈相同,函數void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj)。代碼部分和子彈相同。

可是旋風的幀刷新有些特殊處理,函數void GAMEMAP::ChangeFrame(int itimeclip)

代碼部分:

       //子彈,攻擊控制

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

              //若是攻擊(子彈、旋風)可見 

              if(FireArray[i].show)

              {

                     switch(FireArray[i].id)

                     {

                     case ID_ANI_FIRE_MAGIC:

                            //旋風當前幀加一

                            FireArray[i].iframe++;

                            //若是幀爲2(即第三張圖片)       ,圖片座標修正,向右移

                            if(FireArray[i].iframe == 2)

                            {

                                   FireArray[i].x+=FIRE_MAGIC_W;                                                             

                            }

                            //若是幀號大於3,即四張圖片播放完,旋風消失,設置爲不可見

                            if(FireArray[i].iframe>3)

                            {

                                   FireArray[i].show=0;

                            }

                            break;

                     }

              }    

至此,旋風顯示,動畫播放結束後消失.

旋風不涉及運動。碰撞檢測的處理和子彈相同,惟一區別是:當旋風和小怪碰撞,旋風不消失。

函數爲:int GAMEMAP::CheckAni(int itimeclip)

代碼以下:

                                          switch(iAttack)

                                          {

                                          case ATTACK_NORMAL:

                                                 //子彈消失

                                                 FireArray[j].show=0;

                                                 break;

                                                 //旋風不消失

                                          default:

                                                 break;

                                          }

那麼,再看小怪消失的函數void GAMEMAP::ClearEnemy(int i)

代碼部分:     

       MapEnemyArray[i].health--;

       if(MapEnemyArray[i].health<=0)

       {

              MapEnemyArray[i].show=0;

       }

能夠看到, 此時並不區分攻擊方式. 但旋風存在的時間長(動畫結束後消失),至關於屢次調用了這個函數,間接提升了殺傷力.

至此,兩種攻擊方式都已實現.

 

再看小怪, 分蘑菇兵和火圈兩種.

存儲問題. 和攻擊方式處理相同, 用數組加遊標的方法.蘑菇兵和火圈存儲在同一數組中,以下:

struct ROLE MapEnemyArray[MAX_MAP_OBJECT];

       int iMapEnemyCursor;

小怪生成:

小怪是由地圖文件設定好的.以第二關的地圖文件爲例,其中小怪部分以下:

;enemy

21 6 1 1 0 15 24

23 6 1 1 0 15 24

48 7 2 2 6 0 0

68 5 2 2 8 0 0

各個參數是什麼意義呢?看一下加載函數就全明白了.函數:int GAMEMAP::LoadMap()

代碼部分:

//若是文件沒有結束後

while(temp[0]!='#' && !feof(fp))

       {

       //讀入小怪數據 橫座標 縱座標 id 運動範圍左邊界 右邊界

              sscanf(temp,"%d %d %d %d %d %d %d",

                     &MapEnemyArray[i].x,

                     &MapEnemyArray[i].y,

                     &MapEnemyArray[i].w,

                     &MapEnemyArray[i].h,

                     &MapEnemyArray[i].id,

                     &MapEnemyArray[i].xleft,

                     &MapEnemyArray[i].xright);

             

              //座標轉換.乘以32

              MapEnemyArray[i].x*=32;

              MapEnemyArray[i].y*=32;

              MapEnemyArray[i].w*=32;

              MapEnemyArray[i].h*=32;

              MapEnemyArray[i].xleft*=32;

              MapEnemyArray[i].xright*=32;          

              MapEnemyArray[i].show=1;

              //設置移動速度(,表示向左)

              MapEnemyArray[i].movex=-ENEMY_STEP_X;

              //動畫幀

              MapEnemyArray[i].iframe=0;

              //動畫最大幀

              MapEnemyArray[i].iframemax=2;

             

              //設置生命值

              switch(MapEnemyArray[i].id)

              {

              case ID_ANI_BOSS_HOUSE:

                     MapEnemyArray[i].health=BOSS_HEALTH;

                     break;

 

              case ID_ANI_BOSS_HOUSE_A:

                     MapEnemyArray[i].health=BOSS_A_HEALTH;

                     break;

 

              default:

                     MapEnemyArray[i].health=1;

                     break;

              }

 

              //將火圈存儲在數組的後半段,數值長30, BOSS_CURSOR15

              if ( i<BOSS_CURSOR

                      && (  MapEnemyArray[i].id == ID_ANI_BOSS_HOUSE

                         || MapEnemyArray[i].id == ID_ANI_BOSS_HOUSE_A) )

              {

                     //move data to BOSS_CURSOR

                     MapEnemyArray[BOSS_CURSOR]=MapEnemyArray[i];

                     memset(&MapEnemyArray[i],0,sizeof(MapEnemyArray[i]));                  

                     i=BOSS_CURSOR;

              }

 

              i++;

              //讀取下一行地圖數據

              FGetLineJumpCom(temp,fp);      

       }

看來比生成子彈要複雜一些.尤爲是火圈, 爲何要從第15個元素上存儲?由於,火圈要不停地生成蘑菇兵,因此"分區管理",數值前一半存儲蘑菇兵,後一半存儲火圈.

小怪和火圈是怎樣顯示、運動的呢?火圈怎樣不斷產生新的小怪?且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

十3、           超級瑪麗製做揭祕13小怪和火圈,模板

小怪的顯示問題.

蘑菇兵和火圈處於同一個數組,很簡單.

函數:void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj)

代碼:

       //顯示敵人

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

              if (MapEnemyArray[i].show)

              {

                     bmobj.DrawItem(MapEnemyArray[i].x,MapEnemyArray[i].y,

                            MapEnemyArray[i].id,MapEnemyArray[i].iframe);     

              }

       }

一樣,如同圖片處理所講,顯示一個圖片,只須要座標,id,.

 

幀刷新和小怪運動.

函數:void GAMEMAP::ChangeFrame(int itimeclip)

代碼:

       //移動時間:每隔一段時間ENEMY_SPEED,移動一下

       if(0 == itimeclip% ENEMY_SPEED)

       {

              for(i=0;i<MAX_MAP_OBJECT;i++)

              {

                     //若是小怪可見

                     if(MapEnemyArray[i].show)

                     {

                            //幀刷新

                            MapEnemyArray[i].iframe=(MapEnemyArray[i].iframe+1)%MapEnemyArray[i].iframemax;

                           

                            switch(MapEnemyArray[i].id)

                            {

                            case ID_ANI_ENEMY_NORMAL:

                            case ID_ANI_ENEMY_SWORD:

                                   //蘑菇兵移動(士兵,刺客)

                                   MapEnemyArray[i].x+=MapEnemyArray[i].movex;

                                  

                                   //控制敵人移動:向左移動到左邊界後,移動速度movex改成向右。移動到右邊界後,改成向左。

                                   if(MapEnemyArray[i].movex<0)

                                   {

                                          if(MapEnemyArray[i].x<=MapEnemyArray[i].xleft)

                                          {

                                                 MapEnemyArray[i].movex=ENEMY_STEP_X;                                     

                                          }

                                   }

                                   else

                                   {

                                          if(MapEnemyArray[i].x>=MapEnemyArray[i].xright)

                                          {

                                                 MapEnemyArray[i].movex=-ENEMY_STEP_X;                      

                                          }

                                   }

                                   break;

                            }

至此,全部小怪不停移動。(火圈的movex0,不會移動)

 

碰撞檢測和消失。

在前面的子彈、旋風的碰撞處理中已講過。碰撞後,生命值減小,減爲0後,消失。

 

火圈.

火圈會產生新的蘑菇兵,怎樣實現的呢?思路:不斷地檢測火圈是否出如今屏幕中,出現後,生成蘑菇兵。

函數:int GAMEMAP::CheckAni(int itimeclip)

代碼部分:

       //若是在顯示範圍以內,則設置顯示屬性

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

              //判斷是否在屏幕範圍內

              if ( IN_AREA(MapEnemyArray[i].x, viewx, VIEWW) )

              {

                     //若是有生命值,設置爲可見

                     if(MapEnemyArray[i].health)

                     {

                            MapEnemyArray[i].show=1;

 

                            switch(MapEnemyArray[i].id)

                            {

                            //普通級火圈

                            case ID_ANI_BOSS_HOUSE:

                                   //每隔一段時間, 產生新的敵人

                                   if(itimeclip == TIME_CREATE_ENEMY)

                                   {

                                          MapEnemyArray[iMapEnemyCursor]=gl_enemy_normal;

                                          MapEnemyArray[iMapEnemyCursor].x=MapEnemyArray[i].x;

                                          MapEnemyArray[iMapEnemyCursor].y=MapEnemyArray[i].y+32;

 

                                          //移動遊標

                                          iMapEnemyCursor=(iMapEnemyCursor+1)%BOSS_CURSOR;                             

                                   }

                                   break;

                                   //下面是戰鬥級火圈,處理類似

                            }

                     }

              }

              else

              {

                     //不在顯示範圍內,設置爲不可見

                     MapEnemyArray[i].show=0;

              }           

       }

這樣,火圈就不斷地產生蘑菇兵.

 

再說一下模板.

這裏的模板不是c++的模板.聽說template技術已發展到藝術的境界.遊戲中用到的和template無關,而是全局變量.以下:

//普通蘑菇兵

struct ROLE gl_enemy_normal=

{

       0,

       0,

       32,

       32,

       ID_ANI_ENEMY_NORMAL,

       0,

       2,

       0,

       0,

       -ENEMY_STEP_X,//speed

       1,

       1

};

當火圈不斷產生新的蘑菇兵時,直接把這個小怪模板放到數組中,再修改一下座標便可.(對於蘑菇刺客,還要修改id和生命值)

遊戲的主要邏輯完成.此外,還有金幣,爆炸效果等其餘動態元素,它們是怎麼實現的?且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

十4、           超級瑪麗製做揭祕14爆炸效果,金幣

子彈每次攻擊到效果,都會顯示一個爆炸效果。因爲只涉及圖片顯示,它的結構很簡單。以下:

struct MapObject

{

       int x;

       int y;

       int w;

       int h;

       int id;

       int iframe;

       int iframemax;//最大幀數

       int show; //是否顯示

};

存儲問題。

爆炸效果仍然使用數組加遊標的方法,以下:

       struct MapObject BombArray[MAX_MAP_OBJECT];

       int iBombNum;

 

生成。

當子彈和小怪碰撞後,生成。

函數:void GAMEMAP::ClearEnemy(int i)

代碼部分:

       //生成

BombArray[iBombNum].show=1;

       BombArray[iBombNum].id=ID_ANI_BOMB;

       BombArray[iBombNum].iframe=0;

       BombArray[iBombNum].x=MapEnemyArray[i].x-BOMB_XOFF;

       BombArray[iBombNum].y=MapEnemyArray[i].y-BOMB_YOFF;

       //修改數組遊標

       iBombNum=(iBombNum+1)%MAX_MAP_OBJECT;

 

顯示。

和子彈、小怪的顯示方法相同。

函數:void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj)

代碼部分:

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

              if (BombArray[i].show)

              {

                     ystart=BombArray[i].y;

                     xstart=BombArray[i].x;

                     bmobj.DrawItem(xstart,ystart,BombArray[i].id, BombArray[i].iframe);    

              }

       }

 

幀刷新。

和子彈、小怪的幀刷新方法相同。

函數:void GAMEMAP::ChangeFrame(int itimeclip)

代碼部分:

for(i=0;i<MAX_MAP_OBJECT;i++)

       {

              if(BombArray[i].show)

              {

                     BombArray[i].iframe++;

//當第四張圖片顯示完畢,設置爲不可見。

                     if(BombArray[i].iframe>3)

                     {

                            BombArray[i].show=0;

                     }

              }    

       }

 

碰撞檢測:爆炸效果不涉及碰撞檢測。

消失:如上所述,爆炸效果在動畫結束後消失。

 

金幣。

金幣的處理比小怪更簡單。當玩家和金幣碰撞後,金幣消失,增長金錢數量。

存儲問題。

用數組加遊標的方法(下一個版本中封裝起來),以下:

       struct MapObject MapCoinArray[MAX_MAP_OBJECT];

       int iCoinNum;

 

金幣的生成。

和小怪類似,從地圖文件中加載。以第二關爲例,地圖文件中的金幣數據是:

6 5 32 32 3    

7 5 32 32 3    

8 5 32 32 3    

9 5 32 32 3    

18 4 32 32 3   

19 4 32 32 3   

20 4 32 32 3

數據依次表示橫座標,縱座標,寬,高,圖片id

函數:int GAMEMAP::LoadMap()

代碼部分:

       while(temp[0]!='#' && !feof(fp))

       {

              sscanf(temp,"%d %d %d %d %d",

                     &MapCoinArray[i].x,

                     &MapCoinArray[i].y,

                     &MapCoinArray[i].w,

                     &MapCoinArray[i].h,

                     &MapCoinArray[i].id);                

              MapCoinArray[i].show=1;

              MapCoinArray[i].iframe=0;

              //座標轉換,乘以32

              MapCoinArray[i].x*=32;

              MapCoinArray[i].y*=32;

              //設置這個動畫元件的最大幀

              switch(MapCoinArray[i].id)

              {

              case ID_ANI_COIN:

                     MapCoinArray[i].iframemax=4;

                     break;

              }

              i++;

              iCoinNum++;

              //讀取下一行數據

              FGetLineJumpCom(temp,fp);      

       }

 

金幣顯示:

和小怪的顯示方法相同.

函數:void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj)

代碼:

       //顯示金幣,和其餘物品

       for(i=0;i<iCoinNum;i++)

       {

              ystart=MapCoinArray[i].y;

              xstart=MapCoinArray[i].x;

              bmobj.DrawItem(xstart,ystart,MapCoinArray[i].id, MapCoinArray[i].iframe);  

       }

 

金幣幀刷新:

和小怪的幀刷新方法相同.

函數:void GAMEMAP::ChangeFrame(int itimeclip)

代碼:

       for(i=0;i<MAX_MAP_OBJECT;i++)

              {

                     //若是金幣可見,幀加一

                     if(MapCoinArray[i].show)

                     {                                               MapCoinArray[i].iframe=(MapCoinArray[i].iframe+1)%MapCoinArray[i].iframemax;

                     }

              }

 

金幣碰撞檢測:

和小怪的碰撞檢測方法類似,區別在於:金幣的碰撞檢測沒有判斷是否可見,只要金幣位於屏幕中,和玩家碰撞,則金幣消失,金錢數量iMoney增長。

函數:int GAMEMAP::CheckAni(int itimeclip)

代碼:

       for(i=0;i<iCoinNum;i++)

       {

              tempx=MapCoinArray[i].x;

              tempy=MapCoinArray[i].y;

             

              if ( IN_AREA(tempx, viewx-32, VIEWW) )

              {

                     //玩家座標是rmain.xpos rmain.ypos

                     if(    RECT_HIT_RECT(rmain.xpos,

                            rmain.ypos,

                            32,32,

                            tempx,

                            tempy,

                            MapCoinArray[i].w,MapCoinArray[i].h)

                            )

                     {

                            switch(MapCoinArray[i].id)

                            {

                            case ID_ANI_COIN:

                                   //增長金錢數量

                                   iMoney+=10; 

                                   //金幣消失

                                   ClearCoin(i);

                                   break;

                            }                         

                            return 0;

                     }           

              }

       } // end of for

 

金幣消失:

和小怪的消失不同.不須要設置show0, 而是直接刪除元素.即數組移動的方法.

函數:void GAMEMAP::ClearCoin(int i)

代碼:

       //檢查合法性

       if(i<0 || i>=iCoinNum)

              return;

       //減小一個金幣,或者減小一個其餘物品

       for(;i<iCoinNum;i++)

       {

              MapCoinArray[i]=MapCoinArray[i+1];

       }

       //修改數量

       iCoinNum--;

因而可知,直接刪除元素,省去了是否可見的判斷。但凡事都有兩面性,移動數組顯然比單個元素的設置要慢(實際上不必定,能夠優化)。方法多種多樣,這就是程序的好處,永遠有更好的答案。

全部的動態元素都介紹完了。所謂動態元素,就是有一個生成、運行、銷燬的過程。只不過,有的複雜一些,如子彈、旋風、蘑菇兵、火圈,有些元素簡單一些,如爆炸效果、金幣。方法都大同小異,要強調的是,這不是最好的方法。碰到金幣後,會出現‘+10’的字樣,怎麼作呢?這個問題會再次說明,方法多種多樣。且聽下回分解。

附:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

十5、           超級瑪麗製做揭祕15金幣提示,攻擊提示

提示信息,是玩家獲得的反饋。好比,碰到金幣,金幣消失,此時就要顯示「+10」;攻擊小怪,小怪卻沒有消失,這時要顯示血條,告知玩家小怪的生命值。下面講提示信息。

金幣提示:

+10的字樣,並無用文字處理,而是用圖片(4幀的動畫)。這樣,實現起來很簡單,和爆炸效果用同一個數組存儲,處理方法相同。

金幣的碰撞檢測函數:int GAMEMAP::CheckAni(int itimeclip)

代碼:

       for(i=0;i<iCoinNum;i++)

       {

              //判斷玩家是否碰到金幣

                            switch(MapCoinArray[i].id)

                            {

                            case ID_ANI_COIN:

                                   //碰到金幣

                                   iMoney+=10; 

                                   //金幣消失,顯示+10字樣

                                   ClearCoin(i);

                                   break;

金幣消失函數:void GAMEMAP::ClearCoin(int i)

代碼:

       switch(MapCoinArray[i].id)

       {

       case ID_ANI_COIN:

              //碰到了金幣,顯示+10字樣. 和爆炸效果的處理同樣, 只是圖片id不一樣

              BombArray[iBombNum].show=1;

              BombArray[iBombNum].id=ID_ANI_COIN_SCORE;

              BombArray[iBombNum].iframe=0;

              BombArray[iBombNum].x=MapCoinArray[i].x-COIN_XOFF;

              BombArray[iBombNum].y=MapCoinArray[i].y-COIN_YOFF;

              iBombNum=(iBombNum+1)%MAX_MAP_OBJECT;

              break;

       }

 

攻擊提示:須要給出攻擊對象名稱,血條。

存儲:

       //攻擊對象提示

       char AttackName[20];//攻擊對象名稱

       int iAttackLife;//攻擊對象當前生命值

       int iAttackMaxLife;//攻擊對象最大生命值

 

提示信息設置:在小怪被攻擊的時候,設置提示信息。

函數:void GAMEMAP::ClearEnemy(int i)

代碼:

//設置攻擊對象生命值

       iAttackLife=MapEnemyArray[i].health;

       switch(MapEnemyArray[i].id)

       {

       case ID_ANI_BOSS_HOUSE:

              //設置名稱

              strcpy(AttackName,"普通級火圈");

              //設置最大生命值

              iAttackMaxLife=BOSS_HEALTH;

其餘攻擊對象處理類似。

 

顯示:

函數: void GAMEMAP::ShowOther(HDC h)

代碼:

//若是攻擊對象生命值不爲0, 顯示提示信息

       if(iAttackLife)

       {

              //輸出名稱

              TextOut(h,viewx+ATTACK_TO_TEXT_X,

                     ATTACK_TO_TEXT_Y,AttackName,strlen(AttackName));

             

              //顯示血條

              xstart=viewx+ATTACK_TO_X-iAttackMaxLife*10;

              //按最大生命值顯示一個矩形, 做爲背景

              bmMap.DrawItemNoMaskWidth(xstart-1, ATTACK_TO_Y-1,ID_MAP_HEALTH_BK,

                     iAttackMaxLife*BMP_WIDTH_HEALTH, 0);

              //按當前生命值對應的寬度, 顯示一個紅色矩形

              bmMap.DrawItemNoMaskWidth(xstart, ATTACK_TO_Y,ID_MAP_HEALTH,

                     iAttackLife*BMP_WIDTH_HEALTH, 0);

       }

提示信息功能完成。

 

金錢數量顯示:和攻擊提示位於同一個函數void GAMEMAP::ShowOther(HDC h)

代碼:

       sprintf(temp,"MONEY: %d",iMoney);

       TextOut(h,viewx+20,20,temp,strlen(temp));

至此,攻擊系統(子彈、旋風、蘑菇兵,火圈),金幣(金幣,金錢數量),提示信息(金幣提示,攻擊提示),這幾類元素都介紹過了,還有一個,武器切換,就是從魂鬥羅裏摳來的那個東西,且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

十6、           超級瑪麗製做揭祕16攻擊方式切換

當玩家碰到武器包(就是魂鬥羅裏那個東西),攻擊方式切換。

思路:把它放到存儲金幣的數組中,用id區別。碰撞檢測時,若是是金幣,金幣消失,若是是武器包,攻擊方式切換。

存儲:和金幣位於同一個數組MapCoinArray

生成:由地圖文件加載。好比第一關的地圖文件數據:

25 4 52 25 5

各參數含義:橫座標 縱座標 圖片id

加載函數:int GAMEMAP::LoadMap()

代碼:和金幣的加載相同,惟一區別是金幣圖片有4幀,武器包只有2幀,設置以下:

MapCoinArray[i].iframemax=2;

 

顯示:和金幣的處理相同,相同函數,相同代碼。(再次顯示出圖像層的好處)

幀刷新:和金幣的處理相同,相同函數,相同代碼。(再再次顯示出圖像層的好處)

 

碰撞檢測:和金幣的處理相同。

函數:int GAMEMAP::CheckAni(int itimeclip)

代碼:若是是武器包,設置新的攻擊方式,武器包消失。

                            switch(MapCoinArray[i].id)

                            {

                            case ID_ANI_ATTACK:       

                                   //設置新的攻擊方式

                                   iAttack=ATTACK_MAGIC;

                                   //武器包消失

                                   ClearCoin(i);

                                   break;

                            }                         

 

武器包的消失:和金幣的處理相同,相同函數,相同代碼,這是邏輯層的好處(放在同一個數組中,處理簡單)。

至此,攻擊系統,金幣系統,提示信息,武器切換,所有完成。只須要一個地圖把全部的物品組織起來,構成一個虛擬世界,呈如今玩家眼前。地圖怎樣處理?且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

十7、           超級瑪麗製做揭祕17地圖物品

自從遊戲機發明以來,地圖是什麼樣的呢?打蜜蜂,吃豆,地圖是一個矩形,玩家在這個矩形框內活動。後來,地圖獲得擴展,能夠縱向移動,好比打飛機;能夠橫向移動,好比超級瑪麗、魂鬥羅等等橫板過關遊戲。再後來,橫向縱向均可以移動,後來又有45度地圖,3D技術後終於實現了高度擬真的虛擬世界。

超級瑪麗的地圖能夠當作是一個二維的格子。每一個格子的大小是32x32像素。

遊戲窗口大小爲12個格子高,16個格子寬。

遊戲地圖寬度是遊戲窗口的5倍,即12個格子高,5x16個格子寬。

地圖物品有哪些呢?地面,磚塊,水管。

 

存儲問題:

先看一下存儲結構:

struct MapObject

{

       int x;

       int y;

       int w;

       int h;

       int id;

       int iframe;

       int iframemax;//最大幀數

       int show; //是否顯示

};

各個成員含義是橫座標,縱座標,寬,高,id,當前幀,最大幀,是否可見。用第一關地圖文件的地圖物品舉例:(只包含5個參數)

0 9 10 3 0

這個物品是什麼呢?橫向第0個格子,縱向第9個格子,寬度10個格子,高度3個格子,id0,表示地面。

在顯示的時候,只要把座標、寬高乘以32,便可正確顯示。

地圖全部物品仍然用數組+遊標的方法存儲,以下:

       struct MapObject MapArray[MAX_MAP_OBJECT];

       int iMapObjNum;

 

地圖生成:從地圖文件中加載。

加載函數:int GAMEMAP::LoadMap()

代碼:

       while(temp[0]!='#' && !feof(fp))

       {

              //讀取一個物品

              sscanf(temp,"%d %d %d %d %d",

                     &MapArray[i].x,

                     &MapArray[i].y,

                     &MapArray[i].w,

                     &MapArray[i].h,

                     &MapArray[i].id);               

              MapArray[i].show=0;

              iMapObjNum++;

              i++;

              //讀取下一個物品

              FGetLineJumpCom(temp,fp);      

       }

 

地圖顯示:和物品顯示同樣,只是地面和磚塊須要雙重循環。

函數:void GAMEMAP::Show(MYANIOBJ & bmobj)

代碼:對於每一個寬w格,高h格的地面、磚塊,須要把單個地面磚塊平鋪w*h次,因此用雙重循環。

       for(i=0;i<iMapObjNum;i++)

       {

              ystart=MapArray[i].y*32;

              switch(MapArray[i].id)

              {

              //進出水管

              case ID_MAP_PUMP_IN:

              case ID_MAP_PUMP_OUT:

                     xstart=MapArray[i].x*32;

                     bmobj.DrawItemNoMask(xstart, ystart, MapArray[i].id, 0);                    

                     break;

      

              default:                 

                     for(j=0;j<MapArray[i].h;j++)

                     {

                            xstart=MapArray[i].x*32;

                            for(k=0;k<MapArray[i].w;k++)

                            {

                                   bmobj.DrawItemNoMask(xstart, ystart, MapArray[i].id, 0);                    

                                   xstart+=32;

                            }

                            ystart+=32;                         

                     } // end of for

                     break;

說明:水管是一個單獨完整的圖片,直接顯示,不須要循環。

 

幀刷新:地面、磚塊、水管都是靜態圖片,不涉及。

 

碰撞檢測:保證玩家順利地行走。若是玩家不踩在物品上,則不停地下落。

函數:int GAMEMAP::CheckRole()

代碼:

              //檢測角色是否站在某個物體上

              for(i=0;i<iMapObjNum;i++)

              {

                     //玩家的下邊線,是否和物品的上邊線重疊

                     if( LINE_ON_LINE(rmain.xpos,

                            rmain.ypos+32,

                            32,

                            MapArray[i].x*32,

                            MapArray[i].y*32,

                            MapArray[i].w*32)

                            )

                     {

                            //返回1,表示玩家踩在這個物品上

                            return 1;

                     }

              }

              //角色開始下落

              rmain.movey=1;    

              rmain.jumpx=0;//此時要清除跳躍速度,不然將變成跳躍,而不是落體

              return 0;

至此,地圖物品的功能完成,且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

十8、           超級瑪麗製做揭祕18背景物品

背景物品更簡單,包括草叢,樹木,河流,win標誌。這些背景物品只須要顯示,不涉及邏輯處理。

存儲:用數組+遊標的方法,以下:

       struct MapObject MapBkArray[MAX_MAP_OBJECT];

       int iMapBkObjNum;

第一關的背景物品數據:

17 5 3 2 0(草叢)

76 7 3 2 1win標誌)

10 10 3 2 2(河流)

含義和地圖物品相同。

 

背景物品加載:和地圖物品加載方法相同。

加載函數:int GAMEMAP::LoadMap()

代碼:    while(temp[0]!='#' && !feof(fp))

       {

              sscanf(temp,"%d %d %d %d %d",

                     &MapBkArray[i].x,

                     &MapBkArray[i].y,

…...

              MapBkArray[i].iframe=0;

              iMapBkObjNum++;

              i++;

              //下一個物品

              FGetLineJumpCom(temp,fp);      

       }

 

顯示:

函數:void GAMEMAP::ShowBkObj(MYANIOBJ & bmobj)

代碼:

       for(i=0;i<iMapBkObjNum;i++)

       {

              bmobj.DrawItem(xstart,ystart,MapBkArray[i].id,ibkobjframe);  

       }

 

幀刷新:背景物品都是2幀動畫。全部背景物品當前幀用ibkobjframe控制。

函數:void GAMEMAP::ChangeFrame(int itimeclip)

代碼:

       if(0 == itimeclip% WATER_SPEED)

       {

              ibkobjframe=1-ibkobjframe;  

至此,背景物品的功能完成。地圖怎樣跟隨玩家移動呢? 且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

十9、           超級瑪麗製做揭祕19視圖

怎樣把全部東西都顯示在窗口中,並隨着玩家移動呢?

思路:玩家看到的區域稱爲視圖,即12格高,16格寬的窗口(每格32*32像素)。先把整個地圖則繪製在一個DC上,而後從這個地圖DC中,截取當前視圖區域的圖像,繪製到窗口中。修改視圖區域的座標(橫座標增長),就實現了地圖的移動。

 

初始化:

函數:BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

代碼:

// hwindow是遊戲窗口的DC句柄

       hwindow=GetDC(hWnd);

       // hscreen是整個地圖對應的DC

       hscreen=CreateCompatibleDC(hwindow);

       //創建一個整個地圖大小(5倍窗口寬)的空位圖,選入hscreen

       hmapnull=CreateCompatibleBitmap(hwindow,GAMEW*32*5,GAMEH*32);

       SelectObject(hscreen,hmapnull);

 

顯示。

函數:WndProc

代碼:

              case WM_PAINT:

                     // hwindow是遊戲窗口的DC句柄

                     hwindow = BeginPaint(hWnd, &ps);

                     SelectObject(hscreen,hmapnull);

 

                     case GAME_IN:

                            //顯示天空

                            bmSky.DrawRollStretch(2,2,gamemap.mapinfo.iBackBmp);

                            //顯示背景物品

                            gamemap.ShowBkObj(bmMapBkObj);

                            //顯示地圖物品

                            gamemap.Show(bmMap);

                            //顯示動態元素

                            gamemap.ShowAniObj(bmAniObj);

                            //顯示提示信息

                            gamemap.ShowOther(hscreen);

                            //顯示玩家

                            rmain.Draw();

                            break;    

                    

if(gamemap.iScreenScale)

                     {

                            //窗口大小調整功能,代碼略

                     }

                     else

                     {                  

       //從整個地圖的DC, 截取當前視圖區域的圖像,繪製到窗口

       BitBlt(hwindow,0,0,GAMEW*32,GAMEH*32,hscreen,gamemap.viewx,0,SRCCOPY);

                     }

能夠看到,視圖的左上角橫座標是viewx,只須要刷新這個座標,就實現了地圖移動。

 

視圖座標刷新:

思路:用一個函數不停地檢測,玩家角色和視圖左邊界的距離,超過特定值,把視圖向右移。

函數:void GAMEMAP::MoveView()

代碼:若是玩家座標和視圖左邊界的距離大於150,移動視圖。

       if(rmain.xpos - viewx > 150)

       {

              viewx+=ROLE_STEP;  

              //判斷視圖座標是否達到最大值(地圖寬度減去一個窗口寬度)

              if(viewx>(mapinfo.viewmax-1)*GAMEW*32)

                     viewx=(mapinfo.viewmax-1)*GAMEW*32;       

       }

至此,地圖跟隨玩家移動。每一關的地圖是怎樣切換的呢?且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

二10、           超級瑪麗製做揭祕20地圖切換

地圖分兩種,普通地圖和隱藏地圖(指經過水管進入的地圖)。先講普圖地圖的切換,再講隱藏地圖的切換。

普通地圖的切換:

思路:很簡單,用一個數字iMatch表示當前是第幾關。每過一關,iMatch+1,加載下一張地圖。

存儲:    int iMatch;

初始化:       iMatch=0;0表示第一關)

 

過關檢測:用一個函數不停地檢測玩家是否到了地圖終點,若是是,加載下一關的地圖。

函數:int GAMEMAP::IsWin()

代碼:

       //判斷玩家的座標是否到達地圖終點(橫座標大於等於地圖寬度)

if(rmain.xpos >= MAX_PAGE*GAMEW*32 )

       {           

              // iMatch增長

              iMatch=mapinfo.iNextMap;

 

              if(iMatch>=MAX_MATCH)

              {

                     //若是iMatch大於關卡數量(即經過最後一關),加載第一關的數據,代碼略

              }

              else

              {

                     //沒有通關

                     InitMatch();//初始化遊戲數據

                     //設置玩家角色座標,初始化玩家角色

                     rmain.SetPos(BM_USER,3*32,8*32);

                     rmain.InitRole(0,GAMEW*32*MAX_PAGE-32);              

                     //加載下一關的地圖

                     LoadMap();  

              }

說明:函數LoadMap()根據iMatch的值加載某一關的地圖。而iMatch的修改代碼是:

              iMatch=mapinfo.iNextMap;

對於普通地圖iMatch取值爲0,1,2,…,只須要+1便可,爲何要有一個複雜的賦值過程呢?是爲了實現隱藏地圖的切換。

 

隱藏地圖的切換:

先看一下LoadMap加載的地圖文件是什麼樣子?超級瑪麗加強版的地圖存儲在一個文本文件中,結構爲:

*0

//0關的地圖數據

*1

//1關的地圖數據

*4

//4關的地圖數據

其中,編號0,1,2表示前三關的普圖地圖,編號3,4是隱藏地圖(3是第0關的隱藏地圖,4是第1關的隱藏地圖)。怎樣表示地圖之間的關係呢?

思路:設計一張「地圖信息表」,格式以下:

0關:下一關編號,隱藏地圖編號

1關:下一關編號,隱藏地圖編號

4關:下一關編號,隱藏地圖編號

這樣就造成一個地圖信息的處理:

(1)從「地圖信息表」中讀取當前關卡的的地圖信息。

(2)當玩家到達地圖終點,讀取「下一關」編號;玩家進入水管,讀取「隱藏地圖編號」。

 

遊戲的地圖信息結構:

struct MAPINFO

{

       int iNextMap;

       int iSubMap;

};

地圖信息表(全局變量): (數組的第i個元素,表示第i關的地圖信息)

struct MAPINFO allmapinfo[]={

{1,3},

{2,4},

{MAX_MATCH,-1, },

{-1,0},

{-1,1}

};

對應的邏輯信息爲:

0關的下一關是第1關,從水管進入第3關。

1關的下一關是第2關,從水管進入第4關。

2關(最後一關)沒有下一關(MAX),沒有從水管進入的地圖。

3關沒有下一關,從水管進入第0關。

4關沒有下一關,從水管進入第1關。

這樣,實現了從水管進入隱藏關,又從水管返回的功能。

 

地圖信息的存儲:       struct MAPINFO mapinfo;

地圖信息的讀取:

函數:void GAMEMAP::InitMatch()

代碼:每一關的遊戲開始前,都要用這個函數初始化遊戲數據。包括讀取地圖信息,以下:

       mapinfo=allmapinfo[iMatch];

 

玩家到達地圖終點的檢測:即int GAMEMAP::IsWin(),經過代碼:

              iMatch=mapinfo.iNextMap;

切換到下一關的地圖編號。

 

玩家進入水管的檢測:

思路:當玩家按下方向鍵「下」,判斷是否站在水管上(固然進入地圖的水管),若是是,切換地圖。

函數:int GAMEMAP::KeyProc(int iKey)

代碼:

              case VK_DOWN:

                     for(i=0;i<iMapObjNum;i++)

                     {

                            //判斷玩家是否站在一個地圖物品上

                            if( LINE_IN_LINE(玩家座標,地圖物品座標))

                            {                         

                                   //這個物品是水管

                                   if(MapArray[i].id == ID_MAP_PUMP_IN)

                                   {

                                          //設置遊戲狀態:進入水管

                                          iGameState=GAME_PUMP_IN;

函數WndProc中,不斷檢測GAME_PUMP_IN狀態,代碼以下:

              case WM_TIMER:

                     switch(gamemap.iGameState)

                     {

                            case GAME_PUMP_IN:

                            if(c1.DecCount())

                            {

                                   //若是GAME_PUMP_IN狀態結束,加載隱藏地圖。

                                   gamemap.ChangeMap();

                                  

是否是複雜一些?確實,它能夠簡化。我想這仍是有好處,它容易擴展。這仍然是我最初的構思,這是一個代碼框架。

看一下ChangeMap的處理:

函數:void GAMEMAP::ChangeMap()

代碼:

       //讀取隱藏地圖編號

       iMatch=mapinfo.iSubMap;

       //遊戲初始化

       InitMatch();                                      

       //加載地圖

       LoadMap();

可見,ChangeMap的簡單很簡單。由於,LoadMap的接口只是iMatch,我只要保證iMatch在不一樣狀況下設置正確,地圖就會正確地加載。我把iMatch從初版遊戲中的++,改爲從「地圖信息表」中讀取,這樣,隱藏地圖的功能實現了。

舉例:若是下一個版本中,一個地圖,有多個水管包含隱藏地圖?怎樣實現呢?很簡單,把地圖信息表改爲「水管編號-地圖信息」的對應結構,功能實現。

至此,地圖切換實現。可是,地圖切換中,還有其它的遊戲數據要刷新,怎樣處理呢?且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

二11、          超級瑪麗製做揭祕21遊戲數據管理

進入每一關以前,須要對全部遊戲數據初始化。進入隱藏地圖,一樣須要初始化。並且,從隱藏地圖返回上層地圖,還要保證玩家出如今「出水管」處。地圖數據、玩家數據、視圖數據,都要設置正確。

 

全部的遊戲數據,即封裝在gamemap中的數據,分紅以下幾種:

場景數據:包含當前關卡的地圖,全部精靈,金幣,提示信息。

視圖數據:視圖窗口座標。

玩家數據:玩家角色的我的信息,例如金錢數量,攻擊方式,遊戲次數。

1.       場景數據:

       int iGameState;//當前遊戲狀態

       int iMatch;      //當前關卡

各類精靈的數組:

       struct MapObject MapArray[MAX_MAP_OBJECT]; //地圖物品

       struct MapObject MapBkArray[MAX_MAP_OBJECT];     //地圖背景物品

       struct ROLE MapEnemyArray[MAX_MAP_OBJECT];      //小怪

       struct MapObject MapCoinArray[MAX_MAP_OBJECT]; //金幣

       struct ROLE FireArray[MAX_MAP_OBJECT]; //子彈

       struct MapObject BombArray[MAX_MAP_OBJECT];       //爆炸效果

       //當前關卡的地圖信息

       struct MAPINFO mapinfo;

       //圖片幀

int ienemyframe;    //小怪圖片幀

       int ibkobjframe;     //背景圖片幀

       //玩家攻擊

       int iTimeFire;//兩個子彈的間隔時間

       int iBeginFire;//是否正在發子彈

       //攻擊對象提示

       char AttackName[20];//攻擊對象名稱

       int iAttackLife;//攻擊對象生命值

       int iAttackMaxLife;// 攻擊對象最大生命值

2. 視圖數據:

       int viewx;//視圖起始座標

3. 玩家數據:

       int iMoney;     //金錢數量

       int iAttack;      //攻擊方式

       int iLife;         //玩家遊戲次數

可見,每次加載地圖前,要初始化場景數據和視圖數據,而玩家數據不變,如金錢數量。

 

遊戲數據處理:

假設沒有隱藏地圖的功能,遊戲數據只須要完成初始化的功能,分別位於如下三個地方:

程序運行前,初始化;

過關後,初始化,再加載下一關地圖;

失敗後,初始化,再加載當前地圖;

 

1.       遊戲程序運行,全部遊戲數據初始化。

函數:BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

代碼:把全部遊戲數據初始化

gamemap.Init();

 

遊戲初始化函數:void GAMEMAP::Init()

代碼:

       //設置遊戲初始狀態

       iGameState=GAME_PRE;

       //設置當前關卡

       iMatch=0;

//設置玩家數據 玩家遊戲次數,金錢數量,攻擊種類

       iLife=3;

       iMoney=0;

       iAttack=ATTACK_NORMAL;

       //設置視圖座標

       viewx=0;

       //初始化場景數據

       InitMatch();

 

場景數據初始化函數:void GAMEMAP::InitMatch()

代碼:把全部遊戲數據清除(置0

       memset(MapArray,0,sizeof(MapArray));

       memset(BombArray,0,sizeof(BombArray));

       ienemyframe=0;

       iFireNum=0;

       ……

這樣,程序啓動,InitInstance中完成第一次初始化。

 

2.       過關後,遊戲數據初始化,加載下一關地圖。

過關檢測函數:int GAMEMAP::IsWin()

代碼:

       //判斷玩家是否到達地圖終點

if(rmain.xpos >= MAX_PAGE*GAMEW*32 )

       {

              //讀取下一關地圖編號

              iMatch=mapinfo.iNextMap;

              if(iMatch>=MAX_MATCH)

              {

                     //若是所有經過

                     Init();      //初始化全部數據

                     LoadMap();   //加載地圖

              }

              else

              {

                     InitMatch();    //初始化場景數據

                     //設置玩家座標

                     rmain.SetPos(BM_USER,3*32,8*32);

                     rmain.InitRole(0,GAMEW*32*MAX_PAGE-32);              

                     //加載下一關的地圖

                     LoadMap();  

              }

 

3.       若是玩家失敗,從新加載當前地圖。

失敗檢測函數:int GAMEMAP::IsWin()

代碼:若是玩家碰到了小怪,或者踩到火圈,遊戲失敗,調用Fail()進一步處理。

       //檢測角色和敵人的碰撞

       for(i=0;i<MAX_MAP_OBJECT;i++)

       {

              if(MapEnemyArray[i].show)

              {

                     if(HLINE_ON_RECT(玩家座標 小怪座標))

                     {

                            if(0 == rmain.movey)

                            {

                                   //玩家在行走過程當中,碰到小怪,遊戲失敗

                                   Fail();

                            }

                            else

                            {

                                   //玩家在下落過程當中,碰到火圈,遊戲失敗

                                   switch(MapEnemyArray[i].id)

                                   {

                                   case ID_ANI_BOSS_HOUSE:

                                   case ID_ANI_BOSS_HOUSE_A:

                                          Fail();

       ……

//玩家到達地圖底端(掉入小河),遊戲失敗

if(rmain.ypos > GAMEH*32)

       {

      

              Fail();

              return 0;

       }                                       

 

失敗處理函數:void GAMEMAP::Fail()

代碼:

       //玩家遊戲次數減1

       iLife--;

       //設置遊戲狀態

       iGameState=GAME_FAIL_WAIT;

 

GAME_FAIL_WAIT狀態結束後,調用函數void GAMEMAP::Fail_Wait()加載地圖。

函數:void GAMEMAP::Fail_Wait()

代碼:

       if(    iLife <=0)

       {

              //遊戲次數爲0,從新開始,初始化全部數據

              Init();

       }

       else

       {

              //還能繼續遊戲

       }

       //設置玩家座標

       rmain.SetPos(BM_USER,3*32,8*32);

       rmain.InitRole(0,GAMEW*32*MAX_PAGE-32);

       //加載當前地圖

       LoadMap();  

 

至此,在沒有隱藏地圖的狀況下,遊戲數據管理(只有初始化)介紹完了。

 

增長了隱藏地圖的功能,遊戲數據管理包括:初始化,數據刷新。哪些數據須要刷新呢?

1. 刷新玩家座標。

例如,從第一關(地圖編號爲0)進入隱藏地圖,玩家出如今(38),即橫向第3格,縱向第8格。玩家返回第一關後,要出如今「出水管」的位置(667)。

2. 刷新視圖座標。

例如,從第一關進入隱藏地圖,玩家出如今(38),視圖對應地圖最左邊,玩家返回第一關後,視圖要移動到「出水管」的位置。

3. 刷新背景圖片的座標。

例如,從第一關進入隱藏地圖,玩家出如今(38),天空背景對應地圖最左邊,玩家返回第一關後,背景圖片要移動到「出水管」的位置。

隱藏地圖加載函數:void GAMEMAP::ChangeMap()

代碼:

       //初始化視圖座標

       viewx=0;

       //獲取隱藏地圖編號

       iMatch=mapinfo.iSubMap;

       //初始化場景數據

       InitMatch();

       //設置玩家座標                                       

       rmain.SetPos(BM_USER,mapinfo.xReturnPoint*32,mapinfo.yReturnPoint*32);

       //玩家角色初始化

rmain.InitRole(0,GAMEW*32*MAX_PAGE-32);

       //設定視圖位置

       if(rmain.xpos - viewx > 150)

       {

              SetView(mapinfo.xReturnPoint*32-32);//往左讓一格

              if(viewx>(mapinfo.viewmax-1)*GAMEW*32)

                     viewx=(mapinfo.viewmax-1)*GAMEW*32;

       }

       //設定人物活動範圍

       rmain.SetLimit(viewx, GAMEW*32*MAX_PAGE);

       //設定背景圖片座標

       bmSky.SetPos(BM_USER,viewx,0);

       //加載地圖

       LoadMap();

}

因此,地圖信息表中,要包含「出水管」的座標。完整的地圖信息表以下:

struct MAPINFO

{

       int iNextMap;  //過關後的下一關編號

       int iSubMap;   //進入水管後的地圖編號

       int xReturnPoint;    //出水管的橫座標

       int yReturnPoint;    //出水管的縱座標

       int iBackBmp;        //背景圖片ID

       int viewmax;          //視圖最大寬度

};

struct MAPINFO allmapinfo[]={

{1,3,66,7,0,5},

{2,4,25,4,1,5},

{MAX_MATCH,-1,-1,-1,2,5},

{-1,0,3,8,3,1},

{-1,1,3,8,3,2}

};

說明:

0關:

{1,3,66,7,0,5},表示第0關的下一關是第1關,從水管進入第3關,出水管位於(667),天空背景id0,視圖最大寬度爲5倍窗口寬度。

3關:

{-1,0,3,8,3,1},表示第3關沒有下一關,從水管進入第0關,出水管位於(38),天空背景id3,視圖最大寬度爲1倍窗口寬度。

這樣,隱藏地圖切換的同時,視圖數據,玩家數據均正確。

 

各個動態元素,地圖的各類處理都已完成,只須要讓玩家控制的小人,走路,跳躍,攻擊,進出水管。玩家的動做控制怎樣實現?且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

二12、          超級瑪麗製做揭祕22玩家角色類MYROLE

玩家控制的小人,和各類小怪基本一致。沒什麼神祕的。主要有三個功能要實現:鍵盤響應,動做控制,圖片顯示。

爲了方便圖片顯示,玩家角色類MYROLE直接派生自圖片類MYBITMAP

 

成員函數功能列表:

class MYROLE:public MYBITMAP

{

public:

       //構造函數,析構函數

       MYROLE();

       ~MYROLE();

       //初始化部分

       //功能 初始化玩家信息

       //入參 玩家運動範圍的左邊界 右邊界()

       void InitRole(int xleft, int xright);

       //功能 設置玩家運動範圍

       //入參 玩家運動範圍的左邊界 右邊界()

       void SetLimit(int xleft, int xright);

 

       //圖片顯示部分

       //功能 顯示玩家角色圖片(當前座標 當前幀)

       //入參

       void Draw();

       //功能 顯示玩家角色圖片

       //入參 指定的橫座標 縱座標

       void Draw(int x,int y,int iframe);

       //功能 刷新幀,該函數沒有使用, 幀刷新的功能在其它地方完成

       //入參

       void ChangeFrame();

       //功能 設置玩家狀態. 該函數沒有使用

       //入參 玩家狀態

       void SetState(int i);

       //動做部分

       //功能 玩家角色移動

       //入參

       void Move();

       //功能 玩家角色跳躍. 該函數沒有使用

       //入參

       void Jump();

       //功能 移動到指定地點

       //入參 指定地點橫座標 縱座標

       void MoveTo(int x,int y);

       //功能 從當前位置移動一個增量

       //入參 橫座標增量 縱座標增量

       void MoveOffset(int x,int y);

       //功能 向指定地點移動一段距離(移動增量是固定的)

       //入參 指定地點橫座標 縱座標

       void MoveStepTo(int x,int y);

       //動畫部分

       //功能 播放動畫

       //入參

       void PlayAni();

       //功能 設置動畫方式

       //入參 動畫方式

       void SetAni(int istyle);

       //功能 判斷是否正在播放動畫, 若是正在播放動畫,返回1.不然,返回0

       //入參

       int IsInAni();

 

       //數據部分

       //玩家狀態, 該變量沒有使用

       int iState;

       //圖片數據

//玩家當前幀

       int iFrame;

       //動做控制數據

       //玩家活動範圍: 左邊界 右邊界(只有橫座標)

       int minx;

       int maxx;

       //運動速度

       int movex;//正值,向右移動

       int movey;//正值,向下移動

       //跳躍

       int jumpheight;//跳躍高度

       int jumpx;//跳躍時, 橫向速度(正值,向右移動)

       //玩家運動方向

       int idirec;

       //動畫數據

       int iAniBegin;//動畫是否開始播放

       int iparam1;//動畫參數

       int iAniStyle;//動畫方式

};

 

各個功能的實現:

鍵盤響應。

玩家經過按鍵,控制人物移動。

消息處理函數函數:WndProc

代碼:

              case WM_KEYDOWN:

                     if(gamemap.KeyProc(wParam))   

                            InvalidateRect(hWnd,NULL,false);

                     break;

              case WM_KEYUP:

                     gamemap.KeyUpProc(wParam);                                     

                     break;

按鍵消息包括「按下」「擡起」兩種方式:

KEYDOWN處理函數:int GAMEMAP::KeyProc(int iKey)

代碼:不一樣遊戲狀態下,按鍵功能不一樣。

       switch(iGameState)

       {

       case GAME_PRE://選擇遊戲菜單

              switch(iKey)

              {

              case 0xd://按下回車鍵

                     switch(iMenu)

                     {

                     case 0:    //菜單項0「開始遊戲」

                            c1.ReStart(TIME_GAME_IN_PRE); //計時兩秒

                            iGameState=GAME_IN_PRE;//進入遊戲LIFE/WORLD提示狀態

                            break;

                           

                     case 1:    //菜單項1「操做說明」

                            SetGameState(GAME_HELP); //進入遊戲狀態「操做說明」,顯示幫助信息

                            break;

                     }

                     break;

                    

              case VK_UP:          //按方向鍵「上」,切換菜單項

                     iMenu=(iMenu+1)%2;

                     break;

              case VK_DOWN:         //按方向鍵「下」,切換菜單項

                     iMenu=(iMenu+1)%2;

                     break;

              }

              return 1;

 

       case GAME_HELP: //遊戲菜單項「操做說明」打開

              switch(iKey)

              {

              case 0xd:        //按回車鍵,返回遊戲菜單

                     SetGameState(GAME_PRE);        //設置遊戲狀態:選擇菜單

                     break;                  

              }

              return 1;

 

       case GAME_IN: //遊戲進行中

              //若是人物正在播放動畫,拒絕鍵盤響應

              if(rmain.IsInAni())

              {

                     break;

              }

              //根據方向鍵, X, Z, 觸發移動,跳躍,攻擊等功能

              switch(iKey)

              {

              case VK_RIGHT:                        

              case VK_LEFT:

              case VK_DOWN:

              case KEY_X: //                

              case KEY_Z: //FIRE

 

              //祕籍J

case 0x 7a ://按鍵F11, 直接切換攻擊方式

                     iAttack=(iAttack+1)%ATTACK_MAX_TYPE;

                     break;

              case 0x7b://按鍵F12 直接通關(遊戲進行中才能夠,即遊戲狀態GAME_IN

                     rmain.xpos = MAX_PAGE*GAMEW*32;

                     break;

              }

              break;

       }

       return 0;

}

可見,按鍵響應只須要處理三個狀態:

菜單選擇GAME_PRE

操做說明菜單打開GAME_HELP

遊戲進行中GAME_IN

說明:前兩個狀態屬於菜單控制,函數返回1,表示當即刷新屏幕。對於狀態GAME_IN,返回0。遊戲過程當中,屏幕刷新由其它地方控制。

 

按鍵「擡起」的處理:

函數:void GAMEMAP::KeyUpProc(int iKey)

代碼:按鍵擡起,只須要清除一些變量。

       switch(iKey)

       {

       //鬆開方向鍵「左右」,清除橫向移動速度

       case VK_RIGHT:   

              rmain.movex=0;

              break;

       case VK_LEFT:

              rmain.movex=0;

              break;

       case KEY_X: //鬆開跳躍鍵,無處理

              break;

 

       case KEY_Z: //鬆開攻擊鍵,清除變量iBeginFire,表示中止攻擊

              iBeginFire=0;

              break;

 

       case KEY_W: //W,調整窗口爲默認大小

              MoveWindow(hWndMain,

                     (wwin-GAMEW*32)/2,

                     (hwin-GAMEH*32)/2,

                     GAMEW*32,

                     GAMEH*32+32,

                     true);                   

 

              break;

       }

這就是遊戲的全部按鍵處理。

 

顯示問題:

函數:void MYROLE::Draw()

代碼:

//判斷是否播放動畫,即iAniBegin1

if(iAniBegin)

       {

              //顯示動畫幀

              PlayAni();      

       }

       else

       {

              //顯示當前圖片

              SelectObject(hdcsrc,hBm);

              BitBlt(hdcdest,xpos,ypos,

                     width,height/2,

                     hdcsrc,iFrame*width,height/2,SRCAND);          

              BitBlt(hdcdest,xpos,ypos,

                     width,height/2,

                     hdcsrc,iFrame*width,0,SRCPAINT);  

       }

 

玩家角色是怎樣行走和跳躍的呢?動畫播放怎樣實現?且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

二十3、          超級瑪麗製做揭祕23玩家動做控制

玩家移動:把行走和跳躍當作兩個狀態,各自用不一樣的變量表示橫縱方向的速度。

相關屬性:

行走:橫向速度爲movex,縱向不移動。

跳躍:橫向速度爲jumpx,縱向速度爲movey。當前跳躍高度jumpheight

運動方向:idirec

 

思路:

第一步:玩家按鍵,按鍵處理函數設置這些屬性。按鍵鬆開,清除動做屬性。

第二步:用一個函數不停檢測這些變量,控制玩家移動。

 

1. 按鍵觸發:

按鍵處理函數:int GAMEMAP::KeyProc(int iKey)

代碼:

              switch(iKey)

              {

              case VK_RIGHT:    //按右

                     //判斷是否正在跳躍, 即縱向速度不爲0

                     if(rmain.movey!=0)

                     {

                            //跳躍過程當中, 設置橫向速度, 方向向右, 大小爲4像素

                            rmain.jumpx=4;

                     }

                     rmain.movex=4;     //設置橫向速度, 方向向右, 大小爲4像素

                     rmain.idirec=0;       //設置玩家方向, 向右

                     break;

                    

              case VK_LEFT:  //按左

                     //若是是跳躍過程當中, 設置橫向速度, 方向向左, 大小爲4像素

                     if(rmain.movey!=0)

                     {

                            rmain.jumpx=-4;

                     }

                     rmain.movex=-4;           //設置橫向速度, 方向向左, 大小爲4像素

                     rmain.idirec=1;              //設置玩家方向, 向左

                     break;

             

              case KEY_X: //X鍵跳

                     //若是已是跳躍狀態,不做處理,代碼中斷

                     if(rmain.movey!=0)

                            break;

                     //設置縱向速度,方向向上(負值),大小爲13

                     rmain.movey=-SPEED_JUMP;

                     //將當前的橫向速度,賦值給「跳躍」中的橫向速度

                     rmain.jumpx=rmain.movex;

                     break;

                    

              case KEY_Z: //FIRE

                     if(iBeginFire)

                            break;     //若是已經開始攻擊,代碼中斷

                     iTimeFire=0;   //初始化子彈間隔時間

                     iBeginFire=1;  //1,表示開始攻擊

                     break;

按鍵鬆開處理函數:void GAMEMAP::KeyUpProc(int iKey)

代碼:

       //鬆開左右鍵,清除橫向速度

       case VK_RIGHT:   

              rmain.movex=0;

              break;

       case VK_LEFT:

              rmain.movex=0;

              break;

       case KEY_X: //

//不能清除跳躍的橫向速度jumpx

//例如,移動過程當中起跳,整個跳躍過程當中都要有橫向速度

              break;

       case KEY_Z: //FIRE

              iBeginFire=0;         //中止攻擊

              break;

 

2.       控制移動。

動做檢測函數:WndProc

代碼:時間片的處理中,根據不一樣狀態,調用各類檢測函數。

              case WM_TIMER:

                     switch(gamemap.iGameState)

                     {

                     case GAME_IN:

                            rmain.Move();//人物移動

                            ……

                            break;

說明:每45毫秒產生一個WM_TIMER消息,在GAME_IN狀態下,調用各類檢測函數。其中rmain.Move()就是不斷檢測玩家動做屬性,實現移動。

函數:void MYROLE::Move()

代碼:

       if(0 == movey)

       {

              //若是不是跳躍, 橫向移動

              MoveOffset(movex, 0);

       }

       else

       {

              //跳躍, 先橫向移動, 再縱向移動

              MoveOffset(jumpx, 0);

              MoveOffset(0, movey);

       }

 

       //玩家幀控制 糾錯法

       if(movex<0 && iFrame<3)

       {

              iFrame=3;       //若是玩家向左移動, 而圖片向右, 則設置爲3(4張圖片)

       }

       if(movex>0 && iFrame>=3)

       {

              iFrame=0;       //若是玩家向右移動, 而圖片向右, 則設置爲0(1張圖片)

       }

       //幀刷新

       if(movex!=0)

       {

              if(0==idirec)

                     iFrame=1-iFrame;   //若是方向向右, 圖片循環播放0,1

              else

                     iFrame=7-iFrame; //若是方向向左, 圖片循環播放3,4

       }

       if(movey!=0)

       {

//跳躍過程當中, 幀設置爲0(向右),3(向左)

//幀刷新後, 從新設置幀, 就實現了跳躍過程當中, 圖片靜止

              iFrame=idirec*3;   

       }

 

       //跳躍控制

       if(movey<0)

       {

              //向上運動(縱向速度movey爲負值)

              jumpheight+=(-movey);        //增長跳躍高度

             

              //重力影響,速度減慢

              if(movey<-1)

              {

                     movey++;

              }

 

              //到達頂點後向下落, 最大跳躍高度爲JUMP_HEIGHT * 32, 3個格子的高度

              if(jumpheight >= JUMP_HEIGHT * 32)

              {    

                     jumpheight =  JUMP_HEIGHT * 32;   //跳躍高度置爲最大

                     movey=4;       //縱向速度置爲4, 表示開始下落

              }

       }

       else if(movey>0)

       {

              //下落過程, 跳躍高度減小

              jumpheight -= movey;

              //重力影響,速度增大

              movey++;                    

       }

玩家移動函數:void MYROLE::MoveOffset(int x,int y)

代碼:根據增量設置座標

       //橫縱增量爲0,不移動,代碼結束

       if(x==0 && y==0)

              return;

 

       //若是碰到物體,不移動,代碼結束

       if(!gamemap.RoleCanMove(x,y))

              return;

       //修改玩家座標

       xpos+=x;

       ypos+=y;

       //判斷是否超出左邊界

       if(xpos<minx)

              xpos=minx;    //設置玩家座標爲左邊界

       //判斷是否超出右邊界

       if(xpos>maxx)

              xpos=maxx;   

 

3.       碰撞檢測

不管行走,跳躍,都是用函數MoveOffset操縱玩家座標。這時,就要判斷是否碰到物體。若是正在行走,則不能前進;若是是跳躍上升,則開始下落。

函數:int GAMEMAP::RoleCanMove(int xoff, int yoff)

代碼:

       int canmove=1;//初始化, 1表示能移動

for(i=0;i<iMapObjNum;i++)

       {

              if( RECT_HIT_RECT(玩家座標加增量,地圖物品座標))

              {

                     //碰到物體,不能移動

                     canmove=0;

                     if(yoff<0)

                     {

                            //縱向增量爲負(即上升運動), 碰到物體開始下落

                            rmain.movey=1;

                     }

                     if(yoff>0)

                     {

//縱向增量爲正(即下落運動), 碰到物體, 中止下落

                            rmain.jumpheight=0;//清除跳躍高度

                            rmain.movey=0;//清除縱向速度

                            rmain.ypos=MapArray[i].y*32-32;//縱座標刷新,保證玩家站在物品上

                     }

                     break;

              }

       }

       return canmove;

 

玩家移動的過程當中,要不斷檢測是否站在地圖物品上。若是在行走過程當中,且沒有站在任何物品上,則開始下落。

函數:int GAMEMAP::CheckRole()

代碼:

       if(rmain.movey == 0 )

       {

              //檢測角色是否站在某個物體上

              for(i=0;i<iMapObjNum;i++)

              {

                     //玩家的下邊線,是否和物品的上邊線重疊

                     if( LINE_ON_LINE(rmain.xpos,

                            rmain.ypos+32,

                            32,

                            MapArray[i].x*32,

                            MapArray[i].y*32,

                            MapArray[i].w*32)

                            )

                     {

                            //返回1,表示玩家踩在這個物品上

                            return 1;

                     }

              }

              //角色開始下落

              rmain.movey=1;    

              rmain.jumpx=0;//此時要清除跳躍速度,不然將變成跳躍,而不是落體

              return 0;

 

至此,玩家在這個虛擬世界能夠作出各類動做,跳躍,行走,攻擊。加強版中,加入了水管,玩家在進出水管,就須要動畫。怎麼實現,且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

二十4、          超級瑪麗製做揭祕24角色動畫

玩家在進出水管的時候,須要進入水管、從水管中升起兩個動畫。當動畫播放結束後,切換到新的地圖。

動畫播放過程當中,禁止鍵盤響應,即玩家不能控制移動。

 

1.       玩家進水管。

地圖物品中,水管分兩個,進水管(玩家進入地圖)和出水管(從別的地圖返回)。兩種水管對應不一樣的圖片ID

#define ID_MAP_PUMP_IN 9

#define ID_MAP_PUMP_OUT 10

玩家進入水管的檢測:

函數:int GAMEMAP::KeyProc(int iKey)

代碼:檢測玩家按「下」,若是玩家站在進水管上,開始播放動畫

              case VK_DOWN:

                     for(i=0;i<iMapObjNum;i++)

                     {

                            if( LINE_IN_LINE(玩家座標的下邊界,地圖物品的上邊界))

                            {                         

                                   //判斷是否站在進水管上

                                   if(MapArray[i].id == ID_MAP_PUMP_IN)

                                   {

                                          //若是站在設置角色動畫方式,向下移動

                                          rmain.SetAni(ROLE_ANI_DOWN);

                                          iGameState=GAME_PUMP_IN;//設置遊戲狀態:進水管

                                          c1.ReStart(TIME_GAME_PUMP_WAIT);//計時2

                                   }

                            }

                     }

                     break;

動畫設置函數:void MYROLE::SetAni(int istyle)

代碼:

       iAniStyle=istyle;     //設置動畫方式

       iparam1=0;     //參數初始化爲0

       iAniBegin=1;   //表示動畫開始播放

說明: iparam1是動畫播放中的一個參數, 根據動畫方式不一樣,能夠有不一樣的含義.

 

2.       動畫播放

玩家角色顯示函數:void MYROLE::Draw()

代碼:

//判斷是否播放動畫,即iAniBegin1

       if(iAniBegin)

       {

              PlayAni();       //播放當前動畫

       }

動畫播放函數:void MYROLE::PlayAni()

代碼:根據不一樣的動畫方式,播放動畫

       switch(iAniStyle)

       {

       case ROLE_ANI_DOWN:

              //玩家進入水管的動畫,iparam1表示降低的距離

              if(iparam1>31)

              {

                     //降低距離超過31(即圖片高度),玩家徹底進入水管,無需圖片顯示

                     break;                  

              }

              //玩家沒有徹底進入水管,截取圖片上半部分,顯示到當前的座標處

              SelectObject(hdcsrc,hBm);

              BitBlt(hdcdest,

                     xpos,ypos+iparam1,

                     width,height/2-iparam1,

                     hdcsrc,

                     iFrame*width,height/2,SRCAND);             

              BitBlt(hdcdest,

                     xpos,ypos+iparam1,

                     width,height/2-iparam1,

                     hdcsrc,

                     iFrame*width,0,SRCPAINT);      

              //增長降低高度

              iparam1++;           

              break;

      

3.       玩家進入水管後,切換地圖

函數:WndProc

代碼:在時間片的處理中,當GAME_PUMP_IN狀態結束,切換地圖,並設置玩家動畫:從水管中上升。

                     case GAME_PUMP_IN:

                            if(c1.DecCount())

                            {

                                   gamemap.ChangeMap();//切換地圖

                                   gamemap.SetGameState(GAME_IN);   //設置遊戲狀態

                                   c1.ReStart(TIME_GAME_IN);            //計時300

                                   rmain.SetAni(ROLE_ANI_UP);            //設置動畫,圖片上升

                            }

                            InvalidateRect(hWnd,NULL,false);

                            break;

4.       從水管中上升

動畫播放函數:void MYROLE::PlayAni()

代碼:根據不一樣的動畫方式,播放動畫

       switch(iAniStyle)

       {

       case ROLE_ANI_UP:

              if(iparam1>31)

              {

                     //若是上升距離超過31(圖片高度),動畫結束

                     break;                  

              }

              //人物上升動畫,截取圖片上部,顯示到當前座標

              SelectObject(hdcsrc,hBm);

              BitBlt(hdcdest,

                     xpos,ypos+32-iparam1,

                     width,iparam1,

                     hdcsrc,

                     iFrame*width,height/2,SRCAND);             

              BitBlt(hdcdest,

                     xpos,ypos+32-iparam1,

                     width,iparam1,

                     hdcsrc,

                     iFrame*width,0,SRCPAINT);      

              //增長上升距離

              iparam1++;

//若是上升距離超過31(圖片高度)

              if(iparam1>31)

              {

                     iAniBegin=0;   //動畫結束,清除動畫播放狀態

              }

至此,兩個動畫方式都實現了。可是,若是在動畫播放過程當中,玩家按左右鍵,移動,就會出現,角色一邊上升,一邊行走,甚至跳躍。怎樣解決?若是播放動畫,屏蔽鍵盤響應。

按鍵響應函數:int GAMEMAP::KeyProc(int iKey)

代碼:

       case GAME_IN:

              //若是人物正在播放動畫,拒絕鍵盤響應

              if(rmain.IsInAni())

              {

                     break;

              }

這樣,在播放過程當中,不受玩家按鍵影響。玩家全部功能所有實現,接下來看一下整個遊戲邏輯,且聽下回分解。

:

超級瑪麗初版源碼連接:http://download.csdn.net/source/497676

超級瑪麗加強版源碼連接:http://download.csdn.net/source/584350

 

 

二十5、          超級瑪麗製做揭祕25GAMEMAP 全局變量

全部遊戲數據都須要封裝到實際的變量中。整個遊戲,就是用類GAMEMAP表示的。

成員函數功能列表:

class GAMEMAP

{

public:

       //加載地圖

       int LoadMap();

       //初始化全部遊戲數據

void Init();

相關文章
相關標籤/搜索