using System.Collections.Generic; using System.Linq; using Assets.Commo; using Assets.Common; using Assets.Generators; using Assets.Map; using Assets.Voronoi; using UnityEngine; using Random = System.Random; namespace Assets { [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] [RequireComponent(typeof(GraphGenerator))] public class ForestGenerator : MonoBehaviour { public GameObject[] trees; [Range(1, 15)] public float radius = 3.0f; public GameObject forest; private Random _random = new Random(); private List _sites; private GraphGenerator Generator => GetComponent(); public void Generate() { Cleanup(); Generator.EnsureGenerated(); FindForestSites(); PlaceTrees(); _random = new Random(Generator.seed); } private void PlaceTrees() { foreach (Site site in _sites) { var sampler = new PoissonDiskSampler(radius) { Generator = _random }; Bounding bounding = BoundingTools.GetBounding(site.Vertices.Select(i => Generator.BoundariesGraph.Vertices[i])); var offset = Vector3.up * site.Location.Type.height; var points = sampler.Generate((float)bounding.Width, (float)bounding.Height); foreach (var point in points.Select(point => point + bounding.Min).Where(point => IsPointInSite(site, point))) { PlaceRandomTree(point.ToVector3() + offset); } } } public void Cleanup() { var destroy = new List(); foreach (var child in forest.transform) if (child is Transform t) destroy.Add(t.gameObject); foreach (var @object in destroy) DestroyImmediate(@object); } private void FindForestSites() { _sites = new List(); foreach (var location in Generator.LocationGraph.Vertices.Skip(1)) { var count = _random.Next(10); for (int i = 0; i < count; i++) _sites.Add(location.Sites[_random.Next(location.Sites.Count)]); } } private void PlaceRandomTree(Vector3 pos) { GameObject tree = Instantiate(trees[_random.Next(trees.Length)], forest.transform); tree.transform.position = pos - Vector3.down * 0.2f; } private bool IsPointInSite(Site site, Point point) { var polygon = site.Vertices.Select(i => Generator.BoundariesGraph[i]).ToArray(); return PointUtils.IsPointInside(point, polygon); } } }