Moving Sprites with Mouse Input

In this tutorial, I would like to present 6 possible ways to change positions of sprites with mouse input in Unity among numerous varied approaches. The scripts and demo gifs are provided for each. In all cases, the sprite will not leave the scene. Also, it is very possible to create more movement behaviors using mouse input. This post has started with only 3 and now we have 6.

The first three are a more direct following of mouse. For the two, If the mouse is not clicked on the object the object will not move smoothly to where the cursor is but teleport there. The first one is the direct teleportation of the object to the position of the mouse release. The second one is moving 2d object with the mouse cursor while it’s pressed. The last one shows a smooth movement from start to end by keeping an offset distance between the object position and mouse position.

The last three implementations use interpolation and thresholded distance checks to a goal point. The first one moves toward to mouse position when the mouse is pressed but stops when it is released even if it has not reached the release position. Continuing to go to release position done in the second one. And the last one moves smoothly towards mouse but when released it returns to its initial position, creating a kind of a yoyo movement. 

original.gif
Original implementation

The last one was a study exercise for my internship project. While browsing my old projects to find a language translation implementation, I found it. This was one of my earlier projects using Unity, which is nostalgic. Actually, it was for touch input for a touch screen project. In Unity, one finger touch can be simulated by mouse input so I was taking an easy path. Also, using Spider-man/Deadpool comic book logo was fun. Since I am not an artist there is a simple flower as a 2d object placeholder.

Moving in Boundaries

For each implementation, this part shows a similar approach. At the start, the minimum and maximum positions of the sprite are calculated. when the target position is set, its value limited inside the precalculated values.

Asset 2.png
Boundary positions

Creating the boundary first starts with taking the left up and right down corner points on the scene and translating them from viewport/screen to wold/game world coordinates.

If you do not want 2d object to be dived by the screen you should add its size(Size) to possible minimum coordinates(MinPos) and remove its size from possible maximum coordinates(MaxPos).


Vector2 initPos;
Vector2 MinPos;
Vector2 MaxPos;
Vector2 targetPos;
public float Speed = 2;
public float thresholdStopDistance = .3f;
void Start()
{
initPos = transform.position;
targetPos = initPos;
Vector2 Size = GetComponent<SpriteRenderer>().bounds.extents;
MinPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(0, 0)) + Size;
MaxPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(1, 1)) – Size;
}

While the first target point is taken by mouse position, again its value is translated from viewport/screen to wold/game world coordinates. Then the value is clamped between the minimum and maximum positions. If an overflow happens the values in minimum and maximum positions are used.


if (Input.GetMouseButton(0)) //this changes for different approaches
{
mousePos = (Input.mousePosition);
Vector2 targetPos= new Vector2(Camera.main.ScreenToWorldPoint(mousePos).x, Camera.main.ScreenToWorldPoint(mousePos).y);
targetPos.x = Mathf.Clamp(targetPos.x, MinPos.x, MaxPos.x);
targetPos.y = Mathf.Clamp(targetPos.y, MinPos.y, MaxPos.y);
transform.position = targetPos; //this changes for different approaches
}

To use the codes in the below examples just add the script to 2d object on the scene.

Move To Mouse Release Position

This is a very simple approach. When the mouse is released object position is set to last mouse position before release. Since it is teleportation we do not see any smooth movement.

0


using UnityEngine;
public class MoveToRelease : MonoBehaviour
{
Vector2 MinPos;
Vector2 MaxPos;
Vector2 mousePos;
void Start()
{
Vector2 Size = GetComponent<SpriteRenderer>().bounds.extents;
MinPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(0, 0)) + Size;
MaxPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(1, 1)) – Size;
}
void Update()
{
if (Input.GetMouseButtonUp(0))
{
mousePos = (Input.mousePosition);
Vector2 targetPos= new Vector2(Camera.main.ScreenToWorldPoint(mousePos).x, Camera.main.ScreenToWorldPoint(mousePos).y);
targetPos.x = Mathf.Clamp(targetPos.x, MinPos.x, MaxPos.x);
targetPos.y = Mathf.Clamp(targetPos.y, MinPos.y, MaxPos.y);
transform.position = targetPos;
}
}
}

Very straightforwardly, when mouse released, get mouse position, transform it to world point, clamp the values with limits and set the position of the object to transformed mouse release position.

Move with Mouse

Another approach would be to move the object with the mouse. When the mouse is released object stays at the point. If the object is not clicked on, when the mouse held down it will blink at the mouse position causing a disturbing flow on movement.

1


