409 lines
14 KiB
C#
409 lines
14 KiB
C#
|
// Copyright 2016 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.
|
||
|
|
||
|
// The controller is not available for versions of Unity without the
|
||
|
// GVR native integration.
|
||
|
|
||
|
using UnityEngine;
|
||
|
|
||
|
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
|
||
|
using UnityEngine.VR;
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
|
||
|
using Gvr.Internal;
|
||
|
|
||
|
/// Represents the controller's current connection state.
|
||
|
/// All values and semantics below (except for Error) are
|
||
|
/// from gvr_types.h in the GVR C API.
|
||
|
public enum GvrConnectionState {
|
||
|
/// Indicates that an error has occurred.
|
||
|
Error = -1,
|
||
|
|
||
|
/// Indicates that the controller is disconnected.
|
||
|
Disconnected = 0,
|
||
|
/// Indicates that the device is scanning for controllers.
|
||
|
Scanning = 1,
|
||
|
/// Indicates that the device is connecting to a controller.
|
||
|
Connecting = 2,
|
||
|
/// Indicates that the device is connected to a controller.
|
||
|
Connected = 3,
|
||
|
};
|
||
|
|
||
|
/// Represents the API status of the current controller state.
|
||
|
/// Values and semantics from gvr_types.h in the GVR C API.
|
||
|
public enum GvrControllerApiStatus {
|
||
|
/// A Unity-localized error occurred.
|
||
|
/// This is the only value that isn't in gvr_types.h.
|
||
|
Error = -1,
|
||
|
|
||
|
/// API is happy and healthy. This doesn't mean the controller itself
|
||
|
/// is connected, it just means that the underlying service is working
|
||
|
/// properly.
|
||
|
Ok = 0,
|
||
|
|
||
|
/// Any other status represents a permanent failure that requires
|
||
|
/// external action to fix:
|
||
|
|
||
|
/// API failed because this device does not support controllers (API is too
|
||
|
/// low, or other required feature not present).
|
||
|
Unsupported = 1,
|
||
|
/// This app was not authorized to use the service (e.g., missing permissions,
|
||
|
/// the app is blacklisted by the underlying service, etc).
|
||
|
NotAuthorized = 2,
|
||
|
/// The underlying VR service is not present.
|
||
|
Unavailable = 3,
|
||
|
/// The underlying VR service is too old, needs upgrade.
|
||
|
ApiServiceObsolete = 4,
|
||
|
/// The underlying VR service is too new, is incompatible with current client.
|
||
|
ApiClientObsolete = 5,
|
||
|
/// The underlying VR service is malfunctioning. Try again later.
|
||
|
ApiMalfunction = 6,
|
||
|
};
|
||
|
|
||
|
/// Represents the controller's current battery level.
|
||
|
/// Values and semantics from gvr_types.h in the GVR C API.
|
||
|
public enum GvrControllerBatteryLevel {
|
||
|
/// A Unity-localized error occurred.
|
||
|
/// This is the only value that isn't in gvr_types.h.
|
||
|
Error = -1,
|
||
|
|
||
|
/// The battery state is currently unreported
|
||
|
Unknown = 0,
|
||
|
|
||
|
/// Equivalent to 1 out of 5 bars on the battery indicator
|
||
|
CriticalLow = 1,
|
||
|
|
||
|
/// Equivalent to 2 out of 5 bars on the battery indicator
|
||
|
Low = 2,
|
||
|
|
||
|
/// Equivalent to 3 out of 5 bars on the battery indicator
|
||
|
Medium = 3,
|
||
|
|
||
|
/// Equivalent to 4 out of 5 bars on the battery indicator
|
||
|
AlmostFull = 4,
|
||
|
|
||
|
/// Equivalent to 5 out of 5 bars on the battery indicator
|
||
|
Full = 5,
|
||
|
};
|
||
|
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
|
||
|
|
||
|
|
||
|
/// Main entry point for the Daydream controller API.
|
||
|
///
|
||
|
/// To use this API, add this behavior to a GameObject in your scene, or use the
|
||
|
/// GvrControllerMain prefab. There can only be one object with this behavior on your scene.
|
||
|
///
|
||
|
/// This is a singleton object.
|
||
|
///
|
||
|
/// To access the controller state, simply read the static properties of this class. For example,
|
||
|
/// to know the controller's current orientation, use GvrController.Orientation.
|
||
|
public class GvrController : MonoBehaviour {
|
||
|
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
|
||
|
private static GvrController instance;
|
||
|
private static IControllerProvider controllerProvider;
|
||
|
|
||
|
private ControllerState controllerState = new ControllerState();
|
||
|
private IEnumerator controllerUpdate;
|
||
|
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
|
||
|
|
||
|
/// Event handler for receiving button, track pad, and IMU updates from the controller.
|
||
|
public delegate void OnControllerUpdateEvent();
|
||
|
public event OnControllerUpdateEvent OnControllerUpdate;
|
||
|
|
||
|
public enum EmulatorConnectionMode {
|
||
|
OFF,
|
||
|
USB,
|
||
|
WIFI,
|
||
|
}
|
||
|
/// Indicates how we connect to the controller emulator.
|
||
|
[Tooltip("How to connect to the emulator: USB cable (recommended) or WIFI.")]
|
||
|
public EmulatorConnectionMode emulatorConnectionMode = EmulatorConnectionMode.USB;
|
||
|
|
||
|
/// Returns the arm model instance associated with the controller.
|
||
|
public static GvrArmModel ArmModel {
|
||
|
get {
|
||
|
return instance != null ? instance.GetComponent<GvrArmModel>() : null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Returns the controller's current connection state.
|
||
|
public static GvrConnectionState State {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.connectionState : GvrConnectionState.Error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Returns the API status of the current controller state.
|
||
|
public static GvrControllerApiStatus ApiStatus {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.apiStatus : GvrControllerApiStatus.Error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Returns the controller's current orientation in space, as a quaternion.
|
||
|
/// The space in which the orientation is represented is the usual Unity space, with
|
||
|
/// X pointing to the right, Y pointing up and Z pointing forward. Therefore, to make an
|
||
|
/// object in your scene have the same orientation as the controller, simply assign this
|
||
|
/// quaternion to the GameObject's transform.rotation.
|
||
|
public static Quaternion Orientation {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.orientation : Quaternion.identity;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Returns the controller's gyroscope reading. The gyroscope indicates the angular
|
||
|
/// about each of its local axes. The controller's axes are: X points to the right,
|
||
|
/// Y points perpendicularly up from the controller's top surface and Z lies
|
||
|
/// along the controller's body, pointing towards the front. The angular speed is given
|
||
|
/// in radians per second, using the right-hand rule (positive means a right-hand rotation
|
||
|
/// about the given axis).
|
||
|
public static Vector3 Gyro {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.gyro : Vector3.zero;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Returns the controller's accelerometer reading. The accelerometer indicates the
|
||
|
/// effect of acceleration and gravity in the direction of each of the controller's local
|
||
|
/// axes. The controller's local axes are: X points to the right, Y points perpendicularly
|
||
|
/// up from the controller's top surface and Z lies along the controller's body, pointing
|
||
|
/// towards the front. The acceleration is measured in meters per second squared. Note that
|
||
|
/// gravity is combined with acceleration, so when the controller is resting on a table top,
|
||
|
/// it will measure an acceleration of 9.8 m/s^2 on the Y axis. The accelerometer reading
|
||
|
/// will be zero on all three axes only if the controller is in free fall, or if the user
|
||
|
/// is in a zero gravity environment like a space station.
|
||
|
public static Vector3 Accel {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.accel : Vector3.zero;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the user is currently touching the controller's touchpad.
|
||
|
public static bool IsTouching {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.isTouching : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the user just started touching the touchpad. This is an event flag (it is true
|
||
|
/// for only one frame after the event happens, then reverts to false).
|
||
|
public static bool TouchDown {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.touchDown : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the user just stopped touching the touchpad. This is an event flag (it is true
|
||
|
/// for only one frame after the event happens, then reverts to false).
|
||
|
public static bool TouchUp {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.touchUp : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static Vector2 TouchPos {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.touchPos : Vector2.zero;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the user is currently performing the recentering gesture. Most apps will want
|
||
|
/// to pause the interaction while this remains true.
|
||
|
public static bool Recentering {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.recentering : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the user just completed the recenter gesture. The controller's orientation is
|
||
|
/// now being reported in the new recentered coordinate system (the controller's orientation
|
||
|
/// when recentering was completed was remapped to mean "forward"). This is an event flag
|
||
|
/// (it is true for only one frame after the event happens, then reverts to false).
|
||
|
/// The headset is recentered together with the controller.
|
||
|
public static bool Recentered {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.recentered : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the click button (touchpad button) is currently being pressed. This is not
|
||
|
/// an event: it represents the button's state (it remains true while the button is being
|
||
|
/// pressed).
|
||
|
public static bool ClickButton {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.clickButtonState : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the click button (touchpad button) was just pressed. This is an event flag:
|
||
|
/// it will be true for only one frame after the event happens.
|
||
|
public static bool ClickButtonDown {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.clickButtonDown : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the click button (touchpad button) was just released. This is an event flag:
|
||
|
/// it will be true for only one frame after the event happens.
|
||
|
public static bool ClickButtonUp {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.clickButtonUp : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the app button (touchpad button) is currently being pressed. This is not
|
||
|
/// an event: it represents the button's state (it remains true while the button is being
|
||
|
/// pressed).
|
||
|
public static bool AppButton {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.appButtonState : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the app button was just pressed. This is an event flag: it will be true for
|
||
|
/// only one frame after the event happens.
|
||
|
public static bool AppButtonDown {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.appButtonDown : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the app button was just released. This is an event flag: it will be true for
|
||
|
/// only one frame after the event happens.
|
||
|
public static bool AppButtonUp {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.appButtonUp : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Always false in the emulator.
|
||
|
public static bool HomeButtonDown {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.homeButtonDown : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Always false in the emulator.
|
||
|
public static bool HomeButtonState {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.homeButtonState : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// If State == GvrConnectionState.Error, this contains details about the error.
|
||
|
public static string ErrorDetails {
|
||
|
get {
|
||
|
if (instance != null) {
|
||
|
return instance.controllerState.connectionState == GvrConnectionState.Error ?
|
||
|
instance.controllerState.errorDetails : "";
|
||
|
} else {
|
||
|
return "GvrController instance not found in scene. It may be missing, or it might "
|
||
|
+ "not have initialized yet.";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Returns the GVR C library controller state pointer (gvr_controller_state*).
|
||
|
public static IntPtr StatePtr {
|
||
|
get {
|
||
|
return instance != null? instance.controllerState.gvrPtr : IntPtr.Zero;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the user is currently touching the controller's touchpad.
|
||
|
public static bool IsCharging {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.isCharging : false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If true, the user is currently touching the controller's touchpad.
|
||
|
public static GvrControllerBatteryLevel BatteryLevel {
|
||
|
get {
|
||
|
return instance != null ? instance.controllerState.batteryLevel : GvrControllerBatteryLevel.Error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Awake() {
|
||
|
if (instance != null) {
|
||
|
Debug.LogError("More than one GvrController instance was found in your scene. "
|
||
|
+ "Ensure that there is only one GvrController.");
|
||
|
this.enabled = false;
|
||
|
return;
|
||
|
}
|
||
|
instance = this;
|
||
|
if (controllerProvider == null) {
|
||
|
controllerProvider = ControllerProviderFactory.CreateControllerProvider(this);
|
||
|
}
|
||
|
|
||
|
// Keep screen on here, since GvrController must be in any GVR scene in order to enable
|
||
|
// controller capabilities.
|
||
|
Screen.sleepTimeout = SleepTimeout.NeverSleep;
|
||
|
}
|
||
|
|
||
|
void OnDestroy() {
|
||
|
instance = null;
|
||
|
}
|
||
|
|
||
|
private void UpdateController() {
|
||
|
controllerProvider.ReadState(controllerState);
|
||
|
|
||
|
#if UNITY_EDITOR
|
||
|
// If a headset recenter was requested, do it now.
|
||
|
if (controllerState.recentered) {
|
||
|
for (int i = 0; i < Camera.allCameras.Length; i++) {
|
||
|
Camera cam = Camera.allCameras[i];
|
||
|
// Do not reset pitch, which is how it works on the device.
|
||
|
cam.transform.localRotation = Quaternion.Euler(cam.transform.localRotation.eulerAngles.x, 0, 0);
|
||
|
}
|
||
|
}
|
||
|
#endif // UNITY_EDITOR
|
||
|
}
|
||
|
|
||
|
void OnApplicationPause(bool paused) {
|
||
|
if (null == controllerProvider) return;
|
||
|
if (paused) {
|
||
|
controllerProvider.OnPause();
|
||
|
} else {
|
||
|
controllerProvider.OnResume();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void OnEnable() {
|
||
|
controllerUpdate = EndOfFrame();
|
||
|
StartCoroutine(controllerUpdate);
|
||
|
}
|
||
|
|
||
|
void OnDisable() {
|
||
|
StopCoroutine(controllerUpdate);
|
||
|
}
|
||
|
|
||
|
IEnumerator EndOfFrame() {
|
||
|
while (true) {
|
||
|
// This must be done at the end of the frame to ensure that all GameObjects had a chance
|
||
|
// to read transient controller state (e.g. events, etc) for the current frame before
|
||
|
// it gets reset.
|
||
|
yield return waitForEndOfFrame;
|
||
|
UpdateController();
|
||
|
if (OnControllerUpdate != null) {
|
||
|
OnControllerUpdate();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
|
||
|
}
|
||
|
|