sdsandbox-rl-scripts/Scripts/camera/RaceCameras.cs

194 lines
6.1 KiB
C#
Executable File

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaceCameras : MonoBehaviour, IWaitCarPath
{
public PathManager pathManager;
List<RaceCamera> raceCameras = new List<RaceCamera>();
List<int> nodeIndexes = new List<int>();
List<float> deltaAngles = new List<float>();
public Dictionary<int, int> carProgress = new Dictionary<int, int>();
public float distanceBetweenCameras = 30.0f;
public float roadWidth = 10.0f;
public float roadHeight = 1.0f;
public float cameraHeight = 10.0f;
public float laneXOffset = 0.0f;
public float startIndexOffset = 0.0f;
public string[] layerMaskNames;
int cullMask = 0;
float distance;
public void Awake()
{
int v = 0;
foreach (string layerName in layerMaskNames)
{
int layer = LayerMask.NameToLayer(layerName);
v |= 1 << layer;
}
cullMask |= ~v;
}
public void Init()
{
if (!GlobalState.raceCameras) { return; }
if (pathManager.carPath.centerNodes.Count < 1) { return; }
// Detect where to put cameras
Vector3 prev_point = pathManager.carPath.centerNodes[0].pos;
nodeIndexes.Add(0);
deltaAngles.Add(0);
distance = startIndexOffset;
for (int i = 1; i < pathManager.carPath.centerNodes.Count; i++)
{
PathNode node = pathManager.carPath.centerNodes[i];
PathNode nextNode = pathManager.carPath.centerNodes[(i + 1) % pathManager.carPath.centerNodes.Count];
float deltaAngle = Vector3.SignedAngle(nextNode.pos - node.pos, node.rotation * Vector3.forward, Vector3.up);
deltaAngles.Add(deltaAngle);
distance = Vector3.Distance(node.pos, nextNode.pos) + distance;
if (distance > distanceBetweenCameras)
{
nodeIndexes.Add(i);
prev_point = node.pos;
distance = 0;
}
}
// Add cameras
for (int i = 0; i < nodeIndexes.Count; i++)
{
int nodeIndex;
if (i < nodeIndexes.Count - 1)
{
nodeIndex = ((nodeIndexes[i] + nodeIndexes[(i + 1)]) / 2) % pathManager.carPath.centerNodes.Count;
}
else
{
nodeIndex = ((nodeIndexes[nodeIndexes.Count - 1] + nodeIndexes[0] + pathManager.carPath.centerNodes.Count) / 2) % pathManager.carPath.centerNodes.Count;
}
float sign = Mathf.Sign(deltaAngles[nodeIndex]);
PathNode node = pathManager.carPath.centerNodes[nodeIndexes[i]];
PathNode midNode = pathManager.carPath.centerNodes[nodeIndex];
Vector3 nodepos = node.pos + node.rotation * (laneXOffset * Vector3.right);
Vector3 midNodepos = midNode.pos + midNode.rotation * (laneXOffset * Vector3.right);
GameObject goRaceCamChild = new GameObject(string.Format("RaceCamera {0}", i));
goRaceCamChild.transform.SetParent(transform);
RaceCamera cmp = goRaceCamChild.AddComponent<RaceCamera>();
cmp.SetCameraTrigger(nodepos, node.rotation * Quaternion.AngleAxis(90, Vector3.up), new Vector3(0.1f, roadHeight, roadWidth));
cmp.SetCam(midNodepos + midNode.rotation * (6f * sign * Vector3.right) + (cameraHeight * Vector3.up), midNodepos);
cmp.index = i;
cmp.camera.cullingMask = cullMask;
raceCameras.Add(cmp);
}
// Enable first camera
raceCameras[0].camera.enabled = true;
float coverage = GetCoverage(raceCameras.ToArray(), pathManager.carPath.centerNodes.ToArray(), nodeIndexes.ToArray());
Debug.Log(string.Format("Race cameras coverage: {0}%", coverage));
}
public void EnableCameras(bool enabled)
{
foreach (RaceCamera raceCamera in raceCameras)
{
raceCamera.camera.enabled = enabled;
}
}
public void CameraTriggered(Collider col, Camera camera, int index)
{
int carID = col.attachedRigidbody.GetInstanceID();
if (carProgress.ContainsKey(carID))
{
carProgress[carID] = index;
int furtherValue = int.MinValue;
int furtherID = 0;
foreach (int key in carProgress.Keys)
{
if (carProgress[key] > furtherValue)
{
furtherValue = carProgress[key];
furtherID = key;
}
}
if (furtherID != 0)
{
EnableCameras(false);
raceCameras[carProgress[furtherID]].camera.enabled = true;
}
}
else
{
carProgress.Add(carID, index);
}
}
public float GetCoverage(RaceCamera[] raceCams, PathNode[] nodes, int[] nodeIndexes)
{
if (raceCams.Length == nodeIndexes.Length)
{
int count = 0;
for (int i = 0; i < nodeIndexes.Length; i++)
{
int from = nodeIndexes[i];
int to = nodeIndexes[(i + 1) % nodeIndexes.Length];
if (i + 1 >= nodeIndexes.Length) { to += nodes.Length; }
for (int j = from; j < to; j++)
{
PathNode node = nodes[j % nodes.Length];
bool isSeenByCamera = IsSeenByCamera(raceCams[i].camera, node.pos + node.rotation * (laneXOffset * Vector3.right));
if (isSeenByCamera) { count++; }
}
}
return ((float)count / (float)nodes.Length) * 100.0f; // get coverage percentage
}
else
{
Debug.LogWarning("No race camera found"); // no cameras found
return 0f;
}
}
bool IsSeenByCamera(Camera camera, Vector3 position)
{
// check whether the object is visible by the camera
Vector3 viewPos = camera.WorldToViewportPoint(position);
if (viewPos.x >= 0 && viewPos.x <= 1 && viewPos.y >= 0 && viewPos.y <= 1 && viewPos.z > 0)
{
return true;
}
else
return false;
}
}