Category Archives: Tutorial

TMI! Stop Making Everything Public!

I see a lot of great tutorials that make a big mistake. They advocate making lots of public fields/variables in Unity scripts. Even some of the best tutorials do this, and it’s BAD! It’s no surprise then, that many beginners make all of their variables public.

Public fields have their place (or they wouldn’t be available), so using public isn’t always wrong. It is just that many Unity developers use public incorrectly, leading to poor programming habits that can make code hard to work with and debug.

tl;dr

Basically, unless your field needs to be accessed from other scripts, keep it private.

Much of the time in Unity tutorials, a field is made public so that its value can be seen and edited in the Inspector window. You don’t need to make a field public for this!

If you want your field to be accessible in the Unity Inspector, you can do the following:


// myField is private, but can be seen and edited in the Inspector

[SerializeField] float myField;

[SerializeField] makes the field appear in the Inspector, where you can view and change it. But it is still private.

You also have the option of using a ‘setter/getter’ if you want other scripts to be able to read a field’s value while only letting the field’s own script modify it:


// myField is publicly readable, but can only be modified in this script

public float myField { get; private set; }

 

What is Public in C#?

When a field (or a method for that matter) is public, it can be accessed from other scripts. Most fields and methods should be private (only accessible to its own parent script), as this makes code more robust, manageable, and modular. When fields and methods are being called from all over the place, it’s hard to keep track of what each script is doing; it can become a nightmare to debug.

Think about it…if a field is public and you decide to change how it behaves, you must also check that any other script accessing it is reworked to fit the change. If you find that your field value is being set incorrectly or a public method is being called at the wrong time, you will need to figure out exactly where the incorrect code is, which gets harder the more complex your project is.

Using public fields and methods can make things easier because you don’t need to think too hard about how to call methods and access variables. But don’t be lazy! It’s really not hard to avoid public stuff in your code, it just needs a bit of experience and practice.

The rule of thumb to always keep in mind is to restrict access to everything as much as possible.

If your code is well structured, public stuff isn’t going to be needed as much. If you find yourself needing lots of public fields or methods, it’s quite likely your code is poorly structured.

Keeping Privacy

Here’s what I do:

  • make every field and method private (this is easy, as it’s the default).
  • Use [SerializeField] by default for anything that needs to be in the Inspector.
  • Only change things to public when you need to access them from other scripts and after thinking about alternatives that don’t require public access (this becomes easier with experience).
  • Regarding the above point, consider a ‘public get, private set’ field if you only need to read the value from another script without modifying it.

Conclusion

Avoid public fields (variables) and methods/functions unless you have a real need for them to be public. Use [SerializeField] to enabled private fields in the Inspector without making them public.

If you want to learn more about the reasons for keeping code private and modular, I recommend picking up a book on C# programming (or even programming in general). It’s one of those topics that doesn’t seem too important to beginners (hey, making a field or method public doesn’t change the way it works, right?), but the more experience you gain, the more important it becomes to make your code higher in quality.

Move Player to Click/Touch Position

In this tutorial, we’ll make a player character move to a position on the screen where the mouse was clicked or a finger tapped, just like in real-time strategy games and many mobile games.

What We’ll Do

The steps required are:

  1. Create a simple player.
  2. Detect a touch/mouse click position on the screen.
  3. Write a script to move the player to the clicked/tapped position.

The skills you’ll learn in this tutorial are:

  • Detecting mouse/touch position and translating that to a point in the game world.
  • Making an object move towards a point on the screen smoothly.

Getting Started

  1. Create a new Unity project, and choose the 2D settings.

As with all my tutorials, if you’re stuck on the basics, or I mention something you’re not familiar with (or just need more detailed steps), please refer to my Pong tutorial, where everything you need to know about using Unity is shown in detail.

I haven’t included any sprites with this tutorial, so find any sprite/image you want to use for your player. I always recommend Kenney.nl, where you can get lots of free art to use in your projects. In my example, I’m using a rabbit sprite from Kenney’s Animal Pack.

1. Create a Player

The player is going to be simple:

  1. Add an empty GameObject to your scene.
  2. Rename the GameObject to ‘Player’ (select and press F2 to rename).
  3. Add a SpriteRenderer component to the Player.
  4. Add any sprite to your project and assign it to the Player’s SpriteRenderer.

Here’s the Game window as it should look with your player:

image

 

Note
If you can’t see the player (but have definitely added a sprite), make sure the player’s Transform position is [0,0,0], which is the centre of the screen.

That’s all we need for the player.

2. Detect Touch/Mouse Position

Detecting a touch or click position is pretty simple, but has two components. You first need to get the touch position, and then need to convert that to an actual game position so that it matches with your game content.

Create a new C# script called ‘MoveToClickInput’, and replace the entire code in that script with the following:

using UnityEngine;

public class MoveToClickInput : MonoBehaviour
{
    void Update ()
    {
        if(Input.GetMouseButtonDown(0))
        {
            Debug.Log("Mouse button clicked");
        }
    }
}
Input.mousePosition also works for touch input, so we don’t need to write separate code to get a touch. More complex uses of touch can use the specific Unity APIs for handling touch and multi-touch.

