青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

海邊沫沫

相濡以沫,不如相忘于江湖
posts - 9, comments - 113, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

寫個小游戲練一練手

Posted on 2007-12-16 12:10 海邊沫沫 閱讀(7128) 評論(14)  編輯 收藏 引用 所屬分類: 高起點C++學習之路
學習C++的最好方式,就是使用C++來做項目。然而,我手中并沒有需要使用C++的工作,咋辦?只好自己寫個小游戲練練手了。

我選擇的游戲是俄羅斯方塊,之所以選擇它,是因為它簡單,簡單到對于很多高手來講,實現(xiàn)這樣一個游戲,他們幾乎只需要一個.cpp源文件就夠了。然而我認為,優(yōu)雅的代碼來自于完善的設(shè)計。下面,我把自己設(shè)計該游戲的過程寫下來,歡迎大家探討。

首先,是選擇用戶界面。我選擇的是一個基于對話框的MFC項目。對話框在這里有兩個作用,一個是作為顯示游戲畫面的畫布,另一個就是用來接受用戶的鍵盤輸入。MFC的CDialog類對鍵盤輸入做了一些預處理,因此在OnKeyDown中根本捕獲不到上下左右四個方向鍵的按鍵動作,因此,必須重寫PreTranslateMessage函數(shù),如下:
BOOL?CRussiaBlockDlg::PreTranslateMessage(MSG*?pMsg)
{
????
//return?CDialog::PreTranslateMessage(pMsg);
????return?FALSE;
}

其次,是考慮游戲運行的方式。在這里,我使用了一個隊列和兩個輔助線程。隊列是干什么的呢?是用來保存游戲動作的。在用戶按下按鍵之后,對話框的OnKeyDown將用戶的按鍵翻譯為游戲動作,然后依次添加到隊列的尾部,然后,在另外的線程中,通過讀取該隊列的頭部,就知道接下來游戲該如何進行了。兩個輔助線程,其中一個就是游戲線程,該線程從隊列中讀取游戲動作,并進行游戲,另外一個輔助線程是干嘛的呢?很簡單,就是定時往隊列中投遞DOWN動作。

先定義一個枚舉類型,如下:
enum?GameAction{LEFT,RIGHT,ROTATE,DOWN,RESTART};

然后,在對話框類中添加一個用于保存游戲動作的隊列,如下:
deque<GameAction>?m_GameActionQue;

這時,對話框類的OnKeyDown函數(shù)看起來是這樣的:
void?CRussiaBlockDlg::OnKeyDown(UINT?nChar,?UINT?nRepCnt,?UINT?nFlags)
{
????
if((nFlags?&?0x7F)?==?57)?//空格鍵開始
????{
????????
if(m_isRunning?==?true)
????????{
????????????m_GameActionQue.push_back(RESTART);
????????}
else
????????{
????????????m_isRunning?
=?true;
????????????
this->Invalidate();
????????????
AfxBeginThread(GameThreadFunc,NULL);
????????????
AfxBeginThread(TimerThreadFunc,NULL);

????????}
????????
????}
else?if((nFlags?&?0x7F)?==?75?&&?m_isRunning?==?true)?//左方向鍵
????{
????????m_GameActionQue.push_back(LEFT);
????}
else?if((nFlags?&?0x7F)?==?72?&&?m_isRunning?==?true)?//上方向鍵
????{
????????m_GameActionQue.push_back(ROTATE);
????}
else?if((nFlags?&?0x7F)?==?77?&&?m_isRunning?==?true)?//右方向鍵
????{
????????m_GameActionQue.push_back(RIGHT);
????}
else?if((nFlags?&?0x7F)?==?80?&&?m_isRunning?==?true)?//下方向鍵
????{
????????m_GameActionQue.push_back(DOWN);
????}

????CDialog::OnKeyDown(nChar,?nRepCnt,?nFlags);
}

這時,對話框類基本上已經(jīng)完成了自己的使命,剩下的工作只是在OnPaint函數(shù)中適當?shù)睦L制一些提示信息而已。工作的重點進入到線程函數(shù)GameThreadFunc中。在該線程中,游戲的進行和幾個類密切相關(guān),它們分別是GameBoard類和Sprite類及Sprite的派生類。

先來看Sprite類及其派生類,它們分別代表了游戲中可以移動和旋轉(zhuǎn)的各種類型的方塊,如下圖:
Sprites.jpg

Sprite類的定義如下:
class?Sprite
{
public:
????Sprite(
void);
????
~Sprite(void);
????
int?x;
????
int?y;
????
int?tiles[4][4];
????
void?left(void);
????
void?right(void);
????
void?down(void);
????virtual?
void?rotate(void);
};

