// HACK: // This will work out of the box, but it's intended to be an example of how to approach // procedural decoration like this. // A better approach would be to operate on the geometry itself. namespace Mapbox.Unity.MeshGeneration.Modifiers { using Mapbox.Unity.MeshGeneration.Data; using Mapbox.Unity.MeshGeneration.Components; using UnityEngine; using System.Collections.Generic; using System; [CreateAssetMenu(menuName = "Mapbox/Modifiers/Spawn Inside Modifier")] public class SpawnInsideModifier : GameObjectModifier { [SerializeField] int _spawnRateInSquareMeters; [SerializeField] int _maxSpawn = 1000; [SerializeField] GameObject[] _prefabs; [SerializeField] LayerMask _layerMask; [SerializeField] bool _scaleDownWithWorld; [SerializeField] bool _randomizeScale; [SerializeField] bool _randomizeRotation; int _spawnedCount; private Dictionary> _objects; private Queue _pool; public override void Initialize() { if (_objects == null || _pool == null) { _objects = new Dictionary>(); _pool = new Queue(); } } public override void Run(VectorEntity ve, UnityTile tile) { _spawnedCount = 0; var bounds = ve.Mesh.bounds; var center = ve.Transform.position + bounds.center; center.y = 0; var area = (int)(bounds.size.x * bounds.size.z); int spawnCount = Mathf.Min(area / _spawnRateInSquareMeters, _maxSpawn); while (_spawnedCount < spawnCount) { var x = UnityEngine.Random.Range(-bounds.extents.x, bounds.extents.x); var z = UnityEngine.Random.Range(-bounds.extents.z, bounds.extents.z); var ray = new Ray(center + new Vector3(x, 100, z), Vector3.down * 2000); RaycastHit hit; if (Physics.Raycast(ray, out hit, 150, _layerMask)) { var index = UnityEngine.Random.Range(0, _prefabs.Length); var transform = GetObject(index, ve.GameObject).transform; transform.position = hit.point; if (_randomizeRotation) { transform.localEulerAngles = new Vector3(0, UnityEngine.Random.Range(-180f, 180f), 0); } if (!_scaleDownWithWorld) { transform.localScale = Vector3.one / tile.TileScale; } if (_randomizeScale) { var scale = transform.localScale; var y = UnityEngine.Random.Range(scale.y * .7f, scale.y * 1.3f); scale.y = y; transform.localScale = scale; } } _spawnedCount++; } } public override void OnPoolItem(VectorEntity vectorEntity) { if(_objects.ContainsKey(vectorEntity.GameObject)) { foreach (var item in _objects[vectorEntity.GameObject]) { item.SetActive(false); _pool.Enqueue(item); } _objects[vectorEntity.GameObject].Clear(); _objects.Remove(vectorEntity.GameObject); } } public override void Clear() { foreach (var go in _pool) { go.Destroy(); } _pool.Clear(); foreach (var tileObject in _objects) { foreach (var go in tileObject.Value) { if (Application.isEditor && !Application.isPlaying) { DestroyImmediate(go); } else { Destroy(go); } } } _objects.Clear(); } private GameObject GetObject(int index, GameObject go) { GameObject ob; if (_pool.Count > 0) { ob = _pool.Dequeue(); ob.SetActive(true); ob.transform.SetParent(go.transform); } else { ob = ((GameObject)Instantiate(_prefabs[index], go.transform, false)); } if (_objects.ContainsKey(go)) { _objects[go].Add(ob); } else { _objects.Add(go, new List() { ob }); } return ob; } } }