The only thing this script does (so far) is check for a mouse click, then print a message to the Unity console window if one is detected.

Input.GetMouseButtonDown checks if the mouse button (button 0 in this case, which is the left mouse button or a touchscreen touch) was pressed down during the current frame. Be careful with these, as it’s easy to get mixed up between GetMouseButtonDown and GetMouseButton (which checks if the mouse button is held down regardless of when it was first pressed down).
Attach that script to the Player object, then test your scene. Click the mouse anywhere in the Game window, and you will see that a message gets printed in the Console window showing you that the code is working.

If you want to try it with a touch device, go ahead and deploy the project and test. It will work the same as with a mouse.

Add a Target

Let’s add a simple target sprite so we can see where the mouse click was (and therefore where the player will move to).

  1. Add a new empty GameObject to the scene.
  2. Rename the new object to ‘Target’.
  3. Add a SpriteRenderer component to Target.
  4. Add a sprite to the Target’s SpriteRenderer component. I used a simple circle sprite and shaded it red and made it semi-transparent.
  5. Make sure the target is centred on [0,0,0] and is visible in the Game window.
If the target is hidden behind the player sprite, change the SpriteRendere’s Order In Layer property so the target has a higher value than the player (which will ensure it is drawn in front of the player sprite).

Here’s my red dot target drawn on top of the player:

image

Now, let’s give the input script a reference to the target, and move the target to the clicked position. Since this script is still very short, here is the entire script with the new code added in:


using UnityEngine;

public class MoveToClickInput : MonoBehaviour
{
    [SerializeField] Transform target;

    void Update ()
    {
        if(Input.GetMouseButtonDown(0))
        {
            Debug.Log("Mouse button clicked");
            target.position = Input.mousePosition;
        }
    }
}

What we’ve added is a Transform variable called ‘target’, and a line of code to move the target transform to the position of a detected click or tap.

To assign the actual Target object to the target variable, go into Unity:

  1. Select the Player GameObject.
  2. Drag-and-drop the Target GameObject into the Target field in the Move to Click script component.

Now run the scene (but beware it won’t work as expected!)…

So you click in the scene, but don’t see the target appear where you click? That’s to be expected because the mouse position reported by Unity is not relative to the game screen. If you look for the target in the Scene window, you’ll find it very far away from where you actually clicked!

Adjust the Mouse Click Position to Game Space

When you get Input.mousePosition, you are getting the position of the mouse in pixel co-ordinates, with [0,0] being the bottom left corner (this is called ‘screen space’). But when you are dealing with transforms in Unity, you are dealing with the ‘world space’, which is the Unity co-ordinate system with [0,0] in the centre, and has a different scale where a single unit represents dozens or hundreds of pixels.

So we need to translate the mouse position from screen space to world space to use it with transforms. Luckily, this is very easy:

var clickedPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);

The above code converts the mouse’s screen position to a game world position. The (Vector2) at the front forces the value to be Vector2 (rather than Vector3 default), which ensures the position is at [0] on the Z-axis, since we’re working in 2D.

Replace the script’s Update() method with the following:

void Update ()
{
    if(Input.GetMouseButtonDown(0))
    {
        var clickedPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);
        target.position = clickedPos;
    }
}

Now run the scene and the red dot (or whatever you used) should move to where you click the mouse.

3. Move the Player Towards the Click

We now have a player and can detect a mouse click in world space. The next thing we need to do is make the player move to the click. This is not as simple as what wo did for the target object because we want the player to ‘walk’ to the clicked spot.

Let’s go through the code piece-by-piece, and I’ll include the final script at the end.

Firstly, we want to keep track of where the player is heading, so we add a script-level variable that will always remember the player’s current target:

Vector2 targetPos;

We also need to give our player a speed so they move at the rate we want, so a speed variable is required:

float speed = 6f;

When the scene starts, the player doesn’t have a target to move towards, so we set the target position to be equal to the player’s current position in the Start() method:

private void Start()
{
    targetPos = transform.position;
}

We should also rejig the code that gets the mouse position and moves the target so it uses the same targetPos variable that the player will move towards, so that will be changed to the following:

if(Input.GetMouseButtonDown(0))
{
    targetPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);
    target.position = targetPos;
}

And after that code, still in the Update() method, we will do the real magic – move the player towards the targetPos:

if((Vector2)transform.position != targetPos)
{
    transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
}

The above code first checks if the player is already at its target position (if it is, there is no point moving it), then uses Vector2.MoveTowards to (you guessed it) move towards the targetPos from its current position. The third parameter of Vector2.MoveTowards is the movement amount, which is effectively the speed. We multiply our player speed by Time.deltaTime (the time since the last frame) to ensure that the movement amount is kept consistent with the framerate (the framerate is always going to fluctuate a little, so if the player moved the same amount every frame, it would change speed all the time and therefore move very roughly).

Here’s the completed code:

using UnityEngine;
public class MoveToClickInput : MonoBehaviour
{

[SerializeField] Transform target;
float speed = 6f;
Vector2 targetPos;

private void Start()
{
    targetPos = transform.position;
}

void Update ()
{
    if(Input.GetMouseButtonDown(0))
    {
        targetPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);
        target.position = targetPos;
    }
    if((Vector2)transform.position != targetPos)
    {
       transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
    }
}

