一、框架视图


二、关键代码
EnviroZone
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[AddComponentMenu("Enviro/Weather Zone")]
public class EnviroZone : MonoBehaviour {
    public enum WeatherUpdateMode
    {
        GameTimeHours,
        RealTimeMinutes
    }
    [Tooltip("Defines the zone name.")]
    public string zoneName;
    [Tooltip("Uncheck to remove OnTriggerExit call when using overlapping zone layout.")]
    public bool ExitToDefault = true;
    public List<EnviroWeatherPrefab> zoneWeather = new List<EnviroWeatherPrefab>();
    public List<EnviroWeatherPrefab> curPossibleZoneWeather;
    [Header("Zone weather settings:")]
    [Tooltip("Add all weather prefabs for this zone here.")]
    public List<EnviroWeatherPreset> zoneWeatherPresets = new List<EnviroWeatherPreset>();
    [Tooltip("Shall weather changes occure based on gametime or realtime?")]
    public WeatherUpdateMode updateMode = WeatherUpdateMode.GameTimeHours;
    [Tooltip("Defines how often (gametime hours or realtime minutes) the system will heck to change the current weather conditions.")]
    public float WeatherUpdateIntervall = 6f;
    [Header("Zone scaling and gizmo:")]
    [Tooltip("Defines the zone scale.")]
    public Vector3 zoneScale = new Vector3 (100f, 100f, 100f);
    [Tooltip("Defines the color of the zone's gizmo in editor mode.")]
    public Color zoneGizmoColor = Color.gray;
    [Header("Current active weather:")]
    [Tooltip("The current active weather conditions.")]
    public EnviroWeatherPrefab currentActiveZoneWeatherPrefab;
    public EnviroWeatherPreset currentActiveZoneWeatherPreset;
    [HideInInspector]public EnviroWeatherPrefab lastActiveZoneWeatherPrefab;
    [HideInInspector]public EnviroWeatherPreset lastActiveZoneWeatherPreset;
    private BoxCollider zoneCollider;
    private double nextUpdate;
    private float nextUpdateRealtime;
    private bool init = false;
    private bool isDefault;
    void Start () 
    {
        if (zoneWeatherPresets.Count > 0)
        {
            zoneCollider = gameObject.AddComponent<BoxCollider> ();
            zoneCollider.isTrigger = true;
            if (!GetComponent<EnviroSky> ())
                EnviroSky.instance.RegisterZone (this);
            else 
                isDefault = true;
            UpdateZoneScale ();
            nextUpdate = EnviroSky.instance.currentTimeInHours + WeatherUpdateIntervall;
            nextUpdateRealtime = Time.time + (WeatherUpdateIntervall * 60f); 
        }
        else
        {
            Debug.LogError("Please add Weather Prefabs to Zone:" + gameObject.name);
        }
    }
    public void UpdateZoneScale ()
    {
        if (!isDefault)
            zoneCollider.size = zoneScale;
        else
            zoneCollider.size = (Vector3.one * (1f / EnviroSky.instance.transform.localScale.y)) * 0.25f;
    }
    public void CreateZoneWeatherTypeList ()
    {
        // Add new WeatherPrefabs
        for ( int i = 0; i < zoneWeatherPresets.Count; i++)
        {
            if (zoneWeatherPresets [i] == null) {
                Debug.Log ("Warning! Missing Weather Preset in Zone: " + this.zoneName);
                return;
            }
            bool addThis = true;
            for (int i2 = 0; i2 < EnviroSky.instance.Weather.weatherPresets.Count; i2++)
            {
                if (zoneWeatherPresets [i] == EnviroSky.instance.Weather.weatherPresets [i2]) 
                {
                    addThis = false;
                    zoneWeather.Add (EnviroSky.instance.Weather.WeatherPrefabs [i2]);
                }
            }
            if (addThis) {
                GameObject wPrefab = new GameObject ();
                EnviroWeatherPrefab wP = wPrefab.AddComponent<EnviroWeatherPrefab> ();
                wP.weatherPreset = zoneWeatherPresets [i];
                wPrefab.name = wP.weatherPreset.Name;
                // Check and create particle systems.
                for (int w = 0; w < wP.weatherPreset.effectSystems.Count; w++)
                {
                    if (wP.weatherPreset.effectSystems [w] == null || wP.weatherPreset.effectSystems [w].prefab == null) {
                        Debug.Log ("Warning! Missing Particle System Entry: " + wP.weatherPreset.Name);
                        Destroy (wPrefab);
                        return;
                    }
                    GameObject eS = (GameObject)Instantiate (wP.weatherPreset.effectSystems [w].prefab, wPrefab.transform);
                    eS.transform.localPosition = wP.weatherPreset.effectSystems [w].localPositionOffset;
                    eS.transform.localEulerAngles = wP.weatherPreset.effectSystems [w].localRotationOffset;
                    ParticleSystem pS = eS.GetComponent<ParticleSystem> ();
                    if (pS != null)
                        wP.effectSystems.Add (pS);
                    else {
                        pS = eS.GetComponentInChildren<ParticleSystem> ();
                        if (pS != null)
                            wP.effectSystems.Add (pS);
                        else {
                            Debug.Log ("No Particle System found in prefab in weather preset: " + wP.weatherPreset.Name);
                            Destroy (wPrefab);
                            return;
                        }
                    }
                }
                wP.effectEmmisionRates.Clear ();
                wPrefab.transform.parent = EnviroSky.instance.Weather.VFXHolder.transform;
                wPrefab.transform.localPosition = Vector3.zero;
                wPrefab.transform.localRotation = Quaternion.identity;
                zoneWeather.Add(wP);
                EnviroSky.instance.Weather.WeatherPrefabs.Add (wP);
                EnviroSky.instance.Weather.weatherPresets.Add (zoneWeatherPresets [i]);
            }
        }
        
        // Setup Particle Systems Emission Rates
        for (int i = 0; i < zoneWeather.Count; i++)
        {
            for (int i2 = 0; i2 < zoneWeather[i].effectSystems.Count; i2++)
            {
                zoneWeather[i].effectEmmisionRates.Add(EnviroSky.GetEmissionRate(zoneWeather[i].effectSystems[i2]));
                EnviroSky.SetEmissionRate(zoneWeather[i].effectSystems[i2],0f);
            }   
        }
            
        //Set initial weather
        if (isDefault && EnviroSky.instance.Weather.startWeatherPreset != null) 
        {
            EnviroSky.instance.SetWeatherOverwrite(EnviroSky.instance.Weather.startWeatherPreset);
            for (int i = 0; i < zoneWeather.Count; i++)
            {
                if(zoneWeather[i].weatherPreset == EnviroSky.instance.Weather.startWeatherPreset)
                {
                    currentActiveZoneWeatherPrefab = zoneWeather[i];
                    lastActiveZoneWeatherPrefab = zoneWeather[i];
                }
            }
            currentActiveZoneWeatherPreset = EnviroSky.instance.Weather.startWeatherPreset;
            lastActiveZoneWeatherPreset = EnviroSky.instance.Weather.startWeatherPreset;
        } 
        else 
        {
            currentActiveZoneWeatherPrefab = zoneWeather [0];
            lastActiveZoneWeatherPrefab = zoneWeather [0];
            currentActiveZoneWeatherPreset = zoneWeatherPresets [0];
            lastActiveZoneWeatherPreset = zoneWeatherPresets [0];
        }
        nextUpdate = EnviroSky.instance.currentTimeInHours + WeatherUpdateIntervall;
    }
        
