using System;
using UnityEngine;
using Framework.Services;
namespace Framework.Cameras {
/// Camera rig that can be used
public class CameraController : ScriptComponent {
#region enum RecenterState
/// State of the recenter functionality
private enum RecenterState {
/// The neutral position is not yet known
NeutralPositionUnknown,
/// A recenter has already been scheduled
RecenterScheduled,
/// Neutral position is known, a recenter is possible at any time
RecenterPossible
}
#endregion // enum RecenterState
/// Counter transform to the user's position in virtual reality
///
/// This is set to the inverse of the user's position in the real world
/// to allow for the camera to take the user's current position as
/// the frame of reference for the camera center.
///
public Transform CalibrationTransform;
/// Camera that is being managed by the rig
public Camera Camera;
/// Target object the camera is following
public Transform Target;
/// Injects the instance's dependencies
/// Camera manager the instance will use
///
/// This method is called automatically by the services framework. Any parameters
/// it requires will be filled out by looking up the respective services and creating
/// them as needed.
///
protected void Inject(ICameraManager cameraManager) {
this.cameraManager = cameraManager;
}
/// Called when the script component gets loaded into a game object
protected override void Awake() {
base.Awake();
if(this.Camera == null) {
this.Camera = GetComponentInChildren();
if(this.Camera == null) {
Debug.LogError(
"CameraRig component added to a game object that does not have a camera. " +
"Please create a camera as a second-level child below the camera rig."
);
}
}
detectOrVerifyCameraRig();
}
/// Maches the camera's orientation and position to the rig
///
/// If the camera is used for virtual reality, the user's current head position
/// will be retrieved and the calibration transform
///
public void Recenter() {
// If the camera is not set up correctly, we can't do a thing (a warning or error
// will have been logged during initialization of the component).
if((this.cameraTransform == null) || (this.CalibrationTransform == null)) {
return;
}
if(this.recenterState == RecenterState.RecenterPossible) {
this.CalibrationTransform.localPosition = -this.cameraTransform.localPosition;
this.CalibrationTransform.localRotation = Quaternion.Inverse(
this.cameraTransform.localRotation
);
} else {
this.recenterState = RecenterState.RecenterScheduled;
}
}
/// Makes this camera the active camera
///
/// Whether the game object holding the previously active camera should be
/// disabled, including any other components such as audio sources that it
/// is holding. This is usually a good idea.
///
public void Activate(bool disablePrevious) {
// If there's no camera attached, do nothing (an error will have been logged
// when the component was initialized).
if(this.Camera != null) {
CameraHelper.ActivateCamera(this.Camera, disablePrevious);
}
}
/// Called once per visual frame to update the state of the scene
protected void Update() {
// If the camera is not set up correctly, we can't do a thing (a warning or error
// will have been logged during initialization of the component).
if((this.cameraTransform == null) || (this.CalibrationTransform == null)) {
return;
}
// If recentering was requested, do so now
if(this.recenterState != RecenterState.RecenterPossible) {
if(this.recenterState == RecenterState.RecenterScheduled) {
this.CalibrationTransform.localPosition = -this.cameraTransform.localPosition;
this.CalibrationTransform.localRotation = Quaternion.Inverse(
this.cameraTransform.localRotation
);
}
this.recenterState = RecenterState.RecenterPossible;
}
}
#if false
/// Called when the camera becomes active
protected virtual void OnEnable() {
Camera camera = GetComponentInChildren();
if(camera != null) {
this.cameraManager.ActiveCamera = camera;
}
}
#endif
/// Camera manager to which the camera reports
protected ICameraManager CameraManager {
get { return this.cameraManager; }
}
///
/// Detects the calibration transform (if not assigned) or verifies that it is
/// in the hierarchy between the ActorController and the Camera if assigned
///
private void detectOrVerifyCameraRig() {
Transform rigTransform = transform;
this.cameraTransform = this.Camera.transform;
if(ReferenceEquals(this.cameraTransform, rigTransform)) {
Debug.LogWarning(
"CameraController component used on same game object as camera. This setup " +
"will prevent virtual reality head calibration from working. Please place " +
"your camera as a second-level child below the camera controller."
);
} else {
Transform discoveredCalibrationTransform = null;
Transform parentTransform = cameraTransform.parent;
while(!ReferenceEquals(parentTransform, rigTransform)) {
if(parentTransform == null) {
break;
}
if(ReferenceEquals(parentTransform, this.CalibrationTransform)) {
discoveredCalibrationTransform = this.CalibrationTransform;
break;
}
discoveredCalibrationTransform = parentTransform;
parentTransform = parentTransform.parent;
}
if(discoveredCalibrationTransform == null) {
Debug.LogWarning(
"Could not identify/verify CalibrationTransform. This setup will " +
"prevent virtual reality head calibration from working. Please place your " +
"camera as a second-level child below the camera controller."
);
}
this.CalibrationTransform = discoveredCalibrationTransform;
}
}
/// Camera manager to which the camera reports
private ICameraManager cameraManager;
/// Transform of the camera itself
///
/// In virtual reality mode, this is updated by Unity to match the user's
/// head position in the real world.
///
private Transform cameraTransform;
/// Current state of the recenter functionality
private RecenterState recenterState;
}
} // namespace Framework.Cameras