using System.Collections; using System.Collections.Generic; using Unity.Collections; using Unity.Entities; using UnityEngine; using System.Linq; public struct CurrentTrafficVolume { public float AB; public float BA; } public class TrafficVisuaizer : MonoBehaviour { //--------------------------------------------------------------- [Header("CityBuilder WebApi")] public string Address = "localhost"; public int Port = 44378; public bool IsHttps = true; [Header("City")] public int CityId = 15; public double LatitudeOrigin; public double LongitudeOrigin; [Header("Traffic Simulation Time")] [Range(0, 1000)] public float Speed = 500.0f; // "Speed" seconds in 1 second [Range(0,86400)] public float CurrentTimeOfDay; // 0 to 86400 seconds [Header("Traffic Visualization")] public GameObject FeaturePrefab; // Lines Render public float[] trafficCoefficients = new float[] { .5f,.5f,.3f,.4f,.4f,1.8f, 0.7f,1.6f,0.7f, 1.3f,1.2f,1.2f,1.2f,1.2f,1.2f,1.3f, 0.7f,1.6f,0.7f,1.5f,1.0f,1.0f,1.0f,1.0f }; //--------------------------------------------------------------- private TrafficData trafficData; private List featuresRenders = new List(); //--------------------------------------------------------------- const float AM_RUSH_INIT = 6.0f; const float AM_RUSH_END = 9.0f; const float AM_RUSH_DUR = AM_RUSH_END - AM_RUSH_INIT; const float PM_RUSH_INIT = 16.0f; const float PM_RUSH_END = 19.0f; const float PM_RUSH_DUR = PM_RUSH_END - PM_RUSH_INIT; const float OFF_PEAK_DUR = 24.0f - AM_RUSH_DUR - PM_RUSH_DUR; const float SECONDS_DAY = 86400.0f; const float SECONDS_HOUR = 3600.0f; private EntityManager manager; private EntityQuery query; private StreetDefinitionSystem m_StreetDefinitionSystem; //--------------------------------------------------------------- // START //--------------------------------------------------------------- private void Start() { CityBuilderConnector.Initialize(Address, Port, IsHttps); trafficData = CityBuilderConnector.Instance.GetTrafficData(CityId); if (trafficData==null || trafficData.Features==null) { Debug.Log($"Features count = No Connection or empty Features"); } else Debug.Log($"Features count = {trafficData.Features.Length}"); manager = World.DefaultGameObjectInjectionWorld.EntityManager; query = manager.CreateEntityQuery(ComponentType.ReadOnly()); m_StreetDefinitionSystem = World.DefaultGameObjectInjectionWorld.GetExistingSystem(); //m_StreetDefinitionSystem.trafficVisualizer = this; InstantiateFeaturesGameObjects(); StartCoroutine(ManageTimeCoroutine()); } //--------------------------------------------------------------- // MAIN COROUTINE - FIXED UPDATE 1 sec. //--------------------------------------------------------------- IEnumerator ManageTimeCoroutine() { CurrentTimeOfDay = 0; while (Application.isPlaying) { yield return new WaitForSecondsRealtime(1f); CurrentTimeOfDay += Speed; if (CurrentTimeOfDay >= SECONDS_DAY) CurrentTimeOfDay = 0; foreach (FeatureRender featureRender in featuresRenders) { CurrentTrafficVolume vol = CalculateCurrentVolume(CurrentTimeOfDay, featureRender.Feature); featureRender.SetTime(CurrentTimeOfDay,vol); } foreach(var q in query.ToEntityArray(Allocator.Temp))// manager.GetComponentData query.ToEntityArray(Allocator.Temp)) { var data = manager.GetComponentData(q); CurrentTrafficVolume vol = CalculateCurrentVolume(data.StreetId); manager.SetComponentData(q, new StreetTraffic { StreetId = data.StreetId, VolTrafficAB = vol.AB, VolTrafficBA = vol.BA, LanesA = data.LanesA, LanesB = data.LanesB, Direction = data.Direction }); } } } //=============================================================== // PRIVATE METHODS //--------------------------------------------------------------- // Create all Features Render on START() //--------------------------------------------------------------- private void InstantiateFeaturesGameObjects() { foreach(TrafficFeature feature in trafficData.Features) { GameObject featureGameObject = Instantiate(FeaturePrefab); featureGameObject.transform.SetParent(this.transform); var renderComponent = featureGameObject.GetComponent(); renderComponent.Feature = feature; renderComponent.LatitudeOrigin = LatitudeOrigin; renderComponent.LongitudeOrigin = LongitudeOrigin; featuresRenders.Add(renderComponent); } } public CurrentTrafficVolume CalculateCurrentVolume(long streetId) { return CalculateCurrentVolume(CurrentTimeOfDay, streetId); } private CurrentTrafficVolume CalculateCurrentVolume(float timestampSeconds, long StreetId) { var street = from x in trafficData.Features where x.Id == StreetId select x; if (street.Any()) { return CalculateCurrentVolume(timestampSeconds, street.First()); } return new CurrentTrafficVolume() { AB=0, BA=0 }; } //--------------------------------------------------------------- // Feature Hourly Traffic VOLUME CALCULATION //--------------------------------------------------------------- //TODO: INTERPOLATION private CurrentTrafficVolume CalculateCurrentVolume(float timestampSeconds, TrafficFeature trafficFeature) { CurrentTrafficVolume mRes = new CurrentTrafficVolume(); //TODO: CONSTANTS float hourOfDay = timestampSeconds / SECONDS_HOUR; //Calculate Current Volume if (hourOfDay >= AM_RUSH_INIT && hourOfDay < AM_RUSH_END) { //am peak mRes.AB = trafficFeature.AMVOL_AB / AM_RUSH_DUR; mRes.BA = trafficFeature.AMVOL_BA / AM_RUSH_DUR; } else { if (hourOfDay >= PM_RUSH_INIT && hourOfDay < PM_RUSH_END) { //pm peak mRes.AB = trafficFeature.PMVOL_AB / PM_RUSH_DUR; mRes.BA = trafficFeature.PMVOL_BA / PM_RUSH_DUR; } else { //offpeak mRes.AB = trafficFeature.OPVOL_AB / OFF_PEAK_DUR; mRes.BA = trafficFeature.OPVOL_BA / OFF_PEAK_DUR; } } //Apply Hourly Coefficient mRes.AB = mRes.AB * trafficCoefficients[Mathf.FloorToInt(hourOfDay)]; mRes.BA = mRes.AB * trafficCoefficients[Mathf.FloorToInt(hourOfDay)]; return mRes; } public CurrentTrafficVolume CalculateCurrentVolume(TrafficFeature trafficFeature) { return CalculateCurrentVolume(CurrentTimeOfDay, trafficFeature); } }