一、前言
在编写车辆寻路以及道路类型的相关脚本后,我对交通工具比如道路车辆、船舶、飞机等的具体控制脚本也进行了编写和完善。
二、编写的脚本模块和功能的简单介绍
1.Trains
机车和所有货车的tag都设置为“Train”。
Wagons
火车可以有从零到64节车厢。每个wagon游戏对象必须附有引用box collider的wagon脚本和Box collider必须设置为匹配网格的长度)。
Max speed
确定机车可以达到的最大速度(以km/h为单位)。
Acceleration
火车的加速度。
Checkpoints
检查站是场景中各地的位置,pathfinder通过它尝试为火车找到一条路径。
火车开始行驶时尝试找到通往第一个检查站的路径。当火车到达终点时,它会通过相同的检查站返回。
2.Road Vehicles
Road Vehicles
每辆车都有PathFinding和CarBehavior脚本,以及设置为kinematic和gravity关闭的刚体组件。box collider设置为在车辆前方触发,以检测前方的车辆/交通灯/人行横道。
脚本和碰撞器所连接的游戏对象的tag必须设置为Car。
Random destination
启用时,从当前位置查找到随机可访问tile的路径。禁用时,通过设置检查点查找路径。
Closed Circuit
仅在“随机目标”处于禁用状态时可见。如果为true,则从检查点创建的路径共享开始和结束。如果为false,则路径通过检查点。当车辆到达终点时,它会通过相同的检查点返回。
Max speed
车辆的最高速度。
Checkpoints
仅在“随机目标”处于禁用状态时可见。检查站是pathfinder尝试为车辆找到路径的场景位置。在开始时,它最先尝试寻找到达第一个检查点的路径。
3.Ships
Ships
船只不会沿着pathfinder创造的路径前进,而是沿着预定的路径前进。
Trajectory
船将要走的路径。
Max speed
确定船舶可以达到的最大速度(以km/h为单位)。
Acceleration
船的加速度。
Ship Tipping
船可以倾斜的最大角度(以度为单位)。
4.Planes
Planes
飞机和船一样,不会沿着pathfinder创建的路径飞行,而是沿着预定的路径飞行。
Trajectory
飞机将沿着这个路径飞行。当它到达路径的终点时,它将从起点继续。
Max speed
确定飞机可以达到的最大速度(以km/h为单位)。
Acceleration
飞机的加速度。
三、主要脚本编写
CarBehavior
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PolyPerfect.City
{
[RequireComponent(typeof(PathFinding)),RequireComponent(typeof(Rigidbody))]
public class CarBehavior : MonoBehaviour
{
//[HideInInspector]
public List<Path> trajectory = new List<Path>();
public bool randomDestination = true;
public bool closedCircuit = false;
private PathFinding pathFinding;
public float minDistance = 90f;
public float maxspeed = 5.0f;
public float acceleration = 0.5f;
private const float KMHTOMS = 0.27777f;
private float currentMaxSpeed;
[HideInInspector]
public float speed;
int activepoint = 0;
bool isMoving = false;
bool drivingBihindCar = false;
bool drivingTrafficLights = false;
int activePath = 0;
int randomPathTries = 10;
private Vector3 targetDrivePoint;
private Vector3 destination;
public List<Vector3> checkpoints = new List<Vector3>();
private Vector3 start;
private CarBehavior carInFront;
private void Awake()
{
pathFinding = GetComponent<PathFinding>();
}
void Start()
{
currentMaxSpeed = maxspeed;
if(closedCircuit)
checkpoints.Add(checkpoints[0]);
StartDriving();
}
//Gets next target point of the trajectory
public void MoveToNextPoint()
{
if (activePath == trajectory.Count - 1)
{
if (activepoint == trajectory[activePath].pathPositions.Count - 1)
{
isMoving = false;
if(randomDestination)
StartDriving();
else
{
if(!closedCircuit)
checkpoints.Reverse();
trajectory = pathFinding.GetPathWithCheckpoints(checkpoints, PathType.Road);
if (trajectory != null)
{
trajectory.RemoveAt(trajectory.Count - 1);
activePath = 0;
activepoint = 0;
speed = 0;
isMoving = true;
}
else
return;
}
}
else
{
activepoint++;
}
}
else
{
if (activepoint == trajectory[activePath].pathPositions.Count - 1)
{
activePath++;
if (trajectory[activePath].speed < maxspeed)
{
currentMaxSpeed = trajectory[activePath].speed;
}
else
{
currentMaxSpeed = maxspeed;
}
activepoint = 1;
}
else
{
activepoint++;
}
}
if (trajectory != null)
{
if (trajectory.Count > activePath)
targetDrivePoint = trajectory[activePath].pathPositions[activepoint].transform.position;
}
}
private void StartDriving()
{
if (randomDestination)
{
//Selects random tile which is at least minDistance away
start = transform.position;
destination = start;
int tries = 0;
randomPathTries--;
while (Vector3.Distance(start, destination) < minDistance && tries < Tile.tiles.Count)
{
tries++;
Tile t = Tile.tiles[UnityEngine.Random.Range(0, Tile.tiles.Count - 1)];
if (t.tileType == Tile.TileType.Road || t.tileType == Tile.TileType.RoadAndRail)
{
if (t.verticalType == Tile.VerticalType.Bridge)
{
destination = t.transform.position + (Vector3.up * 12);
}
else
{
destination = t.transform.position;
}
}
}
if(tries == Tile.tiles.Count)
{
Debug.Log(name + ": Path not found");
return;
}
}
else
{
//Destination of the first checkpoint
destination = checkpoints[0];
start = transform.position;
}
//Pathfinder finds best path
trajectory = pathFinding.GetPath(start,destination, PathType.Road);
if (trajectory != null)
{
speed = 0;
activePath = 0;
activepoint = 0;
float closest = float.MaxValue;
for(int i = 0;i <trajectory[0].pathPositions.Count;i++)
{
float tmp = Vector3.Distance(trajectory[0].pathPositions[i].position, transform.position);
if(tmp < closest)
{
closest = tmp;
activepoint = i;
}
}
targetDrivePoint = trajectory[0].pathPositions[activepoint].transform.position;
isMoving = true;
if(!closedCircuit)
checkpoints.Reverse();
}
else
{
Debug.Log(name + ": Path not found");
if(randomDestination && randomPathTries>0)
{
StartDriving();
}
}
}
void FixedUpdate()
{
if (isMoving && trajectory != null)
{
//Calculate remaing distance to current checkpoint and direction to it
float pointDistance = Vector2.Distance(new Vector2(transform.position.x, transform.position.z), new Vector2(targetDrivePoint.x, targetDrivePoint.z));
Vector3 direction = targetDrivePoint - transform.position;
//If car reaches target it gets the next target
if (pointDistance < 0.02f*speed)
{
MoveToNextPoint();
}
if (!drivingBihindCar)
{
speed = Mathf.Lerp(speed, maxspeed, acceleration * Time.deltaTime);
}
else
{
if(carInFront.speed < speed)
speed = speed - carInFront.speed;
}
if (speed > currentMaxSpeed)
{
speed = Mathf.Lerp(speed, currentMaxSpeed, 10 * Time.deltaTime);
}
Vector3 newPosition = transform.position + (direction.normalized * speed * KMHTOMS * Time.deltaTime);
transform.position = newPosition;
if(direction != Vector3.zero)
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(direction, Vector3.up), maxspeed * Time.deltaTime *15);
}
else
{
speed = 0;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("TrafficLight") && !drivingTrafficLights )
{
TrafficLight trafic = other.GetComponent<TrafficLight>();
if (Vector3.Angle(-trafic.transform.forward,transform.forward) < 25)
{
if (!trafic.isGreen)
{
drivingTrafficLights = true;
isMoving = false;
trafic.lightChange += StartMoving;
}
}
}
if (other.CompareTag("Crosswalk"))
{
Crosswalk crosswalk = other.GetComponent<Crosswalk>();
if (crosswalk.PedestriansAreCrossing)
{
crosswalk.stateChange += CrosswalkChange;
isMoving = false;
}
}
else if (other.CompareTag("LevelCrossing"))
{
LevelCrossingController levelCrossing = other.GetComponent<LevelCrossingController>();
if (levelCrossing.trainCrossing)
{
levelCrossing.stateChange += LevelCrossingChange;
isMoving = false;
}
}
else if (other.CompareTag("Car") && !other.isTrigger && activePath >1)
{
float direction = Vector3.Angle(transform.forward, other.transform.forward);
float carDirection = Vector3.Angle(transform.right, (other.transform.position - transform.position).normalized);
if (direction < 50)
{
drivingBihindCar = true;
carInFront = other.GetComponentInParent<CarBehavior>();
speed = carInFront.speed *0.8f;
}
if (direction > 40 && carDirection < 80 && carDirection > 45)
{
isMoving = false;
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Car") && !other.isTrigger)
{
StopCoroutine(StartMovingAfterWait(0.2f));
StartCoroutine(StartMovingAfterWait(0.2f));
drivingBihindCar = false;
}
else if (other.CompareTag("TrafficLight"))
{
TrafficLight trafic = other.GetComponent<TrafficLight>();
trafic.lightChange -= StartMoving;
drivingTrafficLights = false;
}
else if (other.CompareTag("Crosswalk"))
{
other.GetComponent<Crosswalk>().stateChange -= CrosswalkChange;
}
else if (other.CompareTag("LevelCrossing"))
{
other.GetComponent<LevelCrossingController>().stateChange -= LevelCrossingChange;
}
}
void StartMoving(bool isGreen)
{
if (isGreen)
{
drivingTrafficLights = false;
isMoving = true;
}
}
void CrosswalkChange(bool crossing)
{
if (!crossing && !drivingTrafficLights)
{
isMoving = true;
}
}
void LevelCrossingChange(bool crossing)
{
if (!crossing)
{
isMoving = true;
}
}
IEnumerator StartMovingAfterWait(float seconds)
{
yield return new WaitForSeconds(seconds);
isMoving = true;
}
}
}
PlaneBehavior
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PolyPerfect.City
{
public class PlaneBehavior : MonoBehaviour
{
public Path trajectory;
public float maxspeed = 5.0f;
private const float KMHTOMS = 0.27777f;
private float currentMaxSpeed;
[HideInInspector]
public float speed;
[Range(0f, 1f)]
public float acceleration;
[Range(0f, 5f)]
public float brakePower;
int activepoint = 0;
private Vector3 targetDrivePoint;
private bool isMoving;
// Start is called before the first frame update
void Start()
{
targetDrivePoint = trajectory.pathPositions[activepoint].position;
isMoving = true;
currentMaxSpeed = maxspeed;
}
private void FixedUpdate()
{
if (isMoving)
{
if (Vector3.Dot(targetDrivePoint - transform.position, transform.forward) < 0 && Vector3.Distance(targetDrivePoint, transform.position) < 20)
{
MoveToNextPoint();
}
Vector3 direction = targetDrivePoint - transform.position;
if(transform.position.y < 150 && activepoint > 5 && transform.position.y > 20)
{
currentMaxSpeed = maxspeed * (transform.position.y) * 0.0075f;
}
else if(activepoint == trajectory.pathPositions.Count-1)
{
currentMaxSpeed = 50;
}
if (speed < currentMaxSpeed)
{
speed += ((maxspeed * Mathf.Cos((speed / maxspeed) * 0.5f * Mathf.PI)) * Time.deltaTime) * acceleration;
}
else
{
speed = Mathf.Lerp(speed, currentMaxSpeed, brakePower * Time.deltaTime);
}
// if (direction != Vector3.zero)
// {
direction = direction.normalized;
float angle = Vector3.SignedAngle(transform.forward, direction, transform.up);
//direction.y = speed / maxspeed * shipTipping;
if (activepoint == 0)
{
transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
}
else
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(direction, Quaternion.AngleAxis(-(angle > 10 ? angle : 0f), transform.forward) * Vector3.up), 1.75f);
if(transform.localRotation.eulerAngles.x < 180)
{
transform.GetChild(0).localRotation = Quaternion.Euler(-transform.localRotation.eulerAngles.x*1.5f,0f,0f);
}
else
transform.GetChild(0).localRotation = Quaternion.identity;
// }
direction = transform.forward;
Vector3 newPosition = transform.position + (direction * speed * KMHTOMS * Time.deltaTime);
transform.position = newPosition;
}
else
{
speed = 0;
}
}
public void MoveToNextPoint()
{
if (activepoint == trajectory.pathPositions.Count - 1)
{
activepoint = 0;
currentMaxSpeed = maxspeed;
speed = 0;
}
else
{
activepoint++;
}
targetDrivePoint = trajectory.pathPositions[activepoint].transform.position;
}
}
}