Build a 2D Top Down Game – Zero to Published – Part 1

Hello All,

Many of you know I have been waiting for the final stars to align so that I could write this article.  I am pleased to announce that all issues technical and legal have been resolved, and I have been able to complete my game “Skeleton Dude”.  The completed version of this game is published to the Windows 8 store and is available for download for FREE!  This link will take you to the final version.  So lets get started!

SplashScreen

Part 1:  Getting Started

Part 2: Animations

Part 3: Prefabs

Part 4: Enemies

Part 5: Modular Scripting

Part 6: Timed Prefab Instantiation

Part 7: Health Bars

Part 8: Finishing Touches

Part 9: Publishing

Download Unity for FREE!

http://unity3d.com/unity/download

One common misconception is that Unity costs money.  That is only true if you want the PRO version.  The free version is fantastic for pretty much everything an indie developer will want to do.

Create a new project

Open Unity, go to File -> New Project.

Import Assets

  1. Free version of Dungeon Kit.
  2. dungeonpack
  3. Unity VS if you are using it.
  4. Click here for all assets in a zip file if you are having issues.  Please note that I received email permission from Icarus Studio to distribute this pack in this manner.  Note that they have a paid for version that is way more awesome and is a great pack to download if you want to take this game to the next level.

Make sure you download the assets anyways, as it includes the splash screen as well as audio that I built using the Demo version of FL Studio.

Prepare workspace

Open your projects tab, right click on the assets folder and create new folder.

  1. Create a scripts folder
  2. Create Scenes folder
    1. Save Scene to Scenes Folder.
  3. Create Animations folder
  4. Prefabs folder
  5. Create an Audio Folder

Prepare Assets

Click on the skeleton image in your project tab and notice the inspector on the left:

spriteEditor

Change the Texture type to Sprite, select Sprite Mode: Multiple.  Click on Sprite Editor.  Change the way it is slicing from Automatic to grid.

  1. Slice Skeleton into 124×94 grid
  2. slice skeleton
  3. Slice Spider into 83×81 grid
  4. slicespider

The reason we are choosing to do a grid in this instance is because these images are part of a sprite sheet.  If the images were different sizes, when the animation were to play, it swaps out each sprite at a frame rate we define.  If the sprites are different sizes, it will cause flashing, inaccurate collisions and other problems.  Automatic is great for anything not being animated, otherwise try to use grid.

Create Spider Character

  1. From the top tab row: GameObject -> CreateEmpty
  2. Drag the first image of the spider sheet we created in the previous step onto the gameobject
  3. Rename gameobject to Spider
  4. Select the spider from the hierarchy tab, notice the inspector changes context to the spider.  Select “Add Component”, select Physics 2D -> Add a circle collider.  Adjust the size to be roughly the same size as your spider.
  5. Add a rigid body 2d (remove gravity) (in the same manner as step 4).
  6. Right Click in your Scripts folder in the project tab.  Add two new C# scripts
    1. CharacterScript
    2. CharacterInputScript
  7. Drag both scripts into the component area of your spider in the inspect

completed spider

Note that this image only has CharacterScript, it should also have CharacterInputScript.

 

Write Input Script

Require that the spider script be available and initialize the spider for control on start. Get Input, and make a call to SpiderScript to move the spider.

Double click the Character Input Script to open the script in Mono Develop or Visual Studio.

Touch Support

I have included touch support in the sample below.  This is just a simple sample of how you can implement touch support for a top down game.  For a more in depth touch support sample, please see the article located here.  Even if you didn’t plan on adding touch support to your game, with most devices these days especially the windows 8 platform being touch devices, it is really something that you should do to increase your market and as you can see below is not too hard.

using UnityEngine;
using System.Collections;

//require Character Script to be attached to this object.
[RequireComponent(typeof(CharacterScript))]
public class CharacterInput : MonoBehaviour {
    //private reference to the character script for making calls to the public api.
    private CharacterScript character;
	
    //reference to the camera
    private Camera mainCamera;

    private Vector2 heading;

    /// <summary>
    /// Use this function for initialization of just this component.
    /// </summary>
	private void Awake () 
    {
        //nothing special to initialize here.
        heading = Vector2.zero;
	}

    /// <summary>
    /// Use this function for initialization that depends on other components being created.
    /// </summary>
    private void Start()
    {
        //we require a built up version of the character script.
        this.character = this.GetComponent<CharacterScript>();

        this.mainCamera = Camera.main;
    }

    /// <summary>
    /// use this function to process updates as fast as the game can process them.
    /// </summary>
    void Update()
    {
        //check for touches
        if (Input.touchCount > 0)
        {
            //what was the position?
            Vector2 touchPosition = Input.GetTouch(0).position;
            Vector3 touchWorldPosition = this.mainCamera.ScreenToWorldPoint(new Vector3(touchPosition.x, touchPosition.y, 15));
            //where is our character currently?
            Vector3 characterPosition = character.gameObject.transform.position;
            //vector math says point to get to - current position = heading.
            this.heading = new Vector2(touchWorldPosition.x - characterPosition.x, touchWorldPosition.y - characterPosition.y);
            //make sure we don't surpass 1.
            this.heading.Normalize();
        }

    }

