“Tipsy Tower” prototype made with Unity and C# – Step 2: camera zoom, timer, score and responsive canvas elements

Read all posts about "" game
The tutorial series about Tipsy Tower continues with another update, this time using Unity (I told you I am taking it seriously). I added a lot of features to the first prototype, like the zoomable camera – which actually zooms, unlike the one I did with Phaser – a gradient background, a timer which only counts but does not stop the game at the moment, for a testing purpose, a score system and a responsive canvas background.
Click to drop a crate. Although most of the concepts have been explained in the completely commented source code, some features like the canvas background and texts would need a video or a series of images to be explained in depth. While I am thinking about the best way to show you how to create a game from scratch with Unity, have a look at the code, I commeted it line by line and you’ll surely learn something from it, especially if you read the first step before digging into this version. crateScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class crateScript : MonoBehaviour {

	// boolean variable to tell us if the crate already hit something
	public bool hit = false;

	// this function is executed when the game object become ivisible, in this case
	// when it leaves the stage
	void OnBecameInvisible(){

		// destroying game object
		Destroy (gameObject);
	}

	// this function is executed once the crate hits something
	void OnCollisionEnter2D(Collision2D coll) {
		hit = true;
	}
}
movingCrateScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class movingCrateScript : MonoBehaviour {

	// this function is executed at each frame
	void Update () {

		// since we can't just change the x position of a game object, we have to create a position which is the same
		// as the original game object position
		Vector3 newPosition = transform.position;

		// this is how we can set a tween, using PingPong on a variable (in this case the time),
		// so that it is never larger than length and never smaller than 0.
		// The returned value will move back and forth between 0 and 4.4f
		newPosition.x =  Mathf.PingPong (Time.time * 5.0f, 5f) - 2.5f;

		// and finally we apply new position to game object's position
		transform.position = newPosition;
	}
}
MainScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// you need to use this class to access UI elements
using UnityEngine.UI;

public class MainScript : MonoBehaviour {

	// crateObject is the physic crate prefab
	public GameObject crateObject;

	// a variable used only inside this class, for PRIVATE use. 
	// canDrop is true if the player can drop a crate, false when the player can't
	private bool canDrop = true;

	// Unity can zoom camera, so we will store into this variable the size of the orthographic projection, that is the level of zoom
	private float targetCameraOrtho = 5f;

	// this will be a time-based game, so here's a variable to keep track of the time left
	public int timeLeft = 60;

	void Start(){

		// Invoking the method called "tick" (first argument) in 1 (second argument) second, then repeatedly every 1 (third argument) second
		InvokeRepeating ("tick", 1, 1);

		// calling updateUIText, will write timeLeft value inside "TimeText" text
		updateUIText ("TimeText", timeLeft.ToString ());
	}

	// this function is executed at each frame
	void Update () {	

		// find an object called "movinCrate" and store it into movingCrate variable
		GameObject movingCrate = GameObject.Find ("movingCrate");

		// checking for player input AND canDrop to be true.
		// it means the player can drop a crate
		if (Input.GetButtonDown ("Fire1") && canDrop) {

			// set canDrop to false
			canDrop = false;

			// calling cangeAlpha function to set movingCrate's alpha to zero (completely transparent)
			changeAlpha (movingCrate, 0f);

			// instantiate a crateObject instance and assign it to physicsCrate variable
			GameObject physicsCrate = Instantiate(crateObject);

			// set physicsCrate position as the same of movingCrate position
			physicsCrate.transform.position = movingCrate.transform.position;
		}

		// declaring an array of game objects
		GameObject[] droppedCrates;

		// filling the array with all objects with tag "Physics Crate"
		droppedCrates = GameObject.FindGameObjectsWithTag("Physics Crate");

		// in maxHeight variable we will store the maximum crate height
		int maxHeight = 0;

		// score is calculated in real time
		int score = 0;

		// this boolean variable will tell us if all crates have landed or hit something
		bool allCratesLanded = true;

		// looping through droppedCrates array
		foreach (GameObject droppedCrate in droppedCrates){

			// looking for crateScript reference
			crateScript crateReference = droppedCrate.GetComponent<crateScript&gt;();

			// hit is now crateReference.hit
			bool hit = crateReference.hit;

			// if the crate already hit something...
			if(hit){

				// getting crate height
				int crateHeight = Mathf.RoundToInt ((droppedCrate.transform.position.y + 4.09f) / 0.77f);

				// update max crate height
				maxHeight = Mathf.Max(maxHeight, crateHeight);

				// if crate height is greater than zero... (when falling down the hill, it could be -1)
				if (crateHeight &gt; 0) {

					// ...update score
					score += crateHeight;
				}
			}
			else{

				// if the crate did not hit anything yet, then allCratesLanded is false
				allCratesLanded = false;
			}
		}

		// calling updateUIText, will write score value inside "ScoreText" text
		updateUIText ("ScoreText", score.ToString ());

		// if the player can't drop and all crates have landed...
		if(!canDrop &amp;&amp; allCratesLanded){		

			// setting canDrop to true again, now the player can drop another crate
			canDrop = true;

			// setting moving crate alpha to 1
			changeAlpha (movingCrate, 1f);

			// getting moving crate position
			Vector2 currentCratePos = movingCrate.transform.position;

			// updating current crate y position
			currentCratePos.y = maxHeight * 0.77f + 5 * 0.77f;

			// moving the crate by actually applying the transform position
			movingCrate.transform.position = currentCratePos;

			// change camera zoom according to moving crate vertical position
			targetCameraOrtho = 5 + (currentCratePos.y - 3.5f) / 2f;
		}

		// getting camera instance
		Camera camera = Camera.main;

		// if we need to update camera zoom...
		if (camera.orthographicSize != targetCameraOrtho) {

			// zooming in/out moving from current camera ortographic size to target camera orthographic size
			camera.orthographicSize = Mathf.Lerp (camera.orthographicSize, targetCameraOrtho, Time.deltaTime * 2);

			// once we zoom, we also need to update camera position to keep the ground at the bottom of the stage
			Vector3 position = camera.transform.position;

			// subtracting 5 (the initial orthographic size) to current ortographic size will do
			position.y = camera.orthographicSize - 5f;

			// updating camera position
			camera.transform.position = position;
		}
	}

	// this function changes the alpha of a game object
	void changeAlpha(GameObject sprite, float alpha){

		// since we can't just change the alpha, we have to create a color which is the same
		// as the original game object color
		Color color = sprite.GetComponent<Renderer&gt;().material.color;

		// then we change color's alpha
		color.a = alpha;

		// and finally we apply new color to game object's color
		sprite.GetComponent<Renderer&gt;().material.color = color;
	}

	// this function is called each second
	void tick(){

		// decreasing time left
		timeLeft--;

		// calling updateUIText, will write timeLeft value inside "TimeText" text
		updateUIText ("TimeText", timeLeft.ToString ());

		// if timeLeft is zero...
		if (timeLeft == 0) {

			// stop invoking the timer
			CancelInvoke ();
		}
	}

	// this function will update a UI text
	void updateUIText(string textElement, string textToWrite){

		// finding a game object called as textElement argument
		GameObject canvasElement = GameObject.Find (textElement);

		// getting the Text compment inside the game object
		Text scoreToDisplay = canvasElement.GetComponent<Text&gt; ();

		// updating text content
		scoreToDisplay.text = textToWrite;
	}
}
Next time I’ll show you the final game, with some more extra features, meanwhile download the complete unity project.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

214 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers