Aetheria/Assets/Scripts/Galaxy.cs
Reinier Vens 1f90d3639a Asteroids & Refactoring
We have proper belts that hug the Grid, reactive properties for planet instance data, and more!
2020-12-01 00:16:11 +00:00

163 lines
No EOL
5.3 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
//using MessagePack;
using UnityEngine;
using NaughtyAttributes;
using JM.LinqFaster;
using QuickGraph;
using Unity.Mathematics;
using static Unity.Mathematics.math;
using static Unity.Mathematics.noise;
[Serializable]
[CreateAssetMenu(menuName = "Aetheria/Galaxy")]
public class Galaxy : ScriptableObject
{
public GalaxyMapData MapData;
}
[Serializable]
public class GalaxyMapData
{
public GalaxyShapeSettings ShapeSettings = new GalaxyShapeSettings();
[HideInInspector]
public GalaxyMapLayerData StarDensity;
[HideInInspector]
public List<GalaxyMapLayerData> ResourceDensities = new List<GalaxyMapLayerData>();
[HideInInspector]
public List<StarData> Stars = new List<StarData>();
}
public static class StarListExtensions
{
class DijkstraStar
{
public float Cost;
public DijkstraStar Parent;
public StarData Star;
}
public static List<StarData> FindPath(this List<StarData> stars, StarData source, StarData target, bool bestFirst = false)
{
SortedList<float,DijkstraStar> members = new SortedList<float,DijkstraStar>{{0,new DijkstraStar{Star = source}}};
List<DijkstraStar> searched = new List<DijkstraStar>();
while (true)
{
var s = members.FirstOrDefault(m => !searched.Contains(m.Value)).Value; // Lowest cost unsearched node
if (s == null) return null; // No vertices left unsearched
if (s.Star == target) // We found the path
{
Stack<DijkstraStar> path = new Stack<DijkstraStar>(); // Since we start at the end, use a LIFO collection
path.Push(s);
while(path.Peek().Parent!=null) // Keep pushing until we reach the start, which has no parent
path.Push(path.Peek().Parent);
return path.Select(dv => dv.Star).ToList();
}
// foreach (var dijkstraStar in s.Star.Links.Select(i => stars[i]) // All adjacent stars
// .Where(n => members.All(m => m.Value.Star != n)) // That are not members
// .Select(n => new DijkstraStar {Parent = s, Star = n, Cost = s.Cost + (s.Star.Position - n.Position).magnitude}))
// For each adjacent star (filter already visited stars unless heuristic is in use)
foreach (var dijkstraStar in s.Star.Links.WhereSelectF(i => !bestFirst || members.All(m => m.Value.Star != stars[i]),
// Cost is parent cost plus distance
i => new DijkstraStar {Parent = s, Star = stars[i], Cost = s.Cost + (s.Star.Position - stars[i].Position).magnitude}))
// Add new member to list, sorted by cost plus optional heuristic
members.Add(bestFirst ? dijkstraStar.Cost + (dijkstraStar.Star.Position - target.Position).magnitude : dijkstraStar.Cost, dijkstraStar);
searched.Add(s);
}
}
}
[Serializable]
public class StarData
{
public Vector2 Position;
public List<int> Links = new List<int>();
}
public class VoronoiLink
{
public Vertex2 point1;
public Vertex2 point2;
public float Length { get { return (point1.ToVector2() - point2.ToVector2()).magnitude; } }
public VoronoiLink(Vertex2 p1, Vertex2 p2)
{
point1 = p1;
point2 = p2;
}
public bool ContainsPoint(Vertex2 p)
{
return point1 == p || point2 == p;
}
}
public static class VoronoiLinkExtensions
{
class DijkstraVertex
{
public double Cost;
public double Heuristic;
public DijkstraVertex Parent;
public Vertex2 Vertex;
}
public static bool ContainsLine(this IEnumerable<VoronoiLink> data, VoronoiLink l)
{
return
data.Any(
line =>
(line.point1 == l.point1 && line.point2 == l.point2) ||
(line.point1 == l.point2 && line.point2 == l.point1));
}
public static IEnumerable<Vertex2> FindPath(this IEnumerable<VoronoiLink> data, Vertex2 source, Vertex2 target, bool bestFirst = true)
{
List<DijkstraVertex> members = new List<DijkstraVertex>{new DijkstraVertex{Vertex = source}};
List<Vertex2> searched = new List<Vertex2>();
while (true)
{
var v = members.Where(m=>!searched.Contains(m.Vertex)).OrderBy(m=>bestFirst?m.Heuristic:m.Cost).FirstOrDefault();
if (v == null) return null; // No vertices left unsearched
if (v.Vertex == target)
{
Stack<DijkstraVertex> path = new Stack<DijkstraVertex>();
path.Push(v);
while(path.Peek().Parent!=null)
path.Push(path.Peek().Parent);
return path.Select(dv => dv.Vertex).ToList();
}
members.AddRange(data.Where(l => l.point1 == v.Vertex).Select(l => l.point2).Concat(data.Where(l => l.point2 == v.Vertex).Select(l => l.point1))
.Where(n => members.All(m => m.Vertex != n)).Select(n =>
{
var c = new DijkstraVertex {Parent = v, Vertex = n, Cost = v.Cost + v.Vertex.Distance(n)};
if (bestFirst)
c.Heuristic = c.Cost + c.Vertex.Distance(target);
return c;
}));
searched.Add(v.Vertex);
}
}
public static IEnumerable<Vertex2> ConnectedRegion(this IEnumerable<VoronoiLink> data, Vertex2 v)
{
List<Vertex2> members = new List<Vertex2>();
members.Add(v);
while (true)
{
int lastCount = members.Count;
// For each member, add all vertices that are connected to it via a line but are not already a member
foreach (Vertex2 m in members.ToArray())
members.AddRange(data.Where(l => l.point1 == m).Select(l => l.point2)
.Concat(data.Where(l => l.point2 == m).Select(l => l.point1))
.Where(n => !members.Contains(n)));
// If we have stopped finding neighbors, stop traversing
if (members.Count == lastCount)
return members;
}
}
}