    /// <summary>
    /// use this function to process updates that should be synchronized 
    /// with the physics engine.  Good for continuous input functions for movement.
    /// </summary>
    void FixedUpdate()
    {
        //get the x factor of movement.
		float xMovement = Input.GetAxis("Horizontal");
        //get the y factor of movement.
        float yMovement = Input.GetAxis("Vertical");

        Vector2 movement = new Vector2(xMovement, yMovement);

        if (movement.magnitude > 0)
        {
            this.heading = movement.normalized;
        }

        //use our character script reference to make a call into its public api
        //to move the character by our input factor.
        character.Move(heading);
    }
}

Navigate to the input manager and add horizontal/vertical input

File->project settings ->Input

Remove everything except vertical and horizontal.  Make sure it is mapped to your arrow keys. left, right, up, down.

inputmanager

Write the move function that allows the spider to move!

double click the CharacterScript from your project tab to open it in mono develop or visual studio.

using Unity Engine;

using System.Collections;

using System;

public class CharacterScript : MonoBehaviour

{
	//max speed of character
	public float maxSpeed =5.0f;

	//cached version of our physics rigid body.
	private Rigidbody2D cachedRigidBody2D;

	void Awake()
	{
	}

	private void Start()
	{
		this.cachedRigidBody2D = this.GetComponent<Rigidbody2D>();
	}

	public void Move(Vector2 movement)
	{
		//move the rigid body, which is part of the physics system
		//This ensures smooth movement.
		this.cachedRigidBody2D.velocity = newVector2(movement.x * maxSpeed, movement.y * maxSpeed);
	}

}

 

Write a function that allows the camera to follow the spider (taken from sample assets)

Create a new c# script in the scripts folder, name it Camera follow and drop the script on the camera.  Also note, you can drop this same exact script on anything you want to follow the character, such as homing missiles.

using UnityEngine;

using System.Collections;

 

public class CameraFollow : MonoBehaviour 
{
	public Transform target;
	public float damping = 1;
	public float lookAheadFactor = 3;
	public float lookAheadReturnSpeed = 0.5f;
	public float lookAheadMoveThreshold = 0.1f;

	private float offsetZ;
	private Vector3 lastTargetPosition;
	private Vector3 currentVelocity;
	private Vector3 lookAheadPos;

	// Use this for initialization
	void Start()
	{
		lastTargetPosition = target.position;
		offsetZ = transform.position.z - target.position.z;
		transform.parent = null;
	}

	// Update is called once per frame
	void Update()
	{
		// only update lookahead pos if accelerating or changed direction
		float xMoveDelta = target.position.x - lastTargetPosition.x;
		bool updateLookAheadTarget = Mathf.Abs(xMoveDelta) > lookAheadMoveThreshold;
		if (updateLookAheadTarget)
		{
			lookAheadPos = lookAheadFactor *Vector3.right *Mathf.Sign(xMoveDelta);
		}
		else
		{
			lookAheadPos = Vector3.MoveTowards(lookAheadPos, Vector3.zero, Time.deltaTime * lookAheadReturnSpeed);
		}
		Vector3 aheadTargetPos = target.position + lookAheadPos +Vector3.forward * offsetZ;
		Vector3 newPos = Vector3.SmoothDamp(transform.position, aheadTargetPos, ref currentVelocity, damping);
		transform.position = newPos;
		lastTargetPosition = target.position;
	}
}

Add the spider as the target for the camera to follow. (I know this image says skeleton).

camerafollow

Summary

So we covered creating a new project, importing assets, creating sprite sheets, simple input, simple physics based movement, accessing components from script, modular scripting and initialization!  Keep an eye out for Part 2, which will be out early next week!

