CardConquest GameDev Blog: Selecting Object with Mouse Click

Disclaimer: I am neither a professional game developer nor am I a professional programmer. I doubt I am following best practices. This is just what worked for me at the time. Hopefully it works for you if you’re playing along while reading!

Version of Unity used: 2019.4.13f1 Personal

If you don’t care about some introductory preamble about why I am making a game, skip to Getting Started with Unity

A few months ago I decided to get into Game Development a little bit. Initially I just wanted to create a version of the board game Stocks & Bonds that I could play online with my friends. I decided to also use it as an opportunity to learn Blazor and and SignalR which I had been curious to learn since a the interface for the Covenant C2 Framework uses both. In the end I made a game called Stalks Stalks Stalks that seems to work most of the time. You can play it at stalks.fatrodzianko.com, or check it out on GitHub. Zip archives for specific posts can be found here.

You may notice that there is a second game there, called CardConquest. After I finished Stalks Stalks Stalks, I got a bit of the game dev bug in me and wanted to make another game. For whatever reason, I decided to try and make a stripped down version of the way combat works in the Game of Thrones board game. The basic idea behind CardConquest is that two players are doing battle with one another. The battles are turned based. The first phase of the turn, players commit a certain number of units for the battle. After units are committed, players select a card to play during the battle. The selected cards increase your fighting power, and also can either kill enemy units or defend your units from being killed, depending on the battle’s outcome. After a battle, units that weren’t killed are in a cool down for one turn. The card the player selected is discarded. After a player has gone through enough battles to play all cards, the discard pile is returned to their hand. The strategy then is to balance how many units to commit to a single battle and the timing of when to play specific cards in your hand.

The game more or less works in Blazor and SignalR. Here’s what it looks like:

I was happy with it, but I decided that I wanted to make it into more of a real game. One where you have a map to interact with and units to move around a board, and potentially more than one opponent to play against (if I ever get that far). To do this, I decided to use the Unity game engine.

I wanted to also create this GameDev Blog to document my progress making this game in Unity to 1.) Reinforce what I was learning in Unity by making myself describe what I was doing, and 2.) Hopefully provide some useful information to any other would-be game makers out there on how I figured out how to do things.

I have NOT finished making this game. Not even close! So, there is a chance that this dev blog will end with a partially finished, non-functional game. Sorry!

Getting Started with Unity

Files for the unity project used in this post can be found on github. A zip file for this specific post can be found here.

Download and install Unity Hub from here. I am using version 2019.4.13f1 Personal for this post, but if you are reading this way off in the future, I imagine newer versions of Unity will more-or-less work the same. I’ve had to figure out a few things by reading / watching other game dev’s tutorials using versions of Unity that were several years old, and things are mostly the same. What does change, Unity will let you know by saying you’re using something that is deprecated, and a quick google search will show you how to do it in the newer, supported way.

Anyway, once you install Unity, start it up and create a new project. Click the “new” button, then select 2d, then enter the name of the project and where you want it saved on your file system.

It will most likely take a few minutes for Unity to create the new project. Step away and grab yourself a drink or something. When the project is created, you should see an empty Unity workspace like this:

The first thing you can do is look at the bottom of the Unity workspace where it says “Project.” Expand “Assets,” then select “Scenes.” To the right of that, you should see something called “SampleScene.” Rename it “Gameplay” or something similar.

Scenes” are kind of like different “areas” of your game. Examples of different scenes would be your Main Menu, Settings Menu, the opening Tutorial level, and then different “scenes” for levels 1, 2, 3, and so on. For this project right now, there’s basically going to be one main scene where the game is played, so I am going to name it “Gameplay.” Later I will create different scenes for menus and stuff of that sort.

Creating Something to Click On

One thing I want players to do in this game is to have units they can click on and move around a map. To do that, I will need to create some sort of object to click on. This is a 2D game, so I will create a new “sprite” that will be displayed to the user.

I don’t really know how to make “art,” so everything you see in this will have been made with MS Paint. It won’t look good, but it will look like something! You can either copy what I use, or make your own. Up to you, really.

To start, I made an infantry unit that looks like this:

To create this, I opened up Paint and re-sized the resolution of the image to be 30×30 pixels. I zoomed in 800% and used the pencil tool to draw each individual pixel. It was tedious, but it worked! It looks like a soldier or something, right?

I then resized the image so it was 300×300 pixels so that way it would scale a bit better when it was eventually imported into Unity. To get the sprite to work correctly in Unity, the background will need to be transparent. This doesn’t work too well in MS Paint, but I found this workaround to make the background transparent.

  1. Save the image as a .png
  2. Close the image, then reopen it
  3. Click on the arrow under “Select,” then click on “Transparent selection” so it is checked

