0
\$\begingroup\$

Basically I have an orthographic camera that is looking at a simple square. The camera is sitting above the square and tilted by x: 45° and y: 45°, so the camera sees the square as a rhombus / diamond shape.

I want the camera to be moveable within a rectangular boundary that appears to be leveled with the camera, so it would need to be rotated by 45° around the y-axis (up-vector in Unity) in World-coordinates. I already figured out the math to constrain the camera to this shape, but there is a weird thing going on when the movement gets clipped: The camera doesn't follow the outline perfectly, but in a rounded shape (see screenshot). Could this be due to a rounding error? Code snipped below. The red outline shows how the camera actually moves.

using System; using UnityEngine; using static UnityEngine.Mathf; public class InputController : MonoBehaviour { private const float CamHeight = 10f; [SerializeField] private GameObject gameBoard; [SerializeField] private GameObject camBounds; [SerializeField] private float camBoundRadius = 5f; private Vector3 _camOrigin; private MeshCollider _collider; private Vector3 _difference; // Change in position of mouse relative to origin private Camera _mainCamera; private Vector3 _origin; // Place where mouse is first pressed private Plane _worldPlane; private void Start() { _mainCamera = Camera.main; if (_mainCamera is null) throw new Exception("No main camera could be found!"); // Set the camera so that the board is always in the center _camOrigin = new Vector3( -CamHeight / Sqrt(2f), CamHeight, -CamHeight / Sqrt(2f)); _mainCamera.transform.position = _camOrigin; _collider = gameBoard.GetComponent<MeshCollider>(); _worldPlane = new Plane(Vector3.up, 0); // For dragging the camera around } private void LateUpdate() { if (Input.GetMouseButtonDown(0)) _origin = MouseWorldPosition(); if (Input.GetMouseButton(0)) { _difference = MouseWorldPosition() - transform.position; var newPos = _origin - _difference; var absDiff = newPos - _camOrigin; var angle = Angle2Pi(Vector3.right, absDiff); var constraint = SawTooth(angle); if (absDiff.magnitude < constraint) transform.position = newPos; else transform.position = _camOrigin + absDiff.normalized * constraint; } } private Vector3 MouseWorldPosition() { var clickedPoint = Input.mousePosition; var ray = _mainCamera.ScreenPointToRay(clickedPoint); var worldPos = Vector3.zero; if (_worldPlane.Raycast(ray, out var raycastHit)) worldPos = ray.GetPoint(raycastHit); return worldPos; } private float SawTooth(float theta) { var x = 4f / PI * camBoundRadius * (1f - 1f / Sqrt(2f)); x *= Abs(theta % (PI / 2f) - PI / 4f); x += camBoundRadius / Sqrt(2f); return x; } private static float Angle2Pi(Vector3 from, Vector3 to, Vector3 normal = default) { if (normal == default) normal = Vector3.up; var angle = Vector3.Angle(from, to) * (PI / 180f); var sign = Sign(Vector3.Dot(normal, Vector3.Cross(from, to))); angle = 2f * PI - angle * sign; return angle; } } 
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Well I took a different approach that works perfectly and actually makes use of the bounding box shown in the screenshot. Here the code:

private void LateUpdate() { if (Input.GetMouseButtonDown(0)) _origin = MouseWorldPosition(); if (Input.GetMouseButton(0)) { _difference = MouseWorldPosition() - transform.position; var newPos = _origin - _difference; var absDiff = newPos - _camOrigin; if (absDiff == Vector3.zero) return; // Check if newPos is outside the bounding box, i.e. if a raycast hits the bounding box var ray = new Ray(newPos, -absDiff.normalized); if (_camBounds.Raycast(ray, out var raycastHit, absDiff.magnitude)) transform.position = ray.GetPoint(raycastHit.distance); else transform.position = newPos; } } 
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.