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(); //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 }