首先,讓我們來構思一下,俄羅斯方塊顯示的部分,該用什麼來實作呢? 我採取了最簡單的Label(標籤)來完成,可是需要用總共200個Label,什麼?200個?你沒看錯,因為我們的俄羅斯方塊總共有20(列) X 10(行)個格子,你可以直接使用Visual Studio內的設計工具,拉出200個Label(那好累喔!),也可以使用程式動態產生(嗯,這個比較好!),然後再用程式把他們在視窗(Form)上面排好,OK! 這樣顯示的部分就完成了,就像這樣:
Label[,] grids = new Label[20,10]; //main area, total 200 grids
for (int i = 0; i < 20; i++)
for (int j = 0; j < 10; j++)
{
grids[i, j] = new Label();
grids[i, j].Width = 30;
grids[i, j].Height = 30;
grids[i, j].BorderStyle = BorderStyle.FixedSingle;
grids[i, j].BackColor = Color.Black;
grids[i, j].Left = 150 + 30 * j;
grids[i, j].Top = 600 - i * 30;
grids[i, j].Visible = true;
this.Controls.Add(grids[i, j]);
}
Label[,] grids = new Label[20,10]; //main area, total 200 grids
for (int i = 0; i < 20; i++)
for (int j = 0; j < 10; j++)
{
grids[i, j] = new Label();
grids[i, j].Width = 30;
grids[i, j].Height = 30;
grids[i, j].BorderStyle = BorderStyle.FixedSingle;
grids[i, j].BackColor = Color.Black;
grids[i, j].Left = 150 + 30 * j;
grids[i, j].Top = 600 - i * 30;
grids[i, j].Visible = true;
this.Controls.Add(grids[i, j]);
}
好啦,接下來當然就是"俄羅斯方塊"本尊了,俄羅斯方塊總共有七種,
Type 1
Type 2
Type 3
Type 4
Type 5
Type 6
Type 7
當方塊出現在最上方時,就只有這七種形式,但是在翻轉之後,會產生其他形式(黑點為參考點,紅點為旋轉支點,沒有紅點者即以參考點為支點):
type 1 type 11
type 2
type 3 type 13
type 4 type 14
type 5 type 15 type 25 type 35
type 6 type 16 type 26 type 36
type 7 type 17 type 27 type 37
以上是我的旋轉規則和分類方式,你也可以改用你喜歡的方式,It's up to you.
分類完之後,就要開始寫程式了:我用了幾個全域變數紀錄遊戲進行的狀況,包括一個24x10的二維陣列(bool Array),紀錄那些格點被佔用了,還有現在正掉落的的方塊的型態和參考點位置,當然你還得啟動一個計時器,由計時器的tick來觸發方塊的掉落,當方塊觸底或被之前的方塊擋住時,就要馬上停止,並啟動下一個方塊。
這個函數根據方塊的型態,和位置,將它紀錄在格點中
void update_block(uint i, uint j, uint type)
{
switch (type)
{
case 1:
signs[i, j] = signs[i+1, j] = signs[i+2, j] = signs[i+3, j] = true;
grids_color[i, j] = grids_color[i + 1, j] = grids_color[i + 2, j] = grids_color[i + 3, j] = Color.Blue;
break;
case 11:
signs[i, j] = signs[i, j+1] = signs[i, j+2] = signs[i, j+3] = true;
grids_color[i, j] = grids_color[i, j + 1] = grids_color[i, j + 2] = grids_color[i, j + 3] = Color.Blue;
break;
case 2:
signs[i, j] = signs[i + 1, j] = signs[i , j+1] = signs[i + 1, j+1] = true;
grids_color[i, j] = grids_color[i + 1, j] = grids_color[i, j + 1] = grids_color[i + 1, j + 1] = Color.Yellow;
break;
case 3:
signs[i, j] = signs[i + 1, j] = signs[i+1, j-1] = signs[i, j + 1] = true;
grids_color[i, j] = grids_color[i + 1, j] = grids_color[i + 1, j - 1] = grids_color[i, j + 1] = Color.Red;
break;
case 13:
signs[i, j] = signs[i - 1, j] = signs[i , j + 1] = signs[i+1, j + 1] = true;
grids_color[i, j] = grids_color[i - 1, j] = grids_color[i, j + 1] = grids_color[i + 1, j + 1] = Color.Red;
break;
case 4:
signs[i, j] = signs[i , j-1] = signs[i + 1, j] = signs[i+1, j + 1] = true;
grids_color[i, j] = grids_color[i, j - 1] = grids_color[i + 1, j] = grids_color[i + 1, j + 1] = Color.Green;
break;
case 14:
signs[i, j] = signs[i+1, j] = signs[i, j+1] = signs[i-1, j + 1] = true;
grids_color[i, j] = grids_color[i + 1, j] = grids_color[i, j + 1] = grids_color[i - 1, j + 1] = Color.Green;
break;
case 5:
signs[i, j] = signs[i+1, j] = signs[i + 1, j+1] = signs[i + 1, j + 2] = true;
grids_color[i, j] = grids_color[i + 1, j] = grids_color[i + 1, j + 1] = grids_color[i + 1, j + 2] = Color.Orange;
break;
case 15:
signs[i, j] = signs[i, j-1] = signs[i + 1, j - 1] = signs[i + 2, j -1] = true;
grids_color[i, j] = grids_color[i, j - 1] = grids_color[i + 1, j - 1] = grids_color[i + 2, j - 1] = Color.Orange;
break;
case 25:
signs[i, j] = signs[i-1, j] = signs[i - 1, j - 1] = signs[i -1, j - 2] = true;
grids_color[i, j] = grids_color[i - 1, j] = grids_color[i - 1, j - 1] = grids_color[i - 1, j - 2] = Color.Orange;
break;
case 35:
signs[i, j] = signs[i, j+1] = signs[i - 1, j + 1] = signs[i - 2, j +1] = true;
grids_color[i, j] = grids_color[i, j + 1] = grids_color[i - 1, j + 1] = grids_color[i - 2, j + 1] = Color.Orange;
break;
case 6:
signs[i, j] = signs[i + 1, j] = signs[i + 1, j - 1] = signs[i + 1, j - 2] = true;
grids_color[i, j] = grids_color[i + 1, j] = grids_color[i + 1, j - 1] = grids_color[i + 1, j - 2] = Color.LightBlue;
break;
case 16:
signs[i, j] = signs[i, j+1] = signs[i + 1, j + 1] = signs[i + 2, j + 1] = true;
grids_color[i, j] = grids_color[i, j + 1] = grids_color[i + 1, j + 1] = grids_color[i + 2, j + 1] = Color.LightBlue;
break;
case 26:
signs[i, j] = signs[i-1, j] = signs[i-1, j + 1] = signs[i -1, j + 2] = true;
grids_color[i, j] = grids_color[i - 1, j] = grids_color[i - 1, j + 1] = grids_color[i - 1, j + 2] = Color.LightBlue;
break;
case 36:
signs[i, j] = signs[i, j-1] = signs[i - 1, j - 1] = signs[i - 2, j -1] = true;
grids_color[i, j] = grids_color[i, j - 1] = grids_color[i - 1, j - 1] = grids_color[i - 2, j - 1] = Color.LightBlue;
break;
case 7:
signs[i, j] = signs[i, j-1] = signs[i, j+1] = signs[i+1, j] = true;
grids_color[i, j] = grids_color[i, j - 1] = grids_color[i, j + 1] = grids_color[i + 1, j] = Color.Purple;
break;
case 17:
signs[i, j] = signs[i, j + 1] = signs[i-1, j] = signs[i + 1, j] = true;
grids_color[i, j] = grids_color[i, j + 1] = grids_color[i - 1, j] = grids_color[i + 1, j] = Color.Purple;
break;
case 27:
signs[i, j] = signs[i, j - 1] = signs[i, j+1] = signs[i - 1, j] = true;
grids_color[i, j] = grids_color[i, j - 1] = grids_color[i, j + 1] = grids_color[i - 1, j] = Color.Purple;
break;
case 37:
signs[i, j] = signs[i, j - 1] = signs[i+1, j] = signs[i - 1, j] = true;
grids_color[i, j] = grids_color[i, j - 1] = grids_color[i + 1, j] = grids_color[i - 1, j] = Color.Purple;
break;
}
}
這個函數偵測垂直方向是否已經落底或有障礙物:
bool y_direction(uint type, uint i, uint j)
{
switch (type)
{
case 1:
if (i != 0 && !signs[i-1, j]) return true;
else return false;
case 11:
if (i != 0 && !signs[i - 1, j] && !signs[i - 1, j + 1] && !signs[i - 1, j + 2] && !signs[i - 1, j + 3]) return true;
else return false;
case 2:
if (i != 0 && !signs[i-1, j] && !signs[i-1, j+1]) return true;
else return false;
case 3:
if (i != 0 && !signs[i, j-1] && !signs[i-1, j] && !signs[i-1, j+1]) return true;
else return false;
case 13:
if (i != 1 && !signs[i-2, j] && !signs[i-1, j+1]) return true;
else return false;
case 4:
if (i != 0 && !signs[i, j+1] && !signs[i-1, j] && !signs[i-1, j-1]) return true;
else return false;
case 14:
if (i != 1 && !signs[i-1, j] && !signs[i-2, j+1]) return true;
else return false;
case 5:
if (i != 0 && !signs[i-1, j] && !signs[i, j+1] && !signs[i, j+2]) return true;
else return false;
case 15:
if (i != 0 && !signs[i - 1, j] && !signs[i-1, j-1]) return true;
else return false;
case 25:
if (i != 1 && !signs[i - 2, j] && !signs[i - 2, j - 1] && !signs[i - 2, j - 2]) return true;
else return false;
case 35:
if (i != 2 && !signs[i - 1, j] && !signs[i - 3, j + 1]) return true;
else return false;
case 6:
if (i != 0 && !signs[i, j-1] && !signs[i, j-2] && !signs[i-1, j]) return true;
else return false;
case 16:
if (i != 0 && !signs[i-1, j] && !signs[i-1, j+1]) return true;
else return false;
case 26:
if (i != 1 && !signs[i-2, j] && !signs[i-2, j + 1] && !signs[i-2, j+2]) return true;
else return false;
case 36:
if (i != 2 && !signs[i-1, j] && !signs[i-3, j-1]) return true;
else return false;
case 7:
if (i != 0 && !signs[i-1, j-1] && !signs[i-1, j] && !signs[i-1, j+1]) return true;
else return false;
case 17:
if (i != 1 && !signs[i-2, j] && !signs[i - 1, j+1]) return true;
else return false;
case 27:
if (i != 1 && !signs[i - 1, j - 1] && !signs[i - 1, j + 1] && !signs[i - 2, j]) return true;
else return false;
case 37:
if (i != 1 && !signs[i-2, j] && !signs[i-1, j-1]) return true;
else return false;
default:
return false;
}
}
這個函數偵測水平方向是否有障礙物:
bool x_direction(uint type, uint i, uint j, int d)
{
switch(type)
{
case 1:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1] && !signs[i + 1, j - 1] && !signs[i + 2, j - 1] && !signs[i + 3, j - 1]) return true;
else return false;
}
else
{
if (j != 9 && !signs[i, j + 1] && !signs[i + 1, j + 1] && !signs[i + 2, j + 1] && !signs[i + 3, j + 1]) return true;
else return false;
}
case 11:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1]) return true;
else return false;
}
else
{
if (j != 6 && !signs[i, j + 4]) return true;
else return false;
}
case 2:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1] && !signs[i + 1, j - 1]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i + 1, j + 2]) return true;
else return false;
}
case 3:
if (d == -1)
{
if (j != 1 && !signs[i, j - 1] && !signs[i + 1, j - 2]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i + 1, j + 1]) return true;
else return false;
}
case 13:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1] && !signs[i + 1, j] && !signs[i + 1, j-1]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i + 1, j + 2] && !signs[i - 1, j + 1]) return true;
else return false;
}
case 4:
if (d == -1)
{
if (j != 1 && !signs[i, j - 2] && !signs[i + 1, j - 1]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 1] && !signs[i + 1, j + 2]) return true;
else return false;
}
case 14:
if (d == -1)
{
if (j != 0 && !signs[i, j-1] && !signs[i+1, j-1] && !signs[i-1, j]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i + 1, j + 1] && !signs[i-1, j + 2]) return true;
else return false;
}
case 5:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1] && !signs[i + 1, j - 1]) return true;
else return false;
}
else
{
if (j != 7 && !signs[i, j + 1] && !signs[i + 1, j + 3]) return true;
else return false;
}
case 15:
if (d == -1)
{
if (j != 1 && !signs[i, j - 2] && !signs[i + 1, j - 2] && !signs[i + 2, j - 2]) return true;
else return false;
}
else
{
if (j != 9 && !signs[i, j + 1] && !signs[i + 1, j] && !signs[i + 2, j]) return true;
else return false;
}
case 25:
if (d == -1)
{
if (j != 2 && !signs[i, j-1] && !signs[i-1, j-3]) return true;
else return false;
}
else
{
if (j != 9 && !signs[i, j + 1] && !signs[i-1, j+1]) return true;
else return false;
}
case 35:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1] && !signs[i-1, j] && !signs[i-2, j]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i-1, j+2] && !signs[i-2, j+2]) return true;
else return false;
}
case 6:
if (d == -1)
{
if (j != 2 && !signs[i, j - 1] && !signs[i + 1, j - 3]) return true;
else return false;
}
else
{
if (j != 9 && !signs[i, j + 1] && !signs[i + 1, j + 1]) return true;
else return false;
}
case 16:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1] && !signs[i + 1, j] && !signs[i + 2, j]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i + 1, j + 2] && !signs[i + 2, j + 2]) return true;
else return false;
}
case 26:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1] && !signs[i - 1, j-1]) return true;
else return false;
}
else
{
if (j != 7 && !signs[i, j + 1] && !signs[i - 1, j + 3]) return true;
else return false;
}
case 36:
if (d == -1)
{
if (j != 1 && !signs[i, j - 2] && !signs[i - 1, j - 2] && !signs[i - 2, j - 2]) return true;
else return false;
}
else
{
if (j != 9 && !signs[i, j + 1] && !signs[i - 1, j] && !signs[i - 2, j]) return true;
else return false;
}
case 7:
if (d == -1)
{
if (j != 1 && !signs[i, j - 2] && !signs[i + 1, j - 1]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i + 1, j + 1]) return true;
else return false;
}
case 17:
if (d == -1)
{
if (j != 0 && !signs[i, j - 1] && !signs[i + 1, j - 1] && !signs[i - 1, j - 1]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i + 1, j + 1] && !signs[i - 1, j + 1]) return true;
else return false;
}
case 27:
if (d == -1)
{
if (j != 1 && !signs[i, j - 2] && !signs[i - 1, j - 1]) return true;
else return false;
}
else
{
if (j != 8 && !signs[i, j + 2] && !signs[i - 1, j + 1]) return true;
else return false;
}
case 37:
if (d == -1)
{
if (j != 1 && !signs[i, j - 2] && !signs[i + 1, j - 1] && !signs[i - 1, j - 1]) return true;
else return false;
}
else
{
if (j != 9 && !signs[i, j + 1] && !signs[i + 1, j + 1] && !signs[i - 1, j + 1]) return true;
else return false;
}
default:
return false;
}
}
這個函數檢查是否有填滿的"列",如果有將其刪除,並將剩餘的格點往下向一列,直到沒有可刪除的列:
void full_line_check()
{
uint row_sum;
uint i, j;
i = 0;
while(i < 20)
{
row_sum = 0;
for (j = 0; j < 10; j++)
if (signs[i, j]) row_sum++;
if (row_sum == 10)
{
score += 20;
label_score.Text = "Score:" + score.ToString();
for (j = 0; j < 10; j++)
signs[i, j] = false;
show_grids(); // show a black line
for (uint y = i; y < 21; y++)
for (j = 0; j < 10; j++)
{
signs[y, j] = signs[y + 1, j];
grids_color[y, j] = grids_color[y + 1, j];
}
show_grids();
}
else i++;
}
}
每次timer tick要做的事: 基本上就是檢查方塊是否遇到障礙物,如果沒有就往下降一格;如果方塊遇到障礙物就停止並檢查是否有可刪除的列,之後再降下新的方塊,最後就是顯示格點內的最新狀況。
private void timer1_Tick(object sender, EventArgs e)
{
if (y_direction(block_type, block_row, block_col))
{
block_row_pre = block_row; block_row_pre = block_row; block_type_pre = block_type;
block_row--;
if (block_row == 19)
{
block_type_next = (uint)rander.Next(0, 7) + 1;
display_next_block(block_type_next);
block_count++;
score += 5;
label_block_count.Text = "Blocks:" + block_count.ToString();
label_score.Text = "Score:" + score.ToString();
if (game_mode == 1)
{
timer_interval = 1010 - (int)(score / 150) * 50;
if (timer_interval <= 0)
timer_interval = 10;
timer1.Interval = timer_interval;
label_level.Text = "Level:" + (1010 - timer_interval) / 50;
}
}
erase_block(block_row_pre, block_col_pre, block_type_pre);
update_block(block_row, block_col, block_type);
show_grids();
block_row_pre = block_row;
block_changed = false;
}
else
{
show_grids();
full_line_check();
if (block_row == 20)
{
label_info.Text = "Game Over!";
button1.Visible = true;
button1.Enabled = true;
timer1.Enabled = false;
return;
};
block_type = block_type_next;
block_row = 20;
block_col = 4;
block_row_pre = 20;
block_col_pre = 4;
block_type_pre = block_type;
block_changed = false;
}
}
另外,偵測鍵盤:
包括左右方向鍵(左右移動):
當方塊觸及左右邊界時,須停止移動。
下方向鍵(將方塊快速拉下): 當方塊遇到障礙物,須停止下降。
空白建(翻轉方塊):
根據方塊目前的型態並偵測是否可進行翻轉(空間是否足夠?),條件符合才進行翻轉,否則型態不變。
偵測按鍵,並做對應的動作:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.P)
{
if (game_mode == 0) { game_mode = 1; timer1.Enabled = true; }
else { game_mode = 0; timer1.Enabled = false; }
}
if (e.KeyCode == Keys.Left)
{
if (x_direction(block_type, block_row, block_col, -1))
{
block_col_pre = block_col; block_col--;
block_changed = true;
}
}
if (e.KeyCode == Keys.Right)
{
if (x_direction(block_type, block_row, block_col, 1))
{
block_col_pre = block_col; block_col++;
block_changed = true;
}
}
if (e.KeyCode == Keys.Space)
{
block_type_pre = block_type;
block_col_pre = block_col; block_row_pre = block_row;
block_type = next_block_type(block_type, block_row, block_col);
if(block_type != block_type_pre)
block_changed = true;
}
if (e.KeyCode == Keys.S)
{
game_mode = 2;
timer_interval -= 50;
if(timer_interval <= 0)
timer_interval = 1;
timer1.Interval = timer_interval;
label_level.Text = "Level:" + (1010 - timer_interval) / 50;
}
if (e.KeyCode == Keys.A)
{
game_mode = 2;
timer_interval += 50;
if (timer_interval >= 1010)
timer_interval = 1010;
timer1.Interval = timer_interval;
label_level.Text = "Level:" + (1010 - timer_interval) / 50;
}
if (e.KeyCode == Keys.Down)
timer1.Interval = 15;
if (block_changed)
{
erase_block(block_row_pre, block_col_pre, block_type_pre);
update_block(block_row, block_col, block_type);
show_grids();
block_row_pre = block_row; block_col_pre = block_col; block_type_pre = block_type;
block_changed = false;
}
}
檢查方塊是否可翻轉,並做對應的動作:
uint next_block_type(uint type, uint i, uint j)
{
switch(type)
{
case 1:
if (j <= 7 && j>=1 && !signs[i+2, j-1] && !signs[i+2, j + 1] && !signs[i+2, j + 2])
{
block_row = i + 2; block_col = j - 1;
return 11;
}
else return 1;
case 11:
if (i >= 2 && !signs[i - 1, j+1] && !signs[i - 2, j+1] && !signs[i +1, j+1])
{
block_row = i -2; block_col = j + 1;
return 1;
}
else return 11;
case 2: return 2;
case 3:
if (i >= 1 && !signs[i + 1, j+1] && !signs[i-1, j])
return 13;
else return 3;
case 13:
if (j >= 1 && !signs[i + 1, j] && !signs[i+1, j-1])
return 3;
else return 13;
case 4:
if (i >= 1 && !signs[i, j+1] && !signs[i-1, j+1])
return 14;
else return 4;
case 14:
if (j >= 1 && !signs[i, j-1] && !signs[i + 1, j + 1])
return 4;
else return 14;
case 5:
if (!signs[i + 2, j] && !signs[i, j + 1])
{
block_col = j + 1;
return 15;
}
else return 5;
case 15:
if (j >= 2 && !signs[i, j-2] && !signs[i + 1, j] )
{
block_row = i + 1;
return 25;
}
else return 15;
case 25:
if (i >= 2 && !signs[i, j - 1] && !signs[i - 2, j])
{
block_col = j - 1;
return 35;
}
else return 25;
case 35:
if (j <= 7 && !signs[i - 1, j] && !signs[i, j + 2])
{
block_row = i - 1;
return 5;
}
else return 35;
case 6:
if (!signs[i, j - 1] && !signs[i + 2, j] )
{
block_col = j - 1;
return 16;
}
else return 6;
case 16:
if (j <= 7 && !signs[i - 1, j] && !signs[i, j + 2])
{
block_row = i + 1;
return 26;
}
else return 16;
case 26:
if (i >= 2 && !signs[i, j +1] && !signs[i - 2, j])
{
block_col = j + 1;
return 36;
}
else return 26;
case 36:
if (j >= 2 && !signs[i, j-2] && !signs[i - 1, j])
{
block_row = i - 1;
return 6;
}
else return 36;
case 7:
if (i>=1 && !signs[i-1, j])
return 17;
else return 7;
case 17:
if (j >= 1 && !signs[i, j-1])
return 27;
else return 17;
case 27:
if (!signs[i+1, j])
return 37;
else return 27;
case 37:
if (j<=8 && !signs[i, j+1])
return 7;
else return 37;
default: return 0;
}
}
程式的邏輯大概如上所述,其他則為旁枝末節,如果還是有疑問,請直接查看完整程式碼。附上整個Visual Studio 的專案,直接打開就可編譯並執行。
如有發現Bug或有任何建議,請不吝賜教,感謝囉。
不好意思想請教您一下,我現在有三個tabpage,然後我想讓您的俄羅斯方塊在第三個tabpage上呈現出來,但我發現用程式拉出的200個黑色方格那邊,會被壓在tabpage下而顯示不出來,想請問一下該如何修改呢?
回覆刪除嗨,你好,如果你要在第三個page呈現這200個label的話,應該把
刪除this.Controls.Add(grids[i, j]);這行程式改成
tabPage3.Controls.Add(grids[i, j]);
這樣才能真的把labels加到你要的分頁,試試看。Good Luck!