How to create a Unity Rhythm Game Part 2: Generating the Steps

Part 1

In the last article we went over how to create a parser to dynamically convert a valid .sm file (with the exception of some edge cases, such as the song having multiple bpms). So we now have a fully populated data structure that looks something like this:

SM Data Structure Example

If that didn’t make much sense, then don’t worry too much about it! The previous article goes over the structure in a little more detail. Now let’s tackle actually using this data. The plan is to dynamically spawn the arrows at the correct time so that when they fall down the screen (at a speed determined by the user’s difficulty) they hit the detection zone in sync with the song.

This may sound like a difficult task, but we can break it down rather easily with a simple physics equation;

Speed = Distance / Time

We want to find the time in the song that we should spawn the arrow, we are given the speed through the difficulty the user has selected (The arrows/steps will move faster at a higher speed) and we pick a location off-screen as our fixed ‘spawn distance.’ So with this information in mind we can re-arrange the equation to

Time = Distance / Speed

 Now remember this equation because it becomes fundamental to the calculation later.

The first thing is to set up the actual spawning process. To do this we need to Initialize some of the variables we’ll need.

    public void InitSteps(Song_Parser.Metadata newSongData, 
                          Song_Parser.difficulties newDifficulty)
        songData = newSongData;
        isInit = true;

        //We estimate how many seconds a single 'bar' will be in the song
        //Using the bpm provided in the song data
        barTime = (60.0f / songData.bpm) * 4.0f;
        difficulty = newDifficulty;
        distance = originalDistance;

        //We then use the provided difficulty to determine how fast the arrows 
        //will be going
        switch (difficulty)
            case Song_Parser.difficulties.beginner:
                arrowSpeed = 0.007f;
                noteData = songData.beginner;
            case Song_Parser.difficulties.easy:
                arrowSpeed = 0.009f;
                noteData = songData.easy;
            case Song_Parser.difficulties.medium:
                arrowSpeed = 0.011f;
                noteData = songData.medium;
            case Song_Parser.difficulties.hard:
                arrowSpeed = 0.013f;
                noteData = songData.hard;
            case Song_Parser.difficulties.challenge:
                arrowSpeed = 0.016f;
                noteData = songData.challenge;
                goto case Song_Parser.difficulties.easy;

        //This variable is needed when we look at changing the speed of the song
        //with the variable BPM mechanic
        originalArrowSpeed = arrowSpeed;

 A lot of whats being done here seems fairly straight forward, we are initializing the variables we need and setting the speed of the arrows to match the chosen difficulty, with the exception of one line.

barTime = (60.0f / songData.bpm) * 4.0f;

 This line may seem a little weird, but what we’re doing is estimating how much time a bar will take in seconds given the song’s bpm. Bpm is recorded as ‘beats per minute’ meaning that if we had 120 bpm then we could represent that as

120 Beats = 60 seconds

So lets take that logic and make it a little more generic

BPM = 60 Seconds

From this we can calculate the time taken for a single ‘beat’ by dividing the BPM by 60

Time for a single note = BPM / 60

And we know that in most cases, a bar will be 4 notes, now this can differ, but in these cases the notes are often closer together, meaning that the total time taken for the bar would be the same, despite the additional notes in it. If a bar is 4 notes we can modify the above equation to

Time for a Bar = 4 * Time for a single note

Time for a Bar = 4 * (BPM / 60)

In case it wasn’t obvious yet, there’s gunna be a bit of Math involved when calculating these values, I’ll go through it the best I can when it crops up however.

So now that initialization is done, let’s look at the meat of the generation process.

    // Update is called once per frame
    void Update () 
        //If we're done initializing the rest of the world
        //And we havent gone through all the bars of the song yet
        if (isInit && barCount < noteData.bars.Count) 
            //We calculate the time offset using the s=d/t equation (t=d/s) 
            distance = originalDistance; 
            float timeOffset = distance / arrowSpeed;

            //Get the current time through the song 
            songTimer = heartAudio.time; 

            //If the current song time - the time Offset is greater than 
            //the time taken for all executed bars so far 
            //then it's time for us to spawn the next bar's notes 
            if (songTimer - timeOffset >= (barExecutedTime - barTime))

                barExecutedTime += barTime;

The above code is called through our update method, which is a function called once per frame by the Unity engine, we need to keep this in mind as it can affect how we structure our game logic.

We first check whether the game is still initializing or not, if it isn’t we check how many bars of notes we have ‘spawned’ so far, as long as it isn’t the end of the song, we go into the main body of the method.

We then calculate the intended time offset to spawn the arrows using the current arrow’s speed and the intended spawn distance, using the equation we showed before; t=d/s.

The last piece of information we need to keep track of is the current time progress of the song (ie, how far through the song we are in seconds). Which we get through the heartAudio variable, which contains the Unity AudioSource object we’re playing music from.

Now that we have all the info we need, we can decide whether to spawn the next bar of notes on this frame or not. In order to do that we check whether the current time of the song, minus the offset required to spawn the arrows on-time is larger than the time taken to go through all the executed bars so far, minus one ‘bar time.’ The logic behind this is a little funny, and it took some trial and error to get it working, so don’t be disheartened if it doesn’t make a lot of sense to you.

If this is true; we call a Unity-specific function called ‘StartCoroutine.’ StartCoroutine runs the function passed into it in a separate thread, allowing it to run parallel to the rest of the game. If we didnt have this then we’d need to wait until all the arrows were spawned before the update loop progressed, in some games this isn’t an issue, but in a rhythm game where timing is key and there’s constant attention by the user we want anything lengthy to be run parallel during the main game loop.