可以看到,我使用x,y來代表Sprite左上角的坐標,使用一個二維數(shù)組來表示Sprite的形狀,這Sprite的派生類中,只有該二維數(shù)組的值不同,rotate函數(shù)的實現(xiàn)不同,所以基類的rotate函數(shù)是虛函數(shù)。

為了創(chuàng)建Sprite對象,還用到了Factory模式,我使用了類SpriteFactory,其GetSprite函數(shù)如下:
Sprite*?SpriteFactory::GetSprite(void)
{
????
switch(std::rand()?%?7)
????{
????
case?0:
????????
return?new?ISprite();
????
case?1:
????????
return?new?LSprite();
????
case?2:
????????
return?new?SSprite();
????
case?3:
????????
return?new?ZSprite();
????
case?4:
????????
return?new?PSprite();
????
case?5:
????????
return?new?OSprite();
????
case?6:
????????
return?new?TSprite();
????}
????
return?NULL;
}


另外一個類GameBoard,代表的是游戲的主界面,其主要結(jié)構(gòu)是個10*20的網(wǎng)格,如下圖:
GameBoard.jpg

這設(shè)計的時候,我將這個網(wǎng)格設(shè)計為12*22的,周圍的這一圈,在顯示的時候是不可見的,之所以要這一圈存在,是為了方便碰撞檢測,不讓Sprite跑出界。GameBoard類的定義如下:
class?GameBoard
{
public:
????GameBoard(
void);
????
~GameBoard(void);
????
int?tiles[22][12];
????
int?m_score;
????
int?m_speed;
????Sprite
*?m_pSprite;

????
void?sprite_left(void);
????
void?sprite_right(void);
????
void?sprite_rotate(void);
????
void?sprite_down(void);
????
void?clear(void);
????
void?combine(void);
????bool?collide(Sprite
*?sprite);
????
void?show(void);
};

其中的m_pSprite指針保存了一個Sprite對象,分別提供四個函數(shù)指揮這個這個Sprite對象向上向下向右移動和旋轉(zhuǎn),在每次移動和旋轉(zhuǎn)之前,都需要進行碰撞檢測,函數(shù)collide提供碰撞檢測的功能。如果Sprite對象下降到底部之后,就應該調(diào)用combine函數(shù)將這個Sprite對象的tiles數(shù)組的數(shù)據(jù)加入到GameBoard的tiles數(shù)組中,并創(chuàng)建另外一個Sprite對象。而clear函數(shù)的作用,主要是在一局游戲Game Over后開始下一局時,清空GameBoard。

有了這幾個類,游戲線程看起來就是這樣的了:
UINT?GameThreadFunc(LPVOID?pParam)
{
????CRussiaBlockDlg
*?pDlg?=?dynamic_cast<CRussiaBlockDlg*>(::AfxGetApp()->GetMainWnd());
????CClientDC?dc(pDlg);
????
if(pDlg->m_isGameOver?==?true)
????{
????????pDlg
->m_isGameOver?=?false;
????????pDlg
->m_GameBoard.clear();
????}
????
while(pDlg->m_isRunning)
????{
????????pDlg
->m_GameBoard.show();
????????
if(pDlg->m_GameActionQue.empty())
????????{
????????????::Sleep(
100);
????????}
????????
else
????????{
????????????
switch(pDlg->m_GameActionQue.front()){
????????????????
case?RESTART:
????????????????????pDlg
->m_isRunning?=?false;
????????????????????pDlg
->Invalidate();
????????????????????
break;
????????????????
case?LEFT:
????????????????????pDlg
->m_GameBoard.sprite_left();
????????????????????
break;
????????????????
case?RIGHT:
????????????????????pDlg
->m_GameBoard.sprite_right();
????????????????????
break;
????????????????
case?DOWN:
????????????????????pDlg
->m_GameBoard.sprite_down();
????????????????????
break;
????????????????
case?ROTATE:
????????????????????pDlg
->m_GameBoard.sprite_rotate();
????????????????????
break;
????????????}
????????????pDlg
->m_GameActionQue.pop_front();
????????}
????}
????
return?0;
}

剩下的事情,就是怎樣去操作數(shù)組,怎么樣去調(diào)用GDI在窗口上畫圖了。最終游戲效果如下圖:
RussiaBlock.jpg

這里是我的源代碼,使用VS 2008可以直接打開。歡迎大家探討。

Feedback

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-16 20:07 by TD
Very good!
很詳細,設(shè)計的也很好,博主很棒!

雞蛋里面挑點骨頭試試~~
1.關(guān)于bool值的判斷:m_isGameOver 本身就是bool值,使用m_isGameOver ==false當然沒錯,只是有點怪,你覺得如何?

2.程序中出現(xiàn)的一些常數(shù),如Board的長和寬12*22,還有些別的,改成宏定義或者enum應該更好點,哪天覺得想改一下尺寸,會比較方便

