WalkPath.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. #if UNITY_EDITOR
  4. using UnityEditor;
  5. #endif
  6. using System;
  7. [System.Serializable]
  8. public class WalkPath : MonoBehaviour
  9. {
  10. [Tooltip("Objects of motion / Объекты движения")] public GameObject[] peoplePrefabs;
  11. [Tooltip("Number of paths / Количество путей")] public int numberOfWays;
  12. [Tooltip("Space between paths / Пространство между путями")] public float lineSpacing;
  13. [Tooltip("Density of movement of objects / Плотность движения объектов")] [Range(0.01f, 0.50f)] public float Density = 0.2f;
  14. [Tooltip("Distance between objects / Дистанция между объектами")][Range(1f, 10f)] public float _minimalObjectLength = 1f;
  15. [Tooltip("Make the path closed in the ring / Сделать путь замкнутым в кольцо")] public bool loopPath;
  16. protected float[] _distances;
  17. [HideInInspector] public List<Vector3> pathPoint = new List<Vector3>();
  18. [HideInInspector] public List<GameObject> pathPointTransform = new List<GameObject>();
  19. [HideInInspector] public Vector3[,] points;
  20. [HideInInspector] public List<Vector3> CalcPoint = new List<Vector3>();
  21. [HideInInspector] public int[] pointLength = new int[10];
  22. [HideInInspector] public bool disableLineDraw = false;
  23. [HideInInspector] public bool[] _forward;
  24. [HideInInspector] public GameObject par;
  25. [HideInInspector] public PathType pathType;
  26. /// <summary>
  27. /// Радиус сферы-стёрки [м]
  28. /// </summary>
  29. [Tooltip("Radius of the sphere-scraper [m] / Радиус сферы-стёрки [м]"), Range(0.1f, 25)]
  30. public float eraseRadius = 2f;
  31. /// <summary>
  32. /// Минимальное расстояние от курсора до линии при котором можно добавить новую точку в путь [м]
  33. /// </summary>
  34. [Tooltip("The minimum distance from the cursor to the line at which you can add a new point to the path [m] / Минимальное расстояние от курсора до линии, при котором можно добавить новую точку в путь [м]")] [Range(0.5f, 10)] public float addPointDistance = 2f;
  35. [Tooltip("Adjust the spawn of cars to the nearest surface. This option will be useful if there are bridges in the scene / Регулировка спавна людей к ближайшей поверхности. Этот параметор будет полезен, если в сцене есть мосты.")] public float highToSpawn = 1.0f;
  36. [Range(0.0f, 5.0f)] [Tooltip("Offset from the line along the X axis / Смещение от линии по оси X")] public float randXPos = 0.1f;
  37. [Range(0.0f, 5.0f)] [Tooltip("Offset from the line along the Z axis / Смещение от линии по оси Z")] public float randZPos = 0.1f;
  38. #region Create And Delete Additional Points
  39. /// <summary>
  40. /// Идёт ли процесс создания новой точки
  41. /// </summary>
  42. [HideInInspector] public bool newPointCreation = false;
  43. /// <summary>
  44. /// Идёт ли процесс удаления некоторой старой точки
  45. /// </summary>
  46. [HideInInspector] public bool oldPointDeleting = false;
  47. /// <summary>
  48. /// Позиция мышки на экране
  49. /// </summary>
  50. [HideInInspector] public Vector3 mousePosition = Vector3.zero;
  51. /// <summary>
  52. /// Индекс точки из массива которую хотят удалить
  53. /// </summary>
  54. private int deletePointIndex = -1;
  55. // точки между которыми будет создаваться дополнительная
  56. /// <summary>
  57. /// Индекс первой точки в массиве всех точек
  58. /// </summary>
  59. private int firstPointIndex = -1;
  60. /// <summary>
  61. /// Индекс второй точки в массиве всех точек
  62. /// </summary>
  63. private int secondPointIndex = -1;
  64. #endregion
  65. public Vector3 getNextPoint(int w, int index)
  66. {
  67. return points[w, index];
  68. }
  69. public Vector3 getStartPoint(int w)
  70. {
  71. return points[w, 1];
  72. }
  73. public int getPointsTotal(int w)
  74. {
  75. return pointLength[w];
  76. }
  77. void Awake()
  78. {
  79. DrawCurved(false);
  80. }
  81. public virtual void SpawnOnePeople(int w, bool forward, float walkSpeed, float runSpeed) { }
  82. public virtual void SpawnPeople() { }
  83. public virtual void DrawCurved(bool withDraw) { }
  84. #if UNITY_EDITOR
  85. public void OnDrawGizmos()
  86. {
  87. if (!disableLineDraw)
  88. {
  89. DrawCurved(true);
  90. }
  91. DrawNewPointCreation();
  92. DrawOldPointDeleting();
  93. }
  94. public void HideExistingIcons()
  95. {
  96. Transform t = transform.Find("points");
  97. foreach (Transform item in t)
  98. {
  99. DrawIcon(item.gameObject, 0, true);
  100. }
  101. }
  102. public void ShowExistingIcons()
  103. {
  104. Transform t = transform.Find("points");
  105. foreach (Transform item in t)
  106. {
  107. DrawIcon(item.gameObject, 1, false);
  108. }
  109. }
  110. private void DrawIcon(GameObject gameObject, int idx, bool basic)
  111. {
  112. GUIContent icon;
  113. if (!basic)
  114. {
  115. var largeIcons = GetTextures("sv_label_", string.Empty, 0, 8);
  116. icon = largeIcons[idx];
  117. }
  118. else
  119. {
  120. icon = EditorGUIUtility.IconContent("sv_icon_none");
  121. }
  122. var egu = typeof(EditorGUIUtility);
  123. var flags = System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic;
  124. var args = new object[] { gameObject, icon.image };
  125. var setIcon = egu.GetMethod("SetIconForObject", flags, null, new Type[] { typeof(UnityEngine.Object), typeof(Texture2D) }, null);
  126. if (basic)
  127. {
  128. setIcon.Invoke(null, new object[] { gameObject, null });
  129. return;
  130. }
  131. setIcon.Invoke(null, args);
  132. }
  133. private GUIContent[] GetTextures(string baseName, string postFix, int startIndex, int count)
  134. {
  135. GUIContent[] array = new GUIContent[count];
  136. for (int i = 0; i < count; i++)
  137. {
  138. array[i] = EditorGUIUtility.IconContent(baseName + (startIndex + i) + postFix);
  139. }
  140. return array;
  141. }
  142. /// <summary>
  143. /// Блокировка разблокировка эдитора
  144. /// </summary>
  145. /// <param name="lockValue">true - залочен, false - разлочен</param>
  146. public void EditorLock(bool lockValue)
  147. {
  148. ActiveEditorTracker.sharedTracker.isLocked = lockValue;
  149. }
  150. /// <summary>
  151. /// Рисует всё что связанно с добавлением новой точки в массив
  152. /// </summary>
  153. public void DrawNewPointCreation()
  154. {
  155. if (!newPointCreation)
  156. {
  157. return;
  158. }
  159. Selection.activeGameObject = gameObject;
  160. bool collizion = false;
  161. for (int i = 0; i < pathPoint.Count - 1; i++)
  162. {
  163. if (PointWithLineCollision(pathPointTransform[i].transform.position,
  164. pathPointTransform[i + 1].transform.position, mousePosition))
  165. {
  166. collizion = true;
  167. firstPointIndex = i;
  168. secondPointIndex = i + 1;
  169. }
  170. }
  171. if (collizion)
  172. {
  173. Gizmos.color = Color.green;
  174. }
  175. else
  176. {
  177. Gizmos.color = Color.red;
  178. firstPointIndex = -1;
  179. secondPointIndex = -1;
  180. }
  181. Gizmos.DrawSphere(mousePosition, addPointDistance);
  182. }
  183. /// <summary>
  184. /// Рисует всё что связано с удалением старой точки из массива
  185. /// </summary>
  186. public void DrawOldPointDeleting()
  187. {
  188. if (!oldPointDeleting)
  189. {
  190. return;
  191. }
  192. Selection.activeGameObject = gameObject;
  193. bool collizion = false;
  194. for (int i = 0; i < pathPoint.Count; i++)
  195. {
  196. if (PointWithSphereCollision(mousePosition, pathPointTransform[i].transform.position))
  197. {
  198. collizion = true;
  199. deletePointIndex = i;
  200. }
  201. }
  202. if (collizion)
  203. {
  204. Gizmos.color = Color.magenta;
  205. }
  206. else
  207. {
  208. Gizmos.color = Color.cyan;
  209. deletePointIndex = -1;
  210. }
  211. Gizmos.DrawSphere(mousePosition, eraseRadius);
  212. }
  213. #endif
  214. protected Vector3 GetRoutePosition(Vector3[] pointArray, float distance, int pointCount, bool loopPath)
  215. {
  216. int point = 0;
  217. float length = _distances[_distances.Length - 1];
  218. distance = Mathf.Repeat(distance, length);
  219. while (_distances[point] < distance)
  220. {
  221. ++point;
  222. }
  223. var point1N = ((point - 1) + pointCount) % pointCount;
  224. var point2N = point;
  225. var i = Mathf.InverseLerp(_distances[point1N], _distances[point2N], distance);
  226. return Vector3.Lerp(pointArray[point1N], pointArray[point2N], i);
  227. }
  228. protected int GetRoutePoint(float distance, int wayIndex, int pointCount, bool forward, bool loopPath)
  229. {
  230. int point = 0;
  231. float length = _distances[_distances.Length - 1];
  232. distance = Mathf.Repeat(distance, length);
  233. while (_distances[point] < distance)
  234. {
  235. ++point;
  236. }
  237. return point;
  238. }
  239. /// <summary>
  240. /// Проверка на столкновение сферы для стирания точек с точкой
  241. /// </summary>
  242. /// <param name="colisionSpherePosition">позиция сферы</param>
  243. /// <param name="pointPosition">позиция точки</param>
  244. private bool PointWithSphereCollision(Vector3 colisionSpherePosition, Vector3 pointPosition)
  245. {
  246. return Vector3.Magnitude(colisionSpherePosition - pointPosition) < eraseRadius;
  247. }
  248. /// <summary>
  249. /// Проверка на столкновение точки и линии
  250. /// </summary>
  251. /// <param name="pointPosition">Координаты новой точки которую планируется создать</param>
  252. /// <returns>True - есть столкновение, False - нет</returns>
  253. private bool PointWithLineCollision(Vector3 lineStartPosition, Vector3 lineEndPosition, Vector3 pointPosition)
  254. {
  255. return Distance(lineStartPosition, lineEndPosition, pointPosition) < addPointDistance;
  256. }
  257. /// <summary>
  258. /// Возвращает минимальное расстояние от точки до прямой [м]
  259. /// </summary>
  260. /// <param name="lineStartPosition">Координата начала прямой</param>
  261. /// <param name="lineEndPosition">Координата конца прямой</param>
  262. /// <param name="pointPosition">Координата точки</param>
  263. private float Distance(Vector3 lineStartPosition, Vector3 lineEndPosition, Vector3 pointPosition)
  264. {
  265. // квадрат длинны линии с началом в точке lineStartPosition и концом в точке lineEndPosition
  266. float l2 = Vector3.SqrMagnitude(lineEndPosition - lineStartPosition);
  267. if (l2 == 0f)
  268. return Vector3.Distance(pointPosition, lineStartPosition);
  269. float t = Mathf.Max(0,
  270. Mathf.Min(1, Vector3.Dot(pointPosition - lineStartPosition, lineEndPosition - lineStartPosition) / l2));
  271. Vector3 projection = lineStartPosition + t * (lineEndPosition - lineStartPosition);
  272. return Vector3.Distance(pointPosition, projection);
  273. }
  274. /// <summary>
  275. /// Добавляет новую точку между двумя созданными до этого
  276. /// </summary>
  277. public void AddPoint()
  278. {
  279. // Если индексы точек между которыми нужно создавать точку не выбраны
  280. // точка не создаётся
  281. if (firstPointIndex == -1 && secondPointIndex == firstPointIndex)
  282. {
  283. return;
  284. }
  285. var prefab = GameObject.Find("Population System").GetComponent<PopulationSystemManager>().pointPrefab;
  286. var obj = Instantiate(prefab, mousePosition, Quaternion.identity) as GameObject;
  287. obj.name = "p+";
  288. obj.transform.parent = pathPointTransform[firstPointIndex].transform.parent;
  289. #if UNITY_EDITOR
  290. //if (dontDrawYoJunkFool)
  291. // DrawIcon(obj, 0, true);
  292. #endif
  293. pathPointTransform.Insert(firstPointIndex + 1, obj);
  294. pathPoint.Insert(firstPointIndex + 1, obj.transform.position);
  295. }
  296. /// <summary>
  297. /// Удаляет выбранную точку
  298. /// </summary>
  299. public void DeletePoint()
  300. {
  301. // Если индекс точек для удаления не выбран
  302. // точка не удаляется
  303. if (deletePointIndex == -1)
  304. {
  305. return;
  306. }
  307. DestroyImmediate(pathPointTransform[deletePointIndex]);
  308. pathPointTransform.RemoveAt(deletePointIndex);
  309. pathPoint.RemoveAt(deletePointIndex);
  310. }
  311. }