295 lines
8.8 KiB
C#
295 lines
8.8 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
/* High-level car controller
|
|
* Az autót irányító állapotgép.
|
|
*
|
|
* Jelölések: public PascalCase : futásidejű "interface"
|
|
* public camelCase : Unityből állítható konstansok
|
|
*
|
|
* Megkötések:
|
|
* Két kanyar nem lehet közvetlen egymás mellett.
|
|
* A 10-es rétegen az útjelek, a 9-esen az autók legyenek.
|
|
*
|
|
* Az itt lévő logika nem túl bonyolult. Igazából 3 állapotgép van, ezek hierarchikusan vannak szervezve.
|
|
* Könnyen bővíthető plusz állapotokkal, úttipusokkal, közlekedési helyezetekkel.
|
|
*
|
|
* Networking az autószínen kívül nincs benne. A kód csak a szerver oldalán fut.
|
|
*
|
|
* A tájékozódáshoz raycastingot használ, ezért megfelelő trigger collidereket kell elhelyezni speciális helyeken.
|
|
* A "junctionAssets/prefabs/roads" mappában látható erre példa.
|
|
* Szükség esetén a raycasting használat kicsit leljebb szorítható, ha csak nagyon gyenge CPU-nk van.
|
|
*
|
|
* Csak akkor áll meg másik autó miatt, ha az irányvektorik 90° > szöget zárnak be.
|
|
*
|
|
* A kereszteződéseket leszámítva a pálya segítségével magától közlekedik.
|
|
* Keresztetződéshez érve a TrafficController-től kérdezi meg, hogy merre mehet.
|
|
*/
|
|
|
|
//Kizárólag az autó színét szinkronizálja
|
|
[NetworkSettings(channel = 0, sendInterval = 0.1f)]
|
|
public class CarHL : NetworkBehaviour
|
|
{
|
|
/* Controls */ //Ezeken kívűl más nem kell a futásidejű irányításához, a többit magától megoldja.
|
|
[SyncVar(hook = "changeColor")]
|
|
public Color CarColor;
|
|
|
|
//Jelenleg melyik úton van?
|
|
public int RoadID;
|
|
/**/
|
|
|
|
[Header("Sensors")]
|
|
public float carSensorLength = 7;
|
|
public float roadSensorLength = 5;
|
|
public Vector3 driverSensorOffset;
|
|
//Driver szenzor széttartás
|
|
public float deltaX = 0.2f;
|
|
|
|
//Innen indulnak a raycastok
|
|
private Vector3 driverSensor;
|
|
|
|
[Header("External references")]
|
|
public string trafficContollerName = "Traffic controller";
|
|
public Renderer body;
|
|
public CarLL carLL;
|
|
private TrafficController traffic;
|
|
|
|
[Header("Misc")]
|
|
public float turningChance = 0.5f;
|
|
|
|
//Állapotgépek
|
|
//Fő állapot
|
|
private CarState carState;
|
|
//Az úton ilyen állapotban van
|
|
private RoadCarState roadCarState;
|
|
//A kereszteződésben ilyen állapotban van
|
|
private CrossroadCarState crossroadCarState;
|
|
|
|
//A következő utak ID-je.
|
|
private int rightRoadID;
|
|
private int forwardRoadID;
|
|
|
|
public void Start ()
|
|
{
|
|
changeColor(CarColor);
|
|
|
|
if (!isServer) return;
|
|
|
|
traffic = GameObject.Find(trafficContollerName).GetComponent<TrafficController>();
|
|
|
|
//Alapállapot
|
|
carState = CarState.road;
|
|
roadCarState = RoadCarState.onRoad;
|
|
}
|
|
|
|
public void FixedUpdate ()
|
|
{
|
|
//Ez az egész modul (a színváltást leszámítva) kliens oldalon nem csinál semmit.
|
|
if (!isServer) return;
|
|
|
|
//Innen indulnak a raycastok
|
|
driverSensor = transform.position + transform.forward * driverSensorOffset.x +
|
|
transform.right * driverSensorOffset.z + transform.up * driverSensorOffset.y;
|
|
|
|
/* A raycastok Debugban:
|
|
* piros : nem talált el semmi lényegeset
|
|
* kék : autót talált, de szembejövő, szóval ignore
|
|
* zöld : meg kell állni miatta
|
|
*/
|
|
|
|
//Itt tárolódnak a raycastok
|
|
RaycastHit hit;
|
|
|
|
switch (carState)
|
|
{
|
|
case CarState.road:
|
|
carLL.IsBraking = shouldStopOfCar();
|
|
|
|
switch (roadCarState)
|
|
{
|
|
case RoadCarState.onRoad:
|
|
|
|
if (Physics.Raycast(driverSensor, -transform.up, out hit, roadSensorLength, 1 << 10))
|
|
{
|
|
if (hit.collider)
|
|
{
|
|
Debug.DrawLine(driverSensor, hit.point, Color.green);
|
|
|
|
if (hit.collider.tag == "rightTurn")
|
|
{
|
|
carLL.StartTurning(false);
|
|
roadCarState = RoadCarState.onTurn;
|
|
}
|
|
else if (hit.collider.tag == "leftTurn")
|
|
{
|
|
carLL.StartTurning(true);
|
|
roadCarState = RoadCarState.onTurn;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
//Erre az állapotra azért van szükség, hogy csak egyszer triggerelődjön a StartTurning
|
|
case RoadCarState.onTurn:
|
|
if (carLL.IsTurningDone())
|
|
roadCarState = RoadCarState.onRoad;
|
|
break;
|
|
}
|
|
|
|
/* KÖZÖS az összes roadStateben, hogy figyel a kereszteződésre. */
|
|
//Amúgy ez a feature nincsen kihasználva, de a jövőben lehet, hogy ez megváltozik.
|
|
if (Physics.Raycast(driverSensor, transform.forward, out hit, roadSensorLength, 1 << 10))
|
|
if (hit.collider)
|
|
for (int i = 0; i < 4; i++)
|
|
if (hit.collider.tag == "crossRoad" + i)
|
|
{
|
|
carState = CarState.crossroad;
|
|
crossroadCarState = CrossroadCarState.beforeLightWaiting;
|
|
}
|
|
|
|
break;
|
|
|
|
case CarState.crossroad:
|
|
//Ez az alapértelmezett
|
|
carLL.IsBraking = true;
|
|
|
|
switch (crossroadCarState)
|
|
{
|
|
case CrossroadCarState.beforeLightWaiting:
|
|
if (Physics.Raycast(driverSensor, transform.forward, out hit, roadSensorLength, 1 << 10))
|
|
if (hit.collider)
|
|
Debug.DrawLine(driverSensor, hit.point, Color.blue);
|
|
for (int i = 0; i < 4; i++)
|
|
if (hit.collider.tag == "crossRoad" + i)
|
|
if (traffic.getInfo(i, hit.collider.name, ref forwardRoadID, ref rightRoadID))
|
|
crossroadCarState = CrossroadCarState.beforeLightStarting;
|
|
|
|
break;
|
|
|
|
case CrossroadCarState.beforeLightStarting:
|
|
crossroadCarState = CrossroadCarState.beforeLightWaiting;
|
|
|
|
if (Random.Range(0.0f, 1.0f) < turningChance)
|
|
{
|
|
if (traffic.changeRoad(RoadID, rightRoadID))
|
|
{
|
|
RoadID = rightRoadID;
|
|
crossroadCarState = CrossroadCarState.preManouverRight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (traffic.changeRoad(RoadID, forwardRoadID))
|
|
{
|
|
RoadID = forwardRoadID;
|
|
crossroadCarState = CrossroadCarState.preManouverForward;
|
|
}
|
|
}
|
|
break;
|
|
|
|
//Maga az áthaladás azonos, a lényeg, hogy előtte be kell gurulni a kereszteződésbe, és csak utána
|
|
//lehet elkezdeni.
|
|
case CrossroadCarState.preManouverForward:
|
|
carLL.IsBraking = false;
|
|
if (Physics.Raycast(driverSensor, -transform.up, out hit, roadSensorLength, 1 << 10))
|
|
if (hit.collider && hit.collider.tag == "crossRoad")
|
|
crossroadCarState = CrossroadCarState.manouver;
|
|
break;
|
|
|
|
case CrossroadCarState.preManouverRight:
|
|
carLL.IsBraking = false;
|
|
if (Physics.Raycast(driverSensor, -transform.up, out hit, roadSensorLength, 1 << 10))
|
|
if (hit.collider && hit.collider.tag == "crossRoad")
|
|
{
|
|
carLL.StartTurning(false);
|
|
crossroadCarState = CrossroadCarState.manouver;
|
|
}
|
|
break;
|
|
|
|
case CrossroadCarState.manouver:
|
|
carLL.IsBraking = false;
|
|
if (Physics.Raycast(driverSensor, -transform.up, out hit, roadSensorLength, 1 << 10))
|
|
if (hit.collider && hit.collider.tag != "crossRoad")
|
|
carState = CarState.road;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void changeColor (Color newColor)
|
|
{
|
|
body.materials[1].color = newColor;
|
|
}
|
|
|
|
private bool shouldStopOfCar ()
|
|
{
|
|
RaycastHit hit;
|
|
|
|
//Az autók a 9-es rétegen vannak
|
|
|
|
//Ezt lehetne változóba tenni, de ez működik, szóval nem kell megváltoztatni.
|
|
//Ha gond a szerveroldali CPU használat, akkor ezen kell javítani.
|
|
for (int i = -3; i <= 3; i++)
|
|
{
|
|
Debug.DrawLine(driverSensor, driverSensor + ( Quaternion.Euler(0, deltaX * i, 0) * transform.forward * carSensorLength ), Color.red);
|
|
if (Physics.Raycast(driverSensor, Quaternion.Euler(0, deltaX * i, 0) * transform.forward, out hit, carSensorLength, 1 << 9, QueryTriggerInteraction.Collide))
|
|
{
|
|
Debug.DrawLine(driverSensor, hit.point, Color.blue);
|
|
float angle = Mathf.Abs(transform.eulerAngles.y - hit.collider.transform.eulerAngles.y);
|
|
if (angle >= 180) angle -= 360;
|
|
angle = Mathf.Abs(angle);
|
|
|
|
if (hit.collider.tag == "car" && angle < 90)
|
|
{
|
|
Debug.DrawLine(driverSensor, hit.point, Color.green);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//A vezető szemét jelképező gömb rajzolása
|
|
private void OnDrawGizmos ()
|
|
{
|
|
driverSensor = transform.position + transform.forward * driverSensorOffset.x +
|
|
transform.right * driverSensorOffset.z + transform.up * driverSensorOffset.y;
|
|
Gizmos.DrawSphere(driverSensor, 0.15f);
|
|
}
|
|
|
|
//Robbanás
|
|
private void OnTriggerEnter (Collider other)
|
|
{
|
|
if (!isServer) return;
|
|
|
|
if (other.tag == "car")
|
|
{
|
|
carLL.ShouldExplode = true;
|
|
traffic.roads[RoadID].carCount--;
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum CarState
|
|
{
|
|
road,
|
|
crossroad
|
|
}
|
|
|
|
public enum RoadCarState
|
|
{
|
|
onRoad,
|
|
onTurn
|
|
}
|
|
|
|
public enum CrossroadCarState
|
|
{
|
|
beforeLightWaiting,
|
|
beforeLightStarting,
|
|
preManouverForward,
|
|
preManouverRight,
|
|
manouver
|
|
}
|