FittsLaw/Assets/GoogleVR/Scripts/Controller/GvrTrackedController.cs
2018-10-08 23:54:11 -04:00

239 lines
8.1 KiB
C#

// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using System.Collections;
/// Represents an object tracked by controller input.
/// Manages the active status of the tracked controller based on controller connection status.
/// Fetches a `GvrControllerInputDevice` for the configured `GvrControllerHand` and propagates
/// the device instance to all `IGvrControllerInputDeviceReceiver`s underneath this object on
/// Start and if the controller handedness changes. If the controller is not positionally
/// tracked, position of the object is updated to approximate arm mechanics by using a
/// `GvrBaseArmModel`. `GvrBaseArmModel`s are also propagated to all `IGvrArmModelReceiver`s
/// underneath this object.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrTrackedController")]
public class GvrTrackedController : MonoBehaviour {
[SerializeField]
[Tooltip("Arm model used to control the pose (position and rotation) of the object, " +
"and to propagate to children that implement IGvrArmModelReceiver.")]
private GvrBaseArmModel armModel;
private GvrControllerInputDevice controllerInputDevice;
[SerializeField]
[Tooltip("Is the object's active status determined by the controller connection status.")]
private bool isDeactivatedWhenDisconnected = true;
[SerializeField]
[Tooltip("Controller Hand")]
private GvrControllerHand controllerHand = GvrControllerHand.Dominant;
public GvrControllerInputDevice ControllerInputDevice {
get {
return controllerInputDevice;
}
}
public GvrControllerHand ControllerHand {
get {
return controllerHand;
}
set {
if (value != controllerHand) {
controllerHand = value;
SetupControllerInputDevice();
}
}
}
/// Arm model used to control the pose (position and rotation) of the object, and to propagate to
/// children that implement IGvrArmModelReceiver.
public GvrBaseArmModel ArmModel {
get {
return armModel;
}
set {
if (armModel == value) {
return;
}
armModel = value;
PropagateControllerInputDeviceToArmModel();
PropagateArmModel();
}
}
/// Is the object's active status determined by the controller connection status.
public bool IsDeactivatedWhenDisconnected {
get {
return isDeactivatedWhenDisconnected;
}
set {
if (isDeactivatedWhenDisconnected == value) {
return;
}
isDeactivatedWhenDisconnected = value;
if (isDeactivatedWhenDisconnected) {
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
}
public void PropagateArmModel() {
IGvrArmModelReceiver[] receivers =
GetComponentsInChildren<IGvrArmModelReceiver>(true);
for (int i = 0; i < receivers.Length; i++) {
IGvrArmModelReceiver receiver = receivers[i];
receiver.ArmModel = armModel;
}
}
void Awake() {
// Adding this event handler calls it immediately.
GvrControllerInput.OnDevicesChanged += SetupControllerInputDevice;
}
void OnEnable() {
// Print an error to console if no GvrControllerInput is found.
if (controllerInputDevice.State == GvrConnectionState.Error) {
Debug.LogWarning(controllerInputDevice.ErrorDetails);
}
// Update the position using OnPostControllerInputUpdated.
// This way, the position and rotation will be correct for the entire frame
// so that it doesn't matter what order Updates get called in.
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
/// Force the pose to update immediately in case the controller isn't updated before the next
/// time a frame is rendered.
UpdatePose();
/// Check the controller state immediately whenever this script is enabled.
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
void OnDisable() {
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
}
void Start() {
PropagateArmModel();
if (controllerInputDevice != null) {
PropagateControllerInputDevice();
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
void OnDestroy() {
GvrControllerInput.OnDevicesChanged -= SetupControllerInputDevice;
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged -= OnControllerStateChanged;
controllerInputDevice = null;
PropagateControllerInputDevice();
}
}
private void PropagateControllerInputDevice() {
IGvrControllerInputDeviceReceiver[] receivers =
GetComponentsInChildren<IGvrControllerInputDeviceReceiver>(true);
foreach (var receiver in receivers) {
receiver.ControllerInputDevice = controllerInputDevice;
}
PropagateControllerInputDeviceToArmModel();
}
private void PropagateControllerInputDeviceToArmModel() {
// Propagate the controller input device to everything in the arm model's object's
// hierarchy in case it is not a child of the tracked controller.
if (armModel != null) {
IGvrControllerInputDeviceReceiver[] receivers =
armModel.GetComponentsInChildren<IGvrControllerInputDeviceReceiver>(true);
foreach (var receiver in receivers) {
receiver.ControllerInputDevice = controllerInputDevice;
}
}
}
private void SetupControllerInputDevice() {
GvrControllerInputDevice newDevice = GvrControllerInput.GetDevice(controllerHand);
if (controllerInputDevice == newDevice) {
return;
}
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged -= OnControllerStateChanged;
controllerInputDevice = null;
}
controllerInputDevice = newDevice;
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged += OnControllerStateChanged;
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
} else {
OnControllerStateChanged(GvrConnectionState.Disconnected, GvrConnectionState.Disconnected);
}
PropagateControllerInputDevice();
}
private void OnPostControllerInputUpdated() {
UpdatePose();
}
private void OnControllerStateChanged(GvrConnectionState state, GvrConnectionState oldState) {
if (isDeactivatedWhenDisconnected && enabled) {
gameObject.SetActive(state == GvrConnectionState.Connected);
}
}
private void UpdatePose() {
if (controllerInputDevice == null) {
return;
}
// Non-positionally tracked controllers always return Position of Vector3.zero.
if (controllerInputDevice.Position != Vector3.zero) {
transform.localPosition = controllerInputDevice.Position;
transform.localRotation = controllerInputDevice.Orientation;
} else {
if (armModel == null || !controllerInputDevice.IsDominantHand) {
return;
}
transform.localPosition = ArmModel.ControllerPositionFromHead;
transform.localRotation = ArmModel.ControllerRotationFromHead;
}
}
#if UNITY_EDITOR
/// If the "armModel" serialized field is changed while the application is playing
/// by using the inspector in the editor, then we need to call the PropagateArmModel
/// to ensure all children IGvrArmModelReceiver are updated.
/// Outside of the editor, this can't happen because the arm model can only change when
/// a Setter is called that automatically calls PropagateArmModel.
void OnValidate() {
if (Application.isPlaying && isActiveAndEnabled) {
PropagateArmModel();
if (controllerInputDevice != null) {
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
}
#endif // UNITY_EDITOR
}