モジュール小物とグリッドスナップ
Assets/
Scenes/
Arena.unity
Scripts/
Prefabs/
プロジェクトの設定
すでにあるものこのレッスンで追加
タブ 01 ですでにこんな一式を作ったことがあります:P_Cube、P_Sphere、P_Capsule。考え方は同じ、ピースはもっと大きく、今度はピース同士がきちんと噛み合っている必要があります。
プロのレベルは、一つの巨大なメッシュとしてモデリングされません。グリッド上で噛み合う小さな部品キットから組み立てられるものです。
すべてのピースが 1、2、4 メートルなので、どれをどれに付けても収まります。
モデリングの前にグリッドを決める
- 1
を開き、Move を 1, 1, 1、Rotate を 90 にします。
- 2
ハンドルをドラッグしているあいだ Ctrl/Cmd を押すと、その間隔でスナップします。
- 3
どのモジュールも、ピボットが中心ではなく角に来るようにモデリングします。タブ 01 で cube を Y = 0.5 に置いたのと同じ罠で、ピボットの置き方がすべてを決めます。
- 4
すべてのモジュールを同じ向き(+Z が前)に作っておけば、90° 回転はいつでもピタリと決まります。
最初のキット
床 4×4、壁 4×3、出入口のある壁、窓のある壁、角の柱、階段 2×3。
6 つのピースで建物一棟、10 もあれば村が作れます。
ProBuilder(Package Manager から無料で入手)なら、エディターを出ずに Unity の中でモデリングできます。
Prefab Variant——ひとつのキット、いくつものテーマ
まずグレーのブロックアウトを作ります。そのあと、マテリアルだけを変えたバリアントを作るのです。基底の Prefab のバグを直せば、すべてのバリアントにその修正が引き継がれます。
- 1
P_Wall を右クリック → Create → Prefab Variant → 名前は P_Wall_Stone。
- 2
バリアントを開いたら material だけを変えます。ほかはすべて親とリンクしたままです。
- 3
あとで P_Wall にコライダーを足すと、P_Wall_Stone と P_Wall_Wood にも同じタイミングで追加されます。
300 個の岩を手で置かずにばらまく
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")] // right-click the 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);
// drop a ray straight down to find the ground
if (!Physics.Raycast(from, Vector3.down, out RaycastHit hit, 200f, groundMask))
continue;
// skip cliffs — props on a vertical wall look broken
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] を付けるとそのメソッドがコンポーネントの右クリックメニューに入り、Play を押さずにエディター内で実行できます。
下向きに raycast を打って地面を探すので、プロップは地形にぴったり沿います。地形を作り直したあとも。
回転とサイズをランダムにすれば、同じ岩 120 個が「同じ岩 120 個」に見えなくなります。
Destroy ではなく DestroyImmediate——Destroy は Play モードの外では何も効きません。