// 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.Events; using UnityEngine.EventSystems; using System.Collections; // This class is an implementation of Basetile in which the image on each tile // zooms inward and scrolls along with the controller retical as it is hovering // over the tile. The edges of the image are masked off if they go beyond the // bounding rectangle of the tile. public class MaskedTile : BaseTile { #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) private const string OBJ_NAME_MASKED_IMAGE = "MaskedImage"; private const float PARENT_CHANGE_THRESHOLD_PERCENT = 0.33f; private Image maskedImage; private GameObject maskedImageObject; private Vector3 originalMaskedPosition; // Start position when pointer is not on tile. private Vector3 maskedScrollOffset; private Vector2 originalImageSize; private Vector2 enlargedImageSize; private float desiredPositionZ; [Range(0.1f, 0.5f)] [Tooltip("Image scroll amount when the pointer over the tile.")] public float movementWeight = 0.15f; [Range(1.1f, 2.0f)] [Tooltip("Image scale amount when the pointer over the tile.")] public float scaleWeight = 1.4f; [Range(0.01f, 0.2f)] [Tooltip("Tile forward distance when the pointer over the tile.")] public float hoverPositionZMeters = 0.125f; [Range(0.1f, 10.0f)] [Tooltip("Speed used for lerping the rotation/scale/position of the tile.")] public float interpolationSpeed = 8.0f; protected override void Awake() { base.Awake(); // Get current image. Image image = GetComponent(); // Create mask. gameObject.AddComponent(); // Save size data. originalImageSize = image.rectTransform.sizeDelta; enlargedImageSize = originalImageSize; enlargedImageSize.x *= scaleWeight; enlargedImageSize.y *= scaleWeight; // Save position data. originalMaskedPosition = new Vector3(originalImageSize.x / 2.0f, -originalImageSize.y / 2.0f, 0); // Set data that varies. maskedScrollOffset = Vector3.zero; // Create game object for masked image. maskedImageObject = new GameObject(OBJ_NAME_MASKED_IMAGE); RectTransform maskedTransform = maskedImageObject.AddComponent(); maskedTransform.SetParent(transform); // Set as child of this game object. // Create maskedImage as component of child game object and initialize to base image. maskedImage = maskedImageObject.AddComponent(); maskedImage.sprite = image.sprite; maskedImage.color = image.color; maskedImage.material = image.material; image.sprite = null; // If this object has a selectable referencing the original image, // then we set the selectable to the masked image. Selectable selectable = GetComponent(); if (selectable != null && selectable.image == image) { selectable.image = maskedImage; } // Set size, scale, rotation and position. maskedImage.rectTransform.sizeDelta = originalImageSize; maskedImage.rectTransform.localScale = Vector3.one; maskedImage.rectTransform.localRotation = Quaternion.identity; maskedImage.rectTransform.anchoredPosition3D = originalMaskedPosition; // Set masked image alignment to top-left. Vector2 anchor = new Vector2(0, 1); maskedImage.rectTransform.anchorMin = anchor; maskedImage.rectTransform.anchorMax = anchor; maskedImage.rectTransform.pivot = new Vector2(0.5f, 0.5f); } void Update() { // Make sure to always ignore raycasts. // This may be set back to true by BaseTile when the Tile becomes interactable. maskedImage.raycastTarget = false; UpdateScrollPosition(); UpdateFloatPosition(); UpdateScale(); } #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) public override void OnPointerEnter(PointerEventData eventData) { #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) isHovering = true; desiredPositionZ = -hoverPositionZMeters / GetMetersToCanvasScale(); #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) } public override void OnPointerExit(PointerEventData eventData) { #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) isHovering = false; maskedScrollOffset = Vector3.zero; desiredPositionZ = 0.0f; #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) } public override void OnGvrPointerHover(PointerEventData eventData) { #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) isHovering = true; Vector3 pos = eventData.pointerCurrentRaycast.worldPosition; RectTransform rectTransform = null; if (maskedImageObject) { rectTransform = maskedImageObject.GetComponent(); } if (!rectTransform || !isInteractable) { return; } Rect rect = rectTransform.rect; Vector3 localCenter = rect.center; Vector3 worldCenter = maskedImageObject.transform.TransformPoint(localCenter); Vector3 localMin = new Vector3(rect.min.x, rect.min.y, 0.0f); Vector3 worldMin = maskedImageObject.transform.TransformPoint(localMin); worldCenter -= worldMin; pos -= worldMin; Vector3 direction = pos - worldCenter; maskedScrollOffset.x = movementWeight * enlargedImageSize.x * direction.x; maskedScrollOffset.y = movementWeight * enlargedImageSize.y * direction.y; #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) } #if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) private void UpdateScrollPosition() { Vector3 desiredPosition = originalMaskedPosition; if (isInteractable && isHovering) { desiredPosition.x += maskedScrollOffset.x; desiredPosition.y += maskedScrollOffset.y; } Vector3 position = maskedImage.rectTransform.anchoredPosition3D; position = Vector3.Lerp(position, desiredPosition, Time.deltaTime * interpolationSpeed); maskedImage.rectTransform.anchoredPosition3D = position; } 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() { Vector2 currentSize = maskedImage.rectTransform.sizeDelta; Vector2 desiredSize; if (IsInteractable && isHovering) { desiredSize = enlargedImageSize; } else { desiredSize = originalImageSize; } currentSize = Vector2.Lerp(currentSize, desiredSize, Time.deltaTime * interpolationSpeed); maskedImage.rectTransform.sizeDelta = currentSize; } #endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR) }