├── .gitattributes
├── 01_starter_project.unitypackage
├── 04_car_prefab_1.unitypackage
├── 05_car_prefab_2.unitypackage
├── 06_car_prefab_AI.unitypackage
├── 07_Testing_car_ai.unitypackage
├── 09_Car_markers_1.unitypackage
├── 10_Car_markers_2.unitypackage
├── 11_Car_markers_3.unitypackage
├── 12_Testing_Car_graph.unitypackage
├── 13_Finding_Start_and_End_Marker_P1.unitypackage
├── 14_Finding_Start_and_End_Marker_P2.unitypackage
├── 15_Finding_Start_and_End_Marker_P3.unitypackage
├── 16_Smart intersections.unitypackage
├── 17_Integrating_Pedestrians_1.unitypackage
├── 18_Integrating_Pedestrians_2.unitypackage
├── 19_Fix_4_way_street_marker_connection.unitypackage
├── README.md
└── Scripts
├── AI
├── CarAI.cs
├── CarAI.cs.meta
├── CarController.cs
├── CarController.cs.meta
├── CarSpawner.cs
├── CarSpawner.cs.meta
├── SmartCrosswalks.cs
└── SmartRoad.cs
├── AiDirector.cs
├── Marker.cs
├── RoadHelper.cs
└── RoadHelperStraight.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/01_starter_project.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/01_starter_project.unitypackage
--------------------------------------------------------------------------------
/04_car_prefab_1.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/04_car_prefab_1.unitypackage
--------------------------------------------------------------------------------
/05_car_prefab_2.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/05_car_prefab_2.unitypackage
--------------------------------------------------------------------------------
/06_car_prefab_AI.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/06_car_prefab_AI.unitypackage
--------------------------------------------------------------------------------
/07_Testing_car_ai.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/07_Testing_car_ai.unitypackage
--------------------------------------------------------------------------------
/09_Car_markers_1.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/09_Car_markers_1.unitypackage
--------------------------------------------------------------------------------
/10_Car_markers_2.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/10_Car_markers_2.unitypackage
--------------------------------------------------------------------------------
/11_Car_markers_3.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/11_Car_markers_3.unitypackage
--------------------------------------------------------------------------------
/12_Testing_Car_graph.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/12_Testing_Car_graph.unitypackage
--------------------------------------------------------------------------------
/13_Finding_Start_and_End_Marker_P1.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/13_Finding_Start_and_End_Marker_P1.unitypackage
--------------------------------------------------------------------------------
/14_Finding_Start_and_End_Marker_P2.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/14_Finding_Start_and_End_Marker_P2.unitypackage
--------------------------------------------------------------------------------
/15_Finding_Start_and_End_Marker_P3.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/15_Finding_Start_and_End_Marker_P3.unitypackage
--------------------------------------------------------------------------------
/16_Smart intersections.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/16_Smart intersections.unitypackage
--------------------------------------------------------------------------------
/17_Integrating_Pedestrians_1.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/17_Integrating_Pedestrians_1.unitypackage
--------------------------------------------------------------------------------
/18_Integrating_Pedestrians_2.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/18_Integrating_Pedestrians_2.unitypackage
--------------------------------------------------------------------------------
/19_Fix_4_way_street_marker_connection.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunnyValleyStudio/Simple-Traffic-System-Unity-2020/9242eadcd16881f51f48fd9309ff159f263fc600/19_Fix_4_way_street_marker_connection.unitypackage
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simple-Traffic-System-Unity-2020
2 | [](https://youtu.be/mu7f3Z1lRsE)
3 |
4 |
How to create a simple traffic system for a city builder type of game in Unity 2020. This is not suited for Unity 2019 since I use attribute [field: SerializeField] to show UnityEvents in the inspector. It only works reliably on Unity version 2020 since they have modified the Serialization process.
5 |
6 |
7 |
Attribution:
8 | Made by Sunny Valley Studio
9 |
--------------------------------------------------------------------------------
/Scripts/AI/CarAI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.Events;
6 |
7 | public class CarAI : MonoBehaviour
8 | {
9 | [SerializeField]
10 | private List path = null;
11 | [SerializeField]
12 | private float arriveDistance = .3f, lastPointArriveDistance = .1f;
13 | [SerializeField]
14 | private float turningAngleOffset = 5;
15 | [SerializeField]
16 | private Vector3 currentTargetPosition;
17 |
18 | [SerializeField]
19 | private GameObject raycastStartingPoint = null;
20 | [SerializeField]
21 | private float collisionRaycastLength = 0.1f;
22 |
23 | internal bool IsThisLastPathIndex()
24 | {
25 | return index >= path.Count-1;
26 | }
27 |
28 | private int index = 0;
29 |
30 | private bool stop;
31 | private bool collisionStop = false;
32 |
33 | public bool Stop
34 | {
35 | get { return stop || collisionStop; }
36 | set { stop = value; }
37 | }
38 |
39 | [field: SerializeField]
40 | public UnityEvent OnDrive { get; set; }
41 |
42 | private void Start()
43 | {
44 | if(path == null || path.Count == 0)
45 | {
46 | Stop = true;
47 | }
48 | else
49 | {
50 | currentTargetPosition = path[index];
51 | }
52 | }
53 |
54 | public void SetPath(List path)
55 | {
56 | if(path.Count == 0)
57 | {
58 | Destroy(gameObject);
59 | return;
60 | }
61 | this.path = path;
62 | index = 0;
63 | currentTargetPosition = this.path[index];
64 |
65 | Vector3 relativepoint = transform.InverseTransformPoint(this.path[index + 1]);
66 |
67 | float angle = Mathf.Atan2(relativepoint.x, relativepoint.z) * Mathf.Rad2Deg;
68 |
69 | transform.rotation = Quaternion.Euler(0, angle, 0);
70 | Stop = false;
71 | }
72 |
73 | private void Update()
74 | {
75 | CheckIfArrived();
76 | Drive();
77 | CheckForCollisions();
78 | }
79 |
80 | private void CheckForCollisions()
81 | {
82 | if(Physics.Raycast(raycastStartingPoint.transform.position, transform.forward,collisionRaycastLength, 1 << gameObject.layer))
83 | {
84 | collisionStop = true;
85 | }
86 | else
87 | {
88 | collisionStop = false;
89 | }
90 | }
91 |
92 | private void Drive()
93 | {
94 | if (Stop)
95 | {
96 | OnDrive?.Invoke(Vector2.zero);
97 | }
98 | else
99 | {
100 | Vector3 relativepoint = transform.InverseTransformPoint(currentTargetPosition);
101 | float angle = Mathf.Atan2(relativepoint.x, relativepoint.z) * Mathf.Rad2Deg;
102 | var rotateCar = 0;
103 | if(angle > turningAngleOffset)
104 | {
105 | rotateCar = 1;
106 | }else if(angle < -turningAngleOffset)
107 | {
108 | rotateCar = -1;
109 | }
110 | OnDrive?.Invoke(new Vector2(rotateCar, 1));
111 | }
112 | }
113 |
114 | private void CheckIfArrived()
115 | {
116 | if(Stop == false)
117 | {
118 | var distanceToCheck = arriveDistance;
119 | if(index == path.Count - 1)
120 | {
121 | distanceToCheck = lastPointArriveDistance;
122 | }
123 | if(Vector3.Distance(currentTargetPosition,transform.position) < distanceToCheck)
124 | {
125 | SetNextTargetIndex();
126 | }
127 | }
128 | }
129 |
130 | private void SetNextTargetIndex()
131 | {
132 | index++;
133 | if(index >= path.Count)
134 | {
135 | Stop = true;
136 | Destroy(gameObject);
137 | }
138 | else
139 | {
140 | currentTargetPosition = path[index];
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/Scripts/AI/CarAI.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b07c9c03ed6ed104c9c79355ef33bba7
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/AI/CarController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using UnityEngine;
4 |
5 | [RequireComponent(typeof(Rigidbody))]
6 | public class CarController : MonoBehaviour
7 | {
8 | Rigidbody rb;
9 |
10 | [SerializeField]
11 | private float power = 5;
12 | [SerializeField]
13 | private float torque = 0.5f;
14 | [SerializeField]
15 | private float maxSpeed = 5;
16 |
17 | [SerializeField]
18 | private Vector2 movementVector;
19 |
20 | private void Awake()
21 | {
22 | rb = GetComponent();
23 | }
24 |
25 | public void Move(Vector2 movementInput)
26 | {
27 | this.movementVector = movementInput;
28 | }
29 |
30 | private void FixedUpdate()
31 | {
32 | if(rb.velocity.magnitude < maxSpeed)
33 | {
34 | rb.AddForce(movementVector.y * transform.forward * power);
35 | }
36 | rb.AddTorque(movementVector.x * Vector3.up * torque * movementVector.y);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/Scripts/AI/CarController.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1ce851434c504124c9e41ec05d8b53aa
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/AI/CarSpawner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using Random = UnityEngine.Random;
6 |
7 | public class CarSpawner : MonoBehaviour
8 | {
9 | public GameObject[] carPrefabs;
10 |
11 | private void Start()
12 | {
13 | Instantiate(SelectACarPrefab(), transform);
14 | }
15 |
16 | private GameObject SelectACarPrefab()
17 | {
18 | var randomIndex = Random.Range(0, carPrefabs.Length);
19 | return carPrefabs[randomIndex];
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Scripts/AI/CarSpawner.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c1ac252ad06c2e34a914cc24c8016b0a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/AI/SmartCrosswalks.cs:
--------------------------------------------------------------------------------
1 | using SimpleCity.AI;
2 | using System;
3 | using System.Collections;
4 | using System.Collections.Generic;
5 | using UnityEngine;
6 | using UnityEngine.Events;
7 |
8 | public class SmartCrosswalks : MonoBehaviour
9 | {
10 | List pedestrianList = new List();
11 |
12 | [field: SerializeField]
13 | public UnityEvent OnPedestrianEnter { get; set; }
14 |
15 | [field: SerializeField]
16 | public UnityEvent OnPedestrianExit { get; set; }
17 |
18 | private void OnTriggerEnter(Collider other)
19 | {
20 | var pedestrian = other.GetComponent();
21 | if(pedestrian != null && pedestrianList.Contains(pedestrian) == false)
22 | {
23 | pedestrianList.Add(pedestrian);
24 | pedestrian.Stop = true;
25 | OnPedestrianEnter?.Invoke();
26 | }
27 | }
28 |
29 | private void OnTriggerExit(Collider other)
30 | {
31 | var pedestrian = other.GetComponent();
32 | if(pedestrian != null)
33 | {
34 | RemovePedestrian(pedestrian);
35 | }
36 | }
37 |
38 | private void RemovePedestrian(AiAgent pedestrian)
39 | {
40 | pedestrianList.Remove(pedestrian);
41 | if (pedestrianList.Count <= 0)
42 | OnPedestrianExit?.Invoke();
43 | }
44 |
45 | public void MovePedestrians()
46 | {
47 | foreach (var pedestrian in pedestrianList)
48 | {
49 | pedestrian.Stop = false;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Scripts/AI/SmartRoad.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.Events;
6 |
7 | public class SmartRoad : MonoBehaviour
8 | {
9 | Queue trafficQueue = new Queue();
10 | public CarAI currentCar;
11 |
12 | [SerializeField]
13 | private bool pedestrianWaiting = false, pedestrianWalking = false;
14 |
15 | [field: SerializeField]
16 | public UnityEvent OnPedestrianCanWalk { get; set; }
17 |
18 | private void OnTriggerEnter(Collider other)
19 | {
20 | if (other.CompareTag("Car"))
21 | {
22 | var car = other.GetComponent();
23 | if(car != null && car != currentCar && car.IsThisLastPathIndex() == false)
24 | {
25 | trafficQueue.Enqueue(car);
26 | car.Stop = true;
27 | }
28 | }
29 | }
30 |
31 | private void Update()
32 | {
33 | if(currentCar == null)
34 | {
35 | if(trafficQueue.Count > 0 && pedestrianWaiting == false && pedestrianWalking == false)
36 | {
37 | currentCar = trafficQueue.Dequeue();
38 | currentCar.Stop = false;
39 | }else if(pedestrianWalking || pedestrianWaiting)
40 | {
41 | OnPedestrianCanWalk?.Invoke();
42 | pedestrianWalking = true;
43 | }
44 | }
45 | }
46 |
47 | private void OnTriggerExit(Collider other)
48 | {
49 | if (other.CompareTag("Car"))
50 | {
51 | var car = other.GetComponent();
52 | if(car != null)
53 | {
54 | RemoveCar(car);
55 | }
56 | }
57 | }
58 |
59 | private void RemoveCar(CarAI car)
60 | {
61 | if(car == currentCar)
62 | {
63 | currentCar = null;
64 | }
65 | }
66 |
67 |
68 | public void SetPedestrianFlag(bool val)
69 | {
70 | if (val)
71 | {
72 | pedestrianWaiting = true;
73 | }
74 | else
75 | {
76 | pedestrianWaiting = false;
77 | pedestrianWalking = false;
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Scripts/AiDirector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using UnityEngine;
6 |
7 | namespace SimpleCity.AI
8 | {
9 | public class AiDirector : MonoBehaviour
10 | {
11 | public PlacementManager placementManager;
12 | public GameObject[] pedestrianPrefabs;
13 |
14 | public GameObject carPrefab;
15 |
16 | AdjacencyGraph pedestrianGraph = new AdjacencyGraph();
17 | AdjacencyGraph carGraph = new AdjacencyGraph();
18 |
19 | List carPath = new List();
20 |
21 | public void SpawnAllAagents()
22 | {
23 | foreach (var house in placementManager.GetAllHouses())
24 | {
25 | TrySpawningAnAgent(house, placementManager.GetRandomSpecialStrucutre());
26 | }
27 | foreach (var specialStructure in placementManager.GetAllSpecialStructures())
28 | {
29 | TrySpawningAnAgent(specialStructure, placementManager.GetRandomHouseStructure());
30 | }
31 | }
32 |
33 | private void TrySpawningAnAgent(StructureModel startStructure, StructureModel endStructure)
34 | {
35 | if(startStructure != null && endStructure != null)
36 | {
37 | var startPosition = ((INeedingRoad)startStructure).RoadPosition;
38 | var endPosition = ((INeedingRoad)endStructure).RoadPosition;
39 |
40 | var startMarkerPosition = placementManager.GetStructureAt(startPosition).GetPedestrianSpawnMarker(startStructure.transform.position);
41 | var endMarkerPosition = placementManager.GetStructureAt(endPosition).GetNearestPedestrianMarkerTo(endStructure.transform.position);
42 |
43 | var agent = Instantiate(GetRandomPedestrian(), startMarkerPosition.Position, Quaternion.identity);
44 | var path = placementManager.GetPathBetween(startPosition, endPosition, true);
45 | if(path.Count > 0)
46 | {
47 | path.Reverse();
48 | List agentPath = GetPedestrianPath(path, startMarkerPosition.Position, endMarkerPosition);
49 | var aiAgent = agent.GetComponent();
50 | aiAgent.Initialize(agentPath);
51 | }
52 | }
53 | }
54 |
55 | public void SpawnACar()
56 | {
57 | foreach (var house in placementManager.GetAllHouses())
58 | {
59 | TrySpawninACar(house, placementManager.GetRandomSpecialStrucutre());
60 | }
61 | }
62 |
63 | private void TrySpawninACar(StructureModel startStructure, StructureModel endStructure)
64 | {
65 | if (startStructure != null && endStructure != null)
66 | {
67 | var startRoadPosition = ((INeedingRoad)startStructure).RoadPosition;
68 | var endRoadPosition = ((INeedingRoad)endStructure).RoadPosition;
69 |
70 | var path = placementManager.GetPathBetween(startRoadPosition, endRoadPosition, true);
71 | path.Reverse();
72 |
73 | if (path.Count == 0 && path.Count>2)
74 | return;
75 |
76 | var startMarkerPosition = placementManager.GetStructureAt(startRoadPosition).GetCarSpawnMarker(path[1]);
77 |
78 | var endMarkerPosition = placementManager.GetStructureAt(endRoadPosition).GetCarEndMarker(path[path.Count-2]);
79 |
80 | carPath = GetCarPath(path, startMarkerPosition.Position, endMarkerPosition.Position);
81 |
82 | if(carPath.Count > 0)
83 | {
84 | var car = Instantiate(carPrefab, startMarkerPosition.Position, Quaternion.identity);
85 | car.GetComponent().SetPath(carPath);
86 | }
87 | }
88 | }
89 |
90 | private List GetPedestrianPath(List path, Vector3 startPosition, Vector3 endPosition)
91 | {
92 | pedestrianGraph.ClearGraph();
93 | CreatAPedestrianGraph(path);
94 | Debug.Log(pedestrianGraph);
95 | return AdjacencyGraph.AStarSearch(pedestrianGraph,startPosition,endPosition);
96 | }
97 |
98 |
99 | private void CreatAPedestrianGraph(List path)
100 | {
101 | Dictionary tempDictionary = new Dictionary();
102 |
103 | for (int i = 0; i < path.Count; i++)
104 | {
105 | var currentPosition = path[i];
106 | var roadStructure = placementManager.GetStructureAt(currentPosition);
107 | var markersList = roadStructure.GetPedestrianMarkers();
108 | bool limitDistance = markersList.Count == 4;
109 | tempDictionary.Clear();
110 | foreach (var marker in markersList)
111 | {
112 | pedestrianGraph.AddVertex(marker.Position);
113 | foreach (var markerNeighbourPosition in marker.GetAdjacentPositions())
114 | {
115 | pedestrianGraph.AddEdge(marker.Position, markerNeighbourPosition);
116 | }
117 |
118 | if(marker.OpenForconnections && i+1 < path.Count)
119 | {
120 | var nextRoadStructure = placementManager.GetStructureAt(path[i + 1]);
121 | if (limitDistance)
122 | {
123 | tempDictionary.Add(marker, nextRoadStructure.GetNearestPedestrianMarkerTo(marker.Position));
124 | }
125 | else
126 | {
127 | pedestrianGraph.AddEdge(marker.Position, nextRoadStructure.GetNearestPedestrianMarkerTo(marker.Position));
128 | }
129 | }
130 | }
131 | if(limitDistance && tempDictionary.Count == 4)
132 | {
133 | var distanceSortedMarkers = tempDictionary.OrderBy(x => Vector3.Distance(x.Key.Position, x.Value)).ToList();
134 | for (int j = 0; j < 2; j++)
135 | {
136 | pedestrianGraph.AddEdge(distanceSortedMarkers[j].Key.Position, distanceSortedMarkers[j].Value);
137 | }
138 | }
139 | }
140 | }
141 |
142 | private List GetCarPath(List path, Vector3 startPosition, Vector3 endPosition)
143 | {
144 | carGraph.ClearGraph();
145 | CreatACarGraph(path);
146 | Debug.Log(carGraph);
147 | return AdjacencyGraph.AStarSearch(carGraph, startPosition, endPosition);
148 | }
149 |
150 | private void CreatACarGraph(List path)
151 | {
152 | Dictionary tempDictionary = new Dictionary();
153 | for (int i = 0; i < path.Count; i++)
154 | {
155 | var currentPosition = path[i];
156 | var roadStructure = placementManager.GetStructureAt(currentPosition);
157 | var markersList = roadStructure.GetCarMarkers();
158 | var limitDistance = markersList.Count > 3;
159 | tempDictionary.Clear();
160 |
161 | foreach (var marker in markersList)
162 | {
163 | carGraph.AddVertex(marker.Position);
164 | foreach (var markerNeighbour in marker.adjacentMarkers)
165 | {
166 | carGraph.AddEdge(marker.Position, markerNeighbour.Position);
167 | }
168 | if(marker.OpenForconnections && i + 1 < path.Count)
169 | {
170 | var nextRoadPosition = placementManager.GetStructureAt(path[i + 1]);
171 | if (limitDistance)
172 | {
173 | tempDictionary.Add(marker, nextRoadPosition.GetNearestCarMarkerTo(marker.Position));
174 | }
175 | else
176 | {
177 | carGraph.AddEdge(marker.Position, nextRoadPosition.GetNearestCarMarkerTo(marker.Position));
178 | }
179 | }
180 | }
181 | if (limitDistance && tempDictionary.Count > 2)
182 | {
183 | var distanceSortedMarkers = tempDictionary.OrderBy(x => Vector3.Distance(x.Key.Position, x.Value)).ToList();
184 | foreach (var item in distanceSortedMarkers)
185 | {
186 | Debug.Log(Vector3.Distance(item.Key.Position, item.Value));
187 | }
188 | for (int j = 0; j < 2; j++)
189 | {
190 | carGraph.AddEdge(distanceSortedMarkers[j].Key.Position, distanceSortedMarkers[j].Value);
191 | }
192 | }
193 | }
194 | }
195 |
196 | private GameObject GetRandomPedestrian()
197 | {
198 | return pedestrianPrefabs[UnityEngine.Random.Range(0, pedestrianPrefabs.Length)];
199 | }
200 |
201 | private void Update()
202 | {
203 | //DrawGraph(carGraph);
204 | for (int i = 1; i < carPath.Count; i++)
205 | {
206 | Debug.DrawLine(carPath[i - 1] + Vector3.up, carPath[i] + Vector3.up, Color.magenta);
207 | }
208 | }
209 |
210 | private void DrawGraph(AdjacencyGraph graph)
211 | {
212 | foreach (var vertex in graph.GetVertices())
213 | {
214 | foreach (var vertexNeighbour in graph.GetConnectedVerticesTo(vertex))
215 | {
216 | Debug.DrawLine(vertex.Position + Vector3.up, vertexNeighbour.Position + Vector3.up, Color.red);
217 | }
218 | }
219 | }
220 |
221 |
222 | }
223 | }
224 |
225 |
--------------------------------------------------------------------------------
/Scripts/Marker.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using UnityEditor;
5 | using UnityEngine;
6 |
7 | namespace SimpleCity.AI
8 | {
9 | public class Marker : MonoBehaviour
10 | {
11 | public Vector3 Position { get => transform.position;}
12 |
13 | public List adjacentMarkers;
14 |
15 | [SerializeField]
16 | private bool openForConnections;
17 |
18 | public bool OpenForconnections
19 | {
20 | get { return openForConnections; }
21 | }
22 |
23 | public List GetAdjacentPositions()
24 | {
25 | return new List(adjacentMarkers.Select(x => x.Position).ToList());
26 | }
27 |
28 | private void OnDrawGizmos()
29 | {
30 | if(Selection.activeObject == gameObject)
31 | {
32 | Gizmos.color = Color.red;
33 | if (adjacentMarkers.Count > 0)
34 | {
35 | foreach (var item in adjacentMarkers)
36 | {
37 | Gizmos.DrawLine(transform.position, item.Position);
38 | }
39 | }
40 | Gizmos.color = Color.white;
41 | }
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/Scripts/RoadHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | namespace SimpleCity.AI
7 | {
8 | public class RoadHelper : MonoBehaviour
9 | {
10 | [SerializeField]
11 | protected List pedestrianMarkers;
12 | [SerializeField]
13 | protected List carMarkers;
14 | [SerializeField]
15 | protected bool isCorner;
16 | [SerializeField]
17 | protected bool hasCrosswalks;
18 |
19 | float approximateThresholdCorner = 0.3f;
20 |
21 | [SerializeField]
22 | private Marker incomming, outgoing;
23 |
24 | public virtual Marker GetpositioForPedestrianToSpwan(Vector3 structurePosition)
25 | {
26 | return GetClosestMarkeTo(structurePosition, pedestrianMarkers);
27 | }
28 |
29 | public virtual Marker GetPositioForCarToSpawn(Vector3 nextPathPosition)
30 | {
31 | return outgoing;
32 | }
33 |
34 | public virtual Marker GetPositioForCarToEnd(Vector3 previousPathPosition)
35 | {
36 | return incomming;
37 | }
38 |
39 | protected Marker GetClosestMarkeTo(Vector3 structurePosition, List pedestrianMarkers, bool isCorner = false)
40 | {
41 | if (isCorner)
42 | {
43 | foreach (var marker in pedestrianMarkers)
44 | {
45 | var direction = marker.Position - structurePosition;
46 | direction.Normalize();
47 | if(Mathf.Abs(direction.x) < approximateThresholdCorner || Mathf.Abs(direction.z) < approximateThresholdCorner)
48 | {
49 | return marker;
50 | }
51 | }
52 | return null;
53 | }
54 | else
55 | {
56 | Marker closestMarker = null;
57 | float distance = float.MaxValue;
58 | foreach (var marker in pedestrianMarkers)
59 | {
60 | var markerDistance = Vector3.Distance(structurePosition, marker.Position);
61 | if(distance > markerDistance)
62 | {
63 | distance = markerDistance;
64 | closestMarker = marker;
65 | }
66 | }
67 | return closestMarker;
68 | }
69 | }
70 |
71 | public Vector3 GetClosestPedestrainPosition(Vector3 currentPosition)
72 | {
73 | return GetClosestMarkeTo(currentPosition, pedestrianMarkers, isCorner).Position;
74 | }
75 |
76 | public Vector3 GetClosestCarMarkerPosition(Vector3 currentPosition)
77 | {
78 | return GetClosestMarkeTo(currentPosition, carMarkers, false).Position;
79 | }
80 |
81 |
82 | public List GetAllPedestrianMarkers()
83 | {
84 | return pedestrianMarkers;
85 | }
86 |
87 | public List GetAllCarMarkers()
88 | {
89 | return carMarkers;
90 | }
91 | }
92 | }
93 |
94 |
--------------------------------------------------------------------------------
/Scripts/RoadHelperStraight.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | namespace SimpleCity.AI
7 | {
8 | public class RoadHelperStraight : RoadHelper
9 | {
10 | [SerializeField]
11 | private Marker leftLaneMarker90, rightLaneMarker90;
12 |
13 | public override Marker GetPositioForCarToSpawn(Vector3 nextPathPosition)
14 | {
15 | int angle = (int)transform.rotation.eulerAngles.y;
16 | var direction = nextPathPosition - transform.position;
17 | return GetCorrectMarker(angle, direction);
18 | }
19 |
20 |
21 | public override Marker GetPositioForCarToEnd(Vector3 previousPathPosition)
22 | {
23 | int angle = (int)transform.rotation.eulerAngles.y;
24 | var direction = transform.position - previousPathPosition;
25 | return GetCorrectMarker(angle, direction);
26 | }
27 |
28 |
29 | private Marker GetCorrectMarker(int angle, Vector3 directionVector)
30 | {
31 | var direction = GetDirection(directionVector);
32 | if (angle == 0)
33 | {
34 | if (direction == Directon.left)
35 | {
36 | return rightLaneMarker90;
37 | }
38 | else
39 | {
40 | return leftLaneMarker90;
41 | }
42 | } else if (angle == 90)
43 | {
44 | if (direction == Directon.up)
45 | {
46 | return rightLaneMarker90;
47 | }
48 | else
49 | {
50 | return leftLaneMarker90;
51 | }
52 | }else if(angle == 270)
53 | {
54 | if (direction == Directon.left)
55 | {
56 | return leftLaneMarker90;
57 | }
58 | else
59 | {
60 | return rightLaneMarker90;
61 | }
62 | }
63 | else
64 | {
65 | if (direction == Directon.up)
66 | {
67 | return leftLaneMarker90;
68 | }
69 | else
70 | {
71 | return rightLaneMarker90;
72 | }
73 | }
74 | }
75 |
76 | public enum Directon
77 | {
78 | up,
79 | down,
80 | left,
81 | right
82 | }
83 |
84 | public Directon GetDirection(Vector3 direction)
85 | {
86 | if(Mathf.Abs(direction.z) > .5f)
87 | {
88 | if(direction.z > 0.5f)
89 | {
90 | return Directon.up;
91 | }
92 | else
93 | {
94 | return Directon.down;
95 | }
96 | }
97 | else
98 | {
99 | if(direction.x > 0.5f)
100 | {
101 | return Directon.right;
102 | }
103 | else
104 | {
105 | return Directon.left;
106 | }
107 | }
108 | }
109 | }
110 | }
111 |
112 |
--------------------------------------------------------------------------------