Để mình bắt đầu mở VS cái đã.
Giới thiệu luật chơi Tetris:
Game này quá quen rồi, mình nói sơ qua cho người chưa rõ.
Đầu tiên chúng ta có một khung trống, các khối gạch xuất hiện ngẫu nhiên (được thấy trước) bên trên và luôn rơi xuống. Chúng ta có thể điều khiển cục gạch đang rơi qua trái, qua phải, xoay (+=90độ) và đi xuống nhanh.
Chúng ta sẽ xắp xếp các khối gạch sao cho có những hàng ngang liên tục là ăn điểm (line), hàng này sẽ mất đi. (Cách tính điểm khác chưa nói tới).
Tổ chức dữ liệu.
Để lưu cái đống gạch trên mình sẽ dùng một mảng 2 chiều kiểu số. Mình làm chẵn luôn 20 dòng và 10 cột. các bạn có thể làm khác nếu muốn. (tạm thời để 10 dòng cho nó ngắn)
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
1111110000
0101111111
1101111111
1111110111
Các khối gạch: Có nhiều loại khối gạch, cơ bản có 7. Mỗi khối mình cũng dùng một mảng 2 chiều để lưu, giống như thế này. Ở đây mình quy định mỗi khối có con số khác nhau đễ vẽ màu khác nhau.
Khối vuông:
11
11
Khối gạch dài
2222
Khối L
333
300
Vân vân và vân vân...
Khi gạch đang rơi thì chúng ta luôn có 2 thành phần là cái đống gạch ở trên và cục gạch đang rơi. Để biết cục gạch đang rơi ở đâu thì ta cần lưu vị trí của nó. (là tọa độ, các bạn muốn lưu bằng gì thì lưu, tọa độ gồm x và y)
Viết một hàm kiểm tra tính phù hợp của viên gạch so với đống gạch và một hàm ghép cục gạch vào đống gạch.
bool Ghép_gạch()
{
return Ghép_gạch(lò_gạch, cục_gạch_đang_rơi);
}
bool Ghép_gạch(byte[,] lg, byte[,] g)
{
int g_cao = g.GetLength(0);
int g_rộng = g.GetLength(1);
lò_gạch_ghép = (byte[,]) lò_gạch.Clone();
Vitri v = new Vitri(); v.x = vị_trí.x; v.y = vị_trí.y;
bool rb = true;
for (int i = 0; i < g_cao; i++)
{
for (int j = 0; j < g_rộng; j++)
{
if (i + v.y < dòng && j + v.x < cột)
{
if (lò_gạch_ghép[i + v.y, j + v.x] == 0 && g[i,j] != 0)
{
lò_gạch_ghép[i + v.y, j + v.x] = g[i, j];
}
else
{
//rb = false;
//break;
}
}
else
{
rb = false;
}
}
if (rb == false) break;
}
return rb;
}
Viết hàm hiển thị cái đống gạch và cái cục gạch đang rơi ghép vào để dễ test các bước sau (rơi, xoay, qua trái phải vv.vv...
Ở đây mình dùng c# vẽ lên graphics của panel, biến lg là cái lò gạch ghép ở bên trên
private void Vẽ_lò_gạch(Graphics graphics, byte[,] lg)
{
for (int i = 0; i < dòng; i++)
{
for (int j = 0; j < cột; j++)
{
int ii = lg[i, j]; //Giá trị khác nhau màu khác nhau
if (ii == 0)
{
graphics.FillRectangle(__Brs[0], j * cao + 1, i * cao + 1, cao - 2, cao - 2);
}
else
{
graphics.FillRectangle(__Brs[ii], j * cao + 1, i * cao + 1, cao - 2, cao - 2);
}
}
}
}
Hàm trên sẽ được gọi trong sự kiện Form_Paint
private void Form1_Paint(object sender, PaintEventArgs e)
{
if(lò_gạch_ghép != null)
Vẽ_lò_gạch(__G, lò_gạch_ghép);
if (cục_gach_tiếp_theo != null)
{
Vẽ_cục_gạch(__Gnext, cục_gach_tiếp_theo);
}
}
__Brs là một biến toàn cục lưu các loại cọ vẽ với màu khác nhau:
__Brs = new SolidBrush[8];
__Brs[0] = new SolidBrush(Color.Black);
__Brs[1] = new SolidBrush(Color.Blue); //O
__Brs[2] = new SolidBrush(Color.Red); //I
__Brs[3] = new SolidBrush(Color.LightPink); //L
__Brs[4] = new SolidBrush(Color.Orange); //J
__Brs[5] = new SolidBrush(Color.Green); //N
__Brs[6] = new SolidBrush(Color.LightBlue);//Z
__Brs[7] = new SolidBrush(Color.LightGoldenrodYellow); //T
Vòng lặp game: mình dùng một timer cứ 1% giây chạy một lần.
Mình dùng một biến đếm. nếu đủ số lần để cục gạch rơi 1 bậc thì chạy hàm rơi gạch và reset biến đếm.
Trong hàm rơi nếu kiểm tra rơi được thì cho vị trí cục gạch đang rơi thay đổi. Nếu không rơi được nữa thì ghép cục gạch đang rơi vào đống gạch, kiểm tra tính điểm, xóa dòng ăn và tạo cục gạch mới (có cục next hay không tùy bạn, có thì ở đây lấy cục next cho rơi và tạo cục next mới.)
Để cục gạch rơi ta cần cái hàm này đây:
int đếm_để_rơi = 100;
int đếm = 0;
private void timer1_Tick(object sender, EventArgs e)
{
đếm++;
if (đếm >= đếm_để_rơi)
{
//Xử lý
chạm đáy, nối luôn cục gạch đang rơi vào lò gạch
//Xử lý điểm.
đếm = 0;
if (được_phép_rơi_xuống)
{
Rơi_xuống();
Ghép_gạch(lò_gạch, cục_gạch_đang_rơi);
}
else
{
lò_gạch = (byte[,]) lò_gạch_ghép.Clone();
if (!Lấy_cục_gạch_tiếp_theo())
{
timer1.Stop(); timer2.Stop();
lbtPause.Visible = false;
//Form
nhập tên
Form2 f = new Form2();
f.ShowDialog();
name = f.inputName;
Thêm_điểm(this.điểm_dòng, name);
//MessageBox.Show("Game
over!");
}
}
}
}
Để thấy cục gạch rơi ta liên tục cập nhập hình ảnh cho game, cần dùng hàm này, mỗi khi gọi hàm này thì sự kiện Form_Paint sẽ được gọi, interver các bạn tự quyết định nhé, ở đây mình chọn khoảng 33 tức khoảng 30 khung hình / giây :D. interver của timer1 thì mình để 10, các bạn muốn tốc độ nhanh hơn thì để cao hơn.
private void timer2_Tick(object sender, EventArgs e)
{
this.Invalidate();
}
abc
void Rơi_xuống()
{
vị_trí.y++;
đếm = 0;
Ghép_gạch();
}
void Qua_trái()
{
vị_trí.x--;
Ghép_gạch();
}
void Qua_phải()
{
vị_trí.x++;
Ghép_gạch();
}
void Xoay_gạch(bool b)
{
cục_gạch_đang_rơi = Xoay_cuc_gach(cục_gạch_đang_rơi,
b);
Ghép_gạch();
}
abc
static Random r = new Random();
void Tạo_cục_gạch_tiếp_theo()
{
int switch_int = r.Next(1000);
switch (switch_int % 7)
{
case 0:
cục_gach_tiếp_theo = (byte[,])cục_gạch_vuông.Clone();
break;
case 1:
cục_gach_tiếp_theo = (byte[,])cục_gạch_dài.Clone();
break;
case 2:
cục_gach_tiếp_theo = (byte[,])cục_gạch_L.Clone();
break;
case 3:
cục_gach_tiếp_theo = (byte[,])cục_gạch_J.Clone();
break;
case 4:
cục_gach_tiếp_theo = (byte[,])cục_gạch_N.Clone();
break;
case 5:
cục_gach_tiếp_theo = (byte[,])cục_gạch_Z.Clone();
break;
case 6:
cục_gach_tiếp_theo = (byte[,])cục_gạch_T.Clone();
break;
default:
cục_gach_tiếp_theo = (byte[,])cục_gạch_vuông.Clone();
break;
}
}
bool Lấy_cục_gạch_tiếp_theo()
{
//Kiểm
tra điểm trước
Kiểm_tra_tính_điểm();
//đặt lại
vị trí
cục_gạch_đang_rơi = (byte[,])cục_gach_tiếp_theo.Clone();
vị_trí.x = cột / 2 - cục_gạch_đang_rơi.GetLength(1)
/ 2;
vị_trí.y = 0;
Ghép_gạch();//cho
hiện hình cục gạch đang rơi
//Kiểm_tra_tính_điểm();
Tạo_cục_gạch_tiếp_theo();
//Kiểm
tra
int g_dọc = cục_gạch_đang_rơi.GetLength(0);
int g_ngang = cục_gạch_đang_rơi.GetLength(1);
Vitri v = new Vitri();
v.x = vị_trí.x;
v.y = vị_trí.y;
//Nếu cục
gạch quá bên phải return false
if (v.x + g_ngang > cột) return false;
for (int i = 0; i < g_dọc; i++)
{
for (int j = 0; j < g_ngang; j++)
{
if (i + v.y < dòng && j + v.x < cột)
{
if (lò_gạch[i + v.y, j + v.x] != 0 && cục_gạch_đang_rơi[i,
j] != 0)
{
return false;
}
}
else
{
return false;
}
}
}
return true;
//Nếu
false sẽ game over
}
abcCode toàn bộ chương trình và hướng dẫn lập trình có thể xem ở đây..
http://laptrinhtungbuoc.blogspot.com/2014/04/bat-au-voi-game-xep-gach-let-start-with.html
File chơi thử:
HuuanTetris
No comments:
Post a Comment