Try out your scene. You should now have everything working!

rabbit

 

Next Steps

Here are some things you can try adding to improve this little project:

  • Animate the player.
  • Make the target sprite fade away after a short time.
  • Add a particle effect to the target sprite to better indicate a mouse click.
  • Adjust the code so that you can drag your finger around the screen and the player follows (hint: change Input.GetMouseButtonDown to Input.GetMouseButton).

UI MouseOver

In this quick tutorial, I’ll show you how to modify a UI object when the player moves the mouse over it.

As always, if anything in this tutorial is new to you, refer to my Pong tutorial where I explain all the major Unity functionality in detail.

In this tutorial we use Event Triggers, which were explained in a bit more detail in the Input Manager tutorial, so if you want more detailed steps, refer to that tutorial.

What We’ll Do

We will create a simple scene with a few UI buttons. We will create a script that changes the colour and size of a button when the mouse passes over it.

Getting Started

  1. Create a new Unity project.
  2. Add a UI Canvas GameObject to the Hierarchy (Unity will automatically add an EventSystem object as well)
  3. Add some buttons to the canvas, and make sure they don’t overlap.

image

Colour Change on MouseOver

Unity already gives buttons a mouseover colour change, though the default setting is quite subtle. Let’s start by making this colour change more obvious.

Select one of the buttons in the Hierarchy so that its components show in the Inspector pane:

image

The ‘Highlighted Color’ property is the shading applied to the button when the mouse is over the button (or when the button is currently the selected button with keyboard/gamepad). Click in the Highlighted Colour box and select a new colour. Pick something obvious:

image

Now play the scene and move the mouse cursor over the button you modified.

mouseover

Advanced MouseOver Using Event Trigger

The built-in mouseover behaviour is fine for simple use and prototyping, but in a game you usually want something more interesting. Using an Event Trigger, we can script anything to happen to the button when the mouse is over it.

Choose one of the other buttons in your scene this time so you can compare the difference with the button you already played with.

Create a Script

  1. Create a new C# script and call it ‘MouseOverEffect’.
  2. Open MouseOverEffect in your code editor of choice.

For now, we’ll just create empty methods in the script. We need these empty methods so we can assign them to the events in the Inspector. Once that is done we will add the actual code to the script.

Put the following code in the script:


using UnityEngine;

public class MouseOverEffect : MonoBehaviour
{
    public void PointerEnter()
    {   
        print(“MouseEntered”);
    }

    public void PointerExit()
    {
        print(“MouseExited”);
    }
}

Create the Events

Your button needs to know when to call the code in the script, so we use an Event Trigger component to set up the events we want to listen to.

  1. Add an Event Trigger component to your button.
  2. Click the Add New Event Type button and choose PointerEnter.
  3. Click the Add New Event Type button again and choose PointerExit.

Your component should look like this:

image

Now you need to tell the Event Trigger what code to call when these events occur.

  1. On the same button you added the Event Trigger component, add your MouseOverEffect script.
  2. Click the plus sign button at the bottom of the Pointer Enter area.
  3. Drag-and-drop the same button GameObject you are working with into the field on the left.
  4. Select MouseOverEffect > PointerEnter from the drop-down selector on the right.

image

You have now told the Event Trigger that when the mouse (pointer) enters this button, run the PointerEnter method on the MouseOverEventSctipt attached to the button’s GameObject.

Now repeat those steps for the PointerExit event, except this time choose the PointerExit method in the final step.

Troubleshooting Tip

If the MouseOverEffect methods don’t show up in the drop-down list, make sure the methods are public, and that you have saved the script.

Now run your scene and test that it is working. You should see text in the Console panel when the mouse cursor enters and exits the button area.

If that’s working correctly, you can move onto the next section where we will add something more interesting to the mouseover effect.

Scale the Button

The basic functionality is all there now. You are detecting when the mouse is over the button and running some code when it happens. Now all you need to do is put something more interesting into that code.

We are detecting two events – PointerEnter and PointerExit. This is so we can do something when the mouse moves over the button, then undo it when the mouse leaves the button.

Let’s scale the button.

Replace the two methods with the following:

 

public void PointerEnter()
{
    transform.localScale = new Vector2(1.2f, 1.2f);
}

public void PointerExit()
{
    transform.localScale = new Vector2(1f, 1f);
}

Now test the scene (make sure you save the script first). You should now have something like this:

scale

You can add any code here you like. You can also combine the built-in colour shading that Unity provides.

Since the button is quite generic, try replacing the button image, text, and font to create something that looks a bit more at home in a game.

Here is a button I’ve created:

play

Next Steps

There is no limit to what you can do to enhance this functionality and create interesting UI. Here are a few things you could try adding to your code:

  • Change the image/sprite
  • Play a sound effect
  • Rotate or animate the button

The scaling technique I’ve shown above is very basic. You will probably want to replace it with a smooth animation. Look into ‘tweening’ to find ways of smoothly animating the scale so it looks slick and professional.