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