'Keeping camera & mouse movement speeds the same on Drag in Unity3D
this question is a bit long but please bare with me.
I am working with Unity3D's newer input system, and I've been able to set up camera movement so when the user clicks the middle mouse button, they can drag the camera around along the X- & Y-axis. However, has a very parallax-y feel to it, which I am trying to avoid. From my understanding this is happening because the speed of the mouse movement & the camera movements are not the same (See, visual).
My InputController class:
public class InputController : MonoBehaviour
{
private InputControls inputControls;
[Header("Camera Movement")]
[SerializeField] CameraController cameraController;
private InputAction.CallbackContext context;
void Awake()
{
inputControls = new InputControls();
}
private void Start()
{
inputControls.Mouse.MiddleButton.started += MiddleButton_started;
}
public void MiddleButton_started(InputAction.CallbackContext ctx)
{
context = ctx;
}
private void Update()
{
bool mouseIsDown = context.performed;
if (mouseIsDown)
{
Vector2 delta = inputControls.Mouse.DeltaPosition.ReadValue<Vector2>();
cameraController.Move(delta, false);
}
}
}
My CameraController class:
public class CameraController : MonoBehaviour
{
[SerializeField] Vector2 minPosition;
[SerializeField] Vector2 maxPosition;
[SerializeField] float speed = 3;
[SerializeField] float zPosition = -10f;
private Camera mainCam;
private void Start()
{
mainCam = GetComponent<Camera>();
}
public void Move(Vector2 deltaPosition, bool convertToViewportPoint = true)
{
if (convertToViewportPoint) deltaPosition = mainCam.ScreenToViewportPoint(deltaPosition);
Vector3 moveTo = new Vector3(deltaPosition.x, deltaPosition.y, 0);
Vector3 target = transform.position - moveTo;
float clampedX = Mathf.Clamp(target.x, minPosition.x, maxPosition.x);
float clampedY = Mathf.Clamp(target.y, minPosition.y, maxPosition.y);
transform.position = Vector3.Lerp(transform.position, new Vector3(clampedX, clampedY, -10), speed * Time.deltaTime);
}
}
Now, in this version of the CameraController, the parallax-y feel might be coming from the fact that the Lerp speed is constant, whereas the speed of the mouse movement is not.
However, I've tested it with various magnitudes (ie., the magnitudes of the delta mouse position, the current mouse position, the current mouse position subtracted from the delta mouse position, etc...) with generally the same results -- either the camera moves too fast for the mouse or vice versa.
From my limited but growing understanding of trig, magnitudes represents the speed of a vector but I CANNOT get the speed of the camera movement and the speed of the mouse movement to match up. I would like for this to happen so that when the user clicks a certain point, that point generally stays under the cursor when in movement (See other visual).
Can someone help me understand how I might achieve this?
Thanks kindly.
Solution 1:[1]
What I did to solve this, in case anyone comes across this, is get the position of the cursor when the mouse is first clicked. We can call that dragOrigin
. Then we can get the change in position as the mouse moves (polling the input through Unity's input system can be done through an Update function). Then we subtract the new mouse position from dragOrigin. Once we have that difference -- lets call it deltaPosition
-- we just add it to the camera's current position.
To ensure that the speed matches that of the mouse, run the deltaPosition and the currentMousePosition through SmoothDamp to get the velocity of the change. Its magnitude will be the speed you want the camera to move.
So for example:
// Call this when the user first clicks the mouse.
// Lets assume this is part of a class that controls the camera.
public void SetDragOrigin(Vector2 mousePosition)
{
// could probably cache Camera.main for efficiency
dragOrigin = Camera.main.ScreenToWorldPoint(mousePosition);
}
// This is called in an update function of another class that deals with input.
// (Or perhaps you can simply pass the input controls to the class controlling the camera, but that's not the case here).
public void Move(Vector2 newMousePosition)
{
mousePosition = mainCam.ScreenToWorldPoint(mousePosition);
Vector2 deltaPosition = dragOrigin - mousePosition;
Vector3.SmoothDamp(deltaPosition, mousePosition, ref dragVelocity, 0.1f, 250f, Time.deltaTime);
cameraPosition += new Vector3(deltaPosition.x, deltaPosition.y, 0) * dragVelocity.magnitude * Time.deltaTime;
// anything else you want to do like clamping camera position
// ....
}
One thing to note here, is that the 5th argument of SmoothDamp
is the maxSpeed. I hardcoded it in this example, but you might want to play around with it. When I did not set the maxSpeed, there was some wonky things happening to the dragVelocity
over time, so it may be better to set the maxSpeed to what suits you best.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | ACour008 |