Category Archives: Scripting

Input: Detect Mouseover

  mouseover Unity has events that tell your scripts when the mouse cursor hovers over an object and stops hovering over an object. This can be great for UI or for prompting the player that they they can interact with something.

OnMouseEnter()

The <span style="font-size: 12pt;">OnMouseEnter()</span> method will run when the mouse cursor first enters the object’s collider area (the object must have a collider for this to work). [code languge=”csharp”] void OnMouseEnter() { // do something, e.g. change the sprite or make a sound } [/code]

OnMouseExit()

The reverse method to <span style="font-size: 12pt;">OnMouseEnter()</span>, <span style="font-size: 12pt;">OnMouseExit()</span> is called when the mouse cursor exits the objects collider area. [code languge=”csharp”] void OnMouseExit() { // do something, e.g. change the sprite or make a sound } [/code]

Sample Script

This following script contains an example of what you can do with this functionality. Add this script to any object with the following:
  • A collider
  • A SpriteRenderer with a default sprite
  • Add a second sprite to the mouseOverSprite inspector field (the SpriteRenderer will show this sprite when the mouse cursor is over the GameObject).
[code language=”csharp”] using UnityEngine; using System.Collections; public class MouseOver : MonoBehaviour { [SerializeField] private Sprite mouseOverSprite; private Sprite defaultSprite; private SpriteRenderer sRenderer; void Start () { defaultSprite = gameObject.GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite; sRenderer = gameObject.GetComponent&amp;lt;SpriteRenderer&amp;gt;(); } void OnMouseEnter() { sRenderer.sprite = mouseOverSprite; } void OnMouseExit() { sRenderer.sprite = defaultSprite; } } [/code]

Expanding This

The Unity documentation has more details of this functionality (http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnMouseOver.html). You can get inspiration for how to use this from the games you play. For games with a lot of UI this technique can bring those menus to life. You can do all kinds of transform tricks with it, such as:
  • Adjust an object’s scale
  • Add a slight rotation
  • Make a subtle sound
  • Animate the object.

Stop Looking, You Already Found It!

Wherever Unity is discussed I see a mistake repeated by new Unity developers. It’s not a dumb or obvious mistake, but it can be a costly one. It’s the use of Unity’s ‘find’ functions to locate gameobjects and components inside update methods. Here’s the kind of thing I’m talking about: [code language=”csharp”] void Update() { var player = GameObject.Find("Player").GetComponent<PlayerScript>(); // do something with ‘player’ } [/code] That line of code is perfectly fine Unity code, but using it inside the Update() method is a big mistake…

Finding is slow

boxImagine your scene components (the player, the enemies, the scripts, etc.) are all in a big, disorganised box. When you use a finding method, like GameObject.FindGameObjectWithTag, Unity trawls through that box to find what you’re asking for. This is quite slow (for reasons I won’t go into). When a particular piece of code requires (relatively) lots of computing power we call it ‘expensive’, and it should be used carefully.

Repeating slow things is very slow

The Update() method in Unity games runs once per frame, so all code in Update() typically runs 30 to 60 times per second! The same applies to LateUpdate() and FixedUpdate(). Keep expensive code out of the update methods so it’s not repeated unnecessarily. If you need to make the player move in every frame, don’t do this: [code language=”csharp”] void Update() { var playerMove = GameObject.Find(“Player”).GetComponent<MoveScript>(); playerMove.Move(); } [/code] That code will work correctly, but it’s inefficient. What if you could just set the Player object aside somewhere Unity could grab it when needed instead of trawling through that big box? Well you can. Simply create a variable in your script, then assign it to point to the object when your scene starts, in the Start() method: [code language=”csharp”] MoveScript playerMove; void Start() { playerMove = GameObject.Find(“Player”).GetComponent<MoveScript>(); } void Update() { playerMove.Move(); } [/code] In the Start() method the expensive search is done (once). From that point on, a reference to the relevant script is easily accessible in the playerScript variable. You’re no longer searching for it during every frame. When I say GameObject.Find and its relatives are expensive, this is relatively speaking. You should not avoid using them entirely, and they won’t affect performance in most cases.

Caching References

You should also apply this to getting components of the current gameobject. Instead of calling this every time you need to access the rigidbody2d: [code language=”csharp”] var rbody = gameObject.GetComponent<RigidBody2D>(); [/code] Cache that variable in Start() and then use rbody to access the rigidbody2d.

Looping

You should also avoid costly searching in loops. Consider the following: [code language=”csharp”] for (int i=0; i < 200; I++) { var player = GameObject.Find("Player"); Player.DoSomething(); } [/code] and: [code language=”csharp”] var player = GameObject.Find("Player"); for (int i=0; i < 200; I++) { player.DoSomething(); } [/code] In the first example Unity searched through the big box of components 200 times, but in the second example the slow search is done once. Now imagine if you put that first loop in Update()! You’d be doing a costly component search 200 times every frame. That would be 6,000 calls (at 30 fps) every second (if your device can keep up).

Cleaner Code

A side benefit of caching references is that it helps keep your code clear. You assign a value to player once, and it can be accessed throughout your script. Your methods and functions don’t need to find objects for themselves, and your code is easier to read and maintain.  

Summary

Finding game components and objects using Unity’s built-in functions (‘finds’ and ‘gets’ like GameObject.Find and GetComponent) is slow. Using those functions is not in itself troublesome; in fact, they are invaluable tools. But you should never repeatedly search for the same items when you can search once and ‘remember’ the object by caching a reference (usually at the beginning of the scene in Start()), and then use the reference whenever you need to access the object.

Drag-and-Drop

Drag-and-drop functionality is useful for many types of games - an inventory system in an RPG, dragging letters onto a word game board, etc. With a simple script you can add drag-and-drop functionality that will work with both mouse and touch input. This is also a perfect problem with which to demonstrate my technique for simplifying complex functionality. drag-and-drop

My Technique

I like to break down a problem into its constituent parts, and tackle each separately. For drag-and-drop I figured I needed to know the following:
  • Is the player touching the screen? And where?
  • Is the player currently touching/dragging an object?
  • Has the player’s input changed since the last frame (has she started or ended a touch gesture?)
That’s all that is needed for drag-and-drop. Three simple problems that are each easily solved. Each of these steps will roughly correspond to a method in my final script.
 Sometimes I’ll use drawings or props to help visualise the small steps that make up a larger problem. For example, put a small object on your desk, then mimic dragging it around and take note of what you do, and what information is being generated by the action. Almost anything in a game is a simulation of a real-world action.

Competencies

I will assume you know Unity basics, but if anything here goes over your head, please refer back to some of my earlier posts (such as my Pong clone for beginners) to fill in your gaps. There is nothing particularly advanced in here, but I will by necessity use some Unity terminology along the way. As usual you can download my sample project or follow along, or some combination of the two. Drag and Drop Demo Unity Project

Create a Unity Project

Create a new 2D Unity project. You can add this script to an existing project, but only do that if you’re comfortable with it. It will be easier to follow if you’re inexperienced if you create a new project.

Create an Object to Drag

Let’s create a simple object with a sprite and a collider. You can use any image you want. Some public domain Kenney images are in the sample project if you want to use those. Note: I like to use Kenney art. Kenney makes great art and releases it into the public domain so that people making games (or tutorials) can make something that doesn’t look rubbish. Please consider buying a subscription to Kenney’s art (or simply download the tons of free assets) from his website: http://kenney.itch.io/kenney-donation Create a new GameObject in the scene and add the following components:
  • A SpriteRenderer with an image of your choosing. Make sure it’s big enough to drag around with your mouse or finger.
  • A Collider2D. Use either BoxCollider2D or CircleCollider2D.
Rename your object to match the image you used.
The Collider2D is used to detect when you are touching the object, and of course the SpriteRenderer gives the object its appearance.
Make sure to add the Collider2D after adding the sprite, that way the collider will automatically conform to the sprite’s shape. You should avoid PolygonCollider2Ds for this kind of use, as the accuracy is not required, and they are less efficient than the simpler circle and rectangle colliders.

The InputManager Script

I’ll go through the script piece-by-piece, and then give you the complete script at the end.

Methods

We only have two methods (plus the Update() MonoBehaviour method):
  • DragOrPickUp() - if an item is being dragged, move it with the input; if an object is not being dragged, pick up an object that’s being touched.
  • DropItem() - releases a picked up item
  • Update() - calls the above methods when required according to input and property values (below).

Properties and Variables

  • CurrentTouchPosition - returns the position of a detected touch/mouse input
  • HasInput - returns true when the player is currently touching the screen/holding the mouse button
We also need a few variables:
  • draggingItem - whether the player is currently dragging an item
  • draggedObject - holds a reference to an object being dragged
  • touchOffset - allows a grabbed object to stick realistically to the player’s touch position (more about this later).
I’ll go through each method separately, and show the full script afterwards, so feel free to jump to the full script or read the following sections to see how each part works.

Update()

The Update() method is quite simple: [code language=”csharp”] void Update () { if (HasInput) { DragOrPickUp(); } else { if (draggingItem) DropItem(); } } [/code] I like to keep methods like Update() free of too much logic, so, as in this example, the method becomes almost a plain-English sequence of events. In this case we just check if there is input from the player, and we either pick up or move the selected object (if there is input) or drop any currently picked up object (if there is no input). You may think that some of the small methods and properties in this script could easily be added to Update(), and you wouldn’t be wrong. However, I like to keep my code modular, which helps readability and debugging. It also allows me to add extra functionality quite easily later.

HasInput

I’ve implemented HasInput as a bool property. It checks if the player is currently interacting via mouse click or touch. Note: I’ve used Input.GetMouseButton(0). In Unity, touch input also counts as GetMouseButton(0), as Unity abstracts away input to some extent. To detect touch directly you can use: [code language=”csharp”] return Input.touchCount > 0; // returns true if at least one touch is detected on the screen [/code]   See the Unity documentation for more details about the various input methods. [code language=”csharp”] private bool HasInput { get { // returns true if either the mouse button is down or at least one touch is felt on the screen return Input.GetMouseButton(0); } } [/code]

CurrentTouchPosition

This property returns the current position of a touch. We get this position during dragging and picking up objects. The input position is attained via Camera.main.ScreenToWorldPoint using either the current input position. Basically, Camera.main.ScreenToWorldPoint lets us compare an input position with the position of items within our scene (the input is in screen space, but our scene objects are in world space co-ordinates).

DragOrPickUp()

This method is the most complex of the script. It is called when there is touch input detected, and it does one of the following:
  • If an object is already picked up, that object is moved according to the input’s current position
  • If an object is not already picked up, it checks if the current input it touching an object (using  a raycast), and if so picks up that object by storing it in the draggedObject variable and setting the draggingItem variable to true.
[code language=”csharp”] private void DragOrPickUp() { var inputPosition = CurrentTouchPosition; if (draggingItem) { draggedObject.transform.position = inputPosition + touchOffset; } else { RaycastHit2D[] touches = Physics2D.RaycastAll(inputPosition, inputPosition, 0.5f); if (touches.Length > 0) { var hit = touches[0]; if (hit.transform != null) { draggingItem = true; draggedObject = hit.transform.gameObject; touchOffset = (Vector2)hit.transform.position - inputPosition; draggedObject.transform.localScale = new Vector3(1.2f,1.2f,1.2f); } } } } [/code] After caching the current input position, we check if the player is already dragging an item (the draggingItem bool variable), and if so the dragged object’s position is changed to the input’s position (minus the offset - more on that in a moment). This means the dragged object effectively sticks to the player’s finger. However, if no item is being dragged we need to check if the current touch (this method is only called when we have input from the player) is hitting an object that can be dragged. We use RaycastHit2D to detect all colliders under the player’s finger. Because this is a simplified script we just take the first object that is ‘hit’ by the raycast (if there is one) and that object is picked up by being stored in the draggedObject variable, and we set the draggingItem bool to true to indicate that we are now in dragging mode. I’ve also added scaling to the dragged object to make objects a little larger when being dragged.

The Offsetdrag_offset_image

When picking up an object we also set an offset value that is used when moving it. This offset makes the object stick to where your finger made initial contact with it. When you move a transform you are telling Unity where the centre of the object goes (normally). When dragging an object, the item being dragged will snap so that its centre is where your finger is, which will feel jerky and unnatural. The offset makes it so the object moves relative to your finger (it basically remembers where the centre of the object should be relative to your finger based on where you first touched the finger). If you want to see what happens without the offset, make touchOffset equal Vector2.zero when the object is first picked up, then drag around an object by its edge.

DropItem()

The last method is used to ‘drop’ an object when the touch input stops. It’s pretty self explanatory, and simply sets the draggingItem bool to false, which means the rest of the script knows that an object is not being dragged, then sets the dragged object’s scale back to 1. The Full Script Create a new C# script called InputManager, and put the following code in it: Note: In Unity, a script’s name must match the class name, so make sure you call your script InputManager.cs or adjust the class name to match whatever name you give it. [code language=”csharp”] using UnityEngine; using System.Collections; public class InputManager : MonoBehaviour { private bool draggingItem = false; private GameObject draggedObject; private Vector2 touchOffset; void Update () { if (HasInput) { DragOrPickUp(); } else { if (draggingItem) DropItem(); } } Vector2 CurrentTouchPosition { get { Vector2 inputPos; inputPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); return inputPos; } } private void DragOrPickUp() { var inputPosition = CurrentTouchPosition; if (draggingItem) { draggedObject.transform.position = inputPosition + touchOffset; } else { RaycastHit2D[] touches = Physics2D.RaycastAll(inputPosition, inputPosition, 0.5f); if (touches.Length > 0) { var hit = touches[0]; if (hit.transform != null) { draggingItem = true; draggedObject = hit.transform.gameObject; touchOffset = (Vector2)hit.transform.position - inputPosition; draggedObject.transform.localScale = new Vector3(1.2f,1.2f,1.2f); } } } } private bool HasInput { get { // returns true if either the mouse button is down or at least one touch is felt on the screen return Input.GetMouseButton(0); } } void DropItem() { draggingItem = false; draggedObject.transform.localScale = new Vector3(1f,1f,1f); } } [/code]

Add the Script to the Scene

Create an empty GameObject in the scene and add the above script to it.

What Next?

There are lots of ways to improve this functionality. You should probably use tags or layers to determine which objects can be picked up. If you’re using physics you may need to disable colliders when an object is being moved. You might need to implement logic for when two items overlap and  you need to determine which one the player is trying to touch.