using System; using System.Globalization; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using static GadgetBehaviour; using UnityEngine.InputSystem; using MoreLinq; using System.Linq; //TODO check whether this can be deleted //using System.Linq; //using static GadgetManager; public class WorldCursor : MonoBehaviour { public RaycastHit Hit; // TODO experimentell for multiple hits public RaycastHit[] Hits; public string deactivateSnapKey; private Camera Cam; private int layerMask; public float MaxRange = 10f; public bool useCamCurser = false; private void Awake() { //Ignore player and TalkingZone this.layerMask = ~LayerMask.GetMask("Player", "TalkingZone"); } void Start() { Cam = Camera.main; CultureInfo.CurrentCulture = new CultureInfo("en-US"); } public void setLayerMask(int layerMask) { this.layerMask = layerMask; } // working currently to include multiple hits // TODO void Update() { updateMaxRange(); Cam = Camera.main; //WARN: Should not called every Update; TODO: Cache in Start/Awake? Vector3 mousePos = UIconfig.InputManagerVersion switch { 1 or 2 or 3 => Input.mousePosition, _ => Vector3.zero }; Ray ray = useCamCurser ? new Ray(Cam.transform.position, Cam.transform.forward) : Cam.ScreenPointToRay(mousePos); transform.up = Cam.transform.forward; transform.position = ray.GetPoint(GlobalBehaviour.GadgetPhysicalDistance); //************************************************ bool deactSnapKey = Input.GetButton(this.deactivateSnapKey) && UIconfig.InputManagerVersion >= 1 && UIconfig.InputManagerVersion <= 3; //************************************************ Hits = Physics.RaycastAll(ray, MaxRange, layerMask); if (Hits.Length == 0) { Hits = Physics.RaycastAll(transform.position, Vector3.down, GlobalBehaviour.GadgetPhysicalDistance, layerMask); if (Hits.Length == 0) return; // in case we dont hit anything, just return } Hits = Hits.OrderBy(h => h.distance).ToArray(); Fact[] facts = Hits .Select(h => h.transform.TryGetComponent(out FactObject factObj) ? FactOrganizer.AllFacts[factObj.URI] : null) .ToArray(); int i = 0; for (; i < Hits.Length; i++) { // check whether we actually hit something if (deactSnapKey || (!Hits[i].collider.transform.CompareTag("SnapZone") && !Hits[i].collider.transform.CompareTag("Selectable"))) continue; if (facts[i] is AbstractLineFact lineFact) { Hits[i].point = Math3d.ProjectPointOnLine(lineFact.Point1.Point, lineFact.Dir, Hits[i].point); } else if (facts[i] is CircleFact circleFact) { var projPlane = Math3d.ProjectPointOnPlane(circleFact.normal, circleFact.Point1.Point, Hits[i].point); var circleDistance = projPlane - circleFact.Point1.Point; if (circleDistance.magnitude >= circleFact.radius) { projPlane = circleFact.Point1.Point + circleDistance.normalized * circleFact.radius; Hits[i].normal = circleDistance.normalized; } Hits[i].point = projPlane; } else { Hits[i].point = Hits[i].collider.transform.position; Hits[i].normal = Vector3.up; } // checking for 2 lines intersection point if (Hits.Length <= i + 1 || Mathf.Abs(Hits[i + 0].distance - Hits[i + 1].distance) > 0.1) break; // we had at least 1 succesfull case // we probably have two objects intersecting if (facts[i + 0] is RayFact rayFact1 && facts[i + 1] is RayFact rayFact2) { if (Math3d.LineLineIntersection(out Vector3 intersectionPoint , rayFact2.Point1.Point, rayFact2.Dir , rayFact1.Point1.Point, rayFact1.Dir)) { Hits[i].point = intersectionPoint; break; } } //TODO: check for other types of intersection. Future Work break; // we had at least 1 succesfull case } int nr_hits = Hits.Length - i; if (i == Hits.Length) { i = 0; nr_hits = 1; } Hits = Hits.Slice(i, nr_hits).ToArray(); Hit = Hits[0]; transform.up = Hit.normal; transform.position = Hit.point + .01f * Hit.normal; if (Input.GetMouseButtonDown(0)) ClickHandler(); } void updateMaxRange() { switch (UIconfig.GameplayMode) { case 2: UIconfig.interactingRangeMode = UIconfig.InteractingRangeMode.fromObserverView; break; case 5: case 6: UIconfig.interactingRangeMode = UIconfig.InteractingRangeMode.fromCharacterView; break; default: break; } } //Check if left Mouse-Button was pressed and handle it void ClickHandler() { //TODO edit for the multiple hits. Right now it only checks the first hit if (Hit.collider.transform.CompareTag("NPC1_text") && UIconfig.nextDialogPlease < 2) UIconfig.nextDialogPlease++; if (IsPointerOverUIObject() //Needed for Android. NOT JUST: EventSystem.current.IsPointerOverGameObject() || Hit.transform.gameObject.layer == LayerMask.NameToLayer("Water") // not allowed to meassure on water || Hit.collider.transform.CompareTag("NPC1_text")) // not allowed to meassure on textfields return; //if (Hit.transform.gameObject.layer == LayerMask.NameToLayer("TransparentFX")) return; // not allowed to meassure on TransparentFX CommunicationEvents.TriggerEvent.Invoke(Hits); return; static bool IsPointerOverUIObject() { PointerEventData eventDataCurrentPosition = new(EventSystem.current) { position = new Vector2(Input.mousePosition.x, Input.mousePosition.y) }; List<RaycastResult> results = new(); EventSystem.current.RaycastAll(eventDataCurrentPosition, results); return results.Count > 0; } } }