Tutorial – Zelda style screen transitions in Unity

UPDATE: ALL THESE AND MORE CAN BE FOUND ON MY NEW SITE http://www.returnofthebrain.co.uk/

A few people have asked me how I’m handling screen transitions in my latest project, so I thought I’d go through how those work.

fnrprkt

update: Fixed a bug in the player movement script

Conceptually, this is pretty simple but there’s a bunch of ways you could implement it. You could lock the camera to a grid and move it in increments when the player steps outside of it, or you could use trigger boxes to start the animation, etc. I’ve chosen to use trigger boxes to keep track of the screen region we want to lock the camera to.

Scene Setup

First thing I’ve done here is create four sample ‘rooms’. You can create these however you like, but I’ve built them out of tiles, each room is 14 tiles wide by 10 tiles high.

kenneyrooms

We also need a player that can move around the screen in order to test that our screen regions work.

Create a new gameobject, and add a SpriteRenderer, then drag on your player sprite. For this test I’ve used a simple brown square. Then create a new script called Player and attach it.

    public class Player : MonoBehaviour {

	BoxCollider2D _col2d;
	Rigidbody2D _rb2d;

	float moveSpeed = 10f;

	void Awake () {
		gameObject.tag = "Player";
		_col2d = gameObject.AddComponent<BoxCollider2D>();
		_rb2d = gameObject.AddComponent<Rigidbody2D>();
		_rb2d.constraints = RigidbodyConstraints2D.FreezeRotation;
	}

	void Update () {
		Vector3 moveVector = Vector3.zero;

		moveVector.x += Input.GetAxisRaw("Horizontal");
		moveVector.y += Input.GetAxisRaw("Vertical");

		_rb2d.MovePosition(transform.position + moveVector * Time.deltaTime * moveSpeed);
	}
    }

This is pretty much the most basic movement controller you could ask for. First thing we do is set the objects tag attribute to ‘Player’, this will be referenced later on so that screen regions can ensure they are tracking the player and not some other collider.

Next we add a collider and a rigibody, ensuring that rotation is constrained and gravity is turned off for this collider. Then every update we just read the X and Y axis input, then move by that input times deltaTime times speed. Simple.

brownman

Camera Movement

Next up, we need to be able to smoothly move our camera to a boundary. Create another script which we’ll be attaching to our camera, called CameraAlign.

This script does a couple of things. Firstly, it keeps track of a Bounds object which will allow us to get all the information we need to move our camera. Secondly, it exposes a public method to set a new target bounds, which we’ll be calling from our trigger volumes.

Lastly is the actual movement code. For this example we lerp smoothly toward the bounds center over a period of one second. Vec3Lerp is a helper function that makes lerping over a fixed duration a little simpler.

    public class CameraAlign : MonoBehaviour {

	private Bounds currentBounds;

	private float alignDuration = 1f; 

	IEnumerator AlignToNewBounds(){

	    Vector3 startVect = transform.position;
	    Vector3 trackingVect = transform.position;

	    float targX = currentBounds.center.x;
	    float targY = currentBounds.center.y;
	    Vector3 targetPosition = new Vector3(targX, targY, transform.position.z);

	    float lerpTime = 0;
	    while(lerpTime < alignDuration){

	        lerpTime += Time.deltaTime;
	        trackingVect = Vec3Lerp(lerpTime, alignDuration, startVect, targetPosition);
	        transform.position = trackingVect;
	        yield return 0;
	    }

	    transform.position = targetPosition;
	}

	public void SetNewBounds(Bounds newBounds){

	    currentBounds = newBounds;
	    StartCoroutine(AlignToNewBounds());
	}

	public static Vector3 Vec3Lerp(float currentTime, float duration, Vector3 v3_start, Vector3 v3_target){
	    float step = (currentTime/duration);
	    Vector3 v3_ret = Vector3.Lerp(v3_start, v3_target, step);
	    return v3_ret;
	}
    }

Screen Regions

Now we can set up our actual screen region objects. Create an empty game object called “Region” and add a BoxCollider2D component.

This collider needs to match your desired screen region size. In this example, our screens are 14 units wide by 10 high. So our collider will need it size set to 14 by 10. You’ll also need to set it to be a trigger volume.

Next up, make sure the regions layer is set to Ignore Raycast. This isn’t strictly necessary for this tutorial, but if you make use of any raycasts in your game, this will stop screen regions from interfering with them.

The script that controls these regions is also quite simple at it’s core, all we really do is pass the collider bounds up to the camera when the player enters the trigger volume.

We also add a kinematic rigidbody to the object, because this is required in order to make the trigger volume work.

    public class ScreenRegion : MonoBehaviour {

	Rigidbody2D _rb2d;
	BoxCollider2D _collider;

	void Awake () {
	    _collider = gameObject.GetComponent<BoxCollider2D>();
	    _rb2d = gameObject.AddComponent<Rigidbody2D>();
	    _rb2d.isKinematic = true;
	}

	void SetNewCameraBounds () {
	    CameraAlign cam = Camera.main.gameObject.GetComponent<CameraAlign>();
	    cam.SetNewBounds(_collider.bounds);
	}

	void OnTriggerEnter2D(Collider2D other){

	    if(other.gameObject.tag == "Player"){
		SetNewCameraBounds();
            }
	}
    }

Attach this code to your screen region object, then place a duplicate of this object at each of your screen locations. You should be able to hit play and see them in action now! We’re basically done.

regionsworking.gif

There’s a couple of issues with this system, however. First, if you enter two trigger volumes at the same time (IE because there’s no walls stopping you and you move off a corner) it’ll glitch out.

How you deal with that will depend on the kind of game you’re making. You could change the cameras alignment code so that it’s a little more intelligent about where it moves, or you could place a second trigger at the edges of the screen which will disable the players input and then move him into position on the next screen, and then re-enable input once the camera has finished moving.

Secondly, setting these up manually is a bit of a pain. So if you use this in your game, you’ll most likely want to write a script which will create and place a grid of screen regions that matches your map and room size.

Advertisements
This entry was posted in Tutorials, Unity3D. Bookmark the permalink.

One Response to Tutorial – Zelda style screen transitions in Unity

  1. Pingback: Szumma #059 – 2016 39. hét | d/fuel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s