    void BuildNewWeatherList ()
    {
        curPossibleZoneWeather = new List<EnviroWeatherPrefab> ();
        for (int i = 0; i < zoneWeather.Count; i++) 
        {
            switch (EnviroSky.instance.Seasons.currentSeasons)
            {
            case EnviroSeasons.Seasons.Spring:
                if (zoneWeather[i].weatherPreset.Spring)
                    curPossibleZoneWeather.Add(zoneWeather[i]);
                break;
            case EnviroSeasons.Seasons.Summer:
                if (zoneWeather[i].weatherPreset.Summer)
                    curPossibleZoneWeather.Add(zoneWeather[i]);
                break;
            case EnviroSeasons.Seasons.Autumn:
                if (zoneWeather[i].weatherPreset.Autumn)
                    curPossibleZoneWeather.Add(zoneWeather[i]);
                break;
            case EnviroSeasons.Seasons.Winter:
                if (zoneWeather[i].weatherPreset.winter)
                    curPossibleZoneWeather.Add(zoneWeather[i]);
                break;
            }
        } 
    }
    EnviroWeatherPrefab PossibiltyCheck ()
    {
        List<EnviroWeatherPrefab> over = new List<EnviroWeatherPrefab> ();
        for (int i = 0 ; i < curPossibleZoneWeather.Count;i++)
        {
            int würfel = UnityEngine.Random.Range (0,100);
            if (EnviroSky.instance.Seasons.currentSeasons == EnviroSeasons.Seasons.Spring)
            {
                if (würfel <= curPossibleZoneWeather[i].weatherPreset.possibiltyInSpring)
                    over.Add(curPossibleZoneWeather[i]);
            }else
            if (EnviroSky.instance.Seasons.currentSeasons == EnviroSeasons.Seasons.Summer)
            {
                    if (würfel <= curPossibleZoneWeather[i].weatherPreset.possibiltyInSummer)
                    over.Add(curPossibleZoneWeather[i]);
            }else
            if (EnviroSky.instance.Seasons.currentSeasons == EnviroSeasons.Seasons.Autumn)
            {
                        if (würfel <= curPossibleZoneWeather[i].weatherPreset.possibiltyInAutumn)
                    over.Add(curPossibleZoneWeather[i]);
            }else
            if (EnviroSky.instance.Seasons.currentSeasons == EnviroSeasons.Seasons.Winter)
            {
                            if (würfel <= curPossibleZoneWeather[i].weatherPreset.possibiltyInWinter)
                    over.Add(curPossibleZoneWeather[i]);
            }
        } 
        if (over.Count > 0)
        {       
            EnviroSky.instance.NotifyZoneWeatherChanged (over [0].weatherPreset, this);
            return over [0];
        }
        else
            return currentActiveZoneWeatherPrefab;
    }
        
