VehicleAI.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Traffic Simulation
  2. // https://github.com/mchrbn/unity-traffic-simulation
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. namespace TrafficSimulation {
  7. /*
  8. [-] Check prefab #6 issue
  9. [-] Deaccelerate when see stop in front
  10. [-] Smooth sharp turns when two segments are linked
  11. */
  12. public struct Target{
  13. public int segment;
  14. public int waypoint;
  15. }
  16. public enum Status{
  17. GO,
  18. STOP,
  19. SLOW_DOWN
  20. }
  21. public class VehicleAI : MonoBehaviour
  22. {
  23. [Header("Traffic System")]
  24. [Tooltip("Current active traffic system")]
  25. public TrafficSystem trafficSystem;
  26. [Tooltip("Determine when the vehicle has reached its target. Can be used to \"anticipate\" earlier the next waypoint (the higher this number his, the earlier it will anticipate the next waypoint)")]
  27. public float waypointThresh = 6;
  28. [Header("Radar")]
  29. [Tooltip("Empty gameobject from where the rays will be casted")]
  30. public Transform raycastAnchor;
  31. [Tooltip("Length of the casted rays")]
  32. public float raycastLength = 5;
  33. [Tooltip("Spacing between each rays")]
  34. public int raySpacing = 2;
  35. [Tooltip("Number of rays to be casted")]
  36. public int raysNumber = 6;
  37. [Tooltip("If detected vehicle is below this distance, ego vehicle will stop")]
  38. public float emergencyBrakeThresh = 2f;
  39. [Tooltip("If detected vehicle is below this distance (and above, above distance), ego vehicle will slow down")]
  40. public float slowDownThresh = 4f;
  41. [HideInInspector] public Status vehicleStatus = Status.GO;
  42. private WheelDrive wheelDrive;
  43. private float initMaxSpeed = 0;
  44. private int pastTargetSegment = -1;
  45. private Target currentTarget;
  46. private Target futureTarget;
  47. void Start()
  48. {
  49. wheelDrive = this.GetComponent<WheelDrive>();
  50. if(trafficSystem == null)
  51. return;
  52. initMaxSpeed = wheelDrive.maxSpeed;
  53. SetWaypointVehicleIsOn();
  54. }
  55. void Update(){
  56. if(trafficSystem == null)
  57. return;
  58. WaypointChecker();
  59. MoveVehicle();
  60. }
  61. void WaypointChecker(){
  62. GameObject waypoint = trafficSystem.segments[currentTarget.segment].waypoints[currentTarget.waypoint].gameObject;
  63. //Position of next waypoint relative to the car
  64. Vector3 wpDist = this.transform.InverseTransformPoint(new Vector3(waypoint.transform.position.x, this.transform.position.y, waypoint.transform.position.z));
  65. //Go to next waypoint if arrived to current
  66. if(wpDist.magnitude < waypointThresh){
  67. //Get next target
  68. currentTarget.waypoint++;
  69. if(currentTarget.waypoint >= trafficSystem.segments[currentTarget.segment].waypoints.Count){
  70. pastTargetSegment = currentTarget.segment;
  71. currentTarget.segment = futureTarget.segment;
  72. currentTarget.waypoint = 0;
  73. }
  74. //Get future target
  75. futureTarget.waypoint = currentTarget.waypoint + 1;
  76. if(futureTarget.waypoint >= trafficSystem.segments[currentTarget.segment].waypoints.Count){
  77. futureTarget.waypoint = 0;
  78. futureTarget.segment = GetNextSegmentId();
  79. }
  80. }
  81. }
  82. void MoveVehicle(){
  83. //Default, full acceleration, no break and no steering
  84. float acc = 1;
  85. float brake = 0;
  86. float steering = 0;
  87. wheelDrive.maxSpeed = initMaxSpeed;
  88. //Calculate if there is a planned turn
  89. Transform targetTransform = trafficSystem.segments[currentTarget.segment].waypoints[currentTarget.waypoint].transform;
  90. Transform futureTargetTransform = trafficSystem.segments[futureTarget.segment].waypoints[futureTarget.waypoint].transform;
  91. Vector3 futureVel = futureTargetTransform.position - targetTransform.position;
  92. float futureSteering = Mathf.Clamp(this.transform.InverseTransformDirection(futureVel.normalized).x, -1, 1);
  93. //Check if the car has to stop
  94. if(vehicleStatus == Status.STOP){
  95. acc = 0;
  96. brake = 1;
  97. wheelDrive.maxSpeed = Mathf.Min(wheelDrive.maxSpeed / 2f, 5f);
  98. }
  99. else{
  100. //Not full acceleration if have to slow down
  101. if(vehicleStatus == Status.SLOW_DOWN){
  102. acc = .3f;
  103. brake = 0f;
  104. }
  105. //If planned to steer, decrease the speed
  106. if(futureSteering > .3f || futureSteering < -.3f){
  107. wheelDrive.maxSpeed = Mathf.Min(wheelDrive.maxSpeed, wheelDrive.steeringSpeedMax);
  108. }
  109. //2. Check if there are obstacles which are detected by the radar
  110. float hitDist;
  111. GameObject obstacle = GetDetectedObstacles(out hitDist);
  112. //Check if we hit something
  113. if(obstacle != null){
  114. WheelDrive otherVehicle = null;
  115. otherVehicle = obstacle.GetComponent<WheelDrive>();
  116. ///////////////////////////////////////////////////////////////
  117. //Differenciate between other vehicles AI and generic obstacles (including controlled vehicle, if any)
  118. if(otherVehicle != null){
  119. //Check if it's front vehicle
  120. float dotFront = Vector3.Dot(this.transform.forward, otherVehicle.transform.forward);
  121. //If detected front vehicle max speed is lower than ego vehicle, then decrease ego vehicle max speed
  122. if(otherVehicle.maxSpeed < wheelDrive.maxSpeed && dotFront > .8f){
  123. float ms = Mathf.Max(wheelDrive.GetSpeedMS(otherVehicle.maxSpeed) - .5f, .1f);
  124. wheelDrive.maxSpeed = wheelDrive.GetSpeedUnit(ms);
  125. }
  126. //If the two vehicles are too close, and facing the same direction, brake the ego vehicle
  127. if(hitDist < emergencyBrakeThresh && dotFront > .8f){
  128. acc = 0;
  129. brake = 1;
  130. wheelDrive.maxSpeed = Mathf.Max(wheelDrive.maxSpeed / 2f, wheelDrive.minSpeed);
  131. }
  132. //If the two vehicles are too close, and not facing same direction, slight make the ego vehicle go backward
  133. else if(hitDist < emergencyBrakeThresh && dotFront <= .8f){
  134. acc = -.3f;
  135. brake = 0f;
  136. wheelDrive.maxSpeed = Mathf.Max(wheelDrive.maxSpeed / 2f, wheelDrive.minSpeed);
  137. //Check if the vehicle we are close to is located on the right or left then apply according steering to try to make it move
  138. float dotRight = Vector3.Dot(this.transform.forward, otherVehicle.transform.right);
  139. //Right
  140. if(dotRight > 0.1f) steering = -.3f;
  141. //Left
  142. else if(dotRight < -0.1f) steering = .3f;
  143. //Middle
  144. else steering = -.7f;
  145. }
  146. //If the two vehicles are getting close, slow down their speed
  147. else if(hitDist < slowDownThresh){
  148. acc = .5f;
  149. brake = 0f;
  150. //wheelDrive.maxSpeed = Mathf.Max(wheelDrive.maxSpeed / 1.5f, wheelDrive.minSpeed);
  151. }
  152. }
  153. ///////////////////////////////////////////////////////////////////
  154. // Generic obstacles
  155. else{
  156. //Emergency brake if getting too close
  157. if(hitDist < emergencyBrakeThresh){
  158. acc = 0;
  159. brake = 1;
  160. wheelDrive.maxSpeed = Mathf.Max(wheelDrive.maxSpeed / 2f, wheelDrive.minSpeed);
  161. }
  162. //Otherwise if getting relatively close decrease speed
  163. else if(hitDist < slowDownThresh){
  164. acc = .5f;
  165. brake = 0f;
  166. }
  167. }
  168. }
  169. //Check if we need to steer to follow path
  170. if(acc > 0f){
  171. Vector3 desiredVel = trafficSystem.segments[currentTarget.segment].waypoints[currentTarget.waypoint].transform.position - this.transform.position;
  172. steering = Mathf.Clamp(this.transform.InverseTransformDirection(desiredVel.normalized).x, -1f, 1f);
  173. }
  174. }
  175. //Move the car
  176. wheelDrive.Move(acc, steering, brake);
  177. }
  178. GameObject GetDetectedObstacles(out float _hitDist){
  179. GameObject detectedObstacle = null;
  180. float minDist = 1000f;
  181. float initRay = (raysNumber / 2f) * raySpacing;
  182. float hitDist = -1f;
  183. for(float a=-initRay; a<=initRay; a+=raySpacing){
  184. CastRay(raycastAnchor.transform.position, a, this.transform.forward, raycastLength, out detectedObstacle, out hitDist);
  185. if(detectedObstacle == null) continue;
  186. float dist = Vector3.Distance(this.transform.position, detectedObstacle.transform.position);
  187. if(dist < minDist) {
  188. minDist = dist;
  189. break;
  190. }
  191. }
  192. _hitDist = hitDist;
  193. return detectedObstacle;
  194. }
  195. void CastRay(Vector3 _anchor, float _angle, Vector3 _dir, float _length, out GameObject _outObstacle, out float _outHitDistance){
  196. _outObstacle = null;
  197. _outHitDistance = -1f;
  198. //Draw raycast
  199. Debug.DrawRay(_anchor, Quaternion.Euler(0, _angle, 0) * _dir * _length, new Color(1, 0, 0, 0.5f));
  200. //Detect hit only on the autonomous vehicle layer
  201. int layer = 1 << LayerMask.NameToLayer("AutonomousVehicle");
  202. int finalMask = layer;
  203. foreach(string layerName in trafficSystem.collisionLayers){
  204. int id = 1 << LayerMask.NameToLayer(layerName);
  205. finalMask = finalMask | id;
  206. }
  207. RaycastHit hit;
  208. if(Physics.Raycast(_anchor, Quaternion.Euler(0, _angle, 0) * _dir, out hit, _length, finalMask)){
  209. _outObstacle = hit.collider.gameObject;
  210. _outHitDistance = hit.distance;
  211. }
  212. }
  213. int GetNextSegmentId(){
  214. if(trafficSystem.segments[currentTarget.segment].nextSegments.Count == 0)
  215. return 0;
  216. int c = Random.Range(0, trafficSystem.segments[currentTarget.segment].nextSegments.Count);
  217. return trafficSystem.segments[currentTarget.segment].nextSegments[c].id;
  218. }
  219. void SetWaypointVehicleIsOn(){
  220. //Find current target
  221. foreach(Segment segment in trafficSystem.segments){
  222. if(segment.IsOnSegment(this.transform.position)){
  223. currentTarget.segment = segment.id;
  224. //Find nearest waypoint to start within the segment
  225. float minDist = float.MaxValue;
  226. for(int j=0; j<trafficSystem.segments[currentTarget.segment].waypoints.Count; j++){
  227. float d = Vector3.Distance(this.transform.position, trafficSystem.segments[currentTarget.segment].waypoints[j].transform.position);
  228. //Only take in front points
  229. Vector3 lSpace = this.transform.InverseTransformPoint(trafficSystem.segments[currentTarget.segment].waypoints[j].transform.position);
  230. if(d < minDist && lSpace.z > 0){
  231. minDist = d;
  232. currentTarget.waypoint = j;
  233. }
  234. }
  235. break;
  236. }
  237. }
  238. //Get future target
  239. futureTarget.waypoint = currentTarget.waypoint + 1;
  240. futureTarget.segment = currentTarget.segment;
  241. if(futureTarget.waypoint >= trafficSystem.segments[currentTarget.segment].waypoints.Count){
  242. futureTarget.waypoint = 0;
  243. futureTarget.segment = GetNextSegmentId();
  244. }
  245. }
  246. public int GetSegmentVehicleIsIn(){
  247. int vehicleSegment = currentTarget.segment;
  248. bool isOnSegment = trafficSystem.segments[vehicleSegment].IsOnSegment(this.transform.position);
  249. if(!isOnSegment){
  250. bool isOnPSegement = trafficSystem.segments[pastTargetSegment].IsOnSegment(this.transform.position);
  251. if(isOnPSegement)
  252. vehicleSegment = pastTargetSegment;
  253. }
  254. return vehicleSegment;
  255. }
  256. }
  257. }