使用C++一步步實(shí)現(xiàn)俄羅斯方塊后續(xù)
一、實(shí)驗(yàn)簡(jiǎn)介
1.1 實(shí)驗(yàn)內(nèi)容
本節(jié)實(shí)驗(yàn)我們將實(shí)現(xiàn)俄羅斯方塊主要函數(shù)的設(shè)計(jì),完成基本功能并運(yùn)行。
1.2 實(shí)驗(yàn)知識(shí)點(diǎn)
窗口的繪制
方塊類的設(shè)計(jì)
旋轉(zhuǎn)算法
移動(dòng)、消除函數(shù)
1.3 實(shí)驗(yàn)環(huán)境
xface 終端
g++ 編譯器
ncurses 庫(kù)
1.4 編譯程序
編譯命令要加上 -l 選項(xiàng)引入 ncurses 庫(kù):
g++ main.c -l ncurses
1.5 運(yùn)行程序
./a.out
1.6 運(yùn)行結(jié)果
二、實(shí)驗(yàn)步驟
2.1 頭文件
首先包含頭文件以及定義一個(gè)交換函數(shù)和隨機(jī)數(shù)函數(shù),后面用到(交換函數(shù)用來(lái)做方塊的旋轉(zhuǎn),隨機(jī)數(shù)用來(lái)設(shè)置方塊的形狀)
#include <iostream>
#include <sys/time.h>
#include <sys/types.h>
#include <stdlib.h>
#include <ncurses.h>
#include <unistd.h>
/* 交換a和b */
void swap(int &a, int &b){
 int t=a;
 a = b;
 b = t;
}
/* 得到一個(gè)(min,max)區(qū)間的隨機(jī)整數(shù)
int getrand(int min, int max)
{
 return(min+rand()%(max-min+1));
}
2.2 定義類
由于程序內(nèi)容相對(duì)簡(jiǎn)單,這里只定義了一個(gè) Piece 類
class Piece
 {
 public:
  int score;  //得分
  int shape;  //表示當(dāng)前方塊的形狀
  int next_shape;  //表示下一個(gè)方塊的形狀
  int head_x;  //當(dāng)前方塊首個(gè)box的位置,標(biāo)記位置
  int head_y;
  int size_h;  //當(dāng)前方塊的size
  int size_w;
  int next_size_h;  //下一個(gè)方塊的size
  int next_size_w;
  int box_shape[4][4]; //當(dāng)前方塊的shpe數(shù)組 4x4
  int next_box_shape[4][4];  //下一個(gè)方塊的shpe數(shù)組 4x4
  int box_map[30][45];  //用來(lái)標(biāo)記游戲框內(nèi)的每個(gè)box
  bool game_over;  //游戲結(jié)束的標(biāo)志
 public:
  void initial();  //初始化函數(shù)
  void set_shape(int &cshape, int box_shape[][4],int &size_w, int & size_h);  //設(shè)置方塊形狀
  void score_next();  //顯示下一個(gè)方塊的形狀以及分?jǐn)?shù)
  void judge();  //判斷是否層滿
  void move(); //移動(dòng)函數(shù) 通過(guò) ← → ↓ 控制
  void rotate(); //旋轉(zhuǎn)函數(shù)
  bool isaggin(); //判斷下一次行動(dòng)是否會(huì)越界或者重合
  bool exsqr(int row); //判斷當(dāng)前行是否空
 };
2.3 設(shè)置方塊形狀
這里通過(guò) case 語(yǔ)句定義了7種方塊的形狀,在每次下一個(gè)方塊掉落之前都要調(diào)用以設(shè)置好它的形狀以及初始位置
void Piece::set_shape(int &cshape, int shape[][4],int &size_w,int &size_h)
{
 /*首先將用來(lái)表示的4x4數(shù)組初始化為0*/
 int i,j;
 for(i=0;i<4;i++)
  for(j=0;j<4;j++)
   shape[i][j]=0;
 /*設(shè)置7種初始形狀并設(shè)置它們的size*/
 switch(cshape)
 {
  case 0: 
   size_h=1;
   size_w=4; 
   shape[0][0]=1;
   shape[0][1]=1;
   shape[0][2]=1;
   shape[0][3]=1;
   break;
  case 1:
   size_h=2;
   size_w=3;
   shape[0][0]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 2:
   size_h=2;
   size_w=3; 
   shape[0][2]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 3:
   size_h=2;
   size_w=3;
   shape[0][1]=1;
   shape[0][2]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   break;
  case 4:
   size_h=2;
   size_w=3;
   shape[0][0]=1;
   shape[0][1]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 5: 
   size_h=2;
   size_w=2;
   shape[0][0]=1;
   shape[0][1]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   break;
  case 6: 
   size_h=2;
   size_w=3;
   shape[0][1]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
 }
 //設(shè)置完形狀以后初始化方塊的起始位置
 head_x=game_win_width/2;
 head_y=1;
 //如果剛初始化就重合了,游戲結(jié)束~
 if(isaggin()) /* GAME OVER ! */
  game_over=true;
}
2.4 旋轉(zhuǎn)函數(shù)
這里用了一個(gè)比較簡(jiǎn)單的算法對(duì)方塊進(jìn)行旋轉(zhuǎn),類似于矩陣的旋轉(zhuǎn),先將 shape 數(shù)組進(jìn)行斜對(duì)角線對(duì)稱化,再進(jìn)行左右對(duì)稱,便完成了旋轉(zhuǎn),需要注意的是要判斷旋轉(zhuǎn)后方塊是否出界或重合,如果是,則取消本次旋轉(zhuǎn)。
void Piece::rotate()
 {
  int temp[4][4]={0}; //臨時(shí)變量
  int temp_piece[4][4]={0}; //備份用的數(shù)組
  int i,j,tmp_size_h,tmp_size_w;
  tmp_size_w=size_w;
  tmp_size_h=size_h;
  for(int i=0; i<4;i++)
   for(int j=0;j<4;j++)
    temp_piece[i][j]=box_shape[i][j]; //備份一下當(dāng)前的方塊,如果旋轉(zhuǎn)失敗則返回到當(dāng)前的形狀
  for(i=0;i<4;i++)
   for(j=0;j<4;j++)
    temp[j][i]=box_shape[i][j]; //斜對(duì)角線對(duì)稱
  i=size_h;
  size_h=size_w;
  size_w=i;
  for(i=0;i<size_h;i++)
   for(j=0;j<size_w;j++)
    box_shape[i][size_w-1-j]=temp[i][j]; //左右對(duì)稱
  /*如果旋轉(zhuǎn)以后重合,則返回到備份的數(shù)組形狀*/
  if(isaggin()){
   for(int i=0; i<4;i++)
    for(int j=0;j<4;j++)
     box_shape[i][j]=temp_piece[i][j];
   size_w=tmp_size_w; //記得size也要變回原來(lái)的size
   size_h=tmp_size_h;
  }
  /*如果旋轉(zhuǎn)成功,那么在屏幕上進(jìn)行顯示*/
  else{
   for(int i=0; i<4;i++)
    for(int j=0;j<4;j++){
     if(temp_piece[i][j]==1){
      mvwaddch(game_win,head_y+i,head_x+j,' '); //移動(dòng)到game_win窗口的某個(gè)坐標(biāo)處打印字符
      wrefresh(game_win);
     }
    }
   for(int i=0; i<size_h;i++)
    for(int j=0;j<size_w;j++){
     if(this->box_shape[i][j]==1){
      mvwaddch(game_win,head_y+i,head_x+j,'#');
      wrefresh(game_win);
     }
   }
  }
}
2.5 移動(dòng)函數(shù)
如果玩家沒(méi)有按下任何按鍵,方塊需要慢速下落,所以我們不能夠因?yàn)榈却存I輸入而阻塞在 getch() ,這里用到了 select() 來(lái)取消阻塞。
/* 這里只是截取了程序的一部分,具體實(shí)現(xiàn)請(qǐng)參考源碼 */ struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec= 500000; if (select(1, &set, NULL, NULL, &timeout) == 0)
timeout 就是我們最多等待按鍵的時(shí)間,這里設(shè)置了 500000us,超過(guò)這個(gè)時(shí)間就不再等待 getch() 的輸入,直接進(jìn)行下一步。
如果在 timeout 時(shí)間內(nèi)檢測(cè)到按鍵,則下面的 if 語(yǔ)句為真,得到輸入的 key 值,通過(guò)判斷不同的 key 值進(jìn)行向左、右、下、旋轉(zhuǎn)等操作。
if (FD_ISSET(0, &set)) 
    while ((key = getch()) == -1) ;