    void WeatherUpdate ()
    {
        nextUpdate = EnviroSky.instance.currentTimeInHours + WeatherUpdateIntervall;
        nextUpdateRealtime = Time.time + (WeatherUpdateIntervall * 60f); 
        BuildNewWeatherList ();
        lastActiveZoneWeatherPrefab = currentActiveZoneWeatherPrefab;
        lastActiveZoneWeatherPreset = currentActiveZoneWeatherPreset;
        currentActiveZoneWeatherPrefab = PossibiltyCheck ();
        currentActiveZoneWeatherPreset = currentActiveZoneWeatherPrefab.weatherPreset;
        EnviroSky.instance.NotifyZoneWeatherChanged (currentActiveZoneWeatherPreset, this);
    }
    IEnumerator CreateWeatherListLate ()
    {
        yield return 0;
        CreateZoneWeatherTypeList ();
        init = true;
    }
    void LateUpdate () 
    {
        if (EnviroSky.instance == null)
        {
            Debug.Log("No EnviroSky instance found!");
            return;
        }
        if (EnviroSky.instance.started && !init) 
        {
            if (isDefault) {
                CreateZoneWeatherTypeList ();          
                init = true;
            } else
                StartCoroutine (CreateWeatherListLate ());
        }
        if (updateMode == WeatherUpdateMode.GameTimeHours) {
            if (EnviroSky.instance.currentTimeInHours > nextUpdate && EnviroSky.instance.Weather.updateWeather && EnviroSky.instance.started)
                WeatherUpdate ();
        } else {
            if (Time.time > nextUpdateRealtime && EnviroSky.instance.Weather.updateWeather && EnviroSky.instance.started)
                WeatherUpdate ();
        }
        if (EnviroSky.instance.Player == null)
        {
            // Debug.Log("No Player Assigned in EnviroSky object!");
            return;
        }
        if (isDefault && init)                               
            zoneCollider.center = new Vector3(0f,(EnviroSky.instance.Player.transform.position.y-EnviroSky.instance.transform.position.y) / EnviroSky.instance.transform.lossyScale.y,0f);
    }
    /// Triggers
    void OnTriggerEnter (Collider col)
    {
        if (EnviroSky.instance == null)
            return;
        if (EnviroSky.instance.profile.weatherSettings.useTag) {
            if (col.gameObject.tag == EnviroSky.instance.gameObject.tag) {
                EnviroSky.instance.Weather.currentActiveZone = this;
                EnviroSky.instance.NotifyZoneChanged (this);
            }
        } else {
            if (col.gameObject.GetComponent<EnviroSky> ()) {
                EnviroSky.instance.Weather.currentActiveZone = this;
                EnviroSky.instance.NotifyZoneChanged (this);
            }
        }
    }
    void OnTriggerExit (Collider col)
    {
        if (ExitToDefault == false || EnviroSky.instance == null)
            return;
        
        if (EnviroSky.instance.profile.weatherSettings.useTag) {
            if (col.gameObject.tag == EnviroSky.instance.gameObject.tag) {
                EnviroSky.instance.Weather.currentActiveZone = EnviroSky.instance.Weather.zones[0];
                EnviroSky.instance.NotifyZoneChanged (EnviroSky.instance.Weather.zones[0]);
            }
        } else {
            if (col.gameObject.GetComponent<EnviroSky> ()) {
                EnviroSky.instance.Weather.currentActiveZone = EnviroSky.instance.Weather.zones[0];
                EnviroSky.instance.NotifyZoneChanged (EnviroSky.instance.Weather.zones[0]);
            }
        }
    }
    void OnDrawGizmos () 
    {
        Gizmos.color = zoneGizmoColor;
        Gizmos.DrawCube (transform.position, new Vector3(zoneScale.x,zoneScale.y,zoneScale.z));
    }
}
EnviroActionEvent
using UnityEngine;
using System.Collections;
public class EnviroEvents : MonoBehaviour {
    [System.Serializable]
    public class EnviroActionEvent : UnityEngine.Events.UnityEvent
    {
    }
    //[Header("Time Events")]
    public EnviroActionEvent onHourPassedActions = new EnviroActionEvent();
    public EnviroActionEvent onDayPassedActions = new EnviroActionEvent();
    public EnviroActionEvent onYearPassedActions = new EnviroActionEvent();
    public EnviroActionEvent onWeatherChangedActions = new EnviroActionEvent();
    public EnviroActionEvent onSeasonChangedActions = new EnviroActionEvent();
    public EnviroActionEvent onNightActions = new EnviroActionEvent();
    public EnviroActionEvent onDayActions = new EnviroActionEvent();
    public EnviroActionEvent onZoneChangedActions = new EnviroActionEvent();
    void Start ()
    {
        EnviroSky.instance.OnHourPassed += () => HourPassed ();
        EnviroSky.instance.OnDayPassed += () => DayPassed ();
        EnviroSky.instance.OnYearPassed += () => YearPassed ();
        EnviroSky.instance.OnWeatherChanged += (EnviroWeatherPreset type) =>  WeatherChanged ();
        EnviroSky.instance.OnSeasonChanged += (EnviroSeasons.Seasons season) => SeasonsChanged ();
        EnviroSky.instance.OnNightTime += () => NightTime ();
        EnviroSky.instance.OnDayTime += () => DayTime ();
        EnviroSky.instance.OnZoneChanged += (EnviroZone zone) =>  ZoneChanged ();
    }
        
    private void HourPassed()
    {
        onHourPassedActions.Invoke();
    }
    private void DayPassed()
    {
        onDayPassedActions.Invoke();
    }
        
