Unity-在Unity中实现简单的交通系统

一、功能描述

1、基于路线,每条路线定时生成车辆,车辆沿着路线预设好的点移动。

2、轻量级,没有使用物理系统,车辆的运动使用的API是transform.Translate()。

3、可以实现曲线路线(为了真实需要设置更多的点位)。

4、在路口可以设置通行规则,目前只实现了十字路口。

5、随机车辆类型、随机生成车辆间隔、随机初始车速。

6、没有实现上下坡,只能在同一水平面。

二、初始化场景

1、搭建场景。

2、在Hierarchy窗口创建一个空物体命名为“TrafficSystem”,在下面再创建三个空物体分布命名为CarPool、PathCollection、CrossingCollection,用来归类存放车辆预制体、行驶路线和路口。

三、行驶路线

1、新建脚本CarPathPair

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CarPathPair : MonoBehaviour
{
    public float Duration;//生成车辆的间隔
    private GameObject[] CarPrefabs;//汽车预制体
    private Transform spawnPoint;//生成点
    private float timer;//计时器
    [SerializeField]
    private List<Vector3> wayPoints = new List<Vector3>();
    void Start()
    {
        Init();
        SpawnCar();
    }
    void Update()
    {
        timer += Time.deltaTime;
        if(timer >= Duration)
        {
            timer = 0;
            SpawnCar();
        }
    }

    private void Init()
    {
        spawnPoint = transform.GetChild(0);
        CarPrefabs = CarPool.Instance.AllCars;
        for (int i=0;i<transform.childCount;i++)
        {
            wayPoints.Add(transform.GetChild(i).position);
        }
    }

    private void SpawnCar()
    {
        int carType = Random.Range(0, CarPrefabs.Length);
        GameObject tempCar = Instantiate(CarPrefabs[carType],spawnPoint.position,spawnPoint.rotation,transform);
        tempCar.SetActive(true);
        CarMovement carMove = tempCar.AddComponent<CarMovement>();
        carMove.WayPoints = wayPoints;
    }

#if UNITY_EDITOR
    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.yellow;
        for(int i=0;i<transform.childCount-1;i++)
        {
            Gizmos.DrawLine(transform.GetChild(i).position, transform.GetChild(i+1).position);
        }
    }
#endif
}

2、在PathCollection下面新建空物体挂载‘CarPathPair.cs’,重命名该物体为Path1(或者其他可以区分不同路线的名字)。

3、在Path1下面相继创建空物体作为这条路线的路径点,在拐角处多放置一些可以让车辆运动平滑一些。

4、在最后一个点上添加BoxCollider,车辆再达到终点时候会自动销毁,如果不添加则会一直沿着最后的方向移动,90秒后再销毁。

四、车辆预制体

1、在CarPool下面添加自己的车辆模型,把车头方向调整为z轴方向。

2、在Project窗口中添加CarMovement脚本,脚本不需要挂载到车辆上,它在实例化车辆的时候自动添加上去。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CarMovement : MonoBehaviour
{
    private float carSpeed = 10f;//车速
    private float initSpeed;//初始车速
    private bool isWaitTraffic = false;//是否在路口等待
    private float detectDis = 5.5f;//向前检测距离

    public List<Vector3> WayPoints { get; set; }
    private int pointIndex = 1;
    private void Start()
    {
        initSpeed = Random.Range(15f, 20f);
        carSpeed = initSpeed;
        Destroy(gameObject, 90);
    }
    private void Update()
    {
        //SimpleMovement();
        Movement();
        DetectForward();
    }

    private void ResetCarSpeed()
    {
        if(!isWaitTraffic)
            carSpeed += 2.5f;
    }

    //simple move 走直线
    private void SimpleMovement()
    {
        transform.Translate(Vector3.forward * Time.deltaTime * carSpeed); 
    }

    private void Movement()
    {
        Vector3 dir = WayPoints[pointIndex] - WayPoints[pointIndex - 1];
        transform.Translate(dir.normalized * Time.deltaTime * carSpeed,Space.World);
        transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(dir,Vector3.up), Time.deltaTime*10);
        if(Vector3.SqrMagnitude(WayPoints[pointIndex]-transform.position) < 0.2f)//根据速度设置检测距离 过小会检测已经到了点位导致车辆不再转向
        {
            pointIndex++;
            if(pointIndex>=WayPoints.Count)
            {
                Destroy(gameObject);
            }
        }
    }

    //向前探测
    private void DetectForward()
    {
        Ray ray = new Ray(transform.position, transform.forward);
        RaycastHit hitInfo;
        if (Physics.Raycast(ray, out hitInfo, detectDis))
        {
            switch (hitInfo.transform.tag)
            {
                case "Car":
                    CarMovement preCar = hitInfo.transform.GetComponent<CarMovement>();
                    if(preCar.isWaitTraffic)
                    {
                        carSpeed = 0;
                        this.isWaitTraffic = true;
                    }
                    else
                    {
                        this.carSpeed = preCar.carSpeed;
                        preCar.ResetCarSpeed();
                    }
                    preCar.ResetCarSpeed();
                    break;
                case "Traffic":
                    isWaitTraffic = true;
                    carSpeed = 0;
                    break;
                default:
                    Destroy(gameObject);
                    break;
            }
        }
        else
        {
            isWaitTraffic = false;
            carSpeed = initSpeed;
        }
    }

#if UNITY_EDITOR
    private void OnDrawGizmos()
    {
        if (Application.isPlaying)
        {
            Gizmos.DrawRay(transform.position, transform.forward * detectDis);
        }
    }
#endif

}

3、在Project窗口添加CarPool脚本,负责添加各种车辆模型。。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DataPaddyBoot.Runtime.Singleton;

public class CarPool : MonoSingletonBase<CarPool>
{
    public GameObject[] AllCars;
}

4、把上面的脚本挂载到CarPool物体上,并为AllCars拖拽赋值。

五、设置路口

1、在CrossingCollection下面新建一个空物体名称为Crossing,再在下面创建四个碰撞体(分别命名为Up/Down/Left/Right),把它们分布放在停止线上。

2、添加脚本CrossingController,挂载到Crossing上。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CrossingController : MonoBehaviour
{
    public float duration;//切换红绿灯间隔
    private float timer = 0;
    private bool vertical = true;

    private void Update()
    {
        timer += Time.deltaTime;
        if(timer>=duration)
        {
            timer = 0;
            vertical = !vertical;
            if(vertical)
            {
                StartCoroutine(ChangeVCrossing());
            }
            else
            {
                StartCoroutine(ChangeUCrossing());
            }
        }
    }

    private IEnumerator ChangeVCrossing()
    {
        transform.Find("Up").gameObject.SetActive(true);
        transform.Find("Down").gameObject.SetActive(true);
        yield return new WaitForSeconds(1.5f);
        transform.Find("Left").gameObject.SetActive(false);
        transform.Find("Right").gameObject.SetActive(false);
    }

    private IEnumerator ChangeUCrossing()
    {
        transform.Find("Left").gameObject.SetActive(true);
        transform.Find("Right").gameObject.SetActive(true);
        yield return new WaitForSeconds(1.5f);
        transform.Find("Up").gameObject.SetActive(false);
        transform.Find("Down").gameObject.SetActive(false);
    }
}

3、为Crossing设置切换间隔。

点赞
  1. a说道:

    大哥,能给下资源么,你的untiy城市这个

    1. Alan Alan说道:

      我这个是公司的项目,你可以在资源商店上找类似的场景。

发表回复

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像(已失效)