using UnityEngine;
public class MoveWithMouse : MonoBehaviour
{
Vector2 MinPos;
Vector2 MaxPos;
Vector2 mousePos;
void Start()
{
Vector2 Size = GetComponent<SpriteRenderer>().bounds.extents;
MinPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(0, 0)) + Size;
MaxPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(1, 1)) – Size;
}
void Update()
{
if (Input.GetMouseButton(0))
{
mousePos = (Input.mousePosition);
Vector2 targetPos= new Vector2(Camera.main.ScreenToWorldPoint(mousePos).x, Camera.main.ScreenToWorldPoint(mousePos).y);
targetPos.x = Mathf.Clamp(targetPos.x, MinPos.x, MaxPos.x);
targetPos.y = Mathf.Clamp(targetPos.y, MinPos.y, MaxPos.y);
transform.position = targetPos;
}
}
}

Similar to the previous approach but this time, not the mouse release is checked but the pressing of the mouse. When mouse pressed, get mouse position, transform it to world point, clamp the values with limits and set the position of the object to transformed mouse position. If the mouse is released stop at the last position where the mouse was pressed.

Move with Offset to Mouse Position

This is very similar to move with a mouse but in this one, when mouse clicked there will be no teleportation. The movement is continuous and will not end where the mouse released but the relative starting distance between the 2d object and mouse position. When the mouse is released object stays at where it is.

2


using UnityEngine;
public class MoveWithOffset : MonoBehaviour
{
Vector2 MinPos;
Vector2 MaxPos;
Vector2 mousePos;
Vector2 Offset;
void Start()
{
Vector2 Size = GetComponent<SpriteRenderer>().bounds.extents;
MinPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(0, 0)) + Size;
MaxPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(1, 1)) – Size;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
mousePos = (Input.mousePosition);
Vector2 mouseInitialPosition = new Vector2(Camera.main.ScreenToWorldPoint(mousePos).x, Camera.main.ScreenToWorldPoint(mousePos).y);
Offset = mouseInitialPosition-(Vector2)transform.position ;
}
else if (Input.GetMouseButton(0))
{
mousePos = (Input.mousePosition);
Vector2 targetPos = new Vector2(Camera.main.ScreenToWorldPoint(mousePos).x, Camera.main.ScreenToWorldPoint(mousePos).y);
targetPos.x = Mathf.Clamp(targetPos.x – Offset.x, MinPos.x, MaxPos.x);
targetPos.y = Mathf.Clamp(targetPos.y – Offset.y, MinPos.y, MaxPos.y);
transform.position = targetPos;
}
}
}

This one differs from the previous ones as it uses an offset distance between the position of the object and the mouse’s clicked position. Except for the overflow areas, the distance between the object and mouse positions is preserved.

Asset 1.png
Representation of game scene

Offset vector is calculated when the mouse is pressed and used the same till the mouse is released. It starts from the position of the object and ends in the position of the initial mouse press. Since the object should be moved Offset away from the mouse position, it is subtracted from the mouse position. Object movement continues while the mouse is pressed and when released, the object stays where it is.

Move After Mouse Position Smoothly

In this smooth movement, interpolation between the initial position of the object and the mouse position is used. Therefore, the movement is again continuos but this time also starts from the initial point. When the mouse is released object stays at the point.

3


using UnityEngine;
public class MoveSmoothly : MonoBehaviour
{
Vector2 MinPos;
Vector2 MaxPos;
public float Speed = 2;
void Start()
{
Vector2 Size = GetComponent<SpriteRenderer>().bounds.extents;
MinPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(0, 0)) + Size;
MaxPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(1, 1)) – Size;
}
void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = (Input.mousePosition);
Vector2 targetPos= new Vector2(Camera.main.ScreenToWorldPoint(mousePos).x, Camera.main.ScreenToWorldPoint(mousePos).y);
targetPos.x = Mathf.Clamp(targetPos.x, MinPos.x, MaxPos.x);
targetPos.y = Mathf.Clamp(targetPos.y, MinPos.y, MaxPos.y);
transform.position = Vector2.Lerp(transform.position, targetPos, Time.deltaTime * Speed);
}
}
}

view raw

MoveSmoothly.cs

hosted with ❤ by GitHub

While the mouse pressed, the position of the game object moved to the mouse position(targetPos) with Speed. If the mouse is too fast the game object stays behind. But the game object always starts from where it is and shows a dynamic, smooth movement towards the mouse position. When the mouse is released object stops where it is.

Move to Click Position