    private void YearPassed()
    {
        onYearPassedActions.Invoke();
    }
    private void WeatherChanged()
    {
        onWeatherChangedActions.Invoke();
    }
    private void SeasonsChanged()
    {
        onSeasonChangedActions.Invoke();
    }
    private void NightTime()
    {
        onNightActions.Invoke ();
    }
    private void DayTime()
    {
        onDayActions.Invoke ();
    }
    private void ZoneChanged()
    {
        onZoneChangedActions.Invoke ();
    }
}
DayNight
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 日夜循环
/// </summary>
public class DayNight : MonoBehaviour 
{
    //天时间
    int currentSecond ;
    int currentMinute ;
    int currentHour ;
    //恒星时间
    float solarTime ;
    float lunarTime ;
    bool isLoop;
    void Start()
    {
     
        isLoop = true;
    }
    // Update is called once per frame
    void Update()
    {
        //if (Input.GetMouseButtonDown(0))
        //{
        //    EnviroSky.instance.GameTime.ProgressTime = EnviroTime.TimeProgressMode.OneDay;
        //}
        currentSecond = EnviroSky.instance.GameTime.Seconds;
        currentMinute = EnviroSky.instance.GameTime.Minutes;
        currentHour = EnviroSky.instance.GameTime.Hours;
        // Debug.Log("currentHour::" + currentHour + "----currentMinute:" + currentMinute + "------currentSecond" + currentSecond);
        solarTime = EnviroSky.instance.GameTime.solarTime;
        lunarTime = EnviroSky.instance.GameTime.lunarTime;
       // Debug.Log(" 太阳solarTime::" + solarTime + " 月亮lunarTime::" + lunarTime);
        //if (Input.GetMouseButtonDown(1))
        //{
        //    isLoop = false;
        //}
        if (!isLoop)
        {
            if (solarTime > 0.85)
            {
                EnviroSky.instance.GameTime.ProgressTime = EnviroTime.TimeProgressMode.None;
            }
        }
    }
    public void StartDayChangeNight()
    {
        isLoop = true;
        EnviroSky.instance.GameTime.ProgressTime = EnviroTime.TimeProgressMode.OneDay;
        Invoke("DelayStopRun", 5f);
        
    }
    private void DelayStopRun() {
        isLoop = false;
    }
}
Rain
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rain : MonoBehaviour
{
    private bool isShow;
    void Start()
    {
    }
    void Update()
    {
    }
    public void StartRain()
    {
        isShow = true;
        gameObject.SetActive(isShow);
    }
    public void StopRain() {
        gameObject.SetActive(false);
    }
}
Snow
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Snow : MonoBehaviour {
    private bool isShow;
    void Start()
    {
    }
    void Update()
    {
    }
    public void StartSnow()
    {
        isShow = true;
        gameObject.SetActive(isShow);
        gameObject.transform.GetChild(0).gameObject.SetActive(isShow);
        
    }
    public void StopSnow()
    {
        gameObject.transform.GetChild(0).gameObject.SetActive(false);
        gameObject.SetActive(false);
    }
}
Typhoon
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Typhoon : MonoBehaviour {
   
    void Start()
    {
    }
    void Update()
    {
    }
    public void StartTyphoon()
    {
      
        gameObject.SetActive(true);
        Invoke("StopTypoon", 15f);
        AudioManager.Instance.PlayMusic(8,false);
    }
   public  void StopTypoon() {
        gameObject.SetActive(false);
        AudioManager.Instance.StopSound();
    }
}
WeathercHandle
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeathercHandle : MonoBehaviour {
    public DayNight dayNight;
    public Snow snow;
    public Rain rain;
    public Typhoon typhoon;
    void Start () {
        EventCenter.AddListener<string>(EventDefine.WeatherBtn, WeathercManager);
    }
    private void WeathercManager(string weather)
    {
        switch (weather)
        {
            case Tags.DayNight:
                dayNight.StartDayChangeNight();
                snow.StopSnow();
                rain.StopRain();
                typhoon.StopTypoon();
                EnviroSky.instance.ChangeWeather("Cloudy 3");
                break;
            case Tags.SnowBtn:
                snow.StartSnow();
                rain.StopRain();
                typhoon.StopTypoon();
                EnviroSky.instance.ChangeWeather("Heavy Snow");
                break;
            case Tags.Rain:
                rain.StartRain();
                snow.StopSnow();
                typhoon.StopTypoon();
                EnviroSky.instance.ChangeWeather("Heavy Rain");
                break;
            case Tags.Typhoon:
                typhoon.StartTyphoon();
                snow.StopSnow();
                rain.StopRain();
                EnviroSky.instance.ChangeWeather("Storm VR");
                break;
            default:
                break;
        }
    }
CarWalkPath
using UnityEngine;
using System.Collections.Generic;
public class CarWalkPath : WalkPath
{
    [Tooltip("Vehicle Speed / Скорость машины")] public float moveSpeed = 12.0f;
    [Tooltip("Vehicle Deseleration / Торможение машины")] public float speadDecrease = 2.0f;
    [Tooltip("Vehicle Acceleration / Ускорение автомобиля")] public float speadIncrease = 2.0f;
    [Tooltip("Distance to Car / Расстояние до автомобиля")] public float distanceToCar = 10.0f;
    [Tooltip("Distance to traffic lights / Расстояние до светофора")] public float distanceToSemaphore = 10.0f;
    [HideInInspector] [SerializeField] [Tooltip("Ignore pedestrian colliders? / Игнорировать коллайдеры пешеходов?")] private bool _ignorePeople = false;
    [Tooltip("Distance to the point / Расстояние до точки")] public float nextPointThreshold = 3;
    [Tooltip("Maximum rotation angle for braking / Максимальный угол поворота для притормаживания")] public float maxAngleToMoveBreak = 8.0f;
    private void Start()
    {
        if(_ignorePeople)
        {
            Physics.IgnoreLayerCollision(9, 8, true);  
        }                 
    }      
    public override void CreateSpawnPoints()
    {
        SpawnPoints = new SpawnPoint[points.GetLength(0)];
        for (int i = 0; i < points.GetLength(0); i++)
        {
            var startPoint = _forward[i] ? points[i, 0] : points[i, points.GetLength(1) - 1];
            var nextPoint = _forward[i] ? points[i, 2] : points[i, points.GetLength(1) - 3];
            SpawnPoints[i] = SpawnPoint.CarCreate(
                string.Format("SpawnPoint (Path {0})", i + 1),
                startPoint,
                nextPoint,
                lineSpacing,
                i,
                _forward[i],
                this,
                3f,
                10f
            );
        }
    }
    public override void SpawnOnePeople(int w, bool forward)
    {
        List<GameObject> pfb = new List<GameObject>(walkingPrefabs);
        for (int i = pfb.Count - 1; i >= 0; i--)
        {
            if (pfb[i] == null)
            {
                pfb.RemoveAt(i);
            }
        }
        walkingPrefabs = pfb.ToArray();
        int prefabNum = UnityEngine.Random.Range(0, walkingPrefabs.Length);
        var people = gameObject;
        if (!forward)
        {
            people = Instantiate(walkingPrefabs[prefabNum], points[w, pointLength[0] - 2], Quaternion.identity) as GameObject;
        }
        else
        {
            people = Instantiate(walkingPrefabs[prefabNum], points[w, 1], Quaternion.identity) as GameObject;
        }
        var movePath = people.AddComponent<MovePath>();
        var car = people.AddComponent<CarAIController>();
        CarInitialize(ref car);
        people.transform.parent = par.transform;
        movePath.walkPath = this;
        if (!forward)
        {
            movePath.InitStartPosition(w, pointLength[0] - 3, loopPath, forward);
            people.transform.LookAt(points[w, pointLength[0] - 3]);
        }
        else
        {
            movePath.InitStartPosition(w, 1, loopPath, forward);
            people.transform.LookAt(points[w, 2]);
        }
        movePath._walkPointThreshold = nextPointThreshold;
    }
    public override void SpawnPeople()
    {
        List<GameObject> pfb = new List<GameObject>(walkingPrefabs);
        for (int i = pfb.Count - 1; i >= 0; i--)
        {
            if (pfb[i] == null)
            {
                pfb.RemoveAt(i);
            }
        }
        walkingPrefabs = pfb.ToArray();
        if (points == null) DrawCurved(false);
        if (par == null)
        {
            par = new GameObject();
            par.transform.parent = gameObject.transform;
            par.name = "walkingObjects";
        }
        int pathPointCount;
        if (!loopPath)
        {
            pathPointCount = pointLength[0] - 2;
        }
        else
        {
            pathPointCount = pointLength[0] - 1;
        }
        if (pathPointCount < 2) return;
        var pCount = loopPath ? pointLength[0] - 1 : pointLength[0] - 2;
        for (int wayIndex = 0; wayIndex < numberOfWays; wayIndex++)
        {
            _distances = new float[pCount];
            float pathLength = 0f;
            for (int i = 1; i < pCount; i++)
            {
                Vector3 vector;
                if (loopPath && i == pCount - 1)
                {
                    vector = points[wayIndex, 1] - points[wayIndex, pCount];
                }
                else
                {
                    vector = points[wayIndex, i + 1] - points[wayIndex, i];
                }
                pathLength += vector.magnitude;
                _distances[i] = pathLength;
            }
            bool forward = false;
            switch (direction.ToString())
            {
                case "Forward":
                    forward = true;
                    break;
                case "Backward":
                    forward = false;
                    break;
                case "HugLeft":
                    forward = (wayIndex + 2) % 2 == 0;
                    break;
                case "HugRight":
                    forward = (wayIndex + 2) % 2 != 0;
                    break;
                case "WeaveLeft":
                    forward = wayIndex != 1 && wayIndex != 2 && (wayIndex - 1) % 4 != 0 && (wayIndex - 2) % 4 != 0;
                    break;
                case "WeaveRight":
                    forward = wayIndex == 1 || wayIndex == 2 || (wayIndex - 1) % 4 == 0 || (wayIndex - 2) % 4 == 0;
                    break;
            }
            int peopleCount = Mathf.FloorToInt((Density * pathLength) / _minimalObjectLength * 0.2f);
            float segmentLen = _minimalObjectLength + (pathLength - (peopleCount * _minimalObjectLength)) / peopleCount;
            int[] pickList = CommonUtils.GetRandomPrefabIndexes(peopleCount, ref walkingPrefabs);
            Vector3[] pointArray = new Vector3[_distances.Length];
            for (int i = 1; i < _distances.Length; i++)
            {
                pointArray[i - 1] = points[wayIndex, i];
            }
            pointArray[_distances.Length - 1] = loopPath ? points[wayIndex, 1] : points[wayIndex, _distances.Length];
            for (int peopleIndex = 0; peopleIndex < peopleCount; peopleIndex++)
            {
                var people = gameObject;
                var randomShift = UnityEngine.Random.Range(-segmentLen / 3f, segmentLen / 3f) + (wayIndex * segmentLen);
                var finalRandomDistance = (peopleIndex + 1) * segmentLen + randomShift;
                var routePosition = GetRoutePosition(pointArray, finalRandomDistance, pCount, loopPath);
                Vector3 or;
                RaycastHit[] rrr = Physics.RaycastAll(or = new Vector3(routePosition.x, routePosition.y + 10000, routePosition.z), Vector3.down, Mathf.Infinity);
                bool isSemaphore = false;
                for (int i = 0; i < rrr.Length; i++)
                {
                    if (rrr[i].collider.GetComponent<SemaphoreSimulator>() != null || rrr[i].collider.GetComponent<SemaphorePeople>() != null)
                    {
                        isSemaphore = true;
                    }
                }
                if (isSemaphore) continue;
                float dist = 0;
                int bestCandidate = 0;
                rrr = Physics.RaycastAll(or = new Vector3(routePosition.x, routePosition.y + highToSpawn, routePosition.z), Vector3.down, Mathf.Infinity);
                for (int i = 0; i < rrr.Length; i++)
                {
                    if (dist < Vector3.Distance(rrr[0].point, or))
                    {
                        bestCandidate = i;
                        dist = Vector3.Distance(rrr[0].point, or);
                    }
                }
                if (rrr.Length > 0)
                {
                    routePosition.y = rrr[bestCandidate].point.y;
                }
                people = Instantiate(walkingPrefabs[pickList[peopleIndex]], routePosition, Quaternion.identity) as GameObject;
                var movePath = people.AddComponent<MovePath>();
                var car = people.AddComponent<CarAIController>();
                CarInitialize(ref car);
                people.transform.parent = par.transform;
                movePath.walkPath = this;
                movePath._walkPointThreshold = nextPointThreshold;
                movePath.InitStartPosition(wayIndex,
                    GetRoutePoint((peopleIndex + 1) * segmentLen + randomShift, wayIndex, pCount, forward, loopPath), loopPath, forward);
                movePath.SetLookPosition();
                if (people.GetComponent<AddTrailer>())
                {
                    people.GetComponent<AddTrailer>().Init();
                }
            }
        }
    }
    private void CarInitialize(ref CarAIController carAIController)
    {
        float speed = moveSpeed + Random.Range(moveSpeed * -0.15f, moveSpeed * 0.15f);
        speed = Mathf.Clamp(speed, 0, 15);
        carAIController.MOVE_SPEED = speed;
        carAIController.INCREASE = speadIncrease;
        carAIController.DECREASE = speadDecrease;
        carAIController.TO_CAR = distanceToCar;
        carAIController.TO_SEMAPHORE = distanceToSemaphore;
        carAIController.MaxAngle = maxAngleToMoveBreak;
    }
}
PeopleWalkPath
using UnityEngine;
using System.Collections.Generic;
public enum AnimationState
{
    idle1, walk, run
}
public class PeopleWalkPath : WalkPath
{
    [Tooltip("Animation of the pedestrian at the start / Анимация пешехода при старте")] public AnimationState animationState = AnimationState.walk;
    [Range(0.0f, 5.0f)] [Tooltip("Offset from the line along the X axis / Смещение от линии по оси X")] public float randXPos = 0.1f;
    [Range(0.0f, 5.0f)] [Tooltip("Offset from the line along the Z axis / Смещение от линии по оси Z")] public float randZPos = 0.1f;
    [HideInInspector] [SerializeField] [Tooltip("Ignore the colliders of other pedestrians / Игнорировать коллайдеры других пешеходов?")] private bool _ignorePeople = false;
    [HideInInspector] [SerializeField] [Tooltip("Set your animation speed? / Установить свою скорость анимации?")] private bool _overrideDefaultAnimationMultiplier = true;
    [HideInInspector] [SerializeField] [Tooltip("Speed animation of walking / Скорость анимации ходьбы")] private float _customWalkAnimationMultiplier = 1.1f;
    [HideInInspector] [SerializeField] [Tooltip("Running animation speed / Скорость анимации бега")] private float _customRunAnimationMultiplier = 0.5f;
    private float nextPointThreshold = 1;
    [Tooltip("Walking speed / Скорость ходьбы")] public float walkSpeed = 1.2f;
    [Tooltip("Running speed / Скорость бега")] public float runSpeed = 3.0f;
    private float speedRotation = 15.0f;
    [Tooltip("Viewing Angle / Угол обзора")] public float viewAngle = 55.0f;
    [Tooltip("Radius of visibility / Радиус видимости")] public float viewRadius = 3.0f;
    [Tooltip("Distance to the pedestrian / Дистанция до пешехода")] public float distToPeople = 4.0f;
    [Tooltip("Layers of car, traffic light, pedestrians, player / Слои автомобиля, светофора, пешеходов, игрока")] public LayerMask targetMask = 3840;
    [HideInInspector] public LayerMask obstacleMask;
    private void Start()
    {
        if(_ignorePeople)
        {
            Physics.IgnoreLayerCollision(8, 8, true);  
        }                 
    }    
    public override void CreateSpawnPoints()
    {
        SpawnPoints = new SpawnPoint[points.GetLength(0)];
        for (int i = 0; i < points.GetLength(0); i++)
        {
            var startPoint = _forward[i] ? points[i, 0] : points[i, points.GetLength(1) - 1];
            var nextPoint = _forward[i] ? points[i, 2] : points[i, points.GetLength(1) - 3];
            SpawnPoints[i] = SpawnPoint.PeopleCreate(
                string.Format("SpawnPoint (Path {0})", i + 1),
                startPoint,
                nextPoint,
                lineSpacing,
                i,
                _forward[i],
                this,
                3f,
                1f
            );
        }
    }
    public override void SpawnOnePeople(int w, bool forward)
    {
        List<GameObject> pfb = new List<GameObject>(walkingPrefabs);
        for (int i = pfb.Count - 1; i >= 0; i--)
        {
            if (pfb[i] == null)
            {
                pfb.RemoveAt(i);
            }
        }
        walkingPrefabs = pfb.ToArray();
        int prefabNum = Random.Range(0, walkingPrefabs.Length);
        var people = gameObject;
        if (!forward)
        {
            people = Instantiate(walkingPrefabs[prefabNum], points[w, pointLength[0] - 2], Quaternion.identity) as GameObject;
        }
        else
        {
            people = Instantiate(walkingPrefabs[prefabNum], points[w, 1], Quaternion.identity) as GameObject;
        }
        var movePath = people.AddComponent<MovePath>();
        var passersby = people.AddComponent<Passersby>();
        movePath.randXFinish = Random.Range(-randXPos, randXPos);
        movePath.randZFinish = Random.Range(-randZPos, randZPos);
        InitializePassersby(ref passersby);
        people.transform.parent = par.transform;
        movePath.walkPath = this;
        if (!forward)
        {
            movePath.InitStartPosition(w, pointLength[0] - 3, loopPath, forward);
            people.transform.LookAt(points[w, pointLength[0] - 3]);
        }
        else
        {
            movePath.InitStartPosition(w, 1, loopPath, forward);
            people.transform.LookAt(points[w, 2]);
        }
        movePath._walkPointThreshold = nextPointThreshold;
    }
    public override void SpawnPeople()
    {
        List<GameObject> pfb = new List<GameObject>(walkingPrefabs);
        for (int i = pfb.Count - 1; i >= 0; i--)
        {
            if (pfb[i] == null)
            {
                pfb.RemoveAt(i);
            }
        }
        walkingPrefabs = pfb.ToArray();
        if (points == null) DrawCurved(false);
        if (par == null)
        {
            par = new GameObject();
            par.transform.parent = gameObject.transform;
            par.name = "walkingObjects";
        }
        int pathPointCount;
        if (!loopPath)
        {
            pathPointCount = pointLength[0] - 2;
        }
        else
        {
            pathPointCount = pointLength[0] - 1;
        }
        if (pathPointCount < 2) return;
        var pCount = loopPath ? pointLength[0] - 1 : pointLength[0] - 2;
        for (int wayIndex = 0; wayIndex < numberOfWays; wayIndex++)
        {
            _distances = new float[pCount];
            float pathLength = 0f;
            for (int i = 1; i < pCount; i++)
            {
                Vector3 vector;
                if (loopPath && i == pCount - 1)
                {
                    vector = points[wayIndex, 1] - points[wayIndex, pCount];
                }
                else
                {
                    vector = points[wayIndex, i + 1] - points[wayIndex, i];
                }
                pathLength += vector.magnitude;
                _distances[i] = pathLength;
            }
            bool forward = false;
            switch (direction.ToString())
            {
                case "Forward":
                    forward = true;
                    break;
                case "Backward":
                    forward = false;
                    break;
                case "HugLeft":
                    forward = (wayIndex + 2) % 2 == 0;
                    break;
                case "HugRight":
                    forward = (wayIndex + 2) % 2 != 0;
                    break;
                case "WeaveLeft":
                    forward = wayIndex != 1 && wayIndex != 2 && (wayIndex - 1) % 4 != 0 && (wayIndex - 2) % 4 != 0;
                    break;
                case "WeaveRight":
                    forward = wayIndex == 1 || wayIndex == 2 || (wayIndex - 1) % 4 == 0 || (wayIndex - 2) % 4 == 0;
                    break;
            }
            int peopleCount = Mathf.FloorToInt((Density * pathLength) / _minimalObjectLength);
            float segmentLen = _minimalObjectLength + (pathLength - (peopleCount * _minimalObjectLength)) / peopleCount;
            int[] pickList = CommonUtils.GetRandomPrefabIndexes(peopleCount, ref walkingPrefabs);
            Vector3[] pointArray = new Vector3[_distances.Length];
            for (int i = 1; i < _distances.Length; i++)
            {
                pointArray[i - 1] = points[wayIndex, i];
            }
            pointArray[_distances.Length - 1] = loopPath ? points[wayIndex, 1] : points[wayIndex, _distances.Length];
            for (int peopleIndex = 0; peopleIndex < peopleCount; peopleIndex++)
            {
                var people = gameObject;
                var randomShift = Random.Range(-segmentLen / 3f, segmentLen / 3f) + (wayIndex * segmentLen);
                var finalRandomDistance = (peopleIndex + 1) * segmentLen + randomShift;
                Vector3 routePosition = GetRoutePosition(pointArray, finalRandomDistance, pCount, loopPath);
                float XPos = Random.Range(-randXPos, randXPos);
                float ZPos = Random.Range(-randZPos, randZPos);
                routePosition = new Vector3(routePosition.x + XPos, routePosition.y, routePosition.z + ZPos);
                Vector3 or;
                RaycastHit[] rrr = Physics.RaycastAll(or = new Vector3(routePosition.x, routePosition.y + 10000, routePosition.z), Vector3.down, Mathf.Infinity);
                bool isSemaphore = false;
                for (int i = 0; i < rrr.Length; i++)
                {
                    if (rrr[i].collider.GetComponent<SemaphoreSimulator>() != null || rrr[i].collider.GetComponent<SemaphorePeople>() != null)
                    {
                        isSemaphore = true;
                    }
                }
                if (isSemaphore) continue;
                float dist = 0;
                int bestCandidate = 0;
                rrr = Physics.RaycastAll(or = new Vector3(routePosition.x, routePosition.y + highToSpawn, routePosition.z), Vector3.down, Mathf.Infinity);
                for (int i = 0; i < rrr.Length; i++)
                {
                    if (dist < Vector3.Distance(rrr[0].point, or))
                    {
                        bestCandidate = i;
                        dist = Vector3.Distance(rrr[0].point, or);
                    }
                }
                if (rrr.Length > 0)
                {
                    routePosition.y = rrr[bestCandidate].point.y;
                }
                people = Instantiate(walkingPrefabs[pickList[peopleIndex]], routePosition, Quaternion.identity) as GameObject;
                var movePath = people.AddComponent<MovePath>();
                var passersby = people.AddComponent<Passersby>();
                movePath.randXFinish = XPos;
                movePath.randZFinish = ZPos;
                InitializePassersby(ref passersby);
                people.transform.parent = par.transform;
                movePath.walkPath = this;
                movePath._walkPointThreshold = nextPointThreshold;
                movePath.InitStartPosition(wayIndex,
                    GetRoutePoint((peopleIndex + 1) * segmentLen + randomShift, wayIndex, pCount, forward, loopPath), loopPath, forward);
                movePath.SetLookPosition();
            }
        }
    }
    private void InitializePassersby(ref Passersby _passersby)
    {
        _passersby.ANIMATION_STATE = animationState;
        _passersby.WALK_SPEED = walkSpeed;
        _passersby.RUN_SPEED = runSpeed;
        _passersby.SPEED_ROTATION = speedRotation;
        _passersby.VIEW_ANGLE = viewAngle;
        _passersby.VIEW_RADIUS = viewRadius;
        _passersby.targetMask = targetMask;
        _passersby.obstacleMask = obstacleMask;
        _passersby.DIST_TO_PEOPLE = distToPeople;
        _passersby.OverrideDefaultAnimationMultiplier = _overrideDefaultAnimationMultiplier;
        _passersby.CustomWalkAnimationMultiplier = _customWalkAnimationMultiplier;
        _passersby.CustomRunAnimationMultiplier = _customRunAnimationMultiplier;
    }
}
BcycleGyroPath
using UnityEngine;
using System.Collections.Generic;
public class BcycleGyroPath : WalkPath
{
    [Tooltip("Bicyclist Speed/ Скорость велосипедиста")] public float moveSpeed = 8.0f;
    [Tooltip("Acceleration / Ускорение")] public float increaseSpeed = 2.0f;
    [Tooltip("Braking / Торможение")] public float decreaseSpeed = 5.0f;
    [Range(0.1f, 5.0f)] [Tooltip("Offset from the line along the X axis / Смещение от линии по оси X")] public float randXPos = 0.1f;
    [Range(0.1f, 5.0f)] [Tooltip("Offset from the line along the Z axis / Смещение от линии по оси Z")] public float randZPos = 0.1f;
    [Tooltip("Скорость поворота")] public float speedRotation = 5.0f;
    [HideInInspector] [SerializeField] [Tooltip("Ignore pedestrian colliders? / Игнорировать коллайдеры пешеходов?")] private bool _ignorePeople = false;
    [HideInInspector] [SerializeField] [Tooltip("Customize your animation speed / Настроить свою скорость анимации?")] private bool _overrideDefaultAnimationMultiplier = true;
    [HideInInspector] [SerializeField] [Tooltip("Animation speed / Скорость анимации")] private float _customAnimationMultiplier = 2.0f;
    [Tooltip("Distance to next point / Расстояние до следующей точки")] public float nextPointThreshold = 3;
    private void Start()
    {
        if(_ignorePeople)
        {
            Physics.IgnoreLayerCollision(12, 8, true);  
        }                 
    }     
    public override void CreateSpawnPoints()
    {
        SpawnPoints = new SpawnPoint[points.GetLength(0)];
        for (int i = 0; i < points.GetLength(0); i++)
        {
            var startPoint = _forward[i] ? points[i, 0] : points[i, points.GetLength(1) - 1];
            var nextPoint = _forward[i] ? points[i, 2] : points[i, points.GetLength(1) - 3];
            SpawnPoints[i] = SpawnPoint.PeopleCreate(
                string.Format("SpawnPoint (Path {0})", i + 1),
                startPoint,
                nextPoint,
                lineSpacing,
                i,
                _forward[i],
                this,
                3f,
                1f
            );
        }
    }
    public override void SpawnOnePeople(int w, bool forward)
    {
        List<GameObject> pfb = new List<GameObject>(walkingPrefabs);
        for (int i = pfb.Count - 1; i >= 0; i--)
        {
            if (pfb[i] == null)
            {
                pfb.RemoveAt(i);
            }
        }
        walkingPrefabs = pfb.ToArray();
        int prefabNum = Random.Range(0, walkingPrefabs.Length);
        var people = gameObject;
        if (!forward)
        {
            people = Instantiate(walkingPrefabs[prefabNum], points[w, pointLength[0] - 2], Quaternion.identity) as GameObject;
        }
        else
        {
            people = Instantiate(walkingPrefabs[prefabNum], points[w, 1], Quaternion.identity) as GameObject;
        }
        var movePath = people.AddComponent<MovePath>();
        var controller = people.AddComponent<BcycleGyroController>();
        InitializeBcycleGyro(ref controller);
        movePath.randXFinish = Random.Range(-randXPos, randXPos);
        movePath.randZFinish = Random.Range(-randZPos, randZPos);
        people.transform.parent = par.transform;
        movePath.walkPath = this;
        if (!forward)
        {
            movePath.InitStartPosition(w, pointLength[0] - 3, loopPath, forward);
            people.transform.LookAt(points[w, pointLength[0] - 3]);
        }
        else
        {
            movePath.InitStartPosition(w, 1, loopPath, forward);
            people.transform.LookAt(points[w, 2]);
        }
        movePath._walkPointThreshold = nextPointThreshold;
    }
    public override void SpawnPeople()
    {
        List<GameObject> pfb = new List<GameObject>(walkingPrefabs);
        for (int i = pfb.Count - 1; i >= 0; i--)
        {
            if (pfb[i] == null)
            {
                pfb.RemoveAt(i);
            }
        }
        walkingPrefabs = pfb.ToArray();
        if (points == null) DrawCurved(false);
        if (par == null)
        {
            par = new GameObject();
            par.transform.parent = gameObject.transform;
            par.name = "walkingObjects";
        }
        int pathPointCount;
        if (!loopPath)
        {
            pathPointCount = pointLength[0] - 2;
        }
        else
        {
            pathPointCount = pointLength[0] - 1;
        }
        if (pathPointCount < 2) return;
        var pCount = loopPath ? pointLength[0] - 1 : pointLength[0] - 2;
        for (int wayIndex = 0; wayIndex < numberOfWays; wayIndex++)
        {
            _distances = new float[pCount];
            float pathLength = 0f;
            for (int i = 1; i < pCount; i++)
            {
                Vector3 vector;
                if (loopPath && i == pCount - 1)
                {
                    vector = points[wayIndex, 1] - points[wayIndex, pCount];
                }
                else
                {
                    vector = points[wayIndex, i + 1] - points[wayIndex, i];
                }
                pathLength += vector.magnitude;
                _distances[i] = pathLength;
            }
            bool forward = false;
            switch (direction.ToString())
            {
                case "Forward":
                    forward = true;
                    break;
                case "Backward":
                    forward = false;
                    break;
                case "HugLeft":
                    forward = (wayIndex + 2) % 2 == 0;
                    break;
                case "HugRight":
                    forward = (wayIndex + 2) % 2 != 0;
                    break;
                case "WeaveLeft":
                    forward = wayIndex != 1 && wayIndex != 2 && (wayIndex - 1) % 4 != 0 && (wayIndex - 2) % 4 != 0;
                    break;
                case "WeaveRight":
                    forward = wayIndex == 1 || wayIndex == 2 || (wayIndex - 1) % 4 == 0 || (wayIndex - 2) % 4 == 0;
                    break;
            }
            int peopleCount = Mathf.FloorToInt((Density * pathLength) / _minimalObjectLength);
            float segmentLen = _minimalObjectLength + (pathLength - (peopleCount * _minimalObjectLength)) / peopleCount;
            int[] pickList = CommonUtils.GetRandomPrefabIndexes(peopleCount, ref walkingPrefabs);
            Vector3[] pointArray = new Vector3[_distances.Length];
            for (int i = 1; i < _distances.Length; i++)
            {
                pointArray[i - 1] = points[wayIndex, i];
            }
            pointArray[_distances.Length - 1] = loopPath ? points[wayIndex, 1] : points[wayIndex, _distances.Length];
            for (int peopleIndex = 0; peopleIndex < peopleCount; peopleIndex++)
            {
                var people = gameObject;
                var randomShift = Random.Range(-segmentLen / 3f, segmentLen / 3f) + (wayIndex * segmentLen);
                var finalRandomDistance = (peopleIndex + 1) * segmentLen + randomShift;
                Vector3 routePosition = GetRoutePosition(pointArray, finalRandomDistance, pCount, loopPath);
                float XPos = Random.Range(-randXPos, randXPos);
                float ZPos = Random.Range(-randZPos, randZPos);
                routePosition = new Vector3(routePosition.x + XPos, routePosition.y, routePosition.z + ZPos);
                Vector3 or;
                RaycastHit[] rrr = Physics.RaycastAll(or = new Vector3(routePosition.x, routePosition.y + 10000, routePosition.z), Vector3.down, Mathf.Infinity);
                bool isSemaphore = false;
                for (int i = 0; i < rrr.Length; i++)
                {
                    if (rrr[i].collider.GetComponent<SemaphoreSimulator>() != null || rrr[i].collider.GetComponent<SemaphorePeople>() != null)
                    {
                        isSemaphore = true;
                    }
                }
                if (isSemaphore) continue;
                float dist = 0;
                int bestCandidate = 0;
                rrr = Physics.RaycastAll(or = new Vector3(routePosition.x, routePosition.y + highToSpawn, routePosition.z), Vector3.down, Mathf.Infinity);
                for (int i = 0; i < rrr.Length; i++)
                {
                    if (dist < Vector3.Distance(rrr[0].point, or))
                    {
                        bestCandidate = i;
                        dist = Vector3.Distance(rrr[0].point, or);
                    }
                }
                if (rrr.Length > 0)
                {
                    routePosition.y = rrr[bestCandidate].point.y;
                }
                people = Instantiate(walkingPrefabs[pickList[peopleIndex]], routePosition, Quaternion.identity) as GameObject;
                var movePath = people.AddComponent<MovePath>();
                var controller = people.AddComponent<BcycleGyroController>();
                InitializeBcycleGyro(ref controller);
                movePath.randXFinish = XPos;
                movePath.randZFinish = ZPos;
                people.transform.parent = par.transform;
                movePath.walkPath = this;
                movePath._walkPointThreshold = nextPointThreshold;
                movePath.InitStartPosition(wayIndex,
                    GetRoutePoint((peopleIndex + 1) * segmentLen + randomShift, wayIndex, pCount, forward, loopPath), loopPath, forward);
                movePath.SetLookPosition();
            }
        }
    }
    private void InitializeBcycleGyro(ref BcycleGyroController _bcycleGyro)
    {
        float randMoveSpeed = moveSpeed + Random.Range(moveSpeed * -0.15f, moveSpeed * 0.15f);
        _bcycleGyro.moveSpeed = randMoveSpeed;
        _bcycleGyro.increaseSpeed = increaseSpeed;
        _bcycleGyro.decreaseSpeed = decreaseSpeed;
        _bcycleGyro.speedRotation = speedRotation;
        _bcycleGyro.OverrideDefaultAnimationMultiplier = _overrideDefaultAnimationMultiplier;
        _bcycleGyro.CustomAnimationMultiplier = _customAnimationMultiplier;
    }
}
三、效果展示





