Build Minecraft in Unity Part 3. Clicking Functionality (a fast and inefficient approach)

Last time we created a textured cube using quads. In Part 3 we want to instantiate a new cube or destroy a cube when clicking the mouse buttons.

This is a series of tutorials oriented towards the research and building of a minimal Minecraft-like game while exploring different facets of the Unity game engine. As this is an introductory tutorial, and because of the need to simplify (for didactic purposes), the algorithms presented in Series 1 are not in any way optimal, nor the structures of the objects used in the game are efficient. Presenting a trivial, fast way to build a minimal Minecraft-like game offers an introductory experience to people just stating to learn Unity, and in the same time, it arises the imperative need for optimization and efficiency. Over the course of the following tutorial series we will cover different implementations with higher degree of complexity.

I recommend to you, The Reader, to state any issues in the comments below. I encourage you to debate anything and everything regarding the contents of this tutorial, for the benefit of the community. Your contribution will help make these tutorials better!

Before we start coding, let’s add a directional light to our scene. We need the light to better see the 3D world.

35_light

Let there be light!

If you want, you can change the default light orientation:

Position a light in the scene.

We can now proceed to create our first script. We want to instantiate a new cube over the side on which we right click and to destroy the cube on which we left click. In the Project tab, go to the Code folder, and create two C# scripts.

33_newscript

Adding a C# script to the project

We will name them WorldGenerator and ClickOnFaceScript.

34_scripts

Scripts we will use in the project.

Before we continue, it is of VITAL IMPORTANCE to take a look at these links:

Now, open WorldGenerator.cs in MonoDevelop (allready installed along with Unity) by double clicking on the script, while in the Project tab, and enter the following code:

/* Andrei Jifcovici
 * In2GPU.com
 */ 
using UnityEngine;
using System.Collections;

public class WorldGenerator : MonoBehaviour
{

    // Use this for initialization
    void Start ()
    {
    
    }

    // Update is called once per frame
    void Update ()
    {
    
    }
    
    public static void CloneAndPlace(Vector3 newPosition, GameObject originalGameobject)
    {
        // Clone
        GameObject clone = (GameObject)Instantiate(originalGameobject, newPosition, Quaternion.identity);
        // Place
        clone.transform.position = newPosition;
        // Rename
        clone.name = "Voxel@" + clone.transform.position;
    }
}

We will use the static method above whenever we create a clone of our original cube. It also allows us to set its position and to automatically generate a suitable and useful name for it. More information about Instantiate here.

In Minecraft, when you left click on a cube (if you are close enough to it), it will eventually disappear into your inventory. When you right click on any side of a cube (if you have enough cubes in your inventory), a new instance will appear close to the face on which you clicked. As we are generous gods, we won’t dabble with such limitations (for now at least). So, we’ll have infinite cubes and infinite clicking distance!

Open ClickOnFaceScript.cs, and enter the code below:

/* Andrei Jifcovici
 * In2GPU.com
 */ 
using UnityEngine;
using System.Collections;

public class ClickOnFaceScript : MonoBehaviour 
{

    // Use this for initialization
	void Start () 
	{
	
	}
	
	// Update is called once per frame
	void Update () 
	{

	}

    // This function is triggered when the mouse cursor is over the GameObject on which this script runs
	void OnMouseOver()
	{
        // If the left mouse button is pressed
		if (Input.GetMouseButtonDown(0))
		{
            // Display a message in the Console tab
			Debug.Log("Left click!");
		}
        
		// If the right mouse button is pressed
		if (Input.GetMouseButtonDown(1))
		{
            // Display a message in the Console tab
			Debug.Log("Right click!");
		}
	}
}

Now, drag and drop the script on each of the six faces of the cube from the scene.

Apply a script on a GameObject

When clicked, each of the six faces should have the script visible in the Inspector tab:

37_inspector_script

A script component

Give it a try! Press 38_play and test if the debug messages appear correctly when clicking the textured cube appearing in the Game tab.

39_test

Left click or right click on the cube to test the functionality

Remember! In playmode, any modification to any element in the Scene will be discarded! So remember not the change anything while the game is running. Press 38_play again to stop the game.

Now, we have the everything we need to create new cubes when clicking the right mouse button. We just need to specify the exact position of the new GameObjects, relative to the face on which we click. In our scene, the distance between the centers of two neighboring cubes is equal to 1.

For simplicity, we will name C, the center of the cube being clicked and N, the center of the newly instantiated object. We consider these centers as positions in 3d space.

  • If we click the top face: N = C + (0, 1, 0)
  • If we click the bottom face: N = C + (0, -1, 0)
  • If we click the right face: N = C + (1, 0, 0)
  • If we click the left face: N = C + (-1, 0, 0)
  • If we click the front face: N = C + (0, 0, 1)
  • If we click the back face: N = C + (0, 0, -1)

If it’s not clear why, run the application below to convince yourself (right click and press Go Fullscreen):

[unity src=”2970″]

We can generalize the above statements into:

N = C + delta,

where delta is the position displacement needed to compute the center of the new cube instance. Each of the six faces holds a different instance of ClickOnFaceScript, and each should contain a different value for delta.

We need to change ClickOnFaceScript.cs to accommodate the functionality presented above. Open the script and extend the code as follows:

/* Andrei Jifcovici
 * In2GPU.com
 */ 
using UnityEngine;
using System.Collections;

public class ClickOnFaceScript : MonoBehaviour 
{
    // Public fields are visible and their values can be changed dirrectly in the editor
	// represents the position displacement needed to compute the position of the new instance
	public Vector3 delta;

	// Use this for initialization
	void Start () 
	{
		
	}
	
	// Update is called once per frame
	void Update () 
	{
		
	}
	
	// This function is triggered when the mouse cursor is over the GameObject on which this script runs
	void OnMouseOver()
	{
		// If the left mouse button is pressed
		if (Input.GetMouseButtonDown(0))
		{
			// Display a message in the Console tab
			Debug.Log("Left click!");
			// Destroy the parent of the face we clicked
			Destroy(this.transform.parent.gameObject);
		}
		
		// If the right mouse button is pressed
		if (Input.GetMouseButtonDown(1))
		{
			// Display a message in the Console tab
			Debug.Log("Right click!");

			// Call method from WorldGenerator class
			WorldGenerator.CloneAndPlace(this.transform.parent.transform.position + delta, // N = C + delta
			                             this.transform.parent.gameObject); // The parent GameObject 
		}
	}
}

Go back to the editor and change the value of delta accordingly:

40_values

Values for delta, for each cube face, marked with rectangles (Click to enlarge)

Test to see if everything works. Position the camera several times (by changing the Transform position in the Inspector tab) and run the game to check if the above values are correct.

41_tempcam

Position the camera and click the sides of the cube

There you have it! In Part 4 we will procedurally populate a small world with cubes and will add a first person player.


Tagged under:
blog comments powered by Disqus