We pass in a function called PlaceBars and the value of barCount before incrementing it (adding 1 to it’s value), barCount allows us to know which bar in our data structure we need to place.

    IEnumerator PlaceBar(List<Song_Parser.Notes> bar)
        for (int i = 0; i &lt; bar.Count; i++)
            if (bar[i].left)
                GameObject obj = (GameObject)Instantiate(leftArrow, new Vector3(leftArrowBack.transform.position.x, leftArrowBack.transform.position.y + distance, leftArrowBack.transform.position.z - 0.3f), Quaternion.identity);
                obj.GetComponent<Arrow_Movement>().arrowBack = leftArrowBack;
            if (bar[i].down)
                GameObject obj = (GameObject)Instantiate(downArrow, new Vector3(downArrowBack.transform.position.x, downArrowBack.transform.position.y + distance, downArrowBack.transform.position.z - 0.3f), Quaternion.identity);
                obj.GetComponent<Arrow_Movement>().arrowBack = downArrowBack;
            if (bar[i].up)
                GameObject obj = (GameObject)Instantiate(upArrow, new Vector3(upArrowBack.transform.position.x, upArrowBack.transform.position.y + distance, upArrowBack.transform.position.z - 0.3f), Quaternion.identity);
                obj.GetComponent<Arrow_Movement>().arrowBack = upArrowBack;
            if (bar[i].right)
                GameObject obj = (GameObject)Instantiate(rightArrow, new Vector3(rightArrowBack.transform.position.x, rightArrowBack.transform.position.y + distance, rightArrowBack.transform.position.z - 0.3f), Quaternion.identity);
                obj.GetComponent<Arrow_Movement>().arrowBack = rightArrowBack;
            yield return new WaitForSeconds((barTime / bar.Count) - Time.deltaTime);

This PlaceBar method is called on a new thread, because we called this method through a Coroutine, it has to have an ‘IEnumerator’ return type. Because of this we can use Unity’s WaitForSeconds class to delay the thread for a certain period of time.

This method goes through all of the notes in that bar, and depending on which ‘step arrows’ are meant to be spawned, it creates an instance of that game object and initializes some stuff in their scripts.

Once spawning the correct note, the method and thread waits for some time with the line:

yield return new WaitForSeconds((barTime / bar.Count) - Time.deltaTime);

In a usual method, a return would end that method’s execution and return a value to whatever called it. With a method that returns IEnumerator however, we can use a ‘yield return’ to return multiple things. What yield return actually does is it returns a value, and then continues the execution of the method until it’s executed all the code in it and executes normally.

In this instance, we exploit this by returning a reference to the previously mentioned WaitForSeconds class. We get the method to wait for however long it takes to reach the time where the next note should appear. Otherwise, all the notes in the bar will be spawned at once, instead of separately when they should.

We use ‘barTime’ which contains the time taken for a single bar from our initial estimate, divide it by the number of notes in this bar (bar.Count) in order to find the time taken for each note in this bar. Finally, we subtract the current deltaTime. Delta Time is the difference in time between this frame and the last, putting this in prevents any delays caused by the instantiation process to prevent notes being spawned on time.

With the steps generated, we add to the total barExecutedTime seen previously, and get ready to spawn the next bar!

In you were after a starting point on making a Rhythm game then this will probably be enough for you, the next part of this writeup will be a quick one discussing how I allowed the user to generate a new BPM for a song by hitting the space bar to the rhythm wanted.

How to create a Unity Rhythm Game Part 1: Parsing the .SM file

Rhythm games are a genre I’ve only really been exposed to heavily this past year, thanks to one of my friends at my internship (Hi Ryan) being really into them. And I mean REALLY into them. Before this I thought the only rhythm games there really were was Dance Dance Revolution and Guitar Hero/Rockband, the ones where you push a button as an icon reaches a certain point on the screen. However my eyes have been opened to the sins of my ignorance, as it seems Japan has decided to expand on the formula, and has gone “pfffffft. Fuck that weak shit, let’s add a vinyl record thing on it, a literal bongo drum, and maybe some giant dials for good measure.” Like, Japan took rhythm games to the extreme, and so its very easy to see why people would be inspired to give it a go themselves.

This was my thought process when I entered my University’s 48hr Game Jam a few months back, I wanted to make a simple rhythm game, adhering more to the formula of DDR, where you just had to press a button in time with an arrow on screen. However, keeping in with Japan’s crazy takes on the genre, I wanted to add a twist of my own, in my game you would be able to change the BPM of the song in real-time, by tapping out a new bpm for it to follow. This would increase/decrease the speed of the song and therefore increase/decrease the score multiplier you would obtain.

With this long overdue article I’ll be walking you guys through the main parts of creating my game, starting with parsing the step files. For this project I decided to adapt the Stepmania files already provided. ‘Stepmania’ is a PC adaptation of the infamous DDR game, with all its ‘step’ info is all stored in a text file.

The game has a huge available fan-base, meaning that I’d have a lot of songs to play around with from the get-go, it also saved me from designing a file format to store all the data I wanted.

First let’s have a look into the Stepmania format, stored in a .sm file. The sm files are  structured with the header information, describing various bits of metadata for the song, including important information we’d need such as Song Name, Artist Name, BPM, and the file location of images the song would use in-game.

SM Header ExampleAfter that comes the step data for each difficulty of the song. The steps for the song are separated into ‘bars’ of the song (I’d explain how bars in music work, but a lot of videos online explain it far better than I ever could!), with 4 number columns corresponding to the position of the ‘step’ and different number values corresponding to different note types.

The step information for the 'Butterfly' song

In the above image you can see for example ‘0010’ which would be nothing in the first, second or fourth column, and an arrow in the 3rd column. In the game this would look something like this:

Heartbeat Example

So now that we’re on the same page, let’s talk abouit how we’re going to store all this data in our game. I went about creating a set of structures to house the various bits of information, including the song metadata, and the steps themselves;

    //This structure contains all the information for this track
    public struct Metadata
        //Is the song's structure valid?
        public bool valid;

        //The Title, Subtitle and Artist for the song
        public string title;
        public string subtitle;
        public string artist;

        //The file paths for the related images and song media
        public string bannerPath;
        public string backgroundPath;
        public string musicPath;

        //The offset that the song starts at compared to the step info
        public float offset;

        //The start and length of the sample that is played when selecting a song
        public float sampleStart;
        public float sampleLength;
        //The bpm the song is played at
        public float bpm;

        //The note data for each difficulty, 
        //as well as a boolean to check that data for that difficulty exists
        public NoteData beginner;
        public bool beginnerExists;
        public NoteData easy;
        public bool easyExists;
        public NoteData medium;
        public bool mediumExists;
        public NoteData hard;
        public bool hardExists;
        public NoteData challenge;
        public bool challengeExists;

    //This structure contains all the bars for a song at a single difficulty
    public struct NoteData
        public List<List<NoteData>> bars;

    //This structure contains note information for a single 'row' of notes
    //Right now it's just a simple "Is there a note there or not"
    //But this could be modified and expanded to support numerous note types
    public struct Notes
        public bool left;
        public bool right;
        public bool up;
        public bool down;

I explain in the code comments what each part of the structure contains, but the concept was that each song would have a Metadata instance, which would contain all the song’s information, and all the step info for each difficulty (beginner, easy, medium, hard, challenge). The NoteData structure has a list of ‘Bars’ with each ‘Bar’ being represented by a list of Note-rows (‘0010’ in the text file would be a single instance of the ‘Notes’ Structure), this is what contains the actual ‘steps’ of the song, the stuff the user will interact with.

The parsing of this file will come in multiple parts, but the first thing to be concerned with is actually extracting the information from the file in a format we can interact with, this is a fairly straight-forward solution, but I’ll include it for the sake of clarity.

        //Check if the file path is empty
        if (IsNullOrWhiteSpace(filePath))
            //If so, Error and return invalid data
            Metadata tempMeta = new Metadata();
            tempMeta.valid = false;
            return tempMeta;

        //Create a boolean variable that we'll use to check whether
        //we're currently parsing the notes or other metadata
        bool inNotes = false;

        Metadata songData = new Metadata();
        //Initialise Metadata
        //If it encounters any major errors during parsing, this is set to false and the song cannot be selected
        songData.valid = true;
        songData.beginnerExists = false;
        songData.easyExists = false;
        songData.mediumExists = false;
        songData.hardExists = false;
        songData.challengeExists = false;

        //Collect the raw data from the sm file all at once
        List fileData = File.ReadAllLines(filePath).ToList();

        //Get the file directory, and make sure it ends with either forward or backslash
        string fileDir = Path.GetDirectoryName(filePath);
        if (!fileDir.EndsWith("\\") &amp;&amp; !fileDir.EndsWith("/"))
            fileDir += "\\";

Above we kindof lay down the framework for our parser, we get the file path, check it’s valid and initialize our Metadata structure. With the groundwork in place we can start parsing in the generic metadata, this will be used to display the song to the player correctly, taking advantage of the provided information to present the song in a more user friendly environment.

The next couple code segments are all within the containing for loop below, but will be separated for ease of reading.

        //Go through the file data
        for (int i = 0; i &lt; fileData.Count; i++)
            //Parse the data from the document
            string line = fileData[i].Trim();

            if (line.StartsWith("//"))
                //It's a comment, ignore it and go to the next line
            else if (line.StartsWith("#"))
                //the # symbol denotes generic metadata for the song
                string key = line.Substring(0, line.IndexOf(':')).Trim('#').Trim(':');

                switch (key.ToUpper())
                    case "TITLE":
                        songData.title = line.Substring(line.IndexOf(':')).Trim(':').Trim(';');
                    case "SUBTITLE":
                        songData.subtitle = line.Substring(line.IndexOf(':')).Trim(':').Trim(';');
                    case "ARTIST":
                        songData.artist = line.Substring(line.IndexOf(':')).Trim(':').Trim(';');
                    case "BANNER":
                        songData.bannerPath = fileDir + line.Substring(line.IndexOf(':')).Trim(':').Trim(';');
                    case "BACKGROUND":
                        songData.backgroundPath = fileDir + line.Substring(line.IndexOf(':')).Trim(':').Trim(';');
                    case "MUSIC":
                        songData.musicPath = fileDir + line.Substring(line.IndexOf(':')).Trim(':').Trim(';');
                        if (IsNullOrWhiteSpace(songData.musicPath) || !File.Exists(songData.musicPath))
                            //No music file found!
                            songData.musicPath = null;
                            songData.valid = false;
                    case "OFFSET":
                        if (!float.TryParse(line.Substring(line.IndexOf(':')).Trim(':').Trim(';'), out songData.offset))
                            //Error Parsing
                            songData.offset = 0.0f;
                    case "SAMPLESTART":
                        if (!float.TryParse(line.Substring(line.IndexOf(':')).Trim(':').Trim(';'), out songData.sampleStart))
                            //Error Parsing
                            songData.sampleStart = 0.0f;
                    case "SAMPLELENGTH":
                        if (!float.TryParse(line.Substring(line.IndexOf(':')).Trim(':').Trim(';'), out songData.sampleLength))
                            //Error Parsing
                            songData.sampleLength = sampleLengthDefault;
                    case "DISPLAYBPM":
                        if (!float.TryParse(line.Substring(line.IndexOf(':')).Trim(':').Trim(';'), out songData.bpm) || songData.bpm &lt;= 0)
                            //Error Parsing - BPM not valid
                            songData.valid = false;
                            songData.bpm = 0.0f;
                    case "NOTES":
                        inNotes = true;

