Bài trước: Học làm game (6) - Lập trình với Unity (LT chức năng)
-----
Tạo lớp Group
Chúng ta cùng viết các đoạn mã còn lại cho game Tetris. Viết
mã cho lớp Group. Viết riêng vào tập tin Group.cs.
Trong giao diện Unity > vào mục Project > Create >
Script C# > đặt tên là Group.
Lớp Group gồm các đoạn mã xử lý liên quan đến khối hình
(group), ví dụ các GroupI, GroupO,…v.v.
Viết hàm kiểm tra một
ô vuông trong một Khối hình là hợp lệ
Như đã biết, mỗi một GameObject đều có một thành phần đi kèm
là transform, nó chứa thông tin về vị trí (position), xoay hình (rotation), co
dãn (scale).
Một Khối hình (Group) là một GameObject, được tạo bởi 4 ô
vuông (block). Mỗi ô vuông cũng là một GameObject, nên cũng có 4 thành phần
transform tương ứng.
Đoạn mã sau sẽ kiểm tra tính hợp lệ của một ô vuông, bằng
cách: duyệt từng ô vuông trong một Khối hình để đảm bảo ô vuông đó nằm trong
Khung hình chữ nhật và không chồng lấn (intersection) lên ô vuông của Khối hình
khác.
//
Kiểm tra một ô vuông trong Group là hợp lệ hay không?
bool isValidGridPos()
{
foreach (Transform child in transform)
{
Vector2 v =
Playfield.roundVec2(child.position);
//
không ở trong Khung chữ nhật?
if (!Playfield.insideBorder(v))
return false;
//
Vẫn ở trong Khung chữ nhật, nhưng nằm đè lên Ô vuông của Group khác
if (Playfield.grid[(int)v.x, (int)v.y] != null &&
Playfield.grid[(int)v.x, (int)v.y].parent != transform)
return false;
}
return true;
}
Viết hàm cập nhật lại
Khung lưới (grid) khi một Khối hình thay đổi vị trí
Khi một Khối hình thay đổi vị trí, chúng ta sẽ duyệt qua
toàn bộ các ô trong Khung lưới, tìm và xóa các Ô vuông thuộc cùng một Khối hình
đang xét (dựa vào thuộc tính parent). Sau đó, thêm các Ô vuông của Khối hình hiện
tại (transform) vào Khung lưới.
//
cập nhật lại Khung lưới (grid) khi một Khối hình thay đổi vị trí
void updateGrid()
{
//
xóa các ô vuông của một Khối hình ra khỏi Khung lưới
for (int y = 0; y < Playfield.h; ++y)
for (int x = 0; x < Playfield.w; ++x)
if (Playfield.grid[x, y] != null)
if (Playfield.grid[x, y].parent
== transform)
Playfield.grid[x, y] = null;
//
thêm các ô vuông của Khối hình đã bị thay đổi vị trí vào Khung lưới
foreach (Transform child in transform)
{
Vector2 v =
Playfield.roundVec2(child.position);
Playfield.grid[(int)v.x, (int)v.y] = child;
}
}
Đi ngang và rớt xuống
Chúng ta sẽ cùng lập trình để xử lý khi người dùng bấm phím
mũi tên sang trái, sang phải, đi xuống.
Chúng ta viết mã nguồn trong hàm void Update(), lớp Group.
//
Update is called once per frame
void Update()
{
//
Đi sang trái
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
//
thay đổi vị trí của Khối hình
transform.position += new Vector3(-1, 0, 0);
//
kiểm tra tính hợp lệ của Khối hình
if (isValidGridPos())
// Nếu hợp lệ, cập nhật Khung chữ nhật (grid)
updateGrid();
else
// nếu Khối hình không hợp lệ, hoàn lại vị trí cũ
transform.position += new Vector3(1, 0, 0);
}
// Đi sang phải
else if
(Input.GetKeyDown(KeyCode.RightArrow))
{
//
thay đổi vị trí của Khối hình
transform.position += new Vector3(1, 0, 0);
//
kiểm tra tính hợp lệ của Khối hình
if (isValidGridPos())
// Nếu hợp lệ, cập nhật Khung chữ nhật (grid)
updateGrid();
else
// nếu Khối hình không hợp lệ, hoàn lại vị trí cũ
transform.position += new Vector3(-1, 0, 0);
}
//
Xoay Khối hình
else if (Input.GetKeyDown(KeyCode.UpArrow))
{
transform.Rotate(0, 0, -90);
//
kiểm tra tính hợp lệ của Khối hình
if (isValidGridPos())
// Nếu hợp lệ, cập nhật Khung chữ nhật (grid)
updateGrid();
else
// nếu Khối hình không hợp lệ, hoàn lại vị trí cũ
transform.Rotate(0, 0, 90);
}
//
Rơi xuống
else if
(Input.GetKeyDown(KeyCode.DownArrow))
{
//
thay đổi vị trí của Khối hình
transform.position += new Vector3(0, -1, 0);
//
kiểm tra tính hợp lệ của Khối hình
if (isValidGridPos())
// Nếu hợp lệ, cập nhật Khung chữ nhật (grid)
updateGrid();
else
// nếu Khối hình không hợp lệ, hoàn lại vị trí cũ
transform.position += new Vector3(0, 1, 0);
//
xóa hàng ngang đã được lấp đầy ô vuông
Playfield.deleteFullRows();
//
sinh ra Khối hình kế tiếp
FindObjectOfType<Spawner>().spawnNext();
//
vô hiệu đoạn mã
enabled = false;
}
}
Chúng ta có thể thiết lập cho các Khối hình rơi tự động, mỗi
giây rơi một mức, như sau:
//
biến thời gian
float lastFall = 0;
Thay đổi mã nguồn một chút,
//
Rơi xuống
else if (Input.GetKeyDown(KeyCode.DownArrow) || Time.time - lastFall
>= 1)
{
//
thay đổi vị trí của Khối hình
transform.position += new Vector3(0, -1, 0);
//
kiểm tra tính hợp lệ của Khối hình
if (isValidGridPos())
// Nếu hợp lệ, cập nhật Khung chữ nhật (grid)
updateGrid();
else
// nếu Khối hình không hợp lệ, hoàn lại vị trí cũ
transform.position += new Vector3(0, 1, 0);
//
xóa hàng ngang đã được lấp đầy ô vuông
Playfield.deleteFullRows();
//
sinh ra Khối hình kế tiếp
FindObjectOfType<Spawner>().spawnNext();
//
vô hiệu đoạn mã
enabled = false;
}
lastFall = Time.time;
}
Đoạn mã xử lý cho trường hợp kết thúc game (gameover): khi một
Khối hình mới tạo ra đã đụng phải Khối hình khác, sẽ kết thúc game.
//
Start is called before the first frame update
void Start()
{
//
Khối hình mới sinh ra đã đụng phải khối hình khác > gameover
if (!isValidGridPos())
{
Debug.Log("Game over");
Destroy(gameObject);
}
}
Việc viết mã nguồn đã hoàn thành.
Với mỗi prefab mà chúng ta đã tạo trong Project Area, bấm vào prefab, sau đó bấm vào nút Open Prefab trong Inspector. Xem hình minh họa.
Sau đó, bấm vào nút Add
Component
Trong mục Component, tìm tới mục Script > chọn đoạn mã
Group.
Bạn sẽ thấy đoạn mã Group đã được thêm vào prefab.
Làm tương tự cho các Group còn lại: GroupO, GroupL, …v.v.
Chúc mừng bạn đã làm xong game.
Chúng ta cùng bấm play để chơi game Tetris.
Bị lỗi:
– Lúc bắt đầu chơi, khi không bấm phím nào. Khối hình chỉ
rơi một mức rồi dừng. Các bạn thử sửa mã nguồn xem sao.
-----
Cập nhật: 7/4/2023