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; //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[] MultipleHits; public string deactivateSnapKey; private Camera Cam; private int layerMask; public float MaxRange = 10f; private float MaxRange_ = 10f; public bool useCamCurser = false; private int whichCheckMouseButton = 1; private void Awake() { //Ignore player and TalkingZone this.layerMask = ~LayerMask.GetMask("Player", "TalkingZone"); } void Start() { Cam = Camera.main; //Set MarkPointMode as the default ActiveToolMode // ActiveToolMode = ToolMode.ExtraMode;//ToolMode.MarkPointMode; // CommunicationEvents.ToolModeChangedEvent.Invoke(activeGadget.id); 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(Input.mousePosition); 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; //************************************************ // in case we dont hit anything, just return if (!(Physics.Raycast(ray, out Hit, MaxRange, layerMask) || (MaxRange <= GlobalBehaviour.GadgetPhysicalDistance && Physics.Raycast(transform.position, Vector3.down, out Hit, GlobalBehaviour.GadgetPhysicalDistance, layerMask)))) return; if (UIconfig.InputManagerVersion == 1) Input.GetButton(this.deactivateSnapKey); RaycastHit[] multipleHits = Physics.RaycastAll(ray, MaxRange, layerMask); if (multipleHits.Length == 0) multipleHits = Physics.RaycastAll(transform.position, Vector3.down, GlobalBehaviour.GadgetPhysicalDistance, layerMask); // sort multipleHits, so the first hit is still the closest for (int i = 0; i < multipleHits.Length; i++) { int minIdx = i; float minValue = multipleHits[i].distance; for (int j = i; j < multipleHits.Length; j++) { if (multipleHits[j].distance < minValue) { minIdx = j; minValue = multipleHits[j].distance; } } (multipleHits[i], multipleHits[minIdx]) = (multipleHits[minIdx], multipleHits[i]); } for (int i = 0; i < multipleHits.Length; i++) { // check whether we actually hit something if (!((multipleHits[i].collider.transform.CompareTag("SnapZone") || multipleHits[i].collider.transform.CompareTag("Selectable")) && (!deactSnapKey))) continue; //TODO see whether the conditions needs to be adjusted //if (Hit.transform.TryGetComponent<FactObject>(out var obj) // && FactOrganizer.AllFacts[obj.URI] is AbstractLineFact lineFact) if (multipleHits[i].collider.gameObject.layer == LayerMask.NameToLayer("Ray") || multipleHits[i].collider.gameObject.layer == LayerMask.NameToLayer("Line")) { var id = multipleHits[i].collider.gameObject.GetComponent<FactObject>().URI; AbstractLineFact lineFact = FactOrganizer.AllFacts[id] as AbstractLineFact; PointFact p1 = lineFact.Point1; multipleHits[i].point = Math3d.ProjectPointOnLine(p1.Point, lineFact.Dir, multipleHits[i].point); } else if (multipleHits[i].collider.gameObject.layer == LayerMask.NameToLayer("Ring")) { #region Ring var id = multipleHits[i].transform.GetComponent<FactObject>().URI; CircleFact circleFact = FactOrganizer.AllFacts[id] as CircleFact; Vector3 middlePoint = circleFact.Point1.Point; // project p on circlePlane var pPlane = Math3d.ProjectPointOnPlane(circleFact.normal, circleFact.Point1.Point, multipleHits[i].point); // check if projectedPoint and circleCenter are identical if (pPlane == middlePoint) pPlane = circleFact.Point2.Point; multipleHits[i].point = middlePoint + (pPlane - middlePoint).normalized * circleFact.radius; // cursor orientation should match circle orientation; dont face downwards multipleHits[i].normal = circleFact.normal.y < 0 ? -circleFact.normal : circleFact.normal; #endregion Ring } else if (multipleHits[i].collider.gameObject.layer == LayerMask.NameToLayer("Circle")) { #region Circle var id = multipleHits[i].transform.parent.GetComponent<FactObject>().URI; CircleFact circleFact = FactOrganizer.AllFacts[id] as CircleFact; multipleHits[i].point = Math3d.ProjectPointOnPlane(circleFact.normal, circleFact.Point1.Point, multipleHits[i].point); // cursor orientation should match circle orientation; dont face downwards multipleHits[i].normal = circleFact.normal.y < 0 ? -circleFact.normal : circleFact.normal; #endregion Circle } else { multipleHits[i].point = multipleHits[i].collider.transform.position; multipleHits[i].normal = Vector3.up; } // checking for 2 lines intersection point if (!((Mathf.Abs(multipleHits[i].distance - multipleHits[0].distance) < 0.03) && (multipleHits.Length > 1) && (Mathf.Abs(multipleHits[1].distance - multipleHits[0].distance) < 0.03))) continue; // we probably have two objects intersecting // check for line x line intersection and if they actually intersect adjust the points coordinates :) if (multipleHits[i].collider.gameObject.layer == LayerMask.NameToLayer("Ray") && multipleHits[0].collider.gameObject.layer == LayerMask.NameToLayer("Ray")) { // case for two intersecting rays var idLine0 = multipleHits[0].collider.gameObject.GetComponent<FactObject>().URI; var id = multipleHits[i].collider.gameObject.GetComponent<FactObject>().URI; // get the two corresponding line facts AbstractLineFact lineFactLine0 = FactOrganizer.AllFacts[idLine0] as AbstractLineFact; AbstractLineFact lineFact = FactOrganizer.AllFacts[id] as AbstractLineFact; // get a point on the line PointFact p1Line0 = lineFactLine0.Point1; PointFact p1 = lineFact.Point1; // get the intersection point and if it actually intersects set it if (Math3d.LineLineIntersection(out Vector3 intersectionPoint, p1Line0.Point, lineFactLine0.Dir, p1.Point, lineFact.Dir)) multipleHits[i].point = intersectionPoint; } //TODO: check for other types of intersection. Future Work } transform.up = multipleHits[0].normal; //TODO check whether this is needed //if (!((multipleHits[0].collider.transform.CompareTag("SnapZone") || multipleHits[0].collider.transform.CompareTag("Selectable")) // && !Input.GetButton(this.deactivateSnapKey))) // transform.position += .01f * multipleHits[0].normal; transform.position = multipleHits[0].point + .01f * multipleHits[0].normal; this.MultipleHits = multipleHits; //Link to CheckMouseButtonHandler if (whichCheckMouseButton == 0) { CheckMouseButtons(); } if (whichCheckMouseButton == 1) { CheckMouseButtons1(); } } 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; } MaxRange_ = UIconfig.interactingRangeMode switch { UIconfig.InteractingRangeMode.fromObserverView => UIconfig.cursorMaxRange_fromObeserverView, UIconfig.InteractingRangeMode.fromCharacterView or _ => MaxRange, }; //Debug.Log("WorldCursorMaxRange :" + MaxRange_); } //Check if left Mouse-Button was pressed and handle it void CheckMouseButtons() { if (Input.GetMouseButtonDown(0) && !EventSystem.current.IsPointerOverGameObject() //this prevents rays from shooting through ui && Hit.transform.gameObject.layer != LayerMask.NameToLayer("Water")) // not allowed to meassure on water CommunicationEvents.TriggerEvent.Invoke(MultipleHits); } //Check if left Mouse-Button was pressed and handle it //Alternative Version void CheckMouseButtons1(bool OnSnap = false, bool onLine = false) { //TODO edit for the multiple hits. Right now it only checks the first hit if (Input.GetMouseButtonDown(0) && checkClickPermission()) { //other Things todo first? if (Hit.collider.transform.CompareTag("NPC1_text") && UIconfig.nextDialogPlease < 2) { UIconfig.nextDialogPlease++; } //if (EventSystem.current.IsPointerOverGameObject()) return; //this prevents rays from shooting through ui if (IsPointerOverUIObject()) return; //Needed for Android if (!checkLayerAndTags()) return; // not allowed to meassure on water //if (Hit.transform.gameObject.layer == LayerMask.NameToLayer("TransparentFX")) return; // not allowed to meassure on TransparentFX if (!OnSnap) { CommunicationEvents.TriggerEvent.Invoke(MultipleHits); } else if (GadgetBehaviour.ActiveGadget is Pointer) { if (!onLine) Hit.collider.enabled = false; CommunicationEvents.TriggerEvent.Invoke(MultipleHits); // CommunicationEvents.SnapEvent.Invoke(Hit); } } } private bool checkLayerAndTags() => 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 private 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; } public bool checkClickPermission() => true; //{ // if (UIconfig.CanvasOnOff_Array[14] > 0) // return true; // //return false; //todo // return true; //} }