4. Select all with CTRL+a, then copy and paste with CTRL+c and CTRL+v
5. Save and exit out of paint
6. Open the .png with Gimp
7. Select the “Select by color” tool in Gimp

8. Click on the white area in Gimp

9. Hit “Delete,” and you should see the white background be replaced with the checkboard background, meaning the background is transparent

10. File -> Export As. Navigate to your Unity project. Go into the “Assets” directory, and then create a new directory called “Sprites.” Export the new .png there

There was surely an easier way to do that all within one application (like, you can probably do it all in just Gimp), but that was how I did it. When the only image editor you’ve ever used is MS Paint you use MS Paint!

Back in Unity, you should see a new “Sprites” directory under assets, and your new infantry sprite should be there.

You can now add the sprite to the “game.” Before we do that, you should make a few changes to Unity. Near the top, click on the “Game” view. Then, make sure that the display resolution ration is 16:9. This isn’t really important now, but it will be later so let’s get it out of the way.

After that, click on “Scene” next to the “Game” view. This will allow you to interact with objects in your scene directly.

Then, drag and drop the sprite from the bottom window into the scene.

Voila! You now have an object you will be able to click on.

Capturing your Mouse Interactions

Right now, if you were to “play” your game and start clicking on the screen, nothing will happen. Unity currently has no way of knowing what to do when you click your mouse or press a button or whatever. You’ll have to do that using scripts.

The steps I am about to cover are almost entirely based on this Unity Tutorial – RTS Controls – Select Objects tutorial I watched on Youtube. The main difference is that tutorial uses a 3D project, and I had to make some slight adjustments to do it all in 2D.

Preparing the Sprite Object

To make the infantry sprite clickable, a few things need to be added to it. Select the sprite in the Unity scene. This should bring up some information on the “Inspector” window on the right hand side. You should see that the sprite has a “transform” and “Sprite Render.”

First, click on “Add Component,” then search for Box Collider 2D.

Right now, leave all the settings for the Box Collider as is. This will (maybe) be used later to detect when the sprite “collides” with other objects in game.

Next, click on “Add Component” again, and add a Rigidbody 2D. This will give your objects some “physics” that the Unity Engine will use. It is required for the object to be clicked on (I’m not 100% why but it is).

On the Rigidbody 2D, you will want to change the “Body Type” from “Dynamic” to “Kinematic.” The important thing this does is prevent your sprite object from falling off your screen due to gravity and have other physics effects acted on it.

Next, near the top of the Inspector window, click on the “Layer: Default” drop down, and select “Add Layer …”

Create a new “layer” called Units. I did this at “User Layer 10.” Where you add it isn’t super important right now. Just added it somewhere.

Select the infantry sprite again, click on the “Layer” drop down, and select the “Units” layer so that it is checked. This Units “layer” will be helpful later when determining if you clicked on a unit or other object.

The sprite object should now all be setup to be clickable. The final thing will be to save it as a “prefab.” Prefabs in Unity are a way of making reusable objects. To create a prefab, create a new directory under “Assets” called “Prefabs.” Then, click on your sprite object in the Scene Hierarchy on the left hand side. Drag and drop it into the project panel. You’ll know that you successfully created a prefab when the empty box next to the sprite object gets filled in with a blue color.

Capturing Mouse Interactions

In order for you to be able to click on the infantry sprite, you will need to create something that will detect when the mouse is clicked, and where on the screen you clicked it. On the left hand side in the Scene Hierarchy, right clicked and create an empty game object.

I renamed this empty object to “MouseClickManager,” but you can name it whatever you want. The purpose of this object will be for you to have something in Unity to attach a C# script to. You could alternatively just use the already created “Main Camera” object for this, but I preferred to create a new object specifically for my mouse script so I could quickly remember what object I was using for that script.

Now, under the Project window, create a new directory under “Assets” called “Scripts.”

In the new “Scripts” directory, right click and create a new C# Script.

I renamed the script “MouseClickManager.” You can now attach the script to the “MouseClickManager” object in the Scene Hierarchy by dragging and dropping, or by using the “Add Component” button on the object.

OR

You can open the script to be edited by double clicking on it in the project window. This will open the script in whatever program you have as the default to edit .cs files. I use Visual Studio, but you can use whatever you want. To configure Visual Studio to play well with the Unity framework, I would read Unity’s and Microsoft’s documentation on how to get that setup. If you’re having issues getting it working on the first time, I recommend exit out of Unity and Visual Studio and reloading everything. That seems to resolve whatever issues I am having with Unity and/or Visual Studio about 99% of the time.

