| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.EventSystems;
- namespace HighlightPlus {
- public enum TriggerMode {
- ColliderEventsOnlyOnThisObject = 0,
- RaycastOnThisObjectAndChildren = 1,
- Volume = 2
- }
- public enum RayCastSource {
- MousePosition = 0,
- CameraDirection = 1
- }
- [RequireComponent(typeof(HighlightEffect))]
- [ExecuteInEditMode]
- [HelpURL("https://kronnect.com/guides/highlight-plus-introduction/")]
- public class HighlightTrigger : MonoBehaviour {
- [Tooltip("Enables highlight when pointer is over this object.")]
- public bool highlightOnHover = true;
- [Tooltip("Used to trigger automatic highlighting including children objects.")]
- #if ENABLE_INPUT_SYSTEM
- public TriggerMode triggerMode = TriggerMode.RaycastOnThisObjectAndChildren;
- #else
- public TriggerMode triggerMode = TriggerMode.ColliderEventsOnlyOnThisObject;
- #endif
- public Camera raycastCamera;
- public RayCastSource raycastSource = RayCastSource.MousePosition;
- public LayerMask raycastLayerMask = -1;
- [Tooltip("Minimum distance for target.")]
- public float minDistance;
- [Tooltip("Maximum distance for target. 0 = infinity")]
- public float maxDistance;
- [Tooltip("Blocks interaction if pointer is over an UI element")]
- public bool respectUI = true;
- public LayerMask volumeLayerMask;
- const int MAX_RAYCAST_HITS = 100;
- [Tooltip("If the object will be selected by clicking with mouse or tapping on it.")]
- public bool selectOnClick;
- [Tooltip("Profile to use when object is selected by clicking on it.")]
- public HighlightProfile selectedProfile;
- [Tooltip("Profile to use whtn object is selected and highlighted.")]
- public HighlightProfile selectedAndHighlightedProfile;
- [Tooltip("Automatically deselects any other selected object prior selecting this one")]
- public bool singleSelection;
- [Tooltip("Toggles selection on/off when clicking object")]
- public bool toggle;
- [Tooltip("Keeps current selection when clicking outside of any selectable object")]
- public bool keepSelection = true;
- [NonSerialized] public Collider[] colliders;
- [NonSerialized] public Collider2D[] colliders2D;
- public bool hasColliders => (colliders != null && colliders.Length > 0);
- public bool hasColliders2D => (colliders2D != null && colliders2D.Length > 0);
- UnityEngine.Object currentCollider;
- static RaycastHit[] hits;
- static RaycastHit2D[] hits2D;
- HighlightEffect hb;
- public HighlightEffect highlightEffect { get { return hb; } }
- public event OnObjectSelectionEvent OnObjectSelected;
- public event OnObjectSelectionEvent OnObjectUnSelected;
- public event OnObjectHighlightEvent OnObjectHighlightStart;
- public event OnObjectHighlightEvent OnObjectHighlightEnd;
- TriggerMode currentTriggerMode;
- [RuntimeInitializeOnLoadMethod]
- static void DomainReloadDisabledSupport() {
- HighlightManager.selectedObjects.Clear();
- }
- void OnEnable() {
- Init();
- }
- private void OnValidate() {
- if (currentTriggerMode != triggerMode) {
- UpdateTriggers();
- }
- }
- void UpdateTriggers() {
- currentTriggerMode = triggerMode;
- if (currentTriggerMode == TriggerMode.RaycastOnThisObjectAndChildren) {
- colliders = GetComponentsInChildren<Collider>();
- colliders2D = GetComponentsInChildren<Collider2D>();
- if (hits == null || hits.Length != MAX_RAYCAST_HITS) {
- hits = new RaycastHit[MAX_RAYCAST_HITS];
- }
- if (hits2D == null || hits2D.Length != MAX_RAYCAST_HITS) {
- hits2D = new RaycastHit2D[MAX_RAYCAST_HITS];
- }
- if (Application.isPlaying) {
- StopAllCoroutines();
- if (gameObject.activeInHierarchy) {
- StartCoroutine(DoRayCast());
- }
- }
- }
- }
- public void Init() {
- if (raycastCamera == null) {
- raycastCamera = HighlightManager.GetCamera();
- }
- UpdateTriggers();
- if (hb == null) {
- hb = GetComponent<HighlightEffect>();
- }
- InputProxy.Init();
- }
- void Start() {
- UpdateTriggers();
- if (triggerMode == TriggerMode.RaycastOnThisObjectAndChildren) {
- if (raycastCamera == null) {
- raycastCamera = HighlightManager.GetCamera();
- if (raycastCamera == null) {
- Debug.LogError("Highlight Trigger on " + gameObject.name + ": no camera found!");
- }
- }
- } else {
- Collider collider = GetComponent<Collider>();
- if (collider == null) {
- if (GetComponent<MeshFilter>() != null) {
- gameObject.AddComponent<MeshCollider>();
- }
- }
- }
- }
- IEnumerator DoRayCast() {
- yield return null;
- WaitForEndOfFrame w = new WaitForEndOfFrame();
- while (triggerMode == TriggerMode.RaycastOnThisObjectAndChildren) {
- if (raycastCamera == null) {
- yield return null;
- continue;
- }
- int hitCount;
- bool hit = false;
- #if ENABLE_INPUT_SYSTEM
- if (respectUI) {
- EventSystem es = EventSystem.current;
- if (es == null) {
- es = CreateEventSystem();
- }
- List<RaycastResult> raycastResults = new List<RaycastResult>();
- PointerEventData eventData = new PointerEventData(es);
- Vector3 cameraPos = raycastCamera.transform.position;
- if (raycastSource == RayCastSource.MousePosition) {
- eventData.position = InputProxy.mousePosition;
- } else {
- eventData.position = new Vector2(raycastCamera.pixelWidth * 0.5f, raycastCamera.pixelHeight * 0.5f);
- }
- es.RaycastAll(eventData, raycastResults);
- hitCount = raycastResults.Count;
- // check UI blocker
- bool blocked = false;
- for (int k = 0; k < hitCount; k++) {
- RaycastResult rr = raycastResults[k];
- if (rr.module is UnityEngine.UI.GraphicRaycaster) {
- blocked = true;
- break;
- }
- }
- if (blocked) {
- yield return null;
- continue;
- }
- // look for our gameobject
- for (int k = 0; k < hitCount; k++) {
- RaycastResult rr = raycastResults[k];
- float distance = Vector3.Distance(rr.worldPosition, cameraPos);
- if (distance < minDistance || (maxDistance > 0 && distance > maxDistance)) continue;
- GameObject theGameObject = rr.gameObject;
- for (int c = 0; c < colliders.Length; c++) {
- if (colliders[c].gameObject == theGameObject) {
- Collider theCollider = colliders[c];
- hit = true;
- if (selectOnClick && InputProxy.GetMouseButtonDown(0)) {
- ToggleSelection();
- break;
- } else if (theCollider != currentCollider) {
- SwitchCollider(theCollider);
- k = hitCount;
- break;
- }
- }
- }
- }
- }
- // if not blocked by UI and no hit found, fallback to raycast (required if no PhysicsRaycaster is present on the camera)
- #endif
- Ray ray;
- if (raycastSource == RayCastSource.MousePosition) {
- #if !ENABLE_INPUT_SYSTEM
- if (!CanInteract()) {
- yield return null;
- continue;
- }
- #endif
- ray = raycastCamera.ScreenPointToRay(InputProxy.mousePosition);
- } else {
- ray = new Ray(raycastCamera.transform.position, raycastCamera.transform.forward);
- }
- bool isMouseButonDown = InputProxy.GetMouseButtonDown(0);
- if (hasColliders2D) {
- if (maxDistance > 0) {
- hitCount = Physics2D.GetRayIntersectionNonAlloc(ray, hits2D, maxDistance, raycastLayerMask);
- } else {
- hitCount = Physics2D.GetRayIntersectionNonAlloc(ray, hits2D, float.MaxValue, raycastLayerMask);
- }
- for (int k = 0; k < hitCount; k++) {
- if (Vector3.Distance(hits2D[k].point, ray.origin) < minDistance) continue;
- Collider2D theCollider = hits2D[k].collider;
- int colliders2DCount = colliders2D.Length;
- for (int c = 0; c < colliders2DCount; c++) {
- if (colliders2D[c] == theCollider) {
- hit = true;
- if (selectOnClick && isMouseButonDown) {
- ToggleSelection();
- break;
- } else if (theCollider != currentCollider) {
- SwitchCollider(theCollider);
- k = hitCount;
- break;
- }
- }
- }
- }
- }
- if (hasColliders) {
- if (maxDistance > 0) {
- hitCount = Physics.RaycastNonAlloc(ray, hits, maxDistance, raycastLayerMask);
- } else {
- hitCount = Physics.RaycastNonAlloc(ray, hits, float.MaxValue, raycastLayerMask);
- }
- for (int k = 0; k < hitCount; k++) {
- if (Vector3.Distance(hits[k].point, ray.origin) < minDistance) continue;
- Collider theCollider = hits[k].collider;
- int collidersCount = colliders.Length;
- for (int c = 0; c < collidersCount; c++) {
- if (colliders[c] == theCollider) {
- hit = true;
- if (selectOnClick && isMouseButonDown) {
- ToggleSelection();
- break;
- } else if (theCollider != currentCollider) {
- SwitchCollider(theCollider);
- k = hitCount;
- break;
- }
- }
- }
- }
- }
- if (!hit && currentCollider != null) {
- SwitchCollider(null);
- }
- if (selectOnClick && isMouseButonDown && !keepSelection && !hit) {
- yield return w; // wait for other potential triggers to act
- if (HighlightManager.lastTriggerFrame < Time.frameCount) {
- HighlightManager.DeselectAll();
- }
- }
- yield return null;
- }
- }
- #if ENABLE_INPUT_SYSTEM
- EventSystem CreateEventSystem() {
- GameObject eo = new GameObject("Event System created by Highlight Plus", typeof(EventSystem), typeof(UnityEngine.InputSystem.UI.InputSystemUIInputModule));
- return eo.GetComponent<EventSystem>();
- }
- #endif
- void SwitchCollider(UnityEngine.Object newCollider) {
- if (!highlightOnHover && !hb.isSelected) return;
- currentCollider = newCollider;
- if (currentCollider != null) {
- Highlight(true);
- } else {
- Highlight(false);
- }
- }
- bool CanInteract() {
- if (!respectUI) return true;
- EventSystem es = EventSystem.current;
- if (es == null) return true;
- if (Application.isMobilePlatform && InputProxy.touchCount > 0 && es.IsPointerOverGameObject(InputProxy.GetFingerIdFromTouch(0))) {
- return false;
- } else if (es.IsPointerOverGameObject(-1))
- return false;
- return true;
- }
- void OnMouseDown() {
- if (isActiveAndEnabled && triggerMode == TriggerMode.ColliderEventsOnlyOnThisObject) {
- if (!CanInteract()) return;
- if (selectOnClick && InputProxy.GetMouseButtonDown(0)) {
- ToggleSelection();
- return;
- }
- Highlight(true);
- }
- }
- void OnMouseEnter() {
- if (isActiveAndEnabled && triggerMode == TriggerMode.ColliderEventsOnlyOnThisObject) {
- if (!CanInteract()) return;
- Highlight(true);
- }
- }
- void OnMouseExit() {
- if (isActiveAndEnabled && triggerMode == TriggerMode.ColliderEventsOnlyOnThisObject) {
- if (!CanInteract()) return;
- Highlight(false);
- }
- }
- void Highlight(bool state) {
- if (state) {
- if (!hb.highlighted) {
- if (OnObjectHighlightStart != null && hb.target != null) {
- if (!OnObjectHighlightStart(hb.target.gameObject)) return;
- }
- }
- } else {
- if (hb.highlighted) {
- if (OnObjectHighlightEnd != null && hb.target != null) {
- OnObjectHighlightEnd(hb.target.gameObject);
- }
- }
- }
- if (selectOnClick || hb.isSelected) {
- if (hb.isSelected) {
- if (state && selectedAndHighlightedProfile != null) {
- selectedAndHighlightedProfile.Load(hb);
- } else if (selectedProfile != null) {
- selectedProfile.Load(hb);
- } else {
- hb.previousSettings.Load(hb);
- }
- if (hb.highlighted) {
- hb.UpdateMaterialProperties();
- } else {
- hb.SetHighlighted(true);
- }
- return;
- } else if (!highlightOnHover) {
- hb.SetHighlighted(false);
- return;
- }
- }
- hb.SetHighlighted(state);
- }
- void ToggleSelection() {
- HighlightManager.lastTriggerFrame = Time.frameCount;
- bool newState = toggle ? !hb.isSelected : true;
- if (newState) {
- if (OnObjectSelected != null && !OnObjectSelected(gameObject)) return;
- } else {
- if (OnObjectUnSelected != null && !OnObjectUnSelected(gameObject)) return;
- }
- if (singleSelection && newState) {
- HighlightManager.DeselectAll();
- }
- hb.isSelected = newState;
- if (newState && !HighlightManager.selectedObjects.Contains(hb)) {
- HighlightManager.selectedObjects.Add(hb);
- } else if (!newState && HighlightManager.selectedObjects.Contains(hb)) {
- HighlightManager.selectedObjects.Remove(hb);
- }
- if (hb.isSelected) {
- if (hb.previousSettings == null) {
- hb.previousSettings = ScriptableObject.CreateInstance<HighlightProfile>();
- }
- hb.previousSettings.Save(hb);
- } else {
- hb.RestorePreviousHighlightEffectSettings();
- }
- Highlight(true);
- }
- public void OnTriggerEnter(Collider other) {
- if (triggerMode == TriggerMode.Volume) {
- if ((volumeLayerMask & (1 << other.gameObject.layer)) != 0) {
- Highlight(true);
- }
- }
- }
- public void OnTriggerExit(Collider other) {
- if (triggerMode == TriggerMode.Volume) {
- if ((volumeLayerMask & (1 << other.gameObject.layer)) != 0) {
- Highlight(false);
- }
- }
- }
- }
- }
|