With the above code, we go through each line of the file, and begin to parse it, as seen in the screenshots at the top of the page, the song’s information is specified with a ‘#’ followed by a key term, we can use this to determine what information each line contains. Once we hit a ‘NOTES’ section we know the next info we find is note data. We flag that this is the case, allowing the next bit of code to be called:

            //If we're now parsing step data
            if (inNotes)
                //We skip some feature we're not implementing for now
                if (line.ToLower().Contains("dance-double"))
                    //And update the for loop we're in to adequately skip this section
                    for(int j = i; j &lt; fileData.Count; j++)
                        if (fileData[j].Contains(";"))
                            i = j - 1;

                //Check if it's a difficulty
                if (line.ToLower().Contains("beginner") ||
                    line.ToLower().Contains("easy") ||
                    line.ToLower().Contains("medium") ||
                    line.ToLower().Contains("hard") ||
                    //And if it does have a difficulty declaration
                    //Then we're at the start of a 'step chart' 
                    string difficulty = line.Trim().Trim(':');

                    //We update the parsing for loop to after the current step chart, and also record the note data along the way
                    //We can then analyse the note data and parse it further
                    List noteChart = new List();
                    for (int j = i; j &lt; fileData.Count; j++)
                        string noteLine = fileData[j].Trim();
                        if (noteLine.EndsWith(";"))
                            i = j - 1;

                    //Here we determine what difficulty we're in, and begin parsing the accompanied note data
                    switch (difficulty.ToLower().Trim())
                        case "beginner":
                            songData.beginnerExists = true;
                            songData.beginner = ParseNotes(noteChart);
                        case "easy":
                            songData.easyExists = true;
                            songData.easy = ParseNotes(noteChart);
                        case "medium":
                            songData.mediumExists = true;
                            songData.medium = ParseNotes(noteChart);
                        case "hard":
                            songData.hardExists = true;
                            songData.hard = ParseNotes(noteChart);
                        case "challenge":
                            songData.challengeExists = true;
                            songData.challenge = ParseNotes(noteChart);
                if (line.EndsWith(";"))
                    inNotes = false;

We’re not quite at parsing the step chart yet, we first need to grab some accompanied data, namely the song’s difficulty, we then call another method called ‘ParseNotes’ and pass in the raw note data, in here it will be translated into the NoteData structure and assigned to the appropriate variable.

    private NoteData ParseNotes(List notes)
        //We first instantiate our structures
        NoteData noteData = new NoteData();
        noteData.bars = new List&lt;List&gt;();

        //And then work through each line of the raw note data
        List bar = new List();
        for(int i = 0; i &lt; notes.Count; i++)
            //Based on different line properties we can determine what data that
            //line contains, such as a semicolon dictating the end of the note data
            //or a comma indicating the end of that bar
            string line = notes[i].Trim();

            if (line.StartsWith(";"))

            if (line.EndsWith(","))
                bar = new List();
            else if (line.EndsWith(":"))
            else if (line.Length &gt;= 4)
                //When we have a single 'note row' such as '0010' or '0110'
                //We check which columns will contain 'steps' and mark the appropriate flags
                Notes note = new Notes();
                note.left = false;
                note.down = false;
                note.up = false;
                note.right = false;

                if (line[0] != '0')
                    note.left = true;
                if (line[1] != '0')
                    note.down = true;
                if (line[2] != '0')
                    note.up = true;
                if (line[3] != '0')
                    note.right = true;

                //We then add this information to our current bar and continue until end

        return noteData;

And with the above code implemented, we now have our full parsing, allowing us to correctly convert the text data into our structure representation of the song’s data. And that’s the parsing done! The next step is to use this structure we have to spawn the arrows/steps in the game for the player to interact with.

There were a few ways to tackle this, one of them being to spawn all the ‘arrow’ instances at once, and have them all just scroll down throughout the song’s run time, however I felt this was a bit resource heavy, and wanted to opt for a bit of a more complex, but efficient route: Spawning the arrows in real-time to have them hit their targets at the exact moment in the song you want them to.

Easy, right?

Part 2

Room-Based Camera Systems & Implementation

Disclaimer: It has been brought to my attention by “doomedbunnies” on reddit that ‘free camera’ may not be the correct term that I’ve used, s/he noted that free cameras often refer to cameras with ‘no clip’ that are just directly controlled by a user. They proposed that the free-following cameras I discuss be referred to as a ‘2D Follow Camera’ or ‘2D Platformer Camera,’ I won;t be editing every reference to free cameras in this article, but it is something to keep in mind should the term confuse you or should you try to reference this article.

So in my previous post I briefly went over my new design for Project GhostLight, and since then I’ve been working on bits and pieces in the evenings and the weekends, and gotten the game to the point where nearly all the required mechanics are in place. One of the things I mentioned in my previous post was a room-based camera system.

And so in this article I want to go through that a little bit, explain what effect I was hoping to achieve, and show my thought process and the final result. I imagine this article will have a fair amount of code, though again I will be going through the code and hopefully explaining it to the point where even those who are unfamiliar with C#, Unity, or even programming in general will be able to get the gist of it.

Okay let’s start! As I may have mentioned, the camera system I wanted to achieve was heavily inspired by the camera’s in Mega man and Shovel Knight, I have already discussed briefly why I wanted a system like this, but just to re-iterate; A free camera system made it difficult for me to plan what the player could see at any given time, and so when it came to planning and designing levels I kept finding myself thinking “If only this was just a contained room with a fixed camera.”

But what is a room-based system? What is a free camera system? What’s the difference? Let me give a little bit of context for those who haven’t played the games already mentioned in this article. So a free camera system is the more common type of camera implementation, where the camera follows the player and moves freely on it’s own. This can be seen in loads of games, from Super Mario Bros. to Metal Gear Solid V.

A free camera system has a single camera that moves to keep the player in frameA free camera system has a single camera that moves to keep the player in frame

A room-based camera, on the other hand, is a style of camera usually reserved for 2D games, the concept is that the camera is locked to the constraints of a single ‘room’ or area, and when the player moves into another ‘room’ then the camera performs a transition animation, and the game continues from there.

Room-based CameraA room-based camera system snaps the camera to the room the player is in

And so based off of this desire, I thought of a few requirements for my system:

  1. Multiple rooms that the camera automatically snaps between when the player crosses that room’s boundaries, either vertically or horizontally.
  2. A small transition animation to give the player a chance to see the contents of the room.
  3. The potential for long panning rooms, either vertically or horizontally.

These simple requirements would be the basis of my camera implementation, which I’ll walk through below in pseudo-code and real-code, there are lots of ways to do this, but this was my choice, and I’ll try and validate it as I go.

My first task was to create a room boundary that the player could cross to trigger the transition and so the camera knows where to stick to, I did this by creating an empty object in Unity, and giving it a 2D Box Collider, this would act as a trigger and would be the size of the room. When the player intersects with a Trigger, unity automatically calls some methods, including “OnTriggerEnter2D,” from there we can trigger the transitions.

Let’s start with the OnTriggerEnter2D Method, the script is attached to the player;

void OnTriggerEnter2D(Collider2D collider)
    if (collider.tag == "Room" &amp;&amp; newRoom.go != collider.gameObject &amp;&amp; !isTransitioning)    //If we're triggering a room, the room isn't the one we're in or transitioning to, and we aren't current transitioning
        //Hold the player's velocity before the transition starts
        tempVelocity = player.GetComponent().velocity;

        //Disable the player from moving

        //Get the new room
        newRoom.go = collider.gameObject;   //Grab the object instance for the room = new Vector2(Mathf.Round((collider.transform.position.x - (collider.bounds.size.x / 2)) * 2) / 2,
                                    Mathf.Round((collider.transform.position.y - (collider.bounds.size.y / 2)) * 2) / 2);  //Get the bottom left of the room, using the center of the room as the origin = new Vector2(Mathf.Round((collider.transform.position.x + (collider.bounds.size.x / 2)) * 2) / 2,
                                    Mathf.Round((collider.transform.position.y + (collider.bounds.size.y / 2)) * 2) / 2);  //Get the top right of the room, using the center of the room as the origin

        //Create a local copy of the player's position at the start of the transition
        Vector3 playerPos = player.transform.position;

        //Find whether the transition will be horizontal or vertical
        if ( &gt;= //If the new room's bottom left is further right than our current top right
            currentTransitionDir = transitionDirection.right; //Then we must be transitioning right
        else if ( &lt;= //Otherwise if the new room's top right is further left than our current bottom left { currentTransitionDir = transitionDirection.left; //Transitioning Left } else if ( &gt;= //If the new room's bottom left is further up (higher y value) than the current room's top right
            currentTransitionDir = transitionDirection.up; //Transitioning Up
        else if ( &lt;= //If the new room's top right is lower than the current rooms bottom left
            currentTransitionDir = transitionDirection.down; //Transitioning Down
            currentTransitionDir = transitionDirection.none; //Placeholder for if something goes wrong

        targetPos = ClampCamera(newRoom, cam.transform.position); //Clamp the camera's position to the bounds of the new room
        isTransitioning = true; //Set it so we are transitioning

And so with the initial trigger completed, isTransitioning is now set to true, and the target position for the camera is now set. With this in mind we can then begin to move the camera & player, causing the transition animation that was in our requirements. In order to do this we’ll use the Mathf.Lerp command, which returns a number between two points, the ‘distance’ between the points id determined by a third parameter, in this case we’ll use the delta time, to provide a smooth transition. As we need this to run each frame (so the change in delta time can smoothly move the object) we’ll incorporate it into the update method, which is a method called by Unity each game ‘tick.’

// Update is called once per frame
void LateUpdate()
    camBounds = new Vector2(cam.nativeResolutionWidth / pixelPerMeter, cam.nativeResolutionHeight / pixelPerMeter); //Save the current camera bounds so even if the screen is re-sized the transition remains consistent

    if (isTransitioning) //If we're transitioning
        //Call the transition camera method
        //Call the transition player method
        //If it's a downward transition, we'll also need to consider other objects in the scene that may move with the player, such as falling platforms
        if (currentTransitionDir == transitionDirection.down)
            //Call the transition falling platforms method
    else //If we're not transitioning
        //We make sure the falling platforms are re-activated
        //And we pan the camera within the bounds of the current room

We can use this to our advantage by having it so when we are not transitioning, we allow the camera to free-move within the bounds of the current room. This satisfies the requirement of long panning rooms as well! We clamp the camera through the use of the Mathf.Clamp command, which restricts a value and makes sure it’s between an upper and lower bound, we have a ClampCamera method that does a lot of it for us.

void PanCamera()
    Vector3 newCamPos = new Vector3(player.transform.position.x, cam.transform.position.y, cam.transform.position.z); //Set the cameras center to the player
    cam.transform.position = ClampCamera(newRoom, newCamPos); //Re-clamp it to the bounds of the room, so that it follows the player, but doesnt leave the current room

So with the panning done, lets get down to the meat of the matter; the transition animation. This is split into two parts, the Camera, and the Player. Technically there is also the falling platforms (if they are close to the player) but for simplicity’s sake we wont include that here.
Let’s start with the camera, we simply move it’s position using the previously mentioned Mathf.Lerp command between it’s current position and the target position. We also have to include a ‘minimum lerp’ distance, otherwise the transition will go on for a lot longer than needed moving a minuscule amount.

void TransitionCamera()
    //Create a local copy of the target position (as the new camera position)
    Vector3 newCamPos = targetPos; 

    //Lerp camera between two positions
    if (Mathf.Abs(targetPos.x - cam.transform.position.x) &gt; minLerp)
        //If the x has changed, then move along the x
        newCamPos.x = Mathf.Lerp(cam.transform.position.x, targetPos.x, Time.deltaTime * transitionSpeed);
    else if (Mathf.Abs(targetPos.y - cam.transform.position.y) &gt; minLerp)
        //If the y has changed, then move along the y
        newCamPos.y = Mathf.Lerp(cam.transform.position.y, targetPos.y, Time.deltaTime * transitionSpeed);
        //Both x and y are equal so the camera has stopped moving
        newCamPos = targetPos;
        isTransitioning = false;

        //Enable Player again once the camera is in position

        //Make the current room equal the new room =; =;
        currentRoom.go = newRoom.go;
    //Set the camera's z axis position to what it was before
    newCamPos.z = cam.transform.position.z;

    //Finally move the camera
    cam.transform.position = newCamPos;

The player’s transition follows similar logic, however since the player retains it’s position at the transition entry point, it has to have some conditional statement for the direction of transition, so it can move the player in the right direction for them to be in the correct room.

void TransitionPlayer()
    Vector3 playerPos = player.transform.position;

    //Lerp player between the four directions
    if (currentTransitionDir == transitionDirection.right)
        //If the player has reached the target position (including a horizontal offset) then we can finish the transition
        if (Mathf.Abs(targetPos.x + horizontalPlayerBuffer - playerPos.x - (camBounds.x / 2)) &gt; minLerp)
            //Right transition
            playerPos.x = Mathf.Lerp(playerPos.x, targetPos.x + horizontalPlayerBuffer - (camBounds.x / 2), Time.deltaTime * transitionSpeed);
            //Right Lerp finished
            playerPos.x = targetPos.x + horizontalPlayerBuffer - (camBounds.x / 2);
    else if (currentTransitionDir == transitionDirection.left)
        if (Mathf.Abs(((targetPos.x + (camBounds.x / 2)) - horizontalPlayerBuffer) - player.transform.position.x) &gt; minLerp)
            //Left transition
            playerPos.x = Mathf.Lerp(playerPos.x, (targetPos.x + (camBounds.x / 2)) - horizontalPlayerBuffer, Time.deltaTime * transitionSpeed);
            //Left Lerp finished
            playerPos.x = (targetPos.x + (camBounds.x / 2)) - horizontalPlayerBuffer;
    else if (currentTransitionDir == transitionDirection.up)
        if (Mathf.Abs((targetPos.y + verticalPlayerBuffer) - playerPos.y - (camBounds.y / 2)) &gt; minLerp)
            //Up Transition
            playerPos.y = Mathf.Lerp(playerPos.y, targetPos.y + verticalPlayerBuffer - (camBounds.y / 2), Time.deltaTime * transitionSpeed);
            //Up Lerp finished
            playerPos.y = targetPos.y + verticalPlayerBuffer - (camBounds.y / 2);
    else if (currentTransitionDir == transitionDirection.down)
        //The player has to be moved a little further due to the space the GUI takes up
        if (Mathf.Abs(((targetPos.y + (camBounds.y / 2)) - (verticalPlayerBuffer + guiBuffer)) - playerPos.y) &gt; minLerp)
            //Down Transition
            playerPos.y = Mathf.Lerp(playerPos.y, (targetPos.y + (camBounds.y / 2)) - (verticalPlayerBuffer + guiBuffer), Time.deltaTime * transitionSpeed);
            //Down Lerp finished
            playerPos.y = (targetPos.y + (camBounds.y / 2)) - (verticalPlayerBuffer + guiBuffer);

    //Finally move the player
    player.transform.position = playerPos;

Hopefully this makes sense, this is just my implementation of the proposed system however, and there are many alternates, each implementation comes with it’s own issues, and it’s always a good habit to point out the issues in your work, if there’s anything I learnt from my last article, it’s that self evaluation is pretty important, and so let’s go over a few issues I’ve thought of with my implementation, in hopes that anyone learning from this can do it better.

The first thing is that the transition only works for the vertical and horizontal direction, and so diagonal transitions are out. The camera is also unable to pan vertically in it’s current state, although I expect with a bit of shenanigans I could find a way to work around it. The only other issue with the current system worth mentioning is that if there is a wall or obstacle in the way of the player’s target position during a transition, the player will be forced inside the object.

Thankfully a lot of these obstacles are avoidable, we dont place walls where the player is meant to transition, and we dont place diagonal transitions, simple!
Anyway, I think I’ve rambled on long enough, hopefully this will help some poor sod out there looking to imitate Mega-Man or Shovel Knight, like I was!

Thanks for reading!

Critical Analysis: How to Tell if Your Game is Shit

So a lot has changed since I last updated this blog, and Project GhostLight has received a massive overhaul, and there’s one good reason for this; It was shit.

I think it’s very important to be able to be critical of your own work, to be able to look objectively at it and ask yourself, is this fun, is this deep, is there a lot of room for me to expand on these mechanics. Those three words that I bolded are very important for this discussion, as it’s when thinking about those three tests that I realized, my vision for GhostLight failed all three (take note of them for later).

And so this article is going to be about Critical Analysis of your own work, why it’s useful in a hobby and real-world scenario, and then a brief discussion of my critical analysis of my own game, what my original plan for it was, and then what has changed.

So, making a game is a HUGE project, especially for a one-man team, there’s a lot of design elements you have to consider; art style, level design, mechanics, characters, plot, genre, controls, game-play, etc. And the difficult thing is that to make a great game, all of these have to work together. Each element of the game has to complement the rest. Take Shovel Knight for example, and the reason I’m choosing this game is I’ve been analyzing it a lot myself lately as it’s inspired a lot of what the new GhostLight is, but we’ll get to that later.

Shovel Knight sells itself as a modern NES game, and everything about it lends credence to that claim. The Graphics are all reminiscent of the NES era, with a modern twist! The developers have talked about how they stuck quite rigorously to the graphical limitations of the NES system, however through the addition of little improvements they managed to make the game look modern and beautiful, without losing this retro feel. The colour palette for Shovel Knight is mostly the NES colour palette, however the developers noted that:

“The problem for us mainly came in trying to display a gradient in most hues. For example, there isn’t a very useful yellow, the darker spectrum of color is very underrepresented, and there aren’t many shades that work for displaying a character with a darker skin tone.”

(This quote was found at a great article on Gamasutra discussing what NES rules Shovel Knight broke), and so in adding a few transitional colours, and a few colours for darker skin tones, the developers of Shovel Knight managed to add that much needed modernization to the game.

Another graphical example would be the Widescreen display and Parallax backgrounds (a topic I am planning to cover), Widescreen display is an obvious choice as it helps make the game more accessible and in-line with modern standards, however this doesn’t alter the old format much, Shovel Knight still keeps the same number of vertical ’tiles’ (suually a 16×16 pixel block) as any old NES game, but increases the number of horizontal tiles in order to allow more breathing room for the player, and creativity in regard to puzzles.

I could go on and on about the NES properties of Shovel Knight, as well as the NES properties it breaks, if you’re interested, give the article above a read, it’s actually written by one of the developers and goes into a lot more detail on the brief things I’ve mentioned here.

Now we know that a game needs to be complimentary to itself, each facet has to be designed with the overall product in mind, even if it’s just a hobby project that will probably be released for free or a small amount (like GhostLight), thats no excuse not to put as much effort as you can into making it ‘good’.

But why ? Why should you bother making sure your Tetris clone has a music track that compliments it’s special effects ? If you’re making a game for practice, just for yourself to practice coding or pixel art, then maybe it is less important. But if you’re trying to develop Game Design skills, if you’re trying to market a product, even if you’re using the product to market yourself as a portfolio piece, then Critical Analysis is so important.

Lets make up a little scenario and see how it plays out, you are the head of a small Indie game group, looking to make some money with a mobile game, maybe it’s not even about money, maybe you want to make it big, make the next Angry Birds or Flappy Bird, so you start on your bird-related adventure into the world of games. Your graphics guy makes some nice retro 8-bit bird sprites, your music guy gives you this awesome funky Dubstep track that gets stuck in your head, your team of programmers work expertly to create a bug-free game involving your bird flying through space.

Excited by your new product, the culmination of all these different areas that, on their own, were brilliant, you eagerly release Dubstep Pixel Aviation Simulator 2015 onto the app store and…
Nothing happens.
Which is to be expected, games don’t explode overnight right ? Look! You’ve already got some people downloading it and playing it! OH LOOK! Your first Review! 3 Stars ? Oh…well what was wrong with it ? The review reads jsut one sentence

“It’s ok, Got boring pretty fast though.”

At this point, you as a Game designer have failed. Each piece of your game, from the art to the music was made independently of each other, with no reviews, no collaboration, no management from you. The music may be great, but it doesn’t really compliment the pixel art in the game, and the blocky pixel art doesn’t really fit well with the smooth flying controls. It’s a disjointed mess, not designed with the final product in mind.

It’s not even the overall feel of the game that you’ve got to consider, it’s the platform it’s going to be on as well. Especially with mobile games, this is so important, and if you don’t analyse your game early on and catch things like this, if you’re too stubborn clinging to your design mistakes you thought were gospel and the center of your masterpiece, then at the end of it all you’ll have an alright product and a big waste of time and money.

Whew! That got a bit intense, so lets take the heat off of this imaginary indie developer and put it on me, let’s turn the light to face GhostLight in this interrogation. What was wrong with it? Well, a lot!

My original design document for GhostLight (Speaking of which, design documents are important to have, even if it’s just a brief paragraph explaining how you want your game to feel and look) described a 2D platformer, following the adventure of a dead flashlight, his only attack would be to shine a beam of light out, this would be used to solve puzzles, to weaken enemies, light lanterns to open doors, etc. Wait…hold on a second, do you see that ‘etc’ I had at the end of that list of 3 things ? That just meant I’d ran out of ideas. Well not really, I had some other ideas like refracting light, different colored filters, shining light through a mirror to burn rope, but as I developed these ideas more I began to realize – I couldn’t do them. Now not in the sense that it’s impossible, but the way I’d designed and created the light-beam of the flash light wouldn’t work with this, it could be reflected and trigger a light source, that was about it.

And it was around this time that I began to understand the issue behind my game. And I’m going to link this back to those 3 words I bolded at the beginning (Told you they were important!):
The game lacked depth, I couldn’t expand on the player’s experience because the player could only do a few things, walk around, jump, double jump, and shoot a torch. That was it. Any puzzles created would be trivial in the sense that if you just stood in a certain location then it’d be solved, and player’s tendencies to shoot at a lot of things when they have unlimited ‘ammo’ means a lot of them may be solved without realizing it.

Other design decisions lended to this too, due to the size of what was allowed on screen, and the free-moving camera, I as a designer, could never be sure what the player would be looking at at any given time, and so puzzles based around bouncing light beams around didn’t really work out very well, as the player either couldnt see all the puzzle, or the puzzle was so cramped that it was trivial. This is not fun.

Even the character design was limiting, it didn’t really have any charm beyond it’s novelty and was restricted to either shooting only horizontally, or it’s legs being bent weirdly out of shape as the body rotated.

And so I looked at my work, realized it just wasn’t going to work, and I scrapped it. Not all of it, mind you, a lot of the art assets and some of the character’s control code is ok to keep, but the character, level design, and game design have to start back from square 1. And so that’s what I did.

So what’s the new GhostLight like ? To be honest it’s heavily inspired by Shovel Knight in terms of gameplay, I’ve incorporated a room-based camera system, similar to the original megaman games, this was for two reasons, one: it means as a designer I can always be aware of what the player will be able to see at any given time, making it MUCH easier to plan levels, and to teach the player the mechanics of the game. I’ll be releasing more about the character soon, but he’s able to attack in at least 3 directions so far! More of the games development will be shown through this blog as time goes on 🙂

Level Creation and Initial Playtesting

So since the last update, the first level of GhostLight, currently being dubbed ‘Purgatory’ has been fleshed out a little, and undergone some very brief playtesting, this little article is going to be a summary of that process, as well as an update to the current state of the game.

So first of all, I’ve actually made the level, using 2D Toolkit’s tilemap it is literally as simple as selecting the tile from your tileset and ‘painting’ it onto the stage. However, some things need to be taken into account; the way 2D Toolkit creates the levels is it lumps all tiles painted on a certain layer together and creates a hitbox through edge colliders. The issue is if you want tiles to overlap, or if you want background/special tiles, then this doesn’t really work. Thankfully there’s a ‘layers’ option, each layer remains separate from each other, so you can have a layer for player hazards, and tag them appropriately using Unity’s tagging system.


The above image shows my current workstation, I use a single layer for Hazards (such as the spikes that can be seen in the level), a platform (Foreground) layer, that the player will actually move on, a foreground effects layer that handles overlapping or transition tiles, (The edges of some tiles have transparent areas, so with this I can overlay them over other tiles to provide a nicer transition), and two background layers for similar reasons.

I had a quick playtest of my admittedly short level, and so did my girlfriend, and immediately the first problem with my desired mechanics arose. So originally, I had planned to introduce a sequential jumping mechanic, similar to the 3D Mario games, where pressing the jump button as the player landed would propel them further, and higher. The level was designed around this mechanic, which may have been a bad idea in retrospect, as trying to navigate the level with this mechanic proved very troubling.

It just didn’t feel natural for a 2D game to move that way, it felt like the control listener was too inaccurate to provide a tight window for the double jump that didn’t seem ‘cheap’ or inconsistent. With a little bit of tweaking I instead swapped it out for a more traditional double jump.

However there were more issues arising from what I thought would be a very basic and simple implementation; making long jumps felt very difficult, not in the sense of timing, but due to the nature of how I’d programmed the jumps (and how it detected whether the player was grounded), trying to jump at the edge of a ledge whilst running nearly always failed, the player would always jump just a little too late, taking away the important aspect of control and fairness required in a 2D platformer.

I came up with a solution, inspired from Juicy Beast’s similar implementation in their game “Toto Temple Deluxe!” where is the player has just left the ground and has not yet used their initial jump, then instead of immediately using their double jump, treat them as if they are still grounded. Let them use their first jump despite being just off of the ground, and the results were great! The player felt much more in control and the game felt much more forgiving in this element.

(By the way, Juice Beast has their own Dev Blog, and wrote about their implementation of the above mechanic here! I highly recommend you check it out and read through their series about Toto Temple Deluxe!)

I’d like to go into a bit of technical depth in how I implemented this, showing some of my code, so that anyone who’s not quite sure about how to make something like this can hopefully learn something!

//The following code is within the Control Handler function, that deals with all the basic player controls
//Unity has built in methods to detect certain Inputs.
//If the player has pressed the jump button, the character is grounded or they haven't double jumped, and the counter to allow them to jump hasn't expired
if (Input.GetKeyDown(KeyCode.Space) && (isGrounded || (!hasDoubleJumped || lastOnLandCounter > 0.0f)))
        //The timer for when the player was last on land is told to begin by setting the buffer variable to true
	lastOnLandBuffer = true;
	jumpPressedTimer = jumpPressedTimerMax;
        //If the land buffer timer has ended, and the player isn't grounded, it's considered a double jump
	if (!isGrounded && lastOnLandCounter <= 0.0f)
		hasDoubleJumped = true;
        //If the counter is still running, it's set to zero, and the player is vaulted along the positive y axis by our jump amount
	lastOnLandCounter = 0.0f;
	GetComponent().velocity = new Vector2(GetComponent().velocity.x, jumpHeight);

//This is called during the Fixed Update function a set amount of times each second
void LastOnLand()
	//A counter to check if the player has just fallen off the land or not, allowing them to jump still
	if (!lastOnLandBuffer && !isGrounded)
		lastOnLandCounter = lastOnLandMax;
		lastOnLandBuffer = true;

	if (lastOnLandCounter > 0.0f) 

If the above just seems like jibberish to you don’t worry about it! Although please let me know how you feel about code examples in the comments to I know whether to include them in future 🙂

Switch-activated doors have also been added but I think this article is long enough, so next time 😉
Hope you all enjoyed!

Level Design and Tile Sets

I’d decided from quite early on that I wanted project ghostlight to use a tile-based aesthetic, as it would not only fit the style I wanted to achieve, but also allow me to create large and complex environments without creating lots of custom areas.

tile-based games are comprised of a tile set, a single image containing all the ’tiles’ a single board will use. Fortunately, unity fully supports a single image having multiple sprites inside of it, unfortunately, however, is each tile would have to be it’s own object with it’s own individual properties, which would create a large amount of overhead and complexity. I was struggling with a way around this concept for a while until I ran into Sam, the dev behind FreakZone Games at a local gaming meetup.

Off-topic for a moment; Sam’s games are really good, so you should go and check them out at the link above!

Sam ended up recommending a Unity asset called the 2D Toolkit, which has a built in Tile set creator, allowing me to create an entire 2D level using my tiles, without all the overhead.

The next step would be designing my level, the most important thing was for it to teach the player the mechanics they required for the rest of the game, so for this level I focused on the most basic mechanics:

  • Jumping
    • A sequential jump (think like the 3D Mario games)
    • Jumping on enemies is how you kill them
  • Shining your torch
    • Shining the torch at a lamp lights it and acts as a puzzle solving mechanic to open doors
    • Shining the torch at flaming enemies (ghost-firey-stuff) extinguishes the fire and allows you to jump on them to kill them
      • Some enemies could only be extinguished from certain directions
    • The torch beam can be reflected by mirrors

Whew, seems like a lot to teach a player over one small level. From looking at the list you can tell some mechanics, such as jumping on enemies to hurt/kill them, are common features of the 2D Platformer genre, and so may be easier to show.


The above was my final design for the first level, I’ll do a little walk through of the first section, but anyone who’s interested in level design or game design, it’d be worth looking through the rest of my plan, and try to figure out what each area is trying to teach or test, or what I could do better.

So the player starts with a pillar to his left blocking that direction, and some pillars in the background, so the natural direction to go is right, moving this direction the player meets a small wall, allowing them to figure out the most basic controls (jumping, moving, etc) without any fear of failure.

The player is then immediately tested on this skill with a basic jump over a gap, but again with no risk of failure, whilst performing this jump the player will probably see the series of platforms with items (coins or something), but at the moment they cant seem to jump high enough to get on top of it. So they keep moving right, the one direction they’ve found that yields progress.

They jump onto the smaller block, as they’ve learnt they can do, and then immediately jump to get the coins/ascend the hill and WOAH SEQUENTIAL JUMPING WOOOOO.

So jumping twice in a row allows the player to go higher and further, now the player can use this skill they learnt and put it to the test to grab more loot from the area they couldn’t previously reach.

Hopefully that made sense, I just finished creating this level design yesterday so wanted to share it with you. Hope you enjoy! And I’ll leave you with a portion of the Tile set I finished last night 🙂tileset

– Josh


An Introduction to Project GhostLight

‘GhostLight’ is my current side project, a simple 2D Platformer with light-based mechanics made in Unity. The projects stems from a recent game jam, as well as a desire to create something of professional quality and finish.

It is my attempt to get something published on an online distribution service, as well as to allow me to explore the full development lifecycle of a game on my own.

The premise of GhostLight is that you are a dead torch (flashlight) and awake in the afterlife, exploring through approximately 8 levels (loosely based on the 7 deadly sins and purgatory) in order to seek redemption. A bit of a weird theme, but I like it!

At the moment I’m about half way through the design for the first level, and I’ve finished the animation set for one of the enemies. I’ll be posting code, art, and design updates to this blog so I hope you enjoy! As an introductory post I’ll show the frames for one of the spider pot enemies in the game: