Ghép cảnh bằng module và hít lưới
Assets/
Scenes/
Arena.unity
Scripts/
Prefabs/
thiết lập dự án
đã 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.
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
Mở , đặt Move là 1, 1, 1 và Rotate là 90.
- 2
Giữ Ctrl/Cmd khi kéo tay cầm để di chuyển theo bước đó.
- 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
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
Chuột phải P_Wall → Create → Prefab Variant → đặt tên P_Wall_Stone.
- 2
Mở variant, chỉ đổi material. Mọi thứ khác vẫn liên kết với prefab cha.
- 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 đá
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.