348 lines
12 KiB
C#
348 lines
12 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;
|
||
|
using System.Collections;
|
||
|
|
||
|
/// Provides visual feedback for the daydream controller.
|
||
|
[RequireComponent(typeof(Renderer))]
|
||
|
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerVisual")]
|
||
|
public class GvrControllerVisual : MonoBehaviour, IGvrArmModelReceiver, IGvrControllerInputDeviceReceiver {
|
||
|
[System.Serializable]
|
||
|
public struct ControllerDisplayState {
|
||
|
|
||
|
public GvrControllerBatteryLevel batteryLevel;
|
||
|
public bool batteryCharging;
|
||
|
|
||
|
public bool clickButton;
|
||
|
public bool appButton;
|
||
|
public bool homeButton;
|
||
|
public bool touching;
|
||
|
public Vector2 touchPos;
|
||
|
}
|
||
|
|
||
|
/// An array of prefabs that will be instantiated and added as children
|
||
|
/// of the controller visual when the controller is created. Used to
|
||
|
/// attach tooltips or other additional visual elements to the control dynamically.
|
||
|
[SerializeField]
|
||
|
private GameObject[] attachmentPrefabs;
|
||
|
[SerializeField] private Color touchPadColor =
|
||
|
new Color(200f / 255f, 200f / 255f, 200f / 255f, 1);
|
||
|
[SerializeField] private Color appButtonColor =
|
||
|
new Color(200f / 255f, 200f / 255f, 200f / 255f, 1);
|
||
|
[SerializeField] private Color systemButtonColor =
|
||
|
new Color(20f / 255f, 20f / 255f, 20f / 255f, 1);
|
||
|
|
||
|
/// Determines if the displayState is set from GvrControllerInputDevice.
|
||
|
[Tooltip("Determines if the displayState is set from GvrControllerInputDevice.")]
|
||
|
public bool readControllerState = true;
|
||
|
|
||
|
/// Used to set the display state of the controller visual.
|
||
|
/// This can be used for tutorials that visualize the controller or other use-cases that require
|
||
|
/// displaying the controller visual without the state being determined by controller input.
|
||
|
/// Additionally, it can be used to preview the controller visual in the editor.
|
||
|
/// NOTE: readControllerState must be disabled to set the display state.
|
||
|
public ControllerDisplayState displayState;
|
||
|
|
||
|
/// This is the preferred, maximum alpha value the object should have
|
||
|
/// when it is a comfortable distance from the head.
|
||
|
[Range(0.0f, 1.0f)]
|
||
|
public float maximumAlpha = 1.0f;
|
||
|
|
||
|
public GvrBaseArmModel ArmModel { get; set; }
|
||
|
|
||
|
public GvrControllerInputDevice ControllerInputDevice { get; set; }
|
||
|
|
||
|
public float PreferredAlpha{
|
||
|
get{
|
||
|
return ArmModel != null ? maximumAlpha * ArmModel.PreferredAlpha : maximumAlpha;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Color TouchPadColor {
|
||
|
get {
|
||
|
return touchPadColor;
|
||
|
}
|
||
|
set {
|
||
|
touchPadColor = value;
|
||
|
if(materialPropertyBlock != null) {
|
||
|
materialPropertyBlock.SetColor(touchPadId, touchPadColor);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Color AppButtonColor {
|
||
|
get {
|
||
|
return appButtonColor;
|
||
|
}
|
||
|
set {
|
||
|
appButtonColor = value;
|
||
|
if(materialPropertyBlock != null){
|
||
|
materialPropertyBlock.SetColor(appButtonId, appButtonColor);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Color SystemButtonColor {
|
||
|
get {
|
||
|
return systemButtonColor;
|
||
|
}
|
||
|
set {
|
||
|
systemButtonColor = value;
|
||
|
if(materialPropertyBlock != null) {
|
||
|
materialPropertyBlock.SetColor(systemButtonId, systemButtonColor);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private Renderer controllerRenderer;
|
||
|
private MaterialPropertyBlock materialPropertyBlock;
|
||
|
|
||
|
private int alphaId;
|
||
|
private int touchId;
|
||
|
private int touchPadId;
|
||
|
private int appButtonId;
|
||
|
private int systemButtonId;
|
||
|
private int batteryColorId;
|
||
|
|
||
|
private bool wasTouching;
|
||
|
private float touchTime;
|
||
|
|
||
|
// Data passed to shader, (xy) touch position, (z) touch duration, (w) battery state.
|
||
|
private Vector4 controllerShaderData;
|
||
|
// Data passed to shader, (x) overall alpha, (y) touchpad click duration,
|
||
|
// (z) app button click duration, (w) system button click duration.
|
||
|
private Vector4 controllerShaderData2;
|
||
|
private Color currentBatteryColor;
|
||
|
|
||
|
// These values control animation times for the controller buttons
|
||
|
public const float APP_BUTTON_ACTIVE_DURATION_SECONDS = 0.111f;
|
||
|
public const float APP_BUTTON_RELEASE_DURATION_SECONDS = 0.0909f;
|
||
|
|
||
|
public const float SYSTEM_BUTTON_ACTIVE_DURATION_SECONDS = 0.111f;
|
||
|
public const float SYSTEM_BUTTON_RELEASE_DURATION_SECONDS = 0.0909f;
|
||
|
|
||
|
public const float TOUCHPAD_CLICK_DURATION_SECONDS = 0.111f;
|
||
|
public const float TOUCHPAD_RELEASE_DURATION_SECONDS = 0.0909f;
|
||
|
|
||
|
public const float TOUCHPAD_CLICK_SCALE_DURATION_SECONDS = 0.075f;
|
||
|
public const float TOUCHPAD_POINT_SCALE_DURATION_SECONDS = 0.15f;
|
||
|
|
||
|
// These values are used by the shader to control battery display
|
||
|
// Only modify these values if you are also modifying the shader.
|
||
|
private const float BATTERY_FULL = 0;
|
||
|
private const float BATTERY_ALMOST_FULL = .125f;
|
||
|
private const float BATTERY_MEDIUM = .225f;
|
||
|
private const float BATTERY_LOW = .325f;
|
||
|
private const float BATTERY_CRITICAL = .425f;
|
||
|
private const float BATTERY_HIDDEN = .525f;
|
||
|
|
||
|
private readonly Color GVR_BATTERY_CRITICAL_COLOR = new Color(1,0,0,1);
|
||
|
private readonly Color GVR_BATTERY_LOW_COLOR = new Color(1,0.6823f,0,1);
|
||
|
private readonly Color GVR_BATTERY_MED_COLOR = new Color(0,1,0.588f,1);
|
||
|
private readonly Color GVR_BATTERY_FULL_COLOR = new Color(0,1,0.588f,1);
|
||
|
|
||
|
// How much time to use as an 'immediate update'.
|
||
|
// Any value large enough to instantly update all visual animations.
|
||
|
private const float IMMEDIATE_UPDATE_TIME = 10f;
|
||
|
|
||
|
void Awake() {
|
||
|
Initialize();
|
||
|
CreateAttachments();
|
||
|
}
|
||
|
|
||
|
void OnEnable() {
|
||
|
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
|
||
|
}
|
||
|
|
||
|
void OnDisable() {
|
||
|
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
|
||
|
}
|
||
|
|
||
|
void OnValidate() {
|
||
|
if (!Application.isPlaying) {
|
||
|
Initialize();
|
||
|
OnVisualUpdate(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void OnPostControllerInputUpdated() {
|
||
|
OnVisualUpdate();
|
||
|
}
|
||
|
|
||
|
private void CreateAttachments() {
|
||
|
if (attachmentPrefabs == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < attachmentPrefabs.Length; i++) {
|
||
|
GameObject prefab = attachmentPrefabs[i];
|
||
|
GameObject attachment = Instantiate(prefab);
|
||
|
attachment.transform.SetParent(transform, false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void Initialize() {
|
||
|
if(controllerRenderer == null) {
|
||
|
controllerRenderer = GetComponent<Renderer>();
|
||
|
}
|
||
|
if(materialPropertyBlock == null) {
|
||
|
materialPropertyBlock = new MaterialPropertyBlock();
|
||
|
}
|
||
|
|
||
|
alphaId = Shader.PropertyToID("_GvrControllerAlpha");
|
||
|
touchId = Shader.PropertyToID("_GvrTouchInfo");
|
||
|
touchPadId = Shader.PropertyToID("_GvrTouchPadColor");
|
||
|
appButtonId = Shader.PropertyToID("_GvrAppButtonColor");
|
||
|
systemButtonId = Shader.PropertyToID("_GvrSystemButtonColor");
|
||
|
batteryColorId = Shader.PropertyToID("_GvrBatteryColor");
|
||
|
|
||
|
materialPropertyBlock.SetColor(appButtonId, appButtonColor);
|
||
|
materialPropertyBlock.SetColor(systemButtonId, systemButtonColor);
|
||
|
materialPropertyBlock.SetColor(touchPadId, touchPadColor);
|
||
|
controllerRenderer.SetPropertyBlock(materialPropertyBlock);
|
||
|
}
|
||
|
|
||
|
private void UpdateControllerState() {
|
||
|
// Return early when the application isn't playing to ensure that the serialized displayState
|
||
|
// is used to preview the controller visual instead of the default GvrControllerInputDevice
|
||
|
// values.
|
||
|
#if UNITY_EDITOR
|
||
|
if (!Application.isPlaying) {
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if(ControllerInputDevice != null) {
|
||
|
displayState.batteryLevel = ControllerInputDevice.BatteryLevel;
|
||
|
displayState.batteryCharging = ControllerInputDevice.IsCharging;
|
||
|
|
||
|
displayState.clickButton = ControllerInputDevice.GetButton(GvrControllerButton.TouchPadButton);
|
||
|
displayState.appButton = ControllerInputDevice.GetButton(GvrControllerButton.App);
|
||
|
displayState.homeButton = ControllerInputDevice.GetButton(GvrControllerButton.System);
|
||
|
displayState.touching = ControllerInputDevice.GetButton(GvrControllerButton.TouchPadTouch);
|
||
|
displayState.touchPos = ControllerInputDevice.TouchPos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void OnVisualUpdate(bool updateImmediately = false) {
|
||
|
|
||
|
// Update the visual display based on the controller state
|
||
|
if(readControllerState) {
|
||
|
UpdateControllerState();
|
||
|
}
|
||
|
|
||
|
float deltaTime = Time.deltaTime;
|
||
|
|
||
|
// If flagged to update immediately, set deltaTime to an arbitrarily large value
|
||
|
// This is particularly useful in editor, but also for resetting state quickly
|
||
|
if(updateImmediately) {
|
||
|
deltaTime = IMMEDIATE_UPDATE_TIME;
|
||
|
}
|
||
|
|
||
|
if (displayState.clickButton) {
|
||
|
controllerShaderData2.y = Mathf.Min(1, controllerShaderData2.y + deltaTime / TOUCHPAD_CLICK_DURATION_SECONDS);
|
||
|
} else{
|
||
|
controllerShaderData2.y = Mathf.Max(0, controllerShaderData2.y - deltaTime / TOUCHPAD_RELEASE_DURATION_SECONDS);
|
||
|
}
|
||
|
|
||
|
if (displayState.appButton) {
|
||
|
controllerShaderData2.z = Mathf.Min(1, controllerShaderData2.z + deltaTime / APP_BUTTON_ACTIVE_DURATION_SECONDS);
|
||
|
} else{
|
||
|
controllerShaderData2.z = Mathf.Max(0, controllerShaderData2.z - deltaTime / APP_BUTTON_RELEASE_DURATION_SECONDS);
|
||
|
}
|
||
|
|
||
|
if (displayState.homeButton) {
|
||
|
controllerShaderData2.w = Mathf.Min(1, controllerShaderData2.w + deltaTime / SYSTEM_BUTTON_ACTIVE_DURATION_SECONDS);
|
||
|
} else {
|
||
|
controllerShaderData2.w = Mathf.Max(0, controllerShaderData2.w - deltaTime / SYSTEM_BUTTON_RELEASE_DURATION_SECONDS);
|
||
|
}
|
||
|
|
||
|
// Set the material's alpha to the multiplied preferred alpha.
|
||
|
controllerShaderData2.x = PreferredAlpha;
|
||
|
materialPropertyBlock.SetVector(alphaId, controllerShaderData2);
|
||
|
|
||
|
controllerShaderData.x = displayState.touchPos.x;
|
||
|
controllerShaderData.y = displayState.touchPos.y;
|
||
|
|
||
|
if (displayState.touching || displayState.clickButton) {
|
||
|
if (!wasTouching) {
|
||
|
wasTouching = true;
|
||
|
}
|
||
|
if(touchTime < 1) {
|
||
|
touchTime = Mathf.Min(touchTime + deltaTime / TOUCHPAD_POINT_SCALE_DURATION_SECONDS, 1);
|
||
|
}
|
||
|
} else {
|
||
|
wasTouching = false;
|
||
|
if(touchTime > 0) {
|
||
|
touchTime = Mathf.Max(touchTime - deltaTime / TOUCHPAD_POINT_SCALE_DURATION_SECONDS, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
controllerShaderData.z = touchTime;
|
||
|
|
||
|
UpdateBatteryIndicator();
|
||
|
|
||
|
materialPropertyBlock.SetVector(touchId, controllerShaderData);
|
||
|
materialPropertyBlock.SetColor(batteryColorId, currentBatteryColor);
|
||
|
// Update the renderer
|
||
|
controllerRenderer.SetPropertyBlock(materialPropertyBlock);
|
||
|
}
|
||
|
|
||
|
private void UpdateBatteryIndicator() {
|
||
|
|
||
|
GvrControllerBatteryLevel level = displayState.batteryLevel;
|
||
|
bool charging = displayState.batteryCharging;
|
||
|
|
||
|
switch (level) {
|
||
|
case GvrControllerBatteryLevel.Full:
|
||
|
controllerShaderData.w = BATTERY_FULL;
|
||
|
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
|
||
|
break;
|
||
|
case GvrControllerBatteryLevel.AlmostFull:
|
||
|
controllerShaderData.w = BATTERY_ALMOST_FULL;
|
||
|
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
|
||
|
break;
|
||
|
case GvrControllerBatteryLevel.Medium:
|
||
|
controllerShaderData.w = BATTERY_MEDIUM;
|
||
|
currentBatteryColor = GVR_BATTERY_MED_COLOR;
|
||
|
break;
|
||
|
case GvrControllerBatteryLevel.Low:
|
||
|
controllerShaderData.w = BATTERY_LOW;
|
||
|
currentBatteryColor = GVR_BATTERY_LOW_COLOR;
|
||
|
break;
|
||
|
case GvrControllerBatteryLevel.CriticalLow:
|
||
|
controllerShaderData.w = BATTERY_CRITICAL;
|
||
|
currentBatteryColor = GVR_BATTERY_CRITICAL_COLOR;
|
||
|
break;
|
||
|
default:
|
||
|
controllerShaderData.w = BATTERY_HIDDEN;
|
||
|
currentBatteryColor.a = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (charging) {
|
||
|
controllerShaderData.w = -controllerShaderData.w;
|
||
|
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void SetControllerTexture(Texture newTexture) {
|
||
|
controllerRenderer.material.mainTexture = newTexture;
|
||
|
}
|
||
|
}
|