1 系統設計要求算法
1.1 需求分析編程
本系統爲一個用C#實現的爲咱們所熟悉的簡單的俄羅斯方塊遊戲,該系統的具體功能以下:數組
1). 能簡便的開始遊戲,遊戲中的方塊的功能與平常咱們所熟悉的遊戲的功能一致,各類塊的設置也一致,包括塊的旋轉,加速降低,平移,滿行消去,到頂遊戲結束功能;數據結構
2). 可以自定義遊戲中功能鍵的具體按鍵,顯示下一方塊提示信息,以及遊戲數據的統計;框架
3). 考慮須要解決的問題:怎麼樣設置圖形顯示;怎樣獲取鍵盤輸入;怎樣控制方塊的移動;怎樣控制時間間隔(用於遊戲中控制形狀的下落);遊戲中的各類形狀及整個遊戲空間怎麼用數據表示;遊戲中怎麼判斷左右及向下移動的可能性;遊戲中怎麼判斷某一形狀旋轉的可能性;按向下方向鍵時加速某一形狀下落速度的處理;怎麼判斷某一形狀已經到底;怎麼判斷某一已經被填滿;怎麼消去已經被填滿的一行;怎麼消去某一形狀落到底後可以消去的全部的行;(如長條最多能夠消去四行)怎樣判斷遊戲結束,關於「下一個」形狀取法的問題。模塊化
2 設計思路函數
2.1 用面向對象的方法分析系統學習
從遊戲的基本玩法出發,主要就是俄羅斯方塊的形狀和旋轉,在設計中在一個圖片框中構造了一個20*20(像素)的小塊,由這些小塊組合成新的形狀,每四個小塊鏈接在一塊兒就能夠構造出一種造型,總共設計了7中造型,每種造型又能夠經過旋轉而變化出2到4種形狀,在遊戲窗體中用戶就可使用鍵盤的方向鍵來控制方塊的運動,而後對每一行進行判斷,若是有某行的方塊是滿的,則消除這行的方塊,而且使上面的方塊自由下落,其中,方塊向下的速度是有時鐘控件控制的。俄羅斯方塊遊戲設計主要包括如下10個方面:this
1). 遊戲界面的設計。spa
2). 俄羅斯方塊的實現。
3). 鍵盤輸入信息的獲取。
4). 俄羅斯方塊的移動(向左,向右和向下)。
5). 俄羅斯方塊的變換。
6). 方塊自動下落與速度的選擇。
7). 慢行的判斷與消行。
8). 遊戲結束判斷。
9). 用戶配置保存。
在主窗口中,經過調用俄羅斯方塊類來實現程序的表示層,在該窗口中經過兩個Panel控件來實現方塊疊放窗口和下一方塊信息窗口;調用設置窗口,保存設計窗口類傳回的信息,並設置到遊戲中去,保存在配置文件中;
在設置窗口中,以良好的界面提供用戶自定義快捷鍵的接口,保存相應設置參數,以提供給調用窗口。
2.2運用的控件和主要對象
在設計過程當中主要用到的控件有:PictureBox控件,MenuStrip控件,Button控件,Label控件,Timer控件,winmm組件,DirectSound等等。
用來顯示狀態信息的框
privateSystem.Windows.Forms.GroupBox statusBox;
開始按鈕
privateSystem.Windows.Forms.Button btnStart;
顯示「下一塊」的標籤
privateSystem.Windows.Forms.Label label3;
顯示「分數」的標籤
privateSystem.Windows.Forms.Label label2;
顯示「等級」的標籤
privateSystem.Windows.Forms.Label label1;
用來畫下一塊方塊的區域
privateSystem.Windows.Forms.PictureBox panel1;
遊戲區域
privateSystem.Windows.Forms.PictureBox gameArea;
實現以下主界面效果圖(圖3-1):
1.1 方塊的實現
在程序中每個方塊都是一個Block類的實例。Block包括的參數有方塊的寬度,高度,最左端橫座標,最上端縱座標,方塊的數組表示。其中一共有7中形狀的方塊,以數組表示爲:
11 11 1 11 010 10 01
01 10 1 11 111 11 11
01 10 1 01 10
1
方塊的7種形狀分別以數字0-6來表明,在構造函數中,隨機生成0-6中數字,以此來隨機生成方塊的形狀。用來在界面上顯示方塊的貼圖也以0-6的數字來表明,一樣以隨機數的形式來隨機的現實方塊的顏色。
1.2 鍵盤輸入事件處理
由於在界面上有一個按鈕,而且只有一個按鈕,因此該按鈕在一般狀況下都是默認爲焦點。在這種狀況下按下某些鍵,好比空格,就會產生出發按鈕事件的狀況。所以必須重載整個WinForm的ProcessCmdKey來避免這樣的問題。
當按向左,向右及旋轉按鈕時,只要相應的處理方塊的位置或者形狀便可,可是當按向下或者當即下落時,須要不一樣的處理。向下移動時,若是移動到最底部但還未固定,則須要從新設置計時器間隔時間,從而使自動下落時,底部未固定的方塊到固定的時間相同。若是方塊在最底部而未固定的時候,向下移動,則當即固定。這兩種狀況,當方塊固定後,都須要判斷是否消行,當即下落時,須要判斷是否消行。
1.3 方塊的移動
遊戲中方塊的移動分爲向左移動,向右移動,向下移動和當即落下。
向左移動:
public void MoveLeft() {
int xPos =runBlock.XPos-1;
intyPos = runBlock.YPos;
for(int i = 0; i < runBlock.Length; i++)
{
if(xPos + runBlock[i].X <0)//若是超出左邊界則失敗
{
return;
}
if(!coorArr[xPos + runBlock[i].X, yPos - runBlock[i].Y].IsEmpty)//若是左邊有東西擋則失敗
{
return;
}
}
runBlock.erase(gpPaltte);//擦除原來位置的轉塊
runBlock.XPos--;
runBlock.Paint(gpPaltte);//在新位置上畫轉塊
}
向右移動:
public void MoveRight()
{
intxPos = runBlock.XPos + 1;
intyPos = runBlock.YPos;
for(int i = 0; i < runBlock.Length; i++)
{
if(xPos + runBlock[i].X >_width-1)//若是超出右邊界則失敗
{
return;
}
if(!coorArr[xPos + runBlock[i].X, yPos - runBlock[i].Y].IsEmpty)//若是右邊有東西擋則失敗
{
return;
}
}
runBlock.erase(gpPaltte);//擦除原來位置的轉塊
runBlock.XPos++;
runBlock.Paint(gpPaltte);//在新位置上畫轉塊
}
向下移動:
public void Drop() {
timerBlock.Stop();
while(Down()) ;
timerBlock.Start();
}
1.4 方塊的變換
public voidDeasilRotate() //順時針旋轉
{
for (int i = 0; i< runBlock.Length;i++ )
{
int x = runBlock.XPos + runBlock[i].Y;
int y = runBlock.YPos + runBlock[i].X;
if (x < 0 || x > _width - 1)//若是超出左右邊界,則失敗
return;
if (y < 0 || y > _height - 1)//若是超出上下邊界,則失敗
return;
if (!coorArr[x, y].IsEmpty)//若是旋轉後的位置上有轉塊,則失敗
return;
}
runBlock.erase(gpPaltte);//擦除原來位置的轉塊
runBlock.DeasilRotate();
runBlock.Paint(gpPaltte);//在新位置上畫轉塊
}
public voidContraRotate() //逆時針旋轉
{
for (int i = 0; i< runBlock.Length; i++)
{
int x = runBlock.XPos - runBlock[i].Y;
int y = runBlock.YPos - runBlock[i].X;
if (x < 0 || x > _width - 1)//若是超出左右邊界,則失敗
return;
if (y < 0|| y > _height - 1)//若是超出上下邊界,則失敗
return;
if (!coorArr[x, y].IsEmpty)//若是旋轉後的位置上有轉塊,則失敗
return;
}
runBlock.erase(gpPaltte);//擦除原來位置的轉塊
runBlock.ContraRotate();
runBlock.Paint(gpPaltte);//在新位置上畫轉塊
}
1.5 判斷方塊是否到底
public voidCheckAndOverBlock()//檢查轉塊是否到底,若是到底則把當前轉塊納入coorArr,併產生新的轉塊
{
boolover = false;//設置一個當前運行轉塊是否到底的標誌
for(int i = 0; i < runBlock.Length;i++ )//遍歷當前運行轉塊的全部小方塊
{
intx = runBlock.XPos + runBlock[i].X;
inty = runBlock.YPos - runBlock[i].Y;
if(y == _height - 1)//若是到達下邊界,則結束當前轉塊
{
over = true;
break;
}
if(!coorArr[x, y+1].IsEmpty) //若是下面有轉塊,則當前轉塊結束
{
over = true;
break;
}
}
if(over)
{
for(int i = 0; i < runBlock.Length; i++)//把當前轉塊納入coordinateArr
{
coorArr[runBlock.XPos +runBlock[i].X, runBlock.YPos - runBlock[i].Y] = runBlock.BlockColor;
}
//檢查是否有滿行狀況,若是有則刪除
CheckAndDelFullRow();
//產生新轉塊
runBlock = readyBlock;//新的轉塊爲準備好的轉塊
runBlock.XPos = _width / 2;//肯定當前運行轉塊的出生位置
int y = 0;//肯定轉塊Ypos,肯定剛出生的轉塊頂上沒空行
for (int i = 0; i < runBlock.Length; i++)
{
if (runBlock[i].Y > y)
y = runBlock[i].Y;
}
runBlock.YPos = y;
//檢查新生成的轉塊所佔用的地方是否已經有轉塊存在,若是有,遊戲結束
for (int i = 0; i <runBlock.Length; i++)
{
if (!coorArr[runBlock.XPos+ runBlock[i].X, runBlock.YPos - runBlock[i].Y].IsEmpty)
{
StringFormat drawFormat= new StringFormat();
drawFormat.Alignment =StringAlignment.Center;
gpPaltte.DrawString("GAME OVER",
newFont("Arial Black", 25f),
newSolidBrush(Color.White),
newRectangleF(0, _height * rectPix / 2 - 100, _width * rectPix, 100),
drawFormat);
timerBlock.Stop();//關閉定時器
return;
}
}
runBlock.Paint(gpPaltte);
//獲取新的準備轉塊
readyBlock = bGroup.GetABlock();
readyBlock.XPos = 2;
readyBlock.YPos = 2;
gpReady.Clear(Color.Black);
readyBlock.Paint(gpReady);
}
}
1.6 滿行判斷並消行
privatevoid CheckAndDelFullRow() //檢查並刪除滿行
{
//找出當前轉塊所在行的範圍
int lowRow = runBlock.YPos - runBlock[0].Y;//lowRow表明當前轉塊的y軸的最小值
int highRow = lowRow;//highRow表明當前轉塊y軸的最大值
for (int i = 0; i < runBlock.Length; i++)//找出當前轉塊所佔行的範圍,放入low和high變量內
{
int y = runBlock.YPos - runBlock[i].Y;
if (y < lowRow)
lowRow = y;
if (y > highRow)
highRow = y;
}
bool repaint = false;//判斷是否重畫標誌
for (int i = lowRow; i <= highRow; i++) //檢查是否滿行,若是有,則刪除
{
bool rowFull = true;
for (int j = 0; j < _width;j++ )
{
if (coorArr[j,i].IsEmpty)//若是有一行爲空,則說明這行不滿
{
rowFull = false;
break;
}
}
if (rowFull) //若是是滿行,則刪除這一行
{
repaint = true;//若是有要刪除的行,則須要重畫
for (int k = i; k > 0;k--) {//把第n行的值用n-1行的值來代替
for (int j = 0; j <_width; j++) {
coorArr[j, k] =coorArr[j, k - 1];
}
}
for (int j = 0; j <_width;j++ )//清空第0行
{
coorArr[j, 0] = Color.Empty;
}
}
}
if(repaint)//重畫
{
PaintBackground(gpPaltte);
}
}
3.8 產生下一方塊
public bool GeneBlock(int shapeNO, Point firstPos, Colorcolor)//產生下一方塊
{
this.SetLastPos();
this.EraseLast();
this.SetPos(shapeNO,firstPos);
if(!this.CanRotate(this.pos))
{
this.pos=null;
returnfalse;
}
else
{
this.color=color;
returntrue;
}
}
3.9 遊戲設置
程序中游戲設置的保存方式爲配置文件,配置文件中保存着遊戲的按鍵設置,在打開程序時,會載入配置文件中的配置。用戶能夠在遊戲中隨時改變配置,改變後的配置將保存到配置文件中而且當即有效。遊戲配置界面以下(圖3-2):
保存配置
private void SaveSetting()
{
try
{
XmlDocumentdoc = new XmlDocument();
XmlDeclarationxmlDec=doc.CreateXmlDeclaration ("1.0","gb2312",null);
XmlElementsetting=doc.CreateElement("SETTING");
doc.AppendChild(setting);
XmlElementlevel=doc.CreateElement("LEVEL");
level.InnerText=this.startLevel.ToString();
setting.AppendChild(level);
XmlElementtrans=doc.CreateElement("TRANSPARENT");
trans.InnerText=this.trans.ToString();
setting.AppendChild(trans);
XmlElementkeys=doc.CreateElement("KEYS");
setting.AppendChild(keys);
foreach(Keysk in this.keys)
{
KeysConverterkc=new KeysConverter();
XmlElementx=doc.CreateElement("SUBKEYS");
x.InnerText=kc.ConvertToString(k);
keys.AppendChild(x);
}
XmlElementroot=doc.DocumentElement;
doc.InsertBefore(xmlDec,root);
doc.Save("c:\\setting.cob");
}
catch(Exceptionxe)
{
MessageBox.Show(xe.Message);
}
}
4 總結
本設計經過Vusial Studio 方便的Windows表單設計界面,增長了相應的按鈕單擊響應函數,經過與用戶的交互,反饋回用戶所須要的信息。
設計出的程序符合設計需求,既有基本的遊戲邏輯功能,又能保存用戶的設置,取得較好的實驗結果。
這個學期「C#程序設計」課程讓我接觸了面向對象的程序設計,Visual stdio的可視化編程環境讓咱們能夠製做界面友好的Windows環境,利用IDE能夠快捷地開發出所要的可視化的環境。C#是是一種徹底面向對象的語言,使用對象的思想來編程,既能夠對相應的數據進行保護,也能夠相應的與其餘的類共享,有利於程序的結構化,方面程序的編寫。Viusal Studio下咱們能夠快速的進行開發。可是,也要看到其對WindowsApi函數的封裝也致使了咱們在學習的時候對Windows程序的運行機制不瞭解,致使了學習時候的迷惑。本學期配套的書籍<< C#實用教程>>雖然簡單明瞭,可是對於機制原理的解釋和說明過少,所以,學習的時候應該不僅知足於這本書中的內容,應該多找一些書籍進行知識的擴展了加深。
開發一個工程時,應該先制定好程序的框架,規劃好相應的功能模塊,使程序模塊化,易於往後的擴展和完善。其次是程序的數據結構,良好的數據結構能使程序高效化,功能強大。本次實現中最重要的是方塊類的編寫,其定義的好壞和封裝性的良好是整個程序運行的基礎,屬於程序的業務邏輯功能塊,主框架中經過調用該類,實現程序的表示層。再之,優秀的算法能提升程序的效率。