// check if a point in a triangle is on the correct side and not on the half that is connected to a mesh // usefull for adding vertixes, all points in local space public static bool IsPointOpposingMesh(Vector3 checkPoint, Vector3 upCorner, Vector3 lowCorner, Mesh m) { //check whether or not the side on which the new vertex should be placed is not positioned towards the mesh => dont allow overlapping-mesh-trinagles Vector3 selectionMiddlePos = new Vector3((upCorner.x + lowCorner.x) / 2, (upCorner.y + lowCorner.y) / 2, upCorner.z); Vector3 selectionDirection = (upCorner - lowCorner).normalized; // directional vectors pointing in 90° angles from the direction of the line between both selections Vector3 leftMiddleCheck = Quaternion.AngleAxis(90, Vector3.forward) * selectionDirection; Vector3 rightMiddleCheck = Quaternion.AngleAxis(-90, Vector3.forward) * selectionDirection; // actual checking positions to the left and right of the middle of the line Vector3 leftMiddleCheckPos = selectionMiddlePos + leftMiddleCheck; Vector3 rightMiddleCheckPos = selectionMiddlePos + rightMiddleCheck; bool leftCheckInsideMesh = VertHelper.IsInsideMesh(m, Vector3.zero, leftMiddleCheckPos); bool rightCheckInsideMesh = VertHelper.IsInsideMesh(m, Vector3.zero, rightMiddleCheckPos); // one of these two poisions is not inside a mesh => allowed direction // get which direction the to-be-added position is facing VertHelper.Side sideToAdd = VertHelper.SideCheck(lowCorner, upCorner, checkPoint); if (!leftCheckInsideMesh && sideToAdd == VertHelper.Side.left || !rightCheckInsideMesh && sideToAdd == VertHelper.Side.right) { return(true); } return(false); //Debug.Log("sideToAdd " + sideToAdd + " leftCheckInsideMesh " + leftCheckInsideMesh + " rightCheckInsideMesh " + rightCheckInsideMesh); //Debug.Log("leftMiddleCheckPos " + leftMiddleCheckPos + " rightMiddleCheckPos " + rightMiddleCheckPos); //Debug.Log("1: " + selectedPosUp + " 2: " + selectedPosDown + " checkPos: " + selectionMiddlePos); }
// while dragging, transform the selected handles public void OnDrag(PointerEventData eventData) { Vector3 dragPos = Camera.main.ScreenToWorldPoint(eventData.position); dragPos.z = 0; Vector3 dragDelta = (dragStart - dragPos); dragDelta.z = 0; Vector3 newPos; // alter positions based on the original position plus the drag delta for (int i = 0; i < VertHandler.selectedHandles.Count; i++) { newPos = beforeDragPositions[i] - dragDelta; Mesh m = LevelPlacer.generatedLevel.moveArea.meshFilter.mesh; Vector3 localOldPos = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(VertHandler.selectedHandles[i].transform.position); Vector3 localnewPos = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(newPos); // check if the desired position is not crossing the triangle and if its not inside the mesh if (!VertHelper.IsInsideMesh(m, localOldPos, localnewPos) && VertHelper.IsHandlerPositionValid(localOldPos, localnewPos)) { // update the selection triangle verts if (VertHandler.selectionTriangleVerts.Any(x => x == localOldPos)) { // get all vector entries that are equal to this position and replace them with the newest position List <int> indexes = Enumerable.Range(0, VertHandler.selectionTriangleVerts.Count).Where(k => VertHandler.selectionTriangleVerts[k] == localOldPos).ToList(); foreach (int ind in indexes) { VertHandler.selectionTriangleVerts[ind] = localnewPos; } } VertHandler.selectedHandles[i].transform.position = newPos; } } if (VertHandler.quickDragHandle != null && VertHandler.selectedHandles.Count == 0) { newPos = quickDragBefore - dragDelta; Mesh m = LevelPlacer.generatedLevel.moveArea.meshFilter.mesh; Vector3 localOldPos = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(transform.position); Vector3 localnewPos = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(newPos); // check if the desired position is not crossing the triangle and if its not inside the mesh if (!VertHelper.IsInsideMesh(m, localOldPos, localnewPos) && VertHelper.IsHandlerPositionValid(localOldPos, localnewPos)) { VertHandler.quickDragHandle.transform.position = newPos; } } }
// Input Control, listens for key input, mouse/touch input and switches EditorModes / selects objects / adds vertices private void Update() { #if UNITY_EDITOR if (Input.GetKeyDown(KeyCode.KeypadMinus)) { if (cam.orthographicSize + keyboardZoomStep <= maxSize) cam.orthographicSize += keyboardZoomStep; } else if (Input.GetKeyDown(KeyCode.KeypadPlus)) { if (cam.orthographicSize - keyboardZoomStep >= minSize) cam.orthographicSize -= keyboardZoomStep; } else if (Input.GetKeyDown(KeyCode.Return)) { if (!UIObjectPreferences.menuOpen) { if (LevelEditor.TryTestLevel()) { SoundManager.ButtonClicked(); // animations } } } #endif #if UNITY_ANDROID // No items or verticies get curretly dragged and no menu is open, thus listen for input if (!vertexDragged && !UIObjectPreferences.menuOpen) { // If there are two touches on the device manage the editor view (zooming/moving) if (Input.touchCount == 2 && UILevelEditor._instance.inventoryScrollRect.velocity.x == 0) { // Store both touches. Touch touchZero = Input.GetTouch(0); Touch touchOne = Input.GetTouch(1); // Find the position in the previous frame of each touch. Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition; Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition; // Find the magnitude of the vector (the distance) between the touches in each frame. float prevTouchMag = (touchZeroPrevPos - touchOnePrevPos).magnitude; float touchMag = (touchZero.position - touchOne.position).magnitude; float touchDeltaMag = (touchZero.position - touchOne.position).magnitude; // Find the difference in the distances between each frame. float deltaMagnitudeDiff = prevTouchMag - touchMag; // fingers moving fast towards each other or fast away from each other => zooming if (touchMag + panThreshold < prevTouchMag || touchMag > prevTouchMag + panThreshold) { // Change the orthographic size based on the change in distance between the touches. float sizeToChange = deltaMagnitudeDiff * zoomSpeed; if ((sizeToChange > 0 && (cam.orthographicSize + sizeToChange) < maxSize) || (sizeToChange < 0 && (cam.orthographicSize + sizeToChange) > minSize)) { cam.orthographicSize += sizeToChange; } } // panning => move the camera else { Vector3 touchDeltaPosition = new Vector3(-touchZero.deltaPosition.x * Time.deltaTime * 500, -touchZero.deltaPosition.y * Time.deltaTime * 500, 0); transform.position += touchDeltaPosition; //transform.Translate(touchDeltaPosition.x * Time.deltaTime, touchDeltaPosition.y * Time.deltaTime, 0); } } // only one finger touches the screen, control object selecting/vertex adding else if (Input.touchCount == 1) { Touch touch = Input.GetTouch(0); // touch click = > handle selection/ vertex adding if (touch.phase == TouchPhase.Began) { Vector3 position = Camera.main.ScreenToWorldPoint(touch.position); LevelObject l = GetLevelObjectAt(position); if (l != null && l == LevelEditor.selectedObject && LevelEditor.selectedObject.objectType != LevelObject.ObjectType.moveArea) { objectDragged = true; } ClickHandler(position); } // touch drag => handle object movement else if (touch.phase == TouchPhase.Moved) { // an object is selected, and it is not the movearea if (objectDragged && LevelEditor.selectedObject != null && LevelEditor.selectedObject.objectType != LevelObject.ObjectType.moveArea && UILevelEditor._instance.inventoryScrollRect.velocity.x == 0) { Vector3 position = Camera.main.ScreenToWorldPoint(touch.position); //position.y = Screen.height - position.y; position.z = LevelEditor.selectedObject.transform.position.z; if (VertHelper.IsInsideMesh(LevelPlacer.generatedLevel.moveArea.meshFilter.mesh, Vector3.zero, LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(position))) LevelEditor.selectedObject.transform.position = position; } } // touch drag end => snap the dragged object and save the changes that were made else if (touch.phase == TouchPhase.Ended) { // an object is selected, and it is not the movearea if (objectDragged && LevelEditor.selectedObject != null && LevelEditor.selectedObject.objectType != LevelObject.ObjectType.moveArea) { // move the selected object Vector3 position = LevelEditor.selectedObject.transform.position; position = VertHelper.Snap(position, false); if (LevelEditor.selectedObject.transform.position != position) { LevelEditor.selectedObject.transform.position = position; UndoManager.AddUndoPoint(); } //itemDragged = false; } objectDragged = false; } } #if UNITY_EDITOR // both mouse buttons are held down => drag the editor view else if (Input.GetMouseButton(0) && Input.GetMouseButton(1) && UILevelEditor._instance.inventoryScrollRect.velocity.x == 0) { Vector3 position = Camera.main.ScreenToWorldPoint(Input.mousePosition); Vector3 deltaPos = new Vector3(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * 40f; deltaPos.z = 0F; transform.position += deltaPos; } // one mouse button got clicked => handle selection/vertex adding else if (Input.GetMouseButtonDown(0)) { Vector3 position = Camera.main.ScreenToWorldPoint(Input.mousePosition); ClickHandler(position); } // one mouse button is held down (aka "dragged") => try to move selected objects else if (Input.GetMouseButton(0) && UILevelEditor._instance.inventoryScrollRect.velocity.x == 0) { // an object is selected, and it is not the movearea if (LevelEditor.selectedObject != null && LevelEditor.selectedObject.objectType != LevelObject.ObjectType.moveArea) { //itemDragged = true; // move the selected object Vector3 position = Camera.main.ScreenToWorldPoint(Input.mousePosition); Vector3 deltaPos = new Vector3(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * 40f; deltaPos.z = 0F; Vector3 newPos = LevelEditor.selectedObject.transform.position + deltaPos; if (VertHelper.IsInsideMesh(LevelPlacer.generatedLevel.moveArea.meshFilter.mesh, Vector3.zero, LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(newPos))) LevelEditor.selectedObject.transform.position = newPos; } } // a mouse drag has ended, snap the dragged object and save the changes that were made else if (Input.GetMouseButtonUp(0)) { // an object is selected, and it is not the movearea if (LevelEditor.selectedObject != null && LevelEditor.selectedObject.objectType != LevelObject.ObjectType.moveArea) { // move the selected object Vector3 position = LevelEditor.selectedObject.transform.position; position = VertHelper.Snap(position, false); if (LevelEditor.selectedObject.transform.position != position) { LevelEditor.selectedObject.transform.position = position; UndoManager.AddUndoPoint(); } //itemDragged = false; } } #endif } // lets handle special preference input when a menu is open (yaaay) else if (UIObjectPreferences.menuOpen) { if (UIObjectPreferences.openedMenuType == LevelObject.ObjectType.portal) { if (Input.touchCount == 1) { Touch touch = Input.GetTouch(0); // touch click = > handle selection/ vertex adding if (touch.phase == TouchPhase.Began) { Vector3 position = Camera.main.ScreenToWorldPoint(touch.position); ClickHandler(position); } } else if (Input.GetMouseButtonDown(0)) { Vector3 position = Camera.main.ScreenToWorldPoint(Input.mousePosition); ClickHandler(position); } } } }
// checks for double clicks and selectes/deselects objects accordingly private void ClickHandler(Vector3 position) { bool objectSelected = false; if (LevelEditor.editorMode == LevelEditor.EditorMode.select) { // double click if (Time.time - doubleClickTime < doubleClickDelay && doubleClickTime > 0) { LevelObject levelObject = GetLevelObjectAt(position); if (levelObject != null) { Debug.Log("SELECTED OBJ " + levelObject.name); LevelEditor.SetSelectedObject(levelObject); objectSelected = true; } // the raycast didn't select any levelobjects if (!objectSelected) { Vector3 localPos = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(position); bool clickInsideMesh = VertHelper.IsInsideMesh(LevelPlacer.generatedLevel.moveArea.meshFilter.mesh, Vector3.zero, localPos); // we double clicked inside the mesh => select the mesh if (clickInsideMesh) { LevelEditor.SetSelectedObject(LevelPlacer.generatedLevel.moveArea); objectSelected = true; } } } } else if (LevelEditor.editorMode == LevelEditor.EditorMode.edit) { if (Time.time - doubleClickTime < doubleClickDelay && doubleClickTime > 0) { LevelObject levelObject = GetLevelObjectAt(position); if (levelObject != null) { // same object got selected again, deselect it if (levelObject == LevelEditor.selectedObject) { LevelEditor.SetSelectedObject(null); objectSelected = true; } // a new object gets selected, replace the old selection with the new one. else if (levelObject != LevelEditor.selectedObject) { LevelEditor.SetSelectedObject(levelObject); objectSelected = true; } } // the raycast didn't select any levelobjects if (!objectSelected) { Vector3 localPos = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(position); bool clickInsideMesh = VertHelper.IsInsideMesh(LevelPlacer.generatedLevel.moveArea.meshFilter.mesh, Vector3.zero, localPos); // we double clicked inside the mesh => select the mesh if (clickInsideMesh) { if (LevelEditor.selectedObject.objectType != LevelObject.ObjectType.moveArea) LevelEditor.SetSelectedObject(LevelPlacer.generatedLevel.moveArea); else LevelEditor.SetSelectedObject(null); objectSelected = true; } } } Debug.Log(LevelEditor.selectedObject); // there was no double click detected, if a movearea is selected, try to add vertices if (LevelEditor.selectedObject != null && LevelEditor.selectedObject.objectType == LevelObject.ObjectType.moveArea && !Handle.vertGettingSelected) { Vector3 screenPosition = Camera.main.WorldToScreenPoint(position); bool isInIgnoreArea = screenPosition.y < Camera.main.pixelHeight * ignoreBottomScreenPercent; if (!isInIgnoreArea) if (VertHandler._instance.VertexAdd(position)) { SoundManager.ButtonClicked(); } } } else if (LevelEditor.editorMode == LevelEditor.EditorMode.place) { // snapping Vector3 snapPos = VertHelper.Snap(position, false); LevelObject.ObjectType objectType = UILevelObject.currentSelectedObject.objectType; Vector3 screenPosition = Camera.main.WorldToScreenPoint(snapPos); bool isInIgnoreArea = screenPosition.y < Camera.main.pixelHeight * ignoreBottomScreenPercent; // the snapping found a valid snap position around the given position if (snapPos != Vector3.zero && !isInIgnoreArea) { Debug.Log("Buttom screen percent " + ignoreBottomScreenPercent + " screenPos.y " + screenPosition.y); // return to selection mode and deselect the inventory item LevelEditor.SetSelectedObject(LevelPlacer.generatedLevel.AddObject(objectType, snapPos)); UILevelObject.onItemSelect.Invoke(null); } } else if (LevelEditor.editorMode == LevelEditor.EditorMode.portalLink) { if (Time.time - doubleClickTime < doubleClickDelay && doubleClickTime > 0) { LevelObject levelObject = GetLevelObjectAt(position); if (levelObject != null) { if (levelObject.objectType == LevelObject.ObjectType.portal) { Portal p = levelObject.GetComponent<Portal>(); UIPortalMenu.SelectLinkPortal(p); } } } } doubleClickTime = Time.time; }
// add a vertex at the given position - called by EditorInput class public bool VertexAdd(Vector3 pos) { Debug.Log("VertexAdd at " + pos); if (showHandles && LevelPlacer.generatedLevel != null && handlesShown) { // two verticies are selected if (selectedHandles.Count == 2) { Mesh m = new Mesh(); m = LevelPlacer.generatedLevel.moveArea.meshFilter.mesh; Vector3 localPos = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(pos); // everything ready for expanding the mesh if (!VertHelper.IsInsideMesh(m, Vector3.zero, localPos)) { // get the currect verticies verts = m.vertices; Vector3[] newVerts = new Vector3[verts.Length + 1]; for (int i = 0; i < verts.Length; i++) { newVerts[i] = verts[i]; } // snap the position pos = VertHelper.Snap(pos, true); pos.z = 0; Vector3 localSnapPos = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(pos); // get both selected vertex positions Vector3 selectedPosUp = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(selectedHandles[0].transform.position); Vector3 selectedPosDown = LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(selectedHandles[1].transform.position); // the first position is not above the second => switch them, otherwise left/right checks fail if (selectedPosUp.y < selectedPosDown.y) { Vector3 upSave = selectedPosUp; selectedPosUp = selectedPosDown; selectedPosDown = upSave; } if (VertHelper.IsPointOpposingMesh(localSnapPos, selectedPosUp, selectedPosDown, m)) { // add the new position to the vertex arrays newVerts[verts.Length] = localSnapPos; // get the triangles int[] triangles = new int[m.triangles.Length + 3]; for (int s = 0; s < m.triangles.Length; s++) { triangles[s] = m.triangles[s]; } // add a new triangle by referencing the two selected verticies plus the new one // add selected verticies into a temporary storage int[] newIndicies = new int[3]; newIndicies[0] = newVerts.Length - 1; for (int i = 0; i < newVerts.Length; i++) { // the interated vertex fits to the first selected handler if (LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(selectedHandles[0].transform.position) == newVerts[i]) { newIndicies[1] = i; } // the interated vertex fits to the second selected handler else if (LevelPlacer.generatedLevel.moveArea.transform.InverseTransformPoint(selectedHandles[1].transform.position) == newVerts[i]) { newIndicies[2] = i; } } // sort the triangle verticies in a clockwise order to ensure the triangle faces our direction and wont get rendered backwards Vector3[] triangleVerts = new Vector3[3]; triangleVerts[0] = newVerts[newIndicies[0]]; triangleVerts[1] = newVerts[newIndicies[1]]; triangleVerts[2] = newVerts[newIndicies[2]]; // calculate the center of the triangle Vector2 center = (triangleVerts[0] + triangleVerts[1] + triangleVerts[2]) / 3; Array.Sort(triangleVerts, new ClockwiseComparer(center)); for (int i = 0; i < newVerts.Length; i++) { // the interated vertex fits to the first selected handler if (triangleVerts[0] == newVerts[i]) { newIndicies[0] = i; } // the interated vertex fits to the second selected handler else if (triangleVerts[1] == newVerts[i]) { newIndicies[1] = i; } else if (triangleVerts[2] == newVerts[i]) { newIndicies[2] = i; } } // add the sorted indicies to the triangles array triangles[m.triangles.Length] = newIndicies[0]; triangles[m.triangles.Length + 1] = newIndicies[1]; triangles[m.triangles.Length + 2] = newIndicies[2]; // update the mesh m.vertices = newVerts; m.triangles = triangles; m.RecalculateBounds(); m.RecalculateNormals(); mesh = m; LevelPlacer.generatedLevel.moveArea.meshFilter.mesh = m; // recalculate handles selectedHandles = new List <Handle>(); DestroyHandles(); Start(); return(true); } } } } return(false); }