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

海邊沫沫

相濡以沫,不如相忘于江湖
posts - 9, comments - 113, trackbacks - 0, articles - 0
  C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
學(xué)習(xí)C++的最好方式,就是使用C++來(lái)做項(xiàng)目。然而,我手中并沒(méi)有需要使用C++的工作,咋辦?只好自己寫(xiě)個(gè)小游戲練練手了。

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

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

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

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

然后,在對(duì)話框類中添加一個(gè)用于保存游戲動(dòng)作的隊(duì)列,如下:
deque<GameAction>?m_GameActionQue;

這時(shí),對(duì)話框類的OnKeyDown函數(shù)看起來(lái)是這樣的:
void?CRussiaBlockDlg::OnKeyDown(UINT?nChar,?UINT?nRepCnt,?UINT?nFlags)
{
????
if((nFlags?&?0x7F)?==?57)?//空格鍵開(kāi)始
????{
????????
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);
}

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

先來(lái)看Sprite類及其派生類,它們分別代表了游戲中可以移動(dòng)和旋轉(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來(lái)代表Sprite左上角的坐標(biāo),使用一個(gè)二維數(shù)組來(lái)表示Sprite的形狀,這Sprite的派生類中,只有該二維數(shù)組的值不同,rotate函數(shù)的實(shí)現(xiàn)不同,所以基類的rotate函數(shù)是虛函數(shù)。

為了創(chuàng)建Sprite對(duì)象,還用到了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;
}


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

這設(shè)計(jì)的時(shí)候,我將這個(gè)網(wǎng)格設(shè)計(jì)為12*22的,周?chē)倪@一圈,在顯示的時(shí)候是不可見(jiàn)的,之所以要這一圈存在,是為了方便碰撞檢測(cè),不讓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指針保存了一個(gè)Sprite對(duì)象,分別提供四個(gè)函數(shù)指揮這個(gè)這個(gè)Sprite對(duì)象向上向下向右移動(dòng)和旋轉(zhuǎn),在每次移動(dòng)和旋轉(zhuǎn)之前,都需要進(jìn)行碰撞檢測(cè),函數(shù)collide提供碰撞檢測(cè)的功能。如果Sprite對(duì)象下降到底部之后,就應(yīng)該調(diào)用combine函數(shù)將這個(gè)Sprite對(duì)象的tiles數(shù)組的數(shù)據(jù)加入到GameBoard的tiles數(shù)組中,并創(chuàng)建另外一個(gè)Sprite對(duì)象。而clear函數(shù)的作用,主要是在一局游戲Game Over后開(kāi)始下一局時(shí),清空GameBoard。

有了這幾個(gè)類,游戲線程看起來(lái)就是這樣的了:
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在窗口上畫(huà)圖了。最終游戲效果如下圖:
RussiaBlock.jpg

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

Feedback

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

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

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

3.線程同步的問(wèn)題

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

2007-12-16 22:47 by 夢(mèng)在天涯
強(qiáng),看到這里越來(lái)越多的高手,超高興,希望以后多交流奧!

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

2007-12-17 01:14 by Fox
看看最近有沒(méi)有時(shí)間,也用MFC實(shí)現(xiàn)過(guò)來(lái)

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

2007-12-17 09:24 by 絕對(duì)零度
在symbian上寫(xiě)過(guò),我也是用位來(lái)存儲(chǔ)的。

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

你說(shuō)得不錯(cuò),游戲中確實(shí)需要考慮線程同步問(wèn)題。首先是多個(gè)線程訪問(wèn)同一個(gè)變量的時(shí)候,最好能加入競(jìng)爭(zhēng)機(jī)制。在我這個(gè)游戲中,就沒(méi)有對(duì)m_GameActionQue的訪問(wèn)進(jìn)行控制,不過(guò)兩個(gè)線程在尾部寫(xiě),一個(gè)線程在頭部讀,倒沒(méi)有出現(xiàn)問(wèn)題。

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

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

2007-12-17 13:49 by 秦歌
強(qiáng)人!

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

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

這樣修改之后,游戲?qū)︽I盤(pán)的響應(yīng)就基本上沒(méi)有延時(shí)了,而且占用CPU要下降不少。

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

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

# re: 寫(xiě)個(gè)小游戲練一練手  回復(fù)  更多評(píng)論   

