U
tab 04Thế giới & Vòng lặp game7 bài — chạm để mở
03 / 07 · 9 phút
Thế giới & Vòng lặp game

Ghép cảnh bằng module và hít lưới

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

Assets/

Scenes/

Arena.unity

Terrain

Scripts/

PropScatter.cs

Prefabs/

module kit (6 pieces)prefab variants

thiết lập dự án

baked lightmaps

đã có sẵnbài này thêm

Bạn từng dựng một bộ như thế này rồi, ở tab 01: P_Cube, P_Sphere, P_Capsule. Cùng ý tưởng, mảnh to hơn, và lần này chúng phải khớp được vào nhau.

Màn chơi chuyên nghiệp không được dựng thành một khối lưới khổng lồ. Chúng được ghép từ một bộ mảnh nhỏ, khớp vào nhau trên lưới.

sơ đồ động

Mỗi mảnh dài 1, 2 hoặc 4 mét, nên mảnh nào cũng ghép được với mảnh nào.

Chốt lưới trước khi dựng bất cứ thứ gì

  1. 1

    Mở Edit▸Grid and Snap Settings, đặt Move là 1, 1, 1 và Rotate là 90.

  2. 2

    Giữ Ctrl/Cmd khi kéo tay cầm để di chuyển theo bước đó.

  3. 3

    Dựng mọi module với tâm xoay ở góc, không phải ở giữa. Đây đúng là cái bẫy cube phải đặt Y = 0.5 ở tab 01 — vị trí tâm xoay quyết định tất cả.

  4. 4

    Mọi module quay cùng một hướng (+Z là hướng trước) để xoay 90° lúc nào cũng đúng.

Bộ mảnh khởi đầu

  • Sàn 4×4, tường 4×3, tường có cửa, tường có cửa sổ, cột góc, cầu thang 2×3.

  • Sáu mảnh dựng được cả toà nhà. Mười mảnh dựng được cả ngôi làng.

  • ProBuilder (miễn phí trong Package Manager) cho phép dựng các mảnh này ngay trong Unity, không cần ra ngoài.

Prefab Variant — một bộ mảnh, nhiều chủ đề

Dựng bản xám thô trước. Sau đó tạo variant chỉ đổi material. Sửa lỗi ở prefab gốc là mọi variant tự nhận bản sửa.

  1. 1

    Chuột phải P_Wall → Create → Prefab Variant → đặt tên P_Wall_Stone.

  2. 2

    Mở variant, chỉ đổi material. Mọi thứ khác vẫn liên kết với prefab cha.

  3. 3

    Sau này thêm collider vào P_Wall là P_Wall_Stone và P_Wall_Wood cũng có ngay.

Rải vật trang trí mà không phải đặt tay 300 hòn đá

PropScatter.cs — run it from the Inspector
using UnityEngine;

public class PropScatter : MonoBehaviour
{
    [SerializeField] private GameObject[] prefabs;
    [SerializeField] private int count = 120;
    [SerializeField] private float radius = 40f;
    [SerializeField] private LayerMask groundMask;
    [SerializeField] private Vector2 scaleRange = new Vector2(0.8f, 1.4f);

    [ContextMenu("Scatter")]            // chuột phải vào component → Scatter
    public void Scatter()
    {
        Clear();

        for (int i = 0; i < count; i++)
        {
            Vector2 flat = Random.insideUnitCircle * radius;
            Vector3 from = transform.position + new Vector3(flat.x, 50f, flat.y);

            //bắn tia xuống tìm mặt đất
            if (!Physics.Raycast(from, Vector3.down, out RaycastHit hit, 200f, groundMask))
                continue;

            //bỏ vách đứng
            if (Vector3.Angle(hit.normal, Vector3.up) > 30f) continue;

            GameObject prefab = prefabs[Random.Range(0, prefabs.Length)];
            GameObject go = Instantiate(prefab, hit.point, Quaternion.identity, transform);

            go.transform.rotation = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
            go.transform.localScale = Vector3.one * Random.Range(scaleRange.x, scaleRange.y);
        }
    }

    [ContextMenu("Clear")]
    public void Clear()
    {
        for (int i = transform.childCount - 1; i >= 0; i--)
            DestroyImmediate(transform.GetChild(i).gameObject);
    }
}
  • [ContextMenu] thêm hàm vào menu chuột phải của component, chạy được ngay trong editor mà không cần bấm Play.

  • Bắn tia xuống mặt đất để vật trang trí bám sát địa hình, kể cả sau khi bạn sửa lại địa hình.

  • Xoay và co giãn ngẫu nhiên là thứ khiến 120 hòn đá giống nhau không còn trông giống hệt nhau.

  • Dùng DestroyImmediate chứ không phải Destroy — Destroy không làm gì khi ngoài Play mode.