Skip to content
Snippets Groups Projects
WorldFactInteraction.cs 3.36 KiB
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/// <summary>
/// This class handles displaying Fact tooltips, when hovering over a fact in the Gameworld
/// </summary>
public class WorldFactInteraction : MonoBehaviour
{
    public LayerMask factLayerMask;
    public Transform HidingCanvas;

    private GameObject currentDisplay;
    private Transform lastHit = null;

    void LateUpdate()
    {
        if (currentDisplay != null && currentDisplay.GetComponent<DragHandling>().dragged)
        {
            // currently dragging -> remove transparency to indicate dragging and let DragHandling.cs take over
            ChangeImageAlpha(currentDisplay.GetComponent<Image>(), 1);
            return;
        }

        UpdateDisplay();
    }

    private void UpdateDisplay()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        // if no fact was hit or pointer was over other UI
        if (!Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, factLayerMask) || WasOtherUIHit())
        {
            // destroy currentDisplay if it exists
            lastHit = null;
            Destroy(currentDisplay);
            return;
        }

        FactObject factObj = hit.transform.gameObject.GetComponentInChildren<FactObject>();

        if (factObj == null)
        {
            // should never happen, if the layerMask is set up correctly
            //Debug.LogError("WorldFactInteraction Raycast collided with object in factLayerMask, that did not contain a FactObject script: " + hit.transform.gameObject.name);
            lastHit = null;
            Destroy(currentDisplay);
            return;
        }

        if (hit.transform != lastHit) // a fact has been hit for the first time -> delete old display and instantiate new one
        {
            InstantiateNewDisplay(factObj);
        }

        currentDisplay.transform.position = Input.mousePosition; // move currentDisplay to mousePosition
        ChangeImageAlpha(currentDisplay.GetComponent<Image>(), 0.5f); // ensure that image alpha is correct, since it could have changed due to dragging

        lastHit = hit.transform;
    }

    private void InstantiateNewDisplay(FactObject factObj)
    {
        if (currentDisplay)
            Destroy(currentDisplay);
        Fact fact = StageStatic.stage.factState[factObj.URI];
        // TODO: this link to DisplayFacts is not ideal: maybe refactor to SciptableObject or such
        currentDisplay = fact.instantiateDisplay(DisplayFacts.prefabDictionary[fact.GetType()], HidingCanvas);
    }

    #region Helper
    private static void ChangeImageAlpha(Image img, float alpha)
    {
        img.color = new Color(img.color.r, img.color.g, img.color.b, alpha);
    }

    /// <summary>
    /// Returns true if any UI other than currentDisplay was hit
    /// </summary>
    /// <returns></returns>
    private bool WasOtherUIHit()
    {
        PointerEventData pointerData = new(EventSystem.current)
        {
            position = Input.mousePosition
        };

        List<RaycastResult> results = new();
        EventSystem.current.RaycastAll(pointerData, results);

        foreach (var res in results)
            if (currentDisplay == null || !res.gameObject.transform.IsChildOf(currentDisplay.transform))
                return true;

        return false;
    }
    #endregion Helper
}