2009-04-23 19:13 by 向往
不錯(cuò),不錯(cuò),看起來(lái)有些老練.繼續(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>
            99re热这里只有精品免费视频| 影视先锋久久| 亚洲午夜av| 久久精品人人做人人爽| 国产私拍一区| 狂野欧美激情性xxxx| 亚洲国产天堂久久综合网| 一本一本大道香蕉久在线精品| 欧美日韩一区在线| 西瓜成人精品人成网站| 欧美不卡视频| 制服丝袜亚洲播放| 国产伦理精品不卡| 欧美va亚洲va香蕉在线| 在线亚洲免费| 免费成人在线视频网站| 亚洲无亚洲人成网站77777| 国产农村妇女毛片精品久久麻豆 | 在线亚洲自拍| 久久三级视频| 一本久道久久综合狠狠爱| 国产欧亚日韩视频| 欧美高清日韩| 欧美中文字幕在线观看| 欧美激情影院| 欧美中文在线观看国产| 99精品久久久| 国产一区二三区| 欧美日韩国产美女| 久久国产免费看| 一本色道久久综合精品竹菊| 久久夜精品va视频免费观看| 亚洲午夜成aⅴ人片| 在线观看欧美一区| 国产欧美日韩伦理| 欧美区一区二| 久久女同互慰一区二区三区| 正在播放欧美视频| 亚洲欧洲精品一区二区三区| 久久精品国产欧美激情| 99亚洲一区二区| 伊人春色精品| 国产女精品视频网站免费| 欧美日韩你懂的| 免费黄网站欧美| 久久九九免费| 欧美一区影院| 性色一区二区| 亚洲欧美日韩精品久久久| 亚洲精品国产精品国自产在线| 另类欧美日韩国产在线| 久久国产一二区| 香蕉尹人综合在线观看| 亚洲在线视频| 亚洲一区二区久久| 一区二区三区日韩欧美| 亚洲日本欧美天堂| 亚洲国产小视频| 亚洲国产aⅴ天堂久久| 伊人狠狠色j香婷婷综合| 国产亚洲女人久久久久毛片| 国产精品久久久久久超碰| 久久久综合网站| 亚洲主播在线观看| 亚洲精选大片| 亚洲国产精品一区制服丝袜| 久久免费精品视频| 亚洲欧美日韩视频一区| 在线视频亚洲欧美| 99re66热这里只有精品4| 狠狠色综合色区| 国产亚洲一区二区三区在线观看| 国产精品高潮呻吟| 欧美三级精品| 欧美日韩午夜在线| 另类av一区二区| 欧美成人免费小视频| 久久一区欧美| 久久久久看片| 久久久久久久网| 久久久久久久久久久久久女国产乱| 亚洲你懂的在线视频| 亚洲视频导航| 亚洲男同1069视频| 欧美一级一区| 欧美在现视频| 久久精品国亚洲| 久久精品国产第一区二区三区| 欧美在线看片a免费观看| 香蕉乱码成人久久天堂爱免费| 亚洲在线观看| 亚洲调教视频在线观看| 欧美一区二区成人6969| 欧美在线电影| 久久久久久亚洲综合影院红桃 | 国产亚洲综合精品| 国产模特精品视频久久久久| 国产精品视频精品| 国产日韩视频一区二区三区| 国内成人精品视频| 国内精品久久久久久影视8 | 日韩亚洲欧美成人| 亚洲欧洲99久久| 久久精品国产成人| 美女视频黄 久久| 欧美精品一区在线播放| 国产精品久久波多野结衣| 国产精品一区二区三区久久久| 国产午夜精品美女视频明星a级| 国产麻豆日韩欧美久久| 亚洲欧洲一二三| 亚洲午夜国产成人av电影男同| 午夜精品电影| 久久久亚洲人| 最新亚洲一区| 亚洲图片在线观看| 久久久久**毛片大全| 久久婷婷亚洲| 国产精品毛片高清在线完整版| 国产在线不卡| 日韩香蕉视频| 久久成人18免费网站| 亚洲第一天堂av| 亚洲综合视频一区| 久久综合一区二区三区| 欧美日韩激情小视频| 国产精品亚洲精品| 在线观看视频一区二区| 亚洲午夜在线观看视频在线| 久久久欧美精品sm网站| 亚洲精品欧洲| 久久成人18免费网站| 欧美日韩精品久久久| 国产欧美日韩视频| 亚洲一二三级电影| 欧美va天堂在线| 亚洲专区在线视频| 欧美ed2k| 黑人操亚洲美女惩罚| 一区二区冒白浆视频| 久久福利一区| 亚洲免费在线电影| 欧美日韩国产在线观看| 一区二区三区亚洲| 欧美在线不卡视频| 99re这里只有精品6| 久久综合五月| 韩国三级电影一区二区| 亚洲专区国产精品| 亚洲欧洲日本在线| 久久久噜噜噜久久人人看| 国产精品久久久久久久久久久久 | 欧美一区二区三区在线看| 亚洲人成久久| 久久精品免费| 红桃视频欧美| 久久国产精品亚洲va麻豆| 一区二区三区免费网站| 欧美福利小视频| 亚洲第一精品夜夜躁人人爽| 久久精品国产综合| 亚洲一区二区伦理| 国产情侣久久| 亚洲欧美制服另类日韩| 一区二区福利| 欧美色道久久88综合亚洲精品| 亚洲全部视频| 欧美激情亚洲一区| 久久综合精品国产一区二区三区| 国产在线不卡视频| 久久国产成人| 欧美一区二区三区啪啪| 国产日韩欧美另类| 久久爱另类一区二区小说| 亚洲图片你懂的| 欧美日韩中文在线观看| 亚洲一级影院| 一区二区三区欧美日韩| 国产精品二区在线| 欧美在线播放视频| 欧美一区1区三区3区公司| 欧美手机在线| 久久激情中文| 久久久高清一区二区三区| 在线观看欧美一区| 久久久在线视频| 久久免费视频在线观看| 亚洲国产清纯| 亚洲三级观看| 国产精品免费在线| 久久精品亚洲精品| 久久人人九九| 亚洲精品久久久久久久久| 亚洲国产精品第一区二区| 欧美体内she精视频| 欧美一区二区三区另类 | 国产精品丝袜xxxxxxx| 欧美在线观看视频一区二区三区 | 久久久久综合| 美国十次成人|