3.線程同步的問題

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-16 22:47 by 夢在天涯
強,看到這里越來越多的高手,超高興,希望以后多交流奧!

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-17 01:13 by Fox
我以前在TC里面寫過一個,可惜后來源碼丟了大半,一個執(zhí)行檔也不是最終版。
和大多數(shù)俄羅斯方塊不同的地方在于,我對方塊和面板的存儲都是用位存儲。

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-17 01:14 by Fox
看看最近有沒有時間,也用MFC實現(xiàn)過來

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-17 09:24 by 絕對零度
在symbian上寫過,我也是用位來存儲的。

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-17 10:29 by 崔友志
博主真厚道 愿更上一層樓!

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-17 12:44 by cpp
用地圖數(shù)據(jù)應該是比較方便的,我用的是bool類型的地圖,方塊信息用十進制數(shù)據(jù),用的時候轉(zhuǎn)化為二進制。當時寫的時候沒有用面向?qū)ο蟮乃枷耄瑪?shù)據(jù)結(jié)構(gòu)用的都是全局變量。擴展性還可以,代碼寫得就有點亂了。Cppblog上也是高手如云。

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-17 13:37 by 海邊沫沫
@TD

你說得不錯,游戲中確實需要考慮線程同步問題。首先是多個線程訪問同一個變量的時候,最好能加入競爭機制。在我這個游戲中,就沒有對m_GameActionQue的訪問進行控制,不過兩個線程在尾部寫,一個線程在頭部讀,倒沒有出現(xiàn)問題。

其次是這里
if(pDlg->m_GameActionQue.empty())
{
::Sleep(100);
}
我在一個循環(huán)中不斷進行判斷隊列是否為空,這種做法雖然可行,但是不好,因為在隊列為空的時候不斷循環(huán)判斷很占用CPU時間。正確的做法應該是調(diào)用WaitForSingleObject使線程阻塞起來,當別的線程向隊列中添加消息的時候解除阻塞。

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-17 13:49 by 秦歌
強人!

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-18 21:20 by DeathKnight
贊一個 設(shè)計很簡潔明快 我喜歡

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-19 18:33 by 柯南
不錯,很有意思

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-19 19:13 by 海邊沫沫
我今天稍微把游戲修改了一下,不用
if(pDlg->m_GameActionQue.empty())
{
::Sleep(100);
}
了,而是在對話框類中加入了一個CEvent類的變量m_event

然后,讀取隊列的時候使用
if(pDlg->m_GameActionQue.empty())
{
pDlg->m_event.Lock();
}
而在其他寫入隊列的時候調(diào)用
m_event.SetEvent();

這樣修改之后,游戲?qū)︽I盤的響應就基本上沒有延時了,而且占用CPU要下降不少。

# re: 寫個小游戲練一練手  回復  更多評論   

2007-12-20 13:53 by 秦歌
厲害

# re: 寫個小游戲練一練手  回復  更多評論   