33 thoughts on “Build a 2D Top Down Game – Zero to Published – Part 1

  1. Wonderful article! This is the type of information that
    are supposed to be shared around the net. Shame on Google
    for not positioning this publish higher! Come on over and discuss with
    my website . Thanks =)

  2. Do you mind if I quote a few of your articles as long as I provide credit and sources back
    to your blog? My website is in the exact same niche as yours and my visitors would definitely benefit from
    some of the information you present here. Please let me know if this ok with you.
    Thanks!

  3. LOVE the code pages already if you stick to this the entire series. Will be sad, but awesome for the next person who has to copy and paste the code. Thank you very much, I loved the series, and going to go over it again.

  4. FYI: Your OneDrive is complaining about the assets. Not a big deal, except you included the extra music, and the HealthBars.

    “This item might not exist or is no longer available
    This item might have been deleted, expired, or you might not have permission to view it. Contact the owner of this item for more information. “

    • I modified the link and also included the completed version (plus some other updates I played with) minus nGUI and UnityVS.

  5. Pingback: Build a 2D Top Down Game - Zero to Published - Dave Crook's Dev Blog - Site Home - MSDN Blogs

  6. Pingback: Build a 2D Top Down Game - Zero to Published - Dave Crook's Dev Blog - Site Home - MSDN Blogs

  7. Assets/Scripts/CameraFollow.cs(22,47): error CS1525: Unexpected symbol `’ I get this error on the CameraFollow script it is not working

    • Hi Boom7Games,

      It looks as though some of the vector mathematics has changed and the – symbol I was using did not copy over to visual studio well. I have fixed the offending lines in the CameraFollow script and it should work now. Please let me know if it does not.

      Thanks,
      ~David

  8. I had a problem with your code which if you run and move spider to a direction, spidy will continue walking even if you pick your hand up from keyboard.
    problem was here: “if (movement.magnitude > 0)” you have to check this value with >= inequality.

    • Hi Rafe,

      You are correct. That is a known issue with the code and I ended up calling it a feature instead of going back and fixing it like I probably should have. If you go to the input for moving characters in 3d worlds with Rays, the touch input is very similar, but done much better and can be applied in this situation.

      I’ll go ahead and throw the fix on my backlog of things to do this week :).

      Thanks,
      ~David

  9. I had a problem with your code which if you run and move spider to a direction, spidy will continue walking even if you pick your hand up from keyboard.
    problem was here: “if (movement.magnitude > 0)” you have to check this value with >= inequality.

  10. here’s another update for your code.
    problem was that lookAhead is only working with X direction. solution is a little large but you may found it interesting, so take a look:
    // add this float variable and initialize like x direction.
    float yMoveDelta = target.position.y – lastTargetPosition.y;
    // continue next line just like x.
    bool updateLookAheadTargetY = Mathf.Abs(yMoveDelta) > lookAheadMoveThreshold;
    // change if else conditions to look like this
    if (updateLookAheadTargetX && !updateLookAheadTargetY) {
    lookAheadPos = lookAheadFactor * Vector3.right * Mathf.Sign(xMoveDelta); }
    else if (updateLookAheadTargetY && !updateLookAheadTargetX) {
    lookAheadPos = lookAheadFactor * Vector3.up * Mathf.Sign(yMoveDelta); }
    else if (updateLookAheadTargetY && updateLookAheadTargetX) {
    lookAheadPos = lookAheadFactor * (Vector3.right * Mathf.Sign(xMoveDelta) + Vector3.up * Mathf.Sign(yMoveDelta)); }

    // the rest of code has no need to change

      • I have double checked and understand that was nothing wrong with my code. problem came from bad initializing public variables. I think the best combination is: “damping = 0.5f; lookAheadFactor = 4.0f; lookAheadReturnSpeed = 6.0f; lookAheadMoveThreshold = 0.001f;”

        Rafe

  11. here’s another update for your code.
    problem was that lookAhead is only working with X direction. solution is a little large but you may found it interesting, so take a look:
    // add this float variable and initialize like x direction.
    float yMoveDelta = target.position.y – lastTargetPosition.y;
    // continue next line just like x.
    bool updateLookAheadTargetY = Mathf.Abs(yMoveDelta) > lookAheadMoveThreshold;
    // change if else conditions to look like this
    if (updateLookAheadTargetX && !updateLookAheadTargetY) {
    lookAheadPos = lookAheadFactor * Vector3.right * Mathf.Sign(xMoveDelta); }
    else if (updateLookAheadTargetY && !updateLookAheadTargetX) {
    lookAheadPos = lookAheadFactor * Vector3.up * Mathf.Sign(yMoveDelta); }
    else if (updateLookAheadTargetY && updateLookAheadTargetX) {
    lookAheadPos = lookAheadFactor * (Vector3.right * Mathf.Sign(xMoveDelta) + Vector3.up * Mathf.Sign(yMoveDelta)); }

    // the rest of code has no need to change

  12. Pingback: 10 Milestones to Building a First Video Game - Sarah Says - Site Home - MSDN Blogs

  13. Pingback: 10 Milestones to Building a First Video Game - Sarah Says - Site Home - MSDN Blogs

  14. Pingback: Build a 2D Top Down Game – Zero to Published – Part 9 | DaCrook

  15. Pingback: Build a 2D Top Down Game – Zero to Published – Part 8 | DaCrook

  16. Pingback: Build a 2D Top Down Game – Zero to Published – Part 2 | DaCrook

  17. Pingback: Build a 2D Top Down Game – Zero to Published – Part 4 | DaCrook

  18. Pingback: Build a 2D Top Down Game – Zero to Published – Part 7 | DaCrook

Leave a Reply

Your email address will not be published. Required fields are marked *