U
tab 01Căn bản11 bài — chạm để mở
11 / 11 · 20 phút
Căn bản★ bài tập

Bài tập: sân đấu thành hình

dự án của bạn tới lúc này

Assets/

Scenes/

Arena.unity

FloorKnightMageArcherCanvas

Scripts/

CharacterSwitcher.csSwapCamera.csGameManager.csArenaHud.csGridSpawner.cs→ArenaBuilder.cs

Prefabs/

P_Cube

đã có sẵnbài này thêmbài này thay thế

bài tập60–90 phút

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ì

int / float / boolbiếnStart / Updatevòng đờipublic field → Inspectortrường publicif / for / Listrẽ nhánh & lặpPlane + materialsàn và vật liệuPrefab + InstantiateprefabGetComponentlấy component
sơ đồ động

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.

điền vào các chỗ TODO
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.

ArenaHud.cs — attach to any object, no UI package needed
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

thêm 1

Cho khối rơi bằng vật lý: thêm Rigidbody và sinh chúng ở độ cao 5 m.

thêm 2

Tô màu khối theo khoảng cách tới tâm thay vì ngẫu nhiên.

thêm 3

Thêm nhân vật thứ tư và để switcher tự xử lý mọi độ dài danh sách.

thêm 4

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.