diff options
author | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 |
---|---|---|
committer | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 |
commit | 8722a9920c1f6119bf6e769cba270e63097f8e25 (patch) | |
tree | 2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/InfiniteWorld/ProceduralWorld.cs | |
parent | 3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff) |
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/InfiniteWorld/ProceduralWorld.cs')
-rw-r--r-- | Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/InfiniteWorld/ProceduralWorld.cs | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/InfiniteWorld/ProceduralWorld.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/InfiniteWorld/ProceduralWorld.cs new file mode 100644 index 0000000..4e369a4 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/InfiniteWorld/ProceduralWorld.cs @@ -0,0 +1,290 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace Pathfinding.Examples { + /// <summary>Example script for generating an infinite procedural world</summary> + [HelpURL("https://arongranberg.com/astar/documentation/stable/proceduralworld.html")] + public class ProceduralWorld : MonoBehaviour { + public Transform target; + + public ProceduralPrefab[] prefabs; + + /// <summary>How far away to generate tiles</summary> + public int range = 1; + + public int disableAsyncLoadWithinRange = 1; + + /// <summary>World size of tiles</summary> + public float tileSize = 100; + public int subTiles = 20; + + /// <summary> + /// Enable static batching on generated tiles. + /// Will improve overall FPS, but might cause FPS drops on + /// some frames when static batching is done + /// </summary> + public bool staticBatching = false; + + Queue<IEnumerator> tileGenerationQueue = new Queue<IEnumerator>(); + + public enum RotationRandomness { + AllAxes, + Y + } + + [System.Serializable] + public class ProceduralPrefab { + /// <summary>Prefab to use</summary> + public GameObject prefab; + + /// <summary>Number of objects per square world unit</summary> + public float density = 0; + + /// <summary> + /// Multiply by [perlin noise]. + /// Value from 0 to 1 indicating weight. + /// </summary> + public float perlin = 0; + + /// <summary> + /// Perlin will be raised to this power. + /// A higher value gives more distinct edges + /// </summary> + public float perlinPower = 1; + + /// <summary>Some offset to avoid identical density maps</summary> + public Vector2 perlinOffset = Vector2.zero; + + /// <summary> + /// Perlin noise scale. + /// A higher value spreads out the maximums and minimums of the density. + /// </summary> + public float perlinScale = 1; + + /// <summary> + /// Multiply by [random]. + /// Value from 0 to 1 indicating weight. + /// </summary> + public float random = 1; + + /// <summary>Minimum scale of prefab</summary> + public float minScale = 1; + + /// <summary>Maximum scale of prefab</summary> + public float maxScale = 1; + + public RotationRandomness randomRotation = RotationRandomness.AllAxes; + + /// <summary>If checked, a single object will be created in the center of each tile</summary> + public bool singleFixed = false; + } + + /// <summary>All tiles</summary> + Dictionary<Int2, ProceduralTile> tiles = new Dictionary<Int2, ProceduralTile>(); + + // Use this for initialization + void Start () { + // Calculate the closest tiles + // and then recalculate the graph + Update(); + AstarPath.active.Scan(); + + StartCoroutine(GenerateTiles()); + } + + // Update is called once per frame + void Update () { + // Calculate the tile the target is standing on + Int2 p = new Int2(Mathf.RoundToInt((target.position.x - tileSize*0.5f) / tileSize), Mathf.RoundToInt((target.position.z - tileSize*0.5f) / tileSize)); + + // Clamp range + range = range < 1 ? 1 : range; + + // Remove tiles which are out of range + bool changed = true; + while (changed) { + changed = false; + foreach (KeyValuePair<Int2, ProceduralTile> pair in tiles) { + if (Mathf.Abs(pair.Key.x-p.x) > range || Mathf.Abs(pair.Key.y-p.y) > range) { + pair.Value.Destroy(); + tiles.Remove(pair.Key); + changed = true; + break; + } + } + } + + // Add tiles which have come in range + // and start calculating them + for (int x = p.x-range; x <= p.x+range; x++) { + for (int z = p.y-range; z <= p.y+range; z++) { + if (!tiles.ContainsKey(new Int2(x, z))) { + ProceduralTile tile = new ProceduralTile(this, x, z); + var generator = tile.Generate(); + // Tick it one step forward + generator.MoveNext(); + // Calculate the rest later + tileGenerationQueue.Enqueue(generator); + tiles.Add(new Int2(x, z), tile); + } + } + } + + // The ones directly adjacent to the current one + // should always be completely calculated + // make sure they are + for (int x = p.x-disableAsyncLoadWithinRange; x <= p.x+disableAsyncLoadWithinRange; x++) { + for (int z = p.y-disableAsyncLoadWithinRange; z <= p.y+disableAsyncLoadWithinRange; z++) { + tiles[new Int2(x, z)].ForceFinish(); + } + } + } + + IEnumerator GenerateTiles () { + while (true) { + if (tileGenerationQueue.Count > 0) { + var generator = tileGenerationQueue.Dequeue(); + yield return StartCoroutine(generator); + } + yield return null; + } + } + + class ProceduralTile { + int x, z; + System.Random rnd; + + ProceduralWorld world; + + public bool destroyed { get; private set; } + + public ProceduralTile (ProceduralWorld world, int x, int z) { + this.x = x; + this.z = z; + this.world = world; + rnd = new System.Random((x * 10007) ^ (z*36007)); + } + + Transform root; + IEnumerator ie; + + public IEnumerator Generate () { + ie = InternalGenerate(); + GameObject rt = new GameObject("Tile " + x + " " + z); + root = rt.transform; + while (ie != null && root != null && ie.MoveNext()) yield return ie.Current; + ie = null; + } + + public void ForceFinish () { + while (ie != null && root != null && ie.MoveNext()) {} + ie = null; + } + + Vector3 RandomInside () { + Vector3 v = new Vector3(); + + v.x = (x + (float)rnd.NextDouble())*world.tileSize; + v.z = (z + (float)rnd.NextDouble())*world.tileSize; + return v; + } + + Vector3 RandomInside (float px, float pz) { + Vector3 v = new Vector3(); + + v.x = (px + (float)rnd.NextDouble()/world.subTiles)*world.tileSize; + v.z = (pz + (float)rnd.NextDouble()/world.subTiles)*world.tileSize; + return v; + } + + Quaternion RandomYRot (ProceduralPrefab prefab) { + return prefab.randomRotation == RotationRandomness.AllAxes ? Quaternion.Euler(360*(float)rnd.NextDouble(), 360*(float)rnd.NextDouble(), 360*(float)rnd.NextDouble()) : Quaternion.Euler(0, 360 * (float)rnd.NextDouble(), 0); + } + + IEnumerator InternalGenerate () { + Debug.Log("Generating tile " + x + ", " + z); + int counter = 0; + + float[, ] ditherMap = new float[world.subTiles+2, world.subTiles+2]; + + //List<GameObject> objs = new List<GameObject>(); + + for (int i = 0; i < world.prefabs.Length; i++) { + ProceduralPrefab pref = world.prefabs[i]; + + if (pref.singleFixed) { + Vector3 p = new Vector3((x+0.5f) * world.tileSize, 0, (z+0.5f) * world.tileSize); + GameObject ob = GameObject.Instantiate(pref.prefab, p, Quaternion.identity) as GameObject; + ob.transform.parent = root; + } else { + float subSize = world.tileSize/world.subTiles; + + for (int sx = 0; sx < world.subTiles; sx++) { + for (int sz = 0; sz < world.subTiles; sz++) { + ditherMap[sx+1, sz+1] = 0; + } + } + + for (int sx = 0; sx < world.subTiles; sx++) { + for (int sz = 0; sz < world.subTiles; sz++) { + float px = x + sx/(float)world.subTiles;//sx / world.tileSize; + float pz = z + sz/(float)world.subTiles;//sz / world.tileSize; + + float perl = Mathf.Pow(Mathf.PerlinNoise((px + pref.perlinOffset.x)*pref.perlinScale, (pz + pref.perlinOffset.y)*pref.perlinScale), pref.perlinPower); + + float density = pref.density * Mathf.Lerp(1, perl, pref.perlin) * Mathf.Lerp(1, (float)rnd.NextDouble(), pref.random); + float fcount = subSize*subSize*density + ditherMap[sx+1, sz+1]; + int count = Mathf.RoundToInt(fcount); + + // Apply dithering + // See http://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering + ditherMap[sx+1+1, sz+1+0] += (7f/16f) * (fcount - count); + ditherMap[sx+1-1, sz+1+1] += (3f/16f) * (fcount - count); + ditherMap[sx+1+0, sz+1+1] += (5f/16f) * (fcount - count); + ditherMap[sx+1+1, sz+1+1] += (1f/16f) * (fcount - count); + + // Create a number of objects + for (int j = 0; j < count; j++) { + // Find a random position inside the current sub-tile + Vector3 p = RandomInside(px, pz); + GameObject ob = GameObject.Instantiate(pref.prefab, p, RandomYRot(pref)) as GameObject; + ob.transform.parent = root; + + var scale = Mathf.Lerp(pref.minScale, pref.maxScale, Mathf.PerlinNoise(p.x * pref.perlinScale, p.z * pref.perlinScale)); + ob.transform.localScale = Vector3.one * scale; + //ob.SetActive ( false ); + //objs.Add ( ob ); + counter++; + if (counter % 2 == 0) + yield return null; + } + } + } + } + } + + ditherMap = null; + + yield return null; + yield return null; + + //Batch everything for improved performance + if (Application.HasProLicense() && world.staticBatching) { + StaticBatchingUtility.Combine(root.gameObject); + } + } + + public void Destroy () { + if (root != null) { + Debug.Log("Destroying tile " + x + ", " + z); + GameObject.Destroy(root.gameObject); + root = null; + } + + // Make sure the tile generator coroutine is destroyed + ie = null; + } + } + } +} |