Initial attempt at road generation
This commit is contained in:
@ -32,9 +32,10 @@ namespace TransportGame.Generator
|
||||
populationGen.Generate(map);
|
||||
|
||||
// Generate roads
|
||||
// TODO: Generate roads
|
||||
RoadGenerator roadGenerator = new RoadGenerator();
|
||||
roadGenerator.Generate(map);
|
||||
|
||||
Logger.DumpMap(map, "withpop.map");
|
||||
Logger.DumpMap(map, "withroads.map");
|
||||
|
||||
// Done
|
||||
return map;
|
||||
|
@ -3,17 +3,145 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TransportGame.Model;
|
||||
using TransportGame.Model.Road;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Generator
|
||||
namespace TransportGame.Generator
|
||||
{
|
||||
public class RoadGenerator
|
||||
{
|
||||
System.Random random = new System.Random();
|
||||
Map map;
|
||||
|
||||
const int segmentCountLimit = 100;
|
||||
const int maxSegmentLength = 20;
|
||||
const int minSegmentLength = 1;
|
||||
|
||||
public RoadGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public void Generate(Map map)
|
||||
{
|
||||
this.map = map;
|
||||
map.RoadNetwork = new RoadNetwork();
|
||||
|
||||
Queue<RoadNode> queue = new Queue<RoadNode>();
|
||||
RoadNode first = map.RoadNetwork.CreateNode();
|
||||
queue.Enqueue(first);
|
||||
|
||||
// Set starting point
|
||||
if (map.PopulationCenters != null && map.PopulationCenters.Count > 0)
|
||||
{
|
||||
first.X = map.PopulationCenters.First().X;
|
||||
first.Y = map.PopulationCenters.First().Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
first.X = random.Next(map.Width);
|
||||
first.Y = random.Next(map.Height);
|
||||
}
|
||||
|
||||
// Go through each node
|
||||
for (int i = 0; i < segmentCountLimit && queue.Count > 0; i++)
|
||||
{
|
||||
RoadNode node = queue.Dequeue();
|
||||
|
||||
// Produce solutions based on global goals
|
||||
foreach (var next in GlobalGoals(node))
|
||||
{
|
||||
if (CheckLocalConstraints(node, next))
|
||||
{
|
||||
// Next is a temporary node - create a node on the road network
|
||||
var other = map.RoadNetwork.CreateNode();
|
||||
other.X = next.X;
|
||||
other.Y = next.Y;
|
||||
|
||||
// Create a segment
|
||||
var segment = map.RoadNetwork.CreateArticulationSegment();
|
||||
|
||||
// Assign IDs
|
||||
segment.Terminal1Id = node.Id;
|
||||
segment.Terminal2Id = other.Id;
|
||||
node.ArticulationSegmentIds.Add(segment.Id);
|
||||
other.ArticulationSegmentIds.Add(segment.Id);
|
||||
|
||||
// Enqueue node
|
||||
queue.Enqueue(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<RoadNode> GlobalGoals(RoadNode node)
|
||||
{
|
||||
// Goal #1 - connect population centers
|
||||
foreach (var popCenter in map.PopulationCenters.Skip(1))
|
||||
{
|
||||
// Get direction vector
|
||||
float dx = popCenter.X - node.X;
|
||||
float dy = popCenter.Y - node.Y;
|
||||
|
||||
// Ignore if too close to population center
|
||||
if (Math.Abs(dx) <= 1 && Math.Abs(dy) <= 1)
|
||||
continue;
|
||||
|
||||
// Calculate length of direction vector (we need to normalize it)
|
||||
float dlen = Mathf.Sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Length of segment
|
||||
int length = random.Next(minSegmentLength, maxSegmentLength);
|
||||
|
||||
// Calculate coordinates
|
||||
yield return new RoadNode()
|
||||
{
|
||||
X = node.X + length * dx / dlen,
|
||||
Y = node.Y + length * dy / dlen
|
||||
};
|
||||
}
|
||||
|
||||
// Goal #2 - random segments depending on how populated is area - max 25% chance
|
||||
if (random.NextDouble() < map.GetPopulation(Convert.ToInt32(node.X), Convert.ToInt32(node.Y)) * 4)
|
||||
{
|
||||
// Generate direction vector
|
||||
float dx = Convert.ToSingle(random.NextDouble()) * 2 - 1;
|
||||
float dy = Convert.ToSingle(random.NextDouble()) * 2 - 1;
|
||||
|
||||
int length = random.Next(minSegmentLength, maxSegmentLength);
|
||||
|
||||
// Calculate coordinates
|
||||
yield return new RoadNode()
|
||||
{
|
||||
X = node.X + dx * length,
|
||||
Y = node.Y + dy * length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckLocalConstraints(RoadNode first, RoadNode second)
|
||||
{
|
||||
// Make sure point is inside map
|
||||
if (!map.IsInside(Convert.ToInt32(second.X), Convert.ToInt32(second.Y)))
|
||||
return false;
|
||||
|
||||
// Cannot build on water
|
||||
if (map.IsWater(Convert.ToInt32(second.X), Convert.ToInt32(second.Y)))
|
||||
return false;
|
||||
|
||||
// Check steepness
|
||||
int mix = Convert.ToInt32(Math.Min(first.X, second.X));
|
||||
int max = Convert.ToInt32(Math.Max(first.X, second.X));
|
||||
int miy = Convert.ToInt32(Math.Min(first.Y, second.Y));
|
||||
int may = Convert.ToInt32(Math.Max(first.Y, second.Y));
|
||||
|
||||
for (int x = mix; x <= max; x++)
|
||||
for (int y = miy; y <= may; y++)
|
||||
{
|
||||
if (map.GetSteepness(x, y) > 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,16 @@ namespace TransportGame.Model.Road
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the road network
|
||||
/// </summary>
|
||||
public RoadNetwork()
|
||||
{
|
||||
Nodes = new Dictionary<int, RoadNode>();
|
||||
ArticulationSegments = new Dictionary<int,RoadSegment>();
|
||||
IntersectionSegments = new Dictionary<int,RoadSegment>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a node and returns it
|
||||
/// </summary>
|
||||
|
Reference in New Issue
Block a user