Follows a similar approach with smooth movement, but this time it waits for mouse release to start moving. It will stop where the mouse was released using a threshold distance value.

4


using UnityEngine;
public class MoveSmoothlyToClick : MonoBehaviour
{
Vector2 initPos;
Vector2 MinPos;
Vector2 MaxPos;
Vector2 targetPos;
public float Speed = 2;
public float thresholdStopDistance = .3f;
void Start()
{
initPos = transform.position;
targetPos = initPos;
Vector2 Size = GetComponent<SpriteRenderer>().bounds.extents;
MinPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(0, 0)) + Size;
MaxPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(1, 1)) – Size;
}
void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = (Input.mousePosition);
targetPos = new Vector2(Camera.main.ScreenToWorldPoint(mousePos).x, Camera.main.ScreenToWorldPoint(mousePos).y);
targetPos.x = Mathf.Clamp(targetPos.x, MinPos.x, MaxPos.x);
targetPos.y = Mathf.Clamp(targetPos.y, MinPos.y, MaxPos.y);
transform.position = Vector2.Lerp(transform.position, targetPos, Time.deltaTime * Speed);
}
else if (!Input.GetMouseButton(0) && Vector2.Distance(targetPos, transform.position) > thresholdStopDistance)
{
transform.position = Vector2.Lerp(transform.position, targetPos, Speed * Time.deltaTime);
}
}
}

While the mouse is pressed this implementation shares a similar behavior with the previous one. But, when the mouse released it continues to move to the release point. It stops when the distance between the release point and the position of the object is smaller than a predefined distance(thresholdStopDistance).

Yoyo Between Initial and Mouse Position

It starts by moving with the mouse smoothly but when the mouse is released, the object returns to its initial point. The back movement also uses interpolation between the current position and initial position. Object stops when the distance between the current position and initial position is smaller than the threshold value.

5


using UnityEngine;
public class YoyoSmooth : MonoBehaviour
{
Vector2 initPos;
Vector2 MinPos;
Vector2 MaxPos;
public float Speed = 2;
public float thresholdStopDistance = .3f;
void Start()
{
initPos = transform.position;
Vector2 Size = GetComponent<SpriteRenderer>().bounds.extents;
MinPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(0, 0)) + Size;
MaxPos = (Vector2)Camera.main.ViewportToWorldPoint(new Vector2(1, 1)) – Size;
}
void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = (Input.mousePosition);
Vector2 targetPos = new Vector2(Camera.main.ScreenToWorldPoint(mousePos).x, Camera.main.ScreenToWorldPoint(mousePos).y);
targetPos.x = Mathf.Clamp(targetPos.x, MinPos.x, MaxPos.x);
targetPos.y = Mathf.Clamp(targetPos.y, MinPos.y, MaxPos.y);
transform.position = Vector2.Lerp(transform.position, targetPos, Time.deltaTime * Speed);
}
else if (!Input.GetMouseButton(0) && Vector2.Distance(initPos, transform.position) > thresholdStopDistance)
{
transform.position = Vector2.Lerp(transform.position, initPos, Speed * Time.deltaTime);
}
}
}

view raw

YoyoSmooth.cs

hosted with ❤ by GitHub

As for the last one, it is like a combination of the previous two. It follows the mouse smoothly and when released it returns to a predefined position. In this case, the predefined position is not the release position of the mouse. It is the initial position(initPos) of the game object stored at the start.

Before finishing the project I would like to emphasize that, you can use mouse input in place of single touch input in Unity(docs-Mouse Simulation). As it is suggested in the docs, it should be very helpful in the early development/experimentation stage. What is more, as these implementations are done in a 2D project it is possible to follow these approaches in a 3D project. If there is a need to move objects with the mouse using correct scene to world point transformations, this can be achieved too.

Links of  some of the methods used from Unity documentation:

Please do not hesitate to ask if a point is not clear and any feedback is welcomed!

Have fun tweaking!

 

 

 

4 thoughts on “Moving Sprites with Mouse Input

  1. Very useful post, thank you! I think for the example “Move to Click Position”, you meant to use GetMouseButtonUp

    Like

    1. Thank you! Happy you liked it.
      In that part, I need to check if the mouse button is not being pressed and if the game object did not reach the target point. If GetMouseButtonUp is used I would only get true for one frame, when the mouse is released and the game object would not move to the target position.
      In my opinion, GetMouseButtonUp could be used to set a state boolean, such as keepMovingTheTarget, and instead of checking !Input.GetMouseButton(0) its value would be checked, and when the game object reaches the target position its value should be set to false.

      Like

Leave a comment