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>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,8 @@ namespace TransportGame.MapViewer
 | 
			
		||||
            None = 0,
 | 
			
		||||
            Elevation = 1,
 | 
			
		||||
            Population = 2,
 | 
			
		||||
            All = Elevation | Population
 | 
			
		||||
            RoadArticulations = 3,
 | 
			
		||||
            All = Elevation | Population | RoadArticulations
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@@ -57,9 +58,13 @@ namespace TransportGame.MapViewer
 | 
			
		||||
                // Create texture on which to draw
 | 
			
		||||
                Bitmap24 bitmap = new Bitmap24(Convert.ToInt32(map.Width * Scale), Convert.ToInt32(map.Height * Scale));
 | 
			
		||||
 | 
			
		||||
                // First layer - cells
 | 
			
		||||
                // Elevation, population
 | 
			
		||||
                DrawPixels(bitmap, map, (layers & Layers.Elevation) > 0, (layers & Layers.Population) > 0);
 | 
			
		||||
 | 
			
		||||
                // Roads
 | 
			
		||||
                if ((layers & Layers.RoadArticulations) > 0)
 | 
			
		||||
                    DrawRoads(bitmap, map);
 | 
			
		||||
 | 
			
		||||
                // Done
 | 
			
		||||
                return bitmap;
 | 
			
		||||
            }
 | 
			
		||||
@@ -83,6 +88,12 @@ namespace TransportGame.MapViewer
 | 
			
		||||
                    int mapX = Convert.ToInt32(x / Scale);
 | 
			
		||||
                    int mapY = Convert.ToInt32(y / Scale);
 | 
			
		||||
 | 
			
		||||
                    if (mapX >= map.Width)
 | 
			
		||||
                        mapX = map.Width - 1;
 | 
			
		||||
 | 
			
		||||
                    if (mapY >= map.Height)
 | 
			
		||||
                        mapY = map.Height - 1;
 | 
			
		||||
 | 
			
		||||
                    // Draw water
 | 
			
		||||
                    if (map.IsWater(mapX, mapY))
 | 
			
		||||
                        bitmap[x, y] = WaterColor;
 | 
			
		||||
@@ -108,5 +119,18 @@ namespace TransportGame.MapViewer
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void DrawRoads(Bitmap24 bitmap, Map map)
 | 
			
		||||
        {
 | 
			
		||||
            // Draw road segments
 | 
			
		||||
            foreach (var pair in map.RoadNetwork.ArticulationSegments)
 | 
			
		||||
            {
 | 
			
		||||
                bitmap.DrawLine(Convert.ToInt32(pair.Value.Terminal1.X * Scale),
 | 
			
		||||
                    Convert.ToInt32(pair.Value.Terminal1.Y * Scale),
 | 
			
		||||
                    Convert.ToInt32(pair.Value.Terminal2.X * Scale),
 | 
			
		||||
                    Convert.ToInt32(pair.Value.Terminal2.Y * Scale),
 | 
			
		||||
                    Colors.Black);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,7 @@
 | 
			
		||||
                    <CheckBox IsChecked="{Binding LayersAll}">(All)</CheckBox>
 | 
			
		||||
                    <CheckBox IsChecked="{Binding LayerElevation}">Elevation</CheckBox>
 | 
			
		||||
                    <CheckBox IsChecked="{Binding LayerPopulation}">Population</CheckBox>
 | 
			
		||||
                    <CheckBox IsChecked="{Binding LayerRoadArticulations}">Roads (articulation map)</CheckBox>
 | 
			
		||||
                    
 | 
			
		||||
                    <Button Name="buttonRender" Grid.Column="1" Content="Render" Click="buttonRender_Click" />
 | 
			
		||||
                    
 | 
			
		||||
 
 | 
			
		||||
@@ -109,10 +109,10 @@ namespace TransportGame.MapViewer
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (LayerElevation && LayerPopulation)
 | 
			
		||||
                if (LayerElevation && LayerPopulation && LayerRoadArticulations)
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                if (!LayerElevation && !LayerPopulation)
 | 
			
		||||
                if (!LayerElevation && !LayerPopulation && !LayerRoadArticulations)
 | 
			
		||||
                    return false;
 | 
			
		||||
 | 
			
		||||
                return null;
 | 
			
		||||
@@ -171,6 +171,28 @@ namespace TransportGame.MapViewer
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the population layer flag
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool LayerRoadArticulations
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return _layers.HasFlag(MapRenderer.Layers.RoadArticulations);
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                if (value) _layers |= MapRenderer.Layers.RoadArticulations;
 | 
			
		||||
                else _layers &= ~MapRenderer.Layers.RoadArticulations;
 | 
			
		||||
 | 
			
		||||
                if (PropertyChanged != null)
 | 
			
		||||
                {
 | 
			
		||||
                    PropertyChanged(this, new PropertyChangedEventArgs("LayerRoadArticulations"));
 | 
			
		||||
                    PropertyChanged(this, new PropertyChangedEventArgs("LayersAll"));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -68,5 +68,35 @@ namespace TransportGame.MapViewer.Model
 | 
			
		||||
 | 
			
		||||
            return 3 * (x + y * Width);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void DrawLine(int x0, int y0, int x1, int y1, Color color)
 | 
			
		||||
        {
 | 
			
		||||
            int dx = Math.Abs(x1 - x0);
 | 
			
		||||
            int sx = x0 < x1 ? 1 : -1;
 | 
			
		||||
 | 
			
		||||
            int dy = Math.Abs(y1 - y0);
 | 
			
		||||
            int sy = y0 < y1 ? 1 : -1;
 | 
			
		||||
 | 
			
		||||
            int err = (dx > dy ? dx : -dy) / 2, e2;
 | 
			
		||||
 | 
			
		||||
            for (; ; )
 | 
			
		||||
            {
 | 
			
		||||
                this[x0, y0] = color;
 | 
			
		||||
                if (x0 == x1 && y0 == y1)
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                e2 = err;
 | 
			
		||||
                if (e2 > -dx) 
 | 
			
		||||
                {
 | 
			
		||||
                    err -= dy; 
 | 
			
		||||
                    x0 += sx; 
 | 
			
		||||
                }
 | 
			
		||||
                if (e2 < dy) 
 | 
			
		||||
                { 
 | 
			
		||||
                    err += dx; 
 | 
			
		||||
                    y0 += sy; 
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,20 @@ namespace TransportGame.MapViewer.Storage
 | 
			
		||||
    {
 | 
			
		||||
        public static Map Read(string file)
 | 
			
		||||
        {
 | 
			
		||||
            return SerializationHelper.DeserializeXml<Map>(file);
 | 
			
		||||
            Map map = SerializationHelper.DeserializeXml<Map>(file);
 | 
			
		||||
 | 
			
		||||
            // Fix road network issue
 | 
			
		||||
            foreach (var pair in map.RoadNetwork.ArticulationSegments)
 | 
			
		||||
                pair.Value.ParentNetwork = map.RoadNetwork;
 | 
			
		||||
 | 
			
		||||
            foreach (var pair in map.RoadNetwork.IntersectionSegments)
 | 
			
		||||
                pair.Value.ParentNetwork = map.RoadNetwork;
 | 
			
		||||
 | 
			
		||||
            foreach (var pair in map.RoadNetwork.Nodes)
 | 
			
		||||
                pair.Value.ParentNetwork = map.RoadNetwork;
 | 
			
		||||
 | 
			
		||||
            // Done
 | 
			
		||||
            return map;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user