123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.AI;
- [DefaultExecutionOrder(10)]
- [RequireComponent(typeof(InstantiatePrefab))]
- public class WanderingAI_LeaveMarket : MonoBehaviour
- {
- [Header("Only for Debugging (do not set from here)")]
- public Transform[] waypoints;
- public List<Transform> waypointsList;
- //public float[] wanderTimer;
- [Header("Scattering around the target")]
- public float targetScattering = 2.0f; //Streuung
- [Header("Leave Settings")]
- public float leaveTimer = 120f;
- public Vector3 leavePosition;
- // Settings Leave Market
- private bool leaveMarket;
- // Settings Humans
- private Vector3[][] target;
- private int[][] humansPrio;
- // Settings Waypoints and Waiting Time
- private int maxWPCount;
- private List<List<int>> waitingTimer;
- private List<List<int>> waitingTimerList;
- private List<int> waitingList;
- private float[] currentWait;
- private int countFilling = 0;
- // Time
- private float[] timer;
- private float globalTimer;
- private GameObject[][] humansGO;
- private NavMeshAgent[][] humansNMA;
- private Animator[][] humansA;
- private const string isWalking = "isWalking";
- void Start()
- {
- // Get initialized Variables from InstantiatePrefab
- humansGO = gameObject.GetComponent<InstantiatePrefab>().humanGameObject;
- humansNMA = gameObject.GetComponent<InstantiatePrefab>().humanNavMeshAgent;
- humansA = gameObject.GetComponent<InstantiatePrefab>().humanAnimator;
- //wanderTimer = gameObject.GetComponent<InstantiatePrefab>().wanderTimer;
- waypoints = gameObject.GetComponent<InstantiatePrefab>().waypointsArray;
- humansPrio = gameObject.GetComponent<InstantiatePrefab>().humanPriorities;
- // Set length of variables to humanGo.Length
- target = new Vector3[humansGO.Length][];
- timer = new float[humansGO.Length];
- // Create copies
- waypointsList = new List<Transform>(waypoints);
- waitingTimer = new List<List<int>>(gameObject.GetComponent<InstantiatePrefab>().waitingTimer);
- //waitingTimerList = new List<List<int>>(gameObject.GetComponent<InstantiatePrefab>().waitingTimer);
- waitingList = new List<int>(waitingTimer[0]);
- currentWait = new float[humansGO.Length];
- waitingTimerList = new List<List<int>>();
- foreach (List<int> timerList in waitingTimer)
- {
- waitingTimerList.Add(new List<int>(timerList));
- }
- maxWPCount = waypointsList.Count;
- // Initialize size and content
- for (int i = 0; i < humansGO.Length; i++)
- {
- target[i] = new Vector3[humansGO[i].Length];
- timer[i] = 0;
- }
- globalTimer = 0;
- }
- // To ensure that all market stalls (WPs) are visited, all entries in the WP-list are first edited in a random order
- // and each edited one is appended to the end of the list so that all unedited ones can be selected by decreasing the range.
- // Since the order of the WP-list is the same as the first dimension of the waitingTimer (WT) matrix,
- // the WT matrix can be edited with the same index as for the selection of the WP. So the arrangement is always the same.
- // The second dimension (the times of the individual market stalls) of the WT matrix is first completely emptied each time and then refilled.
- // After the leaving time is reached all humans sets their destination outside the market, iff the WPs with their timers are visited completely.
- private void FixedUpdate()
- {
-
- if (waypoints.Length == 0) return;
- // Leave Market if time is up
- if(globalTimer >= leaveTimer)
- leaveMarket = true;
-
- for (int i = 0; i < humansGO.Length; ++i)
- {
- // Set a new destination if the waiting time is over
- if (timer[i] >= currentWait[i])
- {
- // Let the class know that the first iteration is finished
- if (countFilling >= waitingTimer.Count)
- gameObject.GetComponent<WriteInCSV>().startWriting = true;
- for (int j = 0; j < humansGO[i].Length; ++j)
- {
- if (humansNMA[i][j].isOnNavMesh)
- {
- // if all WTs are processed and leaving time is reached, then leave market
- if (countFilling >= waitingTimer.Count && leaveMarket)
- {
- Vector3 endTarget = leavePosition;
- if (humansNMA[i][j].isActiveAndEnabled && Vector3.Distance(humansNMA[i][j].destination, endTarget) > 0.01f)
- humansNMA[i][j].SetDestination(endTarget);
- humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
- if (leaveMarket && (Vector3.Distance(humansNMA[i][j].transform.position, endTarget) <= humansNMA[i][j].stoppingDistance))
- {
- humansGO[i][j].SetActive(false);
- }
- }
- // Set new destination iff the human reached his destination or (only in first iteration) target vector is zero
- else if (target[i][j] == Vector3.zero ||
- humansNMA[i][j].velocity.magnitude <= 0.01f ||
- humansNMA[i][j].remainingDistance <= humansNMA[i][j].stoppingDistance ||
- humansNMA[i][j].pathStatus == NavMeshPathStatus.PathComplete ||
- !humansNMA[i][j].hasPath)
- {
- // Reset countFilling to start a new iteration
- if (countFilling >= waitingTimer.Count && !leaveMarket)
- countFilling = 0;
- // Random index for waypointsList
- if (maxWPCount <= 0) maxWPCount = waypointsList.Count;
- int currentWPIndex = Random.Range(0, maxWPCount);
- target[i][j] = CheckTarget(waypointsList[currentWPIndex].transform.position, targetScattering);
- // Set destination and animation
- if (target[i][0].x != float.PositiveInfinity)
- {
- // Random index for waitingTimer
- int currentWaitingIndex = Random.Range(0, waitingTimer[currentWPIndex].Count);
- currentWait[i] = waitingTimer[currentWPIndex][currentWaitingIndex];
- RemoveIndexFromWaitingTimer(currentWPIndex, currentWaitingIndex);
- // Fill waiting timer with default, if it is empty, to get a new waiting time for the next iteration
- if (waitingTimer[currentWPIndex].Count <= 0)
- {
- //waitingTimer[currentWPIndex] = new List<int>(waitingList);
- waitingTimer[currentWPIndex] = new List<int>(waitingTimerList[currentWPIndex]);
- countFilling++; // to check if ALL waitingTimer entries are processed
- // countFilling >= waitingTimer.Count -> All WP and WT entries are processed, the waitingTimer is filled for next iteration
- }
- if (humansNMA[i][j].isActiveAndEnabled)
- humansNMA[i][j].SetDestination(target[i][j]);
- humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
- // Append the current WP and the waiting time from both lists to the end to ensure that all WPs and waiting timers are visited at the frequency indicated,
- // afterwords decrement the max length by 1
- AppendIndexToEndWP(currentWPIndex); // Waypoints
- AppendIndexToEndWT(currentWPIndex); // Waiting Timer which is modified
- AppendIndexToEndWTDefault(currentWPIndex); // Default Waiting Timer, with all the times for each WP
- maxWPCount--;
- timer[i] = 0;
- }
- }
- }
- }
- }
- // Wait until the waiting time is over
- else
- {
- for (int j = 0; j < humansGO[i].Length; ++j)
- {
- // Set walking state and priority; increment timer
- humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
- if (humansNMA[i][j].remainingDistance <= humansNMA[i][j].stoppingDistance)
- timer[i] += Time.deltaTime;
- // if humans are not walking, then set priority to 0 so that the other humans dont disturb them
- if (humansNMA[i][j].velocity.magnitude <= 0.01f)
- humansNMA[i][j].avoidancePriority = 0;
- else
- humansNMA[i][j].avoidancePriority = humansPrio[i][j];
- }
- }
- }
- globalTimer += Time.deltaTime;
- }
- public static Vector3 CheckTarget(Vector3 target, float dist)
- {
- Vector3 modifiedTarget = Random.insideUnitSphere * dist + target;
- // SamplePosition also checks the y axis. However, this is not relevant for me, so valid positions (x & z) are also discarded.
- // 0.0f is the only valid entry for y
- modifiedTarget = new Vector3(modifiedTarget.x, 0f, modifiedTarget.z);
-
- NavMeshHit navHit;
- NavMesh.SamplePosition(modifiedTarget, out navHit, 1.0f, -1);
- return navHit.position;
- }
- public void AppendIndexToEndWP(int index)
- {
- waypointsList.Add(waypointsList[index]);
- waypointsList.RemoveAt(index);
- }
- public void AppendIndexToEndWT(int index)
- {
- waitingTimer.Add(waitingTimer[index]);
- waitingTimer.RemoveAt(index);
- }
- public void AppendIndexToEndWTDefault(int index)
- {
- waitingTimerList.Add(waitingTimerList[index]);
- waitingTimerList.RemoveAt(index);
- }
- public void RemoveIndexFromWaitingTimer(int i, int removeIndex)
- {
- waitingTimer[i].RemoveAt(removeIndex);
- }
- private void LateUpdate()
- {
- for (int i = 0; i < humansGO.Length; ++i)
- {
- for (int j = 0; j < humansGO[i].Length; ++j)
- {
- humansA[i][j].speed = 0.5f + (humansNMA[i][j].velocity.magnitude / gameObject.GetComponent<InstantiatePrefab>().speedMinMax.y);
- }
- }
- }
- }
|