Day 5

Today we'll finish off our 2D games with some tile/block features (like in Minecraft or Terraria) and we will experiment with 3D games.


5.1: Block

To start, we will make blocks capable of:

  • Destroys when clicked
  • Snaps to a grid

To do this, try:

  1. Add a tile sprite to the level
  2. Scale the block such that it is 1x1 in size (fits exactly inside one square of the grid)
  3. Add a box collider 2D to the block
  4. Add a script to the block and add this code to it:
using UnityEngine;
using System.Collections;

public class ClickAndDisappear : MonoBehaviour {

    void OnMouseDown () {
        Destroy (this.gameObject);
    }

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

    void SnapToGrid() {
        // Read current values
        Vector3 pos = this.transform.position;
        
        // Calculate new ones
        float finalX = Mathf.Ceil (pos.x);
        float finalY = Mathf.Ceil (pos.y);
        
        // Place it in the world
        pos.x = finalX;
        pos.y = finalY;
        pos.z = 0;
        this.transform.position = pos;
    }
}

5.2: Block Inventory

To make a complete place-able block feature for our game, we will also need the ability to:

  • A counter for how many blocks the character is "Carrying"
  • The ability to increment that counter when the blocks disappear
  • The ability to clone new blocks into the game based on user input

To accomplish this, we are going to:

  1. Add a new script to the level, in a single place (such as on the robot boy or on the camera)
  2. Change the script on the block to increment the block inventory script when the tile is clicked
  3. Make a prefab out of the block with "ClickAndDisappear", then set the "Target Block" field for the inventory script in the editor to target that block

Change the block script to this:

using UnityEngine;
using System.Collections;

public class ClickAndDisappear : MonoBehaviour {

    void OnMouseDown () {
        Destroy (this.gameObject);
        InventoryForBlocks.AddBlock ();
    }

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

    void SnapToGrid() {
        // Read current values
        Vector3 pos = this.transform.position;
        
        // Calculate new ones
        float finalX = Mathf.Ceil (pos.x);
        float finalY = Mathf.Ceil (pos.y);
        
        // Place it in the world
        pos.x = finalX;
        pos.y = finalY;
        pos.z = 0;
        this.transform.position = pos;
    }
}

Set the inventory script to this:

using UnityEngine;
using System.Collections;

public class InventoryForBlocks : MonoBehaviour {

    public GameObject targetBlock;
    public static int blockCount = 0;
    bool placingBlock = false;
    
    public static void AddBlock() {
        blockCount = blockCount + 1;
    }
    
    public static bool HasBlock() {
        return blockCount > 0;
    }
    
    public static void RemoveBlock() {
        blockCount -= 1;
    }
    
    public void CloneAndPlaceBlock(float worldX, float worldY) {
        // Make a new block
        GameObject newBlock = Instantiate (targetBlock) as GameObject;
        Vector3 vec = new Vector3 ();
        vec.x = worldX;
        vec.y = worldY;
        vec.z = 0;
        newBlock.transform.position = vec;
        newBlock.SendMessage ("SnapToGrid");
    }
    
    void Update() {
        if (Input.GetKeyDown (KeyCode.E) && !placingBlock && InventoryForBlocks.HasBlock()) 
        {
            StartCoroutine(PlaceBlock());
        }
    }
    
    void Start() {
        blockCount = 0;
    }
    
    IEnumerator PlaceBlock() {
        placingBlock = true;
        
        InventoryForBlocks.RemoveBlock ();
        
        Vector3 pos = this.transform.position;
        pos.x += 0.8f;
        CloneAndPlaceBlock(pos.x, pos.y);
        
        yield return new WaitForSeconds(0.1F);
        placingBlock = false;
    }
    
    // Update is called once per frame
    void OnGUI () {
        
        // Make the rectangle the full size of the screen
        Rect nameBoxRect = new Rect (Screen.width - 100, 0, 100, 100);
        
        GUIStyle style = new GUIStyle();
        
        // Set text color to red
        style.normal.textColor = Color.white;
        
        // Set text size to very big
        style.fontSize = 24;
        
        // Set alignment to be in the middle and center of the box
        style.alignment = TextAnchor.MiddleCenter;
        
        // Draw a box with the level name in it
        GUI.Box(nameBoxRect, blockCount.ToString(), style);
    }
}

5.3: Death Timer

Some games have a defined time limit in them, that will force the player to complete the level in a fixed amount of time, or suffer restarting the level. To do this, we will need a script that:

  • Has a configurable duration of seconds for the player
  • Captures the current time when the level starts
  • Shows a GUI text on the screen of the time remaining (time limit minus time elapsed)
  • Kills the player when they run out of time

To accomplish this, you must:

  1. Create a new script on a single place in the level (on the camera or the character), setting it to the following:
using UnityEngine;
using System.Collections;
using System;

public class DeathTimer : MonoBehaviour {

    private System.DateTime startTime;
    public float secondsToFinish = 5;
    

    private int screnWidth = Screen.width;
    
    // Use this for initialization
    void Start () {
        startTime = DateTime.Now;
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    void ShowDeathMessage() {
        // Determine screen height and width
        int screenWidth = Screen.width;
        int screenHeight = Screen.height;
        
        // Make the rectangle the full size of the screen
        Rect nameBoxRect = new Rect (0, 0, screenWidth, screenHeight);
        
        GUIStyle style = new GUIStyle();
        
        // Set text color to red
        style.normal.textColor = Color.red;
        
        // Set text size to very big
        style.fontSize = 32;
        
        // Set alignment to be in the middle and center of the box
        style.alignment = TextAnchor.MiddleCenter;
        
        // Draw a box with the level name in it
        GUI.Box(nameBoxRect, "You Ran Out Of Time", style);
    }
    
    void OnGUI () {

        TimeSpan time = DateTime.Now - startTime;
        float seconds = secondsToFinish - (float)time.TotalSeconds;
        
        if (seconds < 0) {
            ShowDeathMessage ();

            if (seconds < -1) {
                Application.LoadLevel (Application.loadedLevel);
            }
        } else {
            int height = 20;
            int width = 200;
            // Setup the dimensions of a rectangle on screen
            int positionX = Screen.width / 2 - width / 2;
            int positionY = Screen.height - height - 10;
            
            Rect nameBoxRect = new Rect (positionX, positionY, width, height);
            
            // Draw a box with the level name in it
            GUI.Box(nameBoxRect, seconds.ToString());
        }
    }
}

You Did It!

You made it all the way through all five days of game camp. Now you can show off your awesome game to your friends. Keep building up your skills and I know you'll build something amazing.