向左、右、下移動(dòng)的函數(shù)處理方式基本相同,這里只拿向下移動(dòng)的函數(shù)進(jìn)行說(shuō)明
/* 這里只是截取了程序的一部分,具體實(shí)現(xiàn)請(qǐng)參考源碼 */
/* 如果輸入的按鍵是 ↓ */
if(key==KEY_DOWN){
  head_y++; //方塊的y坐標(biāo)+1
  if(isaggin()){ //如果重合或出界,則取消這次移動(dòng)
   head_y--;
   /*既然停下來(lái)了,那么把地圖上對(duì)應(yīng)的box設(shè)置為已被占用,用1表示,0表示未被占用
   for(int i=0;i<size_h;i++)
    for(int j=0;j<size_w;j++)
     if(box_shape[i][j]==1)
      box_map[head_y+i][head_x+j]=1;
   score_next(); //顯示分?jǐn)?shù)以及提示下一個(gè)方塊
  }
  /*如果能夠向下移動(dòng),那么取消當(dāng)前方塊的顯示,向下移動(dòng)一行進(jìn)行顯示,這里注意for循環(huán)的行要從下往上
  else{
   for(int i=size_h-1; i>=0;i--)
    for(int j=0;j<size_w;j++){
     if(this->box_shape[i][j]==1){
      mvwaddch(game_win,head_y-1+i,head_x+j,' ');
      mvwaddch(game_win,head_y+i,head_x+j,'#');
     }
    }
   wrefresh(game_win);
}
2.6 重復(fù)函數(shù)
每次移動(dòng)或旋轉(zhuǎn)之后要進(jìn)行判斷的函數(shù),函數(shù)返回真則不能行動(dòng),返回假則可以進(jìn)行下一步。
bool Piece::isaggin(){
 for(int i=0;i<size_h;i++)
  for(int j=0;j<size_w;j++){
   if(box_shape[i][j]==1){
    if(head_y+i > game_win_height-2) //下面出界
     return true;
    if(head_x+j > game_win_width-2 || head_x+i-1<0) //左右出界
     return true;
    if(box_map[head_y+i][head_x+j]==1) //與已占用的box重合
     return true ;
   }
  }
 return false;
}
2.7 層滿函數(shù)
最后一個(gè)很重要的功能是對(duì)方塊已滿的行進(jìn)行消除,每當(dāng)一個(gè)方塊向下移動(dòng)停止后都需要進(jìn)行判斷。
void Piece::judge(){
 int i,j;
 int line=0; //用來(lái)記錄層滿的行數(shù)
 bool full;
 for(i=1;i<game_win_height-1;i++){ //除去邊界
  full=true;
  for(j=1;j<game_win_width-1;j++){
   if(box_map[i][j]==0) //存在未被占用的box
    full=false; //說(shuō)明本層未滿
  }
  if(full){ //如果該層滿
   line++; //行滿+1
   score+=50; //加分~
   for(j=1;j<game_win_width-1;j++)
    box_map[i][j]=0; //把該層清空(標(biāo)記為未被占用)
  }
 }
 /*上面判斷完后 看line的值,如果非 0 說(shuō)明有層已滿需要進(jìn)行消除*/
 if(line!=0){
 for(i=game_win_height-2;i>=2;i--){
  int s=i;
  if(exsqr(i)==0){
   while(s>1 && exsqr(--s)==0); //查找存在方塊的行,將其下移
   for(j=1;j<game_win_width-1;j++){
    box_map[i][j]=box_map[s][j]; //上層下移
    box_map[s][j]=0; //上層清空
   }
  }
 }
 /*清空和移動(dòng)標(biāo)記完成以后就要屏幕刷新了,重新打印game_win*/
 for(int i=1;i<game_win_height-1;i++)
   for(int j=1;j<game_win_width-1;j++){
    if(box_map[i][j]==1){
     mvwaddch(game_win,i,j,'#');
     wrefresh(game_win);
    }
    else{
     mvwaddch(game_win,i,j,' ');
     wrefresh(game_win);
    }
   }
 }
}
三、實(shí)驗(yàn)總結(jié)
到這里幾個(gè)關(guān)鍵函數(shù)的介紹也就完成了,搞明白這些函數(shù)的功能并實(shí)現(xiàn),再參考源碼補(bǔ)全其他函數(shù)以及main函數(shù)就可以運(yùn)行啦!當(dāng)然俄羅斯方塊的實(shí)現(xiàn)方法還有很多,每個(gè)人的思路和方法可能會(huì)不一樣,或許你寫(xiě)出來(lái)的俄羅斯方塊更簡(jiǎn)潔、更流暢! Enjoy it !:)
上一篇:C語(yǔ)言結(jié)構(gòu)體定義的方法匯總
欄 目:C語(yǔ)言
下一篇:C++二叉樹(shù)實(shí)現(xiàn)詞頻分析功能
本文標(biāo)題:使用C++一步步實(shí)現(xiàn)俄羅斯方塊后續(xù)
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/1010.html
您可能感興趣的文章
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
 - 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
 - 01-10c語(yǔ)言 跳臺(tái)階問(wèn)題的解決方法
 - 01-10如何判斷一個(gè)數(shù)是否為2的冪次方?若是,并判斷出來(lái)是多少次方
 - 01-10使用OpenGL實(shí)現(xiàn)3D立體顯示的程序代碼
 - 01-10深入理解C++中常見(jiàn)的關(guān)鍵字含義
 - 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
 - 01-10如何判斷一個(gè)數(shù)是否為4的冪次方?若是,并判斷出來(lái)是多少次方
 - 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
 - 01-10c++中inline的用法分析
 


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
 - 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
 - 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
 - 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
 - 5c語(yǔ)言計(jì)算三角形面積代碼
 - 6什么是 WSH(腳本宿主)的詳細(xì)解釋
 - 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
 - 8正則表達(dá)式匹配各種特殊字符
 - 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
 - 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
 
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
 - 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
 - 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
 - 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
 - 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
 - 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
 - 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
 - 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
 - 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
 - 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
 
隨機(jī)閱讀
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
 - 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
 - 01-10delphi制作wav文件的方法
 - 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
 - 01-10C#中split用法實(shí)例總結(jié)
 - 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
 - 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
 - 04-02jquery與jsp,用jquery
 - 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
 - 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
 


