中級チュートリアル: ガーデンチャンクのメッシング

現実世界を庭園に変えるシンプルなカスタムARメッシング体験をビルドする。

../../_images/meshing_garden.jpg

概念

環境をスキャンすると、検出されたジオメトリに応じてさまざまな種類の植物が出現します。

  • グランドプランツ(被覆植物)は、メッシュの平面部分で育ちます。

  • ウォールプラント(壁を伝う植物)は、メッシュの垂直部分で育ちます。

これを行うには、メッシュジオメトリをランダムにサンプリングし、適切な頂点(法線が真上または横を向いている)で植物を成長させます。この例では、1つのチャンクに1つの植物のみスポーンします。つまり、植物が成長し始めたら、そのチャンクのメッシュデータのサンプリングを停止することができます。

まずはじめに、グランドプランツとウォールプラントの両方で選択する少量の植物アセットが必要になります。

シーンをセットアップする

  1. メッシング: はじめに チュートリアルのステップに従ってください。

  2. ARMeshManagerUse Invisible Material(見えない素材を使用する) にチェックを入れます。この場合、メッシュを見えないようにして、庭の体験を損なわないようにします。

Unityエディタで、 モックメッシュ を使用して繰り返すことをお勧めします。

GardenChunk.cs

メッシュジオメトリを読み込んで植物をスポーンするために、デフォルトの MeshChunk プレハブを複製し、そこに以下のスクリプトを追加します。

using System;
using System.Collections;
using System.Collections.Generic;

using UnityEngine;

using Random = UnityEngine.Random;

public class GardenChunk: MonoBehaviour
{
  public List<GameObject> _groundPrefabs;
  public List<GameObject> _wallPrefabs;
  public float _groundNormalTolerance = 0.01f;
  public float _wallNormalTolerance = 0.001f;
  public int _spawnMinVertexCount = 100;
  public int _despawnMaxVertexCount = 50;
  public float _growthDuration = 4.0f;
  private MeshFilter _filter;
  private GameObject _plant;

  private void Start()
  {
    _filter = GetComponent<MeshFilter>();

    // don't update the object if there's no mesh filter
    enabled = (bool)_filter;
  }

  private void Update()
  {
    int vertexCount = _filter.sharedMesh.vertexCount;
    if (vertexCount >= _spawnMinVertexCount && !(bool)_plant)
    {
      // plant a plant! (might not succeed)
      _plant = InstantiatePlant(_filter.sharedMesh);
    }
    else if (vertexCount <= _despawnMaxVertexCount && (bool)_plant)
    {
      // pull a plant!
      StopCoroutine(GrowPlant());
      Destroy(_plant);
      _plant = null;
    }
  }

  private GameObject InstantiatePlant(Mesh mesh)
  {
    bool wall;
    Vector3 position;
    Vector3 normal;
    // if we find a suitable vertex, plop a plant at that location
    if (FindVertex(_filter.sharedMesh, out wall, out position, out normal))
    {
      GameObject prefab = wall
        ? _wallPrefabs[Random.Range(0, _wallPrefabs.Count)]
        : _groundPrefabs[Random.Range(0, _groundPrefabs.Count)];

      Quaternion rotation = wall
        ? Quaternion.LookRotation(normal, Vector3.up)
        : Quaternion.Euler(0, Random.Range(0.0f, 360.0f), 0);

      // use local position/rotation because of the different coordinate system
      GameObject plant = Instantiate(prefab, transform, false);
      plant.transform.localPosition = position;
      plant.transform.localRotation = rotation;
      plant.transform.localScale = Vector3.zero;
      StartCoroutine(GrowPlant());
      return plant;
    }

    return null;
  }

  private bool FindVertex(Mesh mesh, out bool wall, out Vector3 position, out Vector3 normal)
  {
    int v = Random.Range(0, mesh.vertexCount);
    position = mesh.vertices[v];
    normal = mesh.normals[v];
    bool ground = normal.y > 1.0f - _groundNormalTolerance && _groundPrefabs.Count > 0;
    wall = Mathf.Abs(normal.y) < _wallNormalTolerance && _wallPrefabs.Count > 0;
    return wall || ground;
  }

  private IEnumerator GrowPlant()
  {
    yield return null;

    float progress = 0.0f;
    // end scale has Y inverted because of the transform on the mesh root
    Vector3 endScale = new Vector3(1.0f, -1.0f, 1.0f);
    while (progress < 1.0f && (bool)_plant)
    {
      progress = Mathf.Min(1.0f, progress + Time.deltaTime / _growthDuration);
      _plant.transform.localScale = Vector3.Lerp(Vector3.zero, endScale, progress);
      yield return null;
    }
  }
}

プレハブにスクリプトを貼り付けたら、必要に応じてパラメーターを調整し、植物アセットを貼り付けます。正しいスケールや変換になるように、アセットのインポート設定の修正が必要になる場合があります。このスクリプトは、アセットが直立しており、その原点が地面とつながっていることを前提としています。

../../_images/meshing_gardenchunk.jpg