U
タブ 04世界とゲームループ7 レッスン — タップで開く
03 / 07 · 9 分
世界とゲームループ

モジュール小物とグリッドスナップ

ここまでのプロジェクト

Assets/

Scenes/

Arena.unity

Terrain

Scripts/

PropScatter.cs

Prefabs/

module kit (6 pieces)prefab variants

プロジェクトの設定

baked lightmaps

すでにあるものこのレッスンで追加

タブ 01 ですでにこんな一式を作ったことがあります:P_Cube、P_Sphere、P_Capsule。考え方は同じ、ピースはもっと大きく、今度はピース同士がきちんと噛み合っている必要があります。

プロのレベルは、一つの巨大なメッシュとしてモデリングされません。グリッド上で噛み合う小さな部品キットから組み立てられるものです。

アニメーション図

すべてのピースが 1、2、4 メートルなので、どれをどれに付けても収まります。

モデリングの前にグリッドを決める

  1. 1

    Edit▸Grid and Snap Settings を開き、Move を 1, 1, 1、Rotate を 90 にします。

  2. 2

    ハンドルをドラッグしているあいだ Ctrl/Cmd を押すと、その間隔でスナップします。

  3. 3

    どのモジュールも、ピボットが中心ではなく角に来るようにモデリングします。タブ 01 で cube を Y = 0.5 に置いたのと同じ罠で、ピボットの置き方がすべてを決めます。

  4. 4

    すべてのモジュールを同じ向き(+Z が前)に作っておけば、90° 回転はいつでもピタリと決まります。

最初のキット

  • 床 4×4、壁 4×3、出入口のある壁、窓のある壁、角の柱、階段 2×3。

  • 6 つのピースで建物一棟、10 もあれば村が作れます。

  • ProBuilder(Package Manager から無料で入手)なら、エディターを出ずに Unity の中でモデリングできます。

Prefab Variant——ひとつのキット、いくつものテーマ

まずグレーのブロックアウトを作ります。そのあと、マテリアルだけを変えたバリアントを作るのです。基底の Prefab のバグを直せば、すべてのバリアントにその修正が引き継がれます。

  1. 1

    P_Wall を右クリック → Create → Prefab Variant → 名前は P_Wall_Stone。

  2. 2

    バリアントを開いたら material だけを変えます。ほかはすべて親とリンクしたままです。

  3. 3

    あとで P_Wall にコライダーを足すと、P_Wall_Stone と P_Wall_Wood にも同じタイミングで追加されます。

300 個の岩を手で置かずにばらまく

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")]            // 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 モードの外では何も効きません。