The MouseClickManager Script

The first thing we’ll want to add to the script is a global variable that will be a reference to the “Units” layer that was assigned to the sprite object. That will look like this.

[SerializeField]
private LayerMask unitLayer;

[Serialized Field] is basically telling Unity to make this variable available in Unity’s Inspector window. Save the script, and then when you look back in Unity you should see the “unitLayer” variable in the Inspector window.

Right now, the “unit layer” is set to “Nothing” because the variable has been unassigned. Click on the drop down and select the “Units” layer.

Now the script will know what layer to detect clicks on for units.

Detecting Mouse Clicks

Back in the MouseClickManager script in Visual Studio, we are going to add some simple code to detect when the mouse is clicked. This will be done under the “Update()” function that should have been pre-populated when the script was created. “Update” is a function that Unity will run every frame of the game.

Update, then, is going to be used to detect whenever a user clicks their mouse. to detect the user clicking with the left mouse button, something like the following can be used:

void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Debug.Log("Left Mouse button clicked.");
        }
    }

Input.GetMouseButtonDown(0) will return “true” whenever the left mouse button is clicked down. If you want to detect right mouse button clicks, you would use the value “1” instead of “0.”

If we were to run the game now, it would output in Unity’s console every time the left mouse button is clicked. To play the game, click on the play button at the top. The console will be displayed at the bottom.

Unity is now detecting when the mouse is clicked, but it is not detecting what you are clicking on. to determine if you have clicked on the sprite object that was created, a “RaycastHit2D” will be used. You can think of a RaycastHit being unity firing a laser out in a direction you tell it to go, and Unity returning any objects that laser “hit” along its path.

The code will initially look like this:

if (Input.GetMouseButtonDown(0))
{
    Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    Vector2 mousePosition2d = new Vector2(mousePosition.x, mousePosition.y);
    RaycastHit2D rayHitUnit = Physics2D.Raycast(mousePosition2d, Vector2.zero, Mathf.Infinity, unitLayer);
}

First, the position of the where the mouse was clicked is capture in the mousePosition variable. The position of where you clicked is captured using the “ScreenToWorldPoint” relative to the camera. This returns a “Vector3”, or the x,y, and z coordinates of where the mouse was in 3D space.

Because this will be in 2d, the Vector3 returned by ScreenToWorldPoint will then be converted to a Vector2 (x and y coordinates) in mousePosition2d.

Finally, this is all used in a RaycastHit2D variable called “rayHitUnit.” This will use Unity’s Physics 2D “Raycast” to send out that laser and return anything it hits. We first provide the raycast the x and y coordinates from mousePosition2d. Then Vector2.zero is provided as the “direction” for the raycast to travel. Vector2.zero basically means go straight ahead from where we clicked in the 2d world. Then, Mathf.Infinity is provided as the “distance” for the raycast to travel, which is currently set to no distance limitation. Then, finally, our unitLayer variable is provided as the “layerMask.” This will limit the raycast to only detect when it hits on objects located in the “Units” layer that was created.

If the RaycastHit2D hits any objects int he Units layer, it will save the object it hit in the rayUnitHit variable. To detect if we hit anything, the following code can be used:

if (Input.GetMouseButtonDown(0))
{
    Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    Vector2 mousePosition2d = new Vector2(mousePosition.x, mousePosition.y);
    RaycastHit2D rayHitUnit = Physics2D.Raycast(mousePosition2d, Vector2.zero, Mathf.Infinity, unitLayer);

    if (rayHitUnit.collider != null)
    {
		Debug.Log("clicked on unit object.");
    }
    else
    {
		Debug.Log("did not click on unit object.");
    }
}

The RaycastHit2D should have certain properties such as a collider, rigidbody, transform, etc. if you clicked on an object. This is why a box collider and rigidbody 2d were added to the sprite object. So, to determine if you clicked on the sprite object, you can check if the RaycastHit2D variable rayHitUnit has a collider attached to it or not. If there is a collider, it will say in the console that you clicked on a unit object. If you clicked anywhere else on the screen, the console will say you clicked on nothing.

To test this, play the game and start clicking around. Watch the console output to see if it is detecting you clicking on the sprite. Here is a riveting video showing everything running.

Next Steps

That concludes this post of detecting mouse clicks on an object. My next post should be about triggering functions when the object is clicked, such as creating an outline around the object when the user clicks it. Hopefully that will be written soon!