2009-04-23 19:13 by 向往
不錯,不錯,看起來有些老練.繼續(xù)加油.
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲欧美日韩国产中文在线| 欧美日韩在线播放三区四区| 国产日本欧美视频| 欧美一区二区福利在线| 亚洲欧美激情视频| 国产在线高清精品| 麻豆精品精华液| 欧美成人第一页| 一本久道久久综合中文字幕| 中文欧美字幕免费| 国产日本欧洲亚洲| 亚洲国产成人久久| 欧美极品一区二区三区| 亚洲欧美日韩国产一区二区三区| 性久久久久久久久| 亚洲狼人精品一区二区三区| 亚洲午夜电影| 亚洲电影免费| 99精品热6080yy久久| 国产欧美视频一区二区三区| 欧美黄污视频| 国产精品嫩草99a| 美女视频黄免费的久久| 欧美视频一区二区三区四区| 欧美在线免费视频| 欧美黑人在线播放| 国产日韩欧美a| 亚洲高清免费| 国产日韩精品一区| 亚洲精品国产精品国自产观看浪潮 | 欧美sm重口味系列视频在线观看| 欧美国产日韩xxxxx| 欧美一区二区三区四区夜夜大片| 欧美wwwwww| 久久久久久久性| 欧美视频免费在线| 免费一级欧美片在线播放| 国产精品成人免费| 最新国产成人在线观看| 狠狠色香婷婷久久亚洲精品| 一区二区三区四区国产| 亚洲激情校园春色| 久久精品视频免费播放| 羞羞漫画18久久大片| 欧美日韩一区二区欧美激情 | 欧美激情久久久久久| 国产性做久久久久久| 亚洲一区亚洲| 一区二区三区精品久久久| 玖玖玖国产精品| 久久综合中文| 国产在线乱码一区二区三区| 亚洲在线中文字幕| 亚洲无吗在线| 欧美三级视频在线观看| 亚洲激情第一页| 亚洲日本激情| 毛片av中文字幕一区二区| 久久综合久久88| 在线视频成人| 久久综合中文字幕| 欧美v亚洲v综合ⅴ国产v| 狠狠干狠狠久久| 久久精品国产精品亚洲精品| 久久激情视频| 国外精品视频| 久久久精品视频成人| 麻豆精品国产91久久久久久| 亚洲福利视频网| 欧美成年人网| 亚洲精品欧美激情| 亚洲亚洲精品在线观看| 国产精品久久久久久久久久妞妞| 在线中文字幕日韩| 欧美一区二区三区在线观看| 国产视频精品xxxx| 久久香蕉精品| 亚洲国内自拍| 亚洲在线视频| 国产亚洲精品v| 久久综合电影一区| 亚洲精品久久久久久一区二区 | 欧美一区二区播放| 亚洲电影免费观看高清| 欧美日韩久久久久久| 亚洲欧美久久久久一区二区三区| 一区二区三区在线免费视频| 久久综合狠狠综合久久激情| 亚洲人www| 欧美在线免费播放| 91久久国产综合久久蜜月精品 | 国内精品一区二区三区| 玖玖国产精品视频| 中文高清一区| 美日韩免费视频| 亚洲视频一区二区| 国产一区二区三区在线免费观看| 免费人成精品欧美精品| 一本色道久久综合亚洲精品不卡| 久久精品成人一区二区三区| 亚洲人被黑人高潮完整版| 国产精品久久久久久久久久ktv| 久久精品欧美日韩精品| 99精品久久久| 欧美阿v一级看视频| 先锋影音国产一区| 亚洲精品久久7777| 国产一区二区三区久久久| 欧美日本国产在线| 久久夜精品va视频免费观看| 亚洲夜晚福利在线观看| 亚洲高清精品中出| 老司机成人在线视频| 亚洲在线视频一区| 亚洲免费av网站| 伊人影院久久| 国产欧美在线观看| 欧美性做爰毛片| 欧美福利一区二区| 久久婷婷国产麻豆91天堂| 亚洲永久精品大片| 一区二区日韩| 亚洲国产一区二区三区青草影视| 久久午夜精品一区二区| 亚洲欧美在线播放| 亚洲手机在线| 一级日韩一区在线观看| 亚洲精品婷婷| 91久久精品国产91久久性色tv | 国产伦精品一区二区三区四区免费 | 99视频国产精品免费观看| 黄色影院成人| 国产一区二区福利| 国产区日韩欧美| 国产日韩精品久久久| 国产色婷婷国产综合在线理论片a| 国产精品毛片大码女人| 欧美特黄视频| 国产精品成人免费视频 | 国产精品婷婷| 国产精品永久免费观看| 国产精品久久久久久久免费软件| 欧美天天在线| 国产精品夜夜夜一区二区三区尤| 欧美视频在线一区| 国产精品v亚洲精品v日韩精品| 欧美日韩一区二区三| 欧美午夜精品电影| 国产精品中文字幕欧美| 国产日韩欧美一区在线| 国内外成人免费激情在线视频网站| 美女网站在线免费欧美精品| 免费高清在线视频一区·| 久久综合色一综合色88| 欧美激情第三页| 欧美日韩在线观看一区二区| 欧美午夜片在线观看| 国产精品有限公司| 在线视频观看日韩| 亚洲图片欧洲图片av| 先锋影音国产精品| 免费在线观看成人av| 亚洲精品国产精品国自产在线| 中文欧美字幕免费| 久久久久久久一区二区| 欧美精品久久一区| 国产精品系列在线播放| 在线观看成人av电影| 日韩小视频在线观看| 欧美一区二区三区在| 欧美大尺度在线| 在线视频亚洲欧美| 久久亚洲精品视频| 国产精品成av人在线视午夜片| 国产综合精品一区| 99在线精品观看| 久久精品91| 亚洲精品自在在线观看| 欧美一区亚洲| 欧美日韩mp4| 亚洲二区视频| 午夜精品久久久久久久99樱桃| 免费不卡亚洲欧美| 亚洲一二三区视频在线观看| 蜜桃av一区| 国产日韩欧美综合在线| 99re热这里只有精品视频| 久久免费高清视频| 亚洲性视频网址| 欧美日韩xxxxx| 影音先锋久久精品| 欧美中文字幕在线| 日韩亚洲欧美一区| 欧美成人高清| 精品99视频| 久久精品国产99| 一本色道久久99精品综合 | 欧美性大战久久久久久久蜜臀 | 亚洲国产精品t66y| 欧美在线观看网址综合|