VR开发实战HTC Vive项目之天气系统及智能交通

一、框架视图

二、关键代码

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;
    }
}

三、效果展示

推荐阅读更多精彩内容