Bài tập: sân đấu thành hình
Assets/
Scenes/
Arena.unity
Scripts/
Prefabs/
đã có sẵnbài này thêmbài này thay thế
Dựng sân đấu, nơi toàn bộ phần còn lại của khoá học diễn ra. Một cảnh tên Arena: một sàn, một lưới khối sinh bằng code — chính những khối bạn sẽ nhảy qua lại ở tab 02 — ba nhân vật đổi qua lại được, và một bảng số nhỏ trên màn hình.
dùng lại những gì
Cảnh hoàn thành phải trông và chạy như thế này.
Làm gì, theo từng bước
Phần 1 — sân khấu
Một cảnh mới tên Arena, lưu trong Assets/Scenes. Mọi bài từ đây tới cuối khoá đều diễn ra trong đúng file này — scene duy nhất khác mà bạn tạo là màn intro.
Một Plane ở 0,0,0 scale 3,1,3 với material tối do bạn tự tạo.
Camera khoảng 0, 14, -16 xoay 38, 0, 0 để thấy trọn cái sàn.
Directional Light xoay 50, -30, 0, bóng mềm.
Phần 2 — lưới khối
Viết một script sinh ra cả lưới. Đừng đặt tay dù chỉ một khối — nếu thấy mình đang kéo thả cube thì quay lại sửa vòng lặp.
using System.Collections.Generic;
using UnityEngine;
public class ArenaBuilder : MonoBehaviour
{
[Header("lưới")]
[SerializeField] private GameObject blockPrefab;
[SerializeField] private int columns = 4;
[SerializeField] private int rows = 4;
[SerializeField] private float spacing = 1.6f;
[SerializeField] private Transform blockParent;
// giữ lại mọi khối đã tạo, để đếm và để xoá
private readonly List<GameObject> blocks = new List<GameObject>();
public int BlockCount => blocks.Count;
void Start()
{
Build();
}
void Update()
{
//bấm R để dựng lại với màu ngẫu nhiên mới
if (Input.GetKeyDown(KeyCode.R))
Build();
}
public void Build()
{
Clear();
float offsetX = (columns - 1) * spacing * 0.5f;
float offsetZ = (rows - 1) * spacing * 0.5f;
for (int x = 0; x < columns; x++)
{
for (int z = 0; z < rows; z++)
{
Vector3 pos = new Vector3(
x * spacing - offsetX,
0.5f,
z * spacing - offsetZ);
GameObject b = Instantiate(blockPrefab, pos, Quaternion.identity, blockParent);
b.name = $"Block_{x}_{z}";
//cho nó màu ngẫu nhiên
//chiều cao ngẫu nhiên từ 0.5 tới 2.5
blocks.Add(b);
}
}
}
public void Clear()
{
foreach (GameObject b in blocks)
if (b != null) Destroy(b);
blocks.Clear();
}
}Phần 3 — ba nhân vật
Ba capsule ở Y = 1, ba màu khác nhau, đều là con của một object rỗng tên Characters.
Gắn SimpleMover cho cả ba, CharacterSwitcher trên GameManager, dùng phím 1 / 2 / 3 và Tab.
Camera bám theo nhân vật nào đang được điều khiển.
Phần 4 — bảng số
Một cái HUD nhỏ chứng minh các script của bạn nói chuyện được với nhau. Đây là lần đầu hai script của bạn đọc dữ liệu từ script thứ ba.
using UnityEngine;
public class ArenaHud : MonoBehaviour
{
[SerializeField] private ArenaBuilder builder;
[SerializeField] private CharacterSwitcher switcher;
// OnGUI là cách vẽ chữ nhanh và bẩn. Dùng cho bài tập thì được,
// đừng dùng trong game thật — UI thật nằm trên Canvas, như HudPanel ở bài trước.
void OnGUI()
{
GUI.skin.label.fontSize = 20;
GUI.Label(new Rect(20, 20, 400, 30), "Blocks: " + builder.BlockCount);
GUI.Label(new Rect(20, 50, 400, 30), "Active: " + switcher.ActiveCharacter.name);
GUI.Label(new Rect(20, 80, 400, 30), "1/2/3 = swap");
}
}Khi nào thì xong
- ✓Bấm Play thấy sàn với 16 khối màu và chiều cao ngẫu nhiên, không khối nào đặt bằng tay.
- ✓Bấm R dựng lại lưới với giá trị ngẫu nhiên mới, con số đếm vẫn đúng.
- ✓Phím 1, 2, 3 chuyển quyền điều khiển; mỗi lúc chỉ một nhân vật đi được.
- ✓Camera bám nhân vật đang chọn một cách mượt, không giật.
- ✓HUD hiện số khối và tên nhân vật đang điều khiển, cập nhật theo thời gian thực.
- ✓Console sạch — không lỗi, không cảnh báo từ script của bạn.
- ✓Đổi columns, rows, spacing trong Inspector là kết quả đổi theo, không phải sửa code.
Muốn làm thêm
Cho khối rơi bằng vật lý: thêm Rigidbody và sinh chúng ở độ cao 5 m.
Tô màu khối theo khoảng cách tới tâm thay vì ngẫu nhiên.
Thêm nhân vật thứ tư và để switcher tự xử lý mọi độ dài danh sách.
Cho nhân vật đẩy được khối: gắn Rigidbody cho các khối, di chuyển nhân vật bằng CharacterController, rồi đẩy chúng trong OnControllerColliderHit.