// 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. using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using UnityEngine.Events; // This class is an implementation of Basetile in which tiles float forward along // the z-axis and tilt towards the camera when the gvr controller pointer is // hovering over them. public class FloatTile : BaseTile { #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) private const float PARENT_CHANGE_THRESHOLD_PERCENT = 0.33f; private const float _360_DEGREES = 360.0f; private const float _180_DEGREES = 180.0f; private Quaternion desiredRotation = Quaternion.identity; private float desiredPositionZ; private Vector3 desiredScale = Vector3.one; [Range(1.0f, 2.0f)] [Tooltip("Tile scale when the pointer over the tile.")] public float hoverScale = 1.2f; [Range(0.01f, 0.5f)] [Tooltip("Tile forward distance when the pointer over the tile.")] public float hoverPositionZMeters = 0.225f; [Range(0.0f, 30.0f)] [Tooltip("Maximum tile rotation towards the camera.")] public float maximumRotationDegreesCamera = 15.0f; [Range(0.0f, 5.0f)] [Tooltip("Maximum tile rotation towards the pointer.")] public float maximumRotationDegreesPointer = 3.0f; [Range(1.0f, 10.0f)] [Tooltip("Speed used for lerping the rotation/scale/position of the tile.")] public float interpolationSpeed = 8.0f; public override void Reset() { base.Reset(); Transform myTransform = transform; myTransform.SetParent(originalParent, true); myTransform.SetAsLastSibling(); } #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) public override void OnPointerEnter(PointerEventData eventData) { #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) isHovering = true; // Since canvas graphics render facing the negative Z direction, // negative z is the forward direction for a canvas element. desiredPositionZ = -hoverPositionZMeters / GetMetersToCanvasScale(); desiredScale = new Vector3(hoverScale, hoverScale, hoverScale); #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) } public override void OnPointerExit(PointerEventData eventData) { #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) isHovering = false; desiredRotation = Quaternion.identity; desiredPositionZ = 0.0f; desiredScale = Vector3.one; #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) } public override void OnGvrPointerHover(PointerEventData eventData) { #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) isHovering = true; UpdateDesiredRotation(eventData.pointerCurrentRaycast.worldPosition); #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) } #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) void Update() { UpdateRotation(); UpdateFloatPosition(); UpdateScale(); } private void UpdateRotation() { Quaternion finalDesiredRotation = desiredRotation; if (!isInteractable) { finalDesiredRotation = Quaternion.identity; } if (finalDesiredRotation != transform.localRotation) { Quaternion localRotation = transform.localRotation; localRotation = Quaternion.Lerp(localRotation, finalDesiredRotation, Time.deltaTime * interpolationSpeed); transform.localRotation = localRotation; } } private void UpdateFloatPosition() { float finalDesiredPositionZ = desiredPositionZ; if (!isInteractable) { finalDesiredPositionZ = 0.0f; } if (finalDesiredPositionZ != transform.localPosition.z) { Vector3 localPosition = transform.localPosition; Vector3 desiredPosition = localPosition; desiredPosition.z = finalDesiredPositionZ; localPosition = Vector3.Lerp(localPosition, desiredPosition, Time.deltaTime * interpolationSpeed); transform.localPosition = localPosition; TiledPage page = GetPage(); if (page != null) { float diff = Mathf.Abs(localPosition.z); if (diff < ((PARENT_CHANGE_THRESHOLD_PERCENT * hoverPositionZMeters) / GetMetersToCanvasScale()) && transform.parent == page.transform) { transform.SetParent(originalParent, true); transform.SetAsLastSibling(); } else if (isHovering && diff >= 0 && transform.parent == originalParent) { transform.SetParent(page.transform, true); } } } } private void UpdateScale() { Vector3 finalDesiredScale = desiredScale; if (!isInteractable) { finalDesiredScale = Vector3.one; } if (finalDesiredScale != transform.localScale) { Vector3 localScale = transform.localScale; localScale = Vector3.Lerp(localScale, finalDesiredScale, Time.deltaTime * interpolationSpeed); transform.localScale = localScale; } } private void UpdateDesiredRotation(Vector3 pointerIntersectionWorldPosition) { Vector3 localCenter = CalculateLocalCenter(); Vector3 worldCenter = transform.TransformPoint(localCenter); Vector2 localSize = CalculateLocalSize(); Vector3 pointerLocalPositionOnTile = transform.InverseTransformPoint(pointerIntersectionWorldPosition); Vector3 pointerDiffFromCenter = pointerLocalPositionOnTile - localCenter; float pointerRatioX = pointerDiffFromCenter.x / localSize.x; float pointerRatioY = pointerDiffFromCenter.y / localSize.y; Vector2 pointerRatioFromCenter = new Vector2(pointerRatioX, pointerRatioY); float axisCoeff = maximumRotationDegreesPointer * 2.0f; Vector3 worldDirection = worldCenter - Camera.main.transform.position; Vector3 localDirection = transform.parent.InverseTransformDirection(worldDirection); Quaternion lookRotation = Quaternion.LookRotation(localDirection, Vector3.up); Vector3 lookEuler = clampEuler(lookRotation.eulerAngles, maximumRotationDegreesCamera); float eulerX = lookEuler.x - pointerRatioFromCenter.y * axisCoeff; float eulerY = lookEuler.y + pointerRatioFromCenter.x * axisCoeff; desiredRotation = Quaternion.Euler(eulerX, eulerY, lookEuler.z); } private Vector2 CalculateLocalSize() { RectTransform rectTransform = GetComponent(); if (rectTransform) { Vector3 localMax = rectTransform.rect.max; Vector3 localMin = rectTransform.rect.min; return localMax - localMin; } return Vector2.zero; } protected Vector3 CalculateLocalCenter() { RectTransform rectTransform = GetComponent(); if (rectTransform) { Vector3 localCenter = rectTransform.rect.center; return localCenter; } return Vector3.zero; } private Vector3 clampEuler(Vector3 rotation, float maxDegrees) { rotation.x = clampDegrees(rotation.x, maxDegrees); rotation.y = clampDegrees(rotation.y, maxDegrees); rotation.z = clampDegrees(rotation.z, maxDegrees); return rotation; } private float clampDegrees(float degrees, float maxDegrees) { if (degrees > _180_DEGREES) { degrees -= _360_DEGREES; } return Mathf.Clamp(degrees, -maxDegrees, maxDegrees); } #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) }