An Old Favorite - Planning & Refactoring

What Am I Doing?

Most people who’ve talked to me about game dev know how fondly I look back on The Right Perspective. It’s a simple game with a lot of depth, and a large portion of its two-day dev cycle was composed of polishing and perfecting it (and maybe a little bit of 4AM Fire Emblem), and it placed second in the jam. In my final semester of school I’m finding myself with more free time than expected, so I figured that polishing and improving one of my old favorites might be a good use of my time.

After pulling it from Unity Collab and setting up a Github repo, I set up some quick goals:

  • Improved Interface. Clearer buttons, better controls, more feedback. May remove ability to select any level at any time.

  • Better tutorialization. Rather than requiring the player to read the controls, showing them ingame as necessary.

  • Improved clarity. Connections between buttons and fans, a lock symbol on locked goals, etc.

  • More Juice! Some animation bounce, improved feedback on actions, more moving parts (visually).

  • Cursors/Indicators. Tiles you can go to will be highlit and have feedback when you choose a tile you can or can not reach.

  • Code refactor. Updating old code to use techniques I have since learned about, and replacing hardcoded animation with actual animators.

  • Level Transitions. Simple animations from level to level.

These are in no particular order, considering that I’ve made serious headway on the refactor already and have done a bit of juice and interface stuff as well. My goal with most of these tasks is to make the game cleaner and more impressive, and I don’t think any of them besides the refactor should take too much effort, and the refactor is going smoothly so far.

The Refactor So Far

I took a look at the code and… Well, it was pretty bad - it really shows how far I’ve come since my team and I made this game. The game’s chess-like character animation was not controlled with an animator - so evidently, this game was before I actually learned how Unity’s animation system worked. It was controlled via… this:

if (moving1 && moveTimer < moveSpeed / 2f)
        {
            this.transform.position = Vector3.Slerp(this.transform.position,
                new Vector3((tileBelow.transform.position.x + this.transform.position.x) / 2f, tileBelow.transform.position.y + .85f, 
                (tileBelow.transform.position.z + this.transform.position.z) / 2f), moveTimer / moveSpeed);
            moveTimer += Time.deltaTime;
            canMove = false;
        }
        else if (moveTimer > moveSpeed / 2f && moving1)
        {
            moveTimer = 0;
            moving1 = false;
            moving2 = true;
        }

        if (moving2 && moveTimer < moveSpeed / 2f)
        {
            this.transform.position = Vector3.Slerp(this.transform.position, 
                new Vector3(tileBelow.transform.position.x, tileBelow.transform.position.y + .65f, tileBelow.transform.position.z), moveTimer / moveSpeed);
            moveTimer += Time.deltaTime;
            canMove = false;
        }
        else if (moveTimer > moveSpeed / 2f && moving2)
        {
            moveTimer = 0;
            moving2 = false;
            canMove = true;
        }

Apologies for the code dump, but it felt appropriate given the insanity of the code for the task at hand. Essentially what’s happening is… slerping up to a midpoint of an arbitrary height, and then slerping back to the top of the next tile. It’s ugly. Thankfully, this was easily replaced by an animation and a lerp.

 

The outcome of the monster.

 

The fix.


One of the things I’ve enjoyed most about this project so far is simply that it’s a good reminder of how far I’ve come. There have been a lot of hardcoded values or other clunky things I’ve had to change so far. Tiles, for example, used to require the developer to go around for each tile and select whichever tiles are next to them in the editor and drag them into slots for north, south, east, and west. This was clunky, took ages, and was hard to test. Since we had to save references based on direction, this also meant that player movement got clunkier; the whole game is based around the camera rotating, so if the player is moving directionally… We had to convert enums for the player’s direction to north, south, east, and west based on camera direction, and we did it with a series of nested ifs and switch statements. That block of code was nearly 170 lines long, and almost none of it was comments or blank lines. Now, tile adjacency is determined at the time of movement by raycasting. It makes way more sense, and all I have to do is put two tiles next to each other.

Tiles

I’ve adjusted a lot of funky code today, and even the monstrosities above I don’t think stand up to the Tile management. The code that determined whether or not a tile was of a particular type was a long series of booleans - not even an enum! I can’t even make up excuses for that one, because there are a number of enums throughout the rest of the project. Now, tile types have been totally revamped. They’re still not totally functional, but here’s the rundown:

  • As stated above, Tile Adjacency is now determined as necessary via a raycast. While the code is a little more advanced, it’s far more readable, debuggable, and implementable, and it just feels better (and it’s also still pretty basic code anyway).

  • Tile is now a fairly barebones class that essential is just a base class with a virtual TileAction function. It has a few other things going on, but a number of them are artifacts of early iteration on the code and will likely either be moved or removed.

  • Tile Types now all inherit from Tile. They all have any necessary member functions, such as the power state of a fan block or whether the key has been collected, but more importantly, they have the TileAction function overridden, where the main function of that tile is implemented.

    • Some of the tile types, such as the Fan, have some static data associated with them as well. Due to the way the fan switch works, the fan class contains a static list of all fans in the level. I feel like this is a little messy, but the code just looks so much cleaner - the entire FanSwitchTile class is 11 lines long, counting whitespace and bracket-only lines - only 4 lines if you don’t.

    • I thought about implementing this with a plain Tile class and then components for each different type of tile. When I started refactoring this part, I had a bunch of extra functions and variables that seemed vital - an enum for tile type, some extra Start() functionality, etc, but now the only thing that seems important is the TileAction inheritance. Of course, that is nice enough that I still think it’s worth it. Replacing 70 lines in the player script with literally a single one made me a happy lad. Might still change it though.

  • Tile Prefabs are now all prefab variants of the original Tile prefab. Now, there is some jank to this: I wasn’t going to go and rebuild every single level with new prefabs, so every tile type prefab (besides plain ol’ Tile) is a child of an empty game object that was once the old tile. It works, and it’s really not confusing.

  • Tile Management is now done more or less entirely within the scripts of the tiles being managed. Before, there was a “Block Manager” that just maintained a list of fans or control switches on the level and would manipulate them as needed, but both either are done or will be done using static members of their own class.

I think today I got a pretty good start on this refactor. I think it’ll be a while yet before I’m done, because this is a nearly 3-year-old project written before I learned, apparently, just about anything about Unity, and some of my goals are pretty lofty. I also don’t know what “done” will look like - I guess “shipped on Android” would be a pretty good “done” state, so let’s say that’s the endgame, but I could see this being more than that.

I would’ve liked to include some more images and videos in this post, but today was mostly code, so hopefully I’ll be able to get to more of that when I post again. I don’t know when my next post will come because, well, school, but I’ll try to make sure it’s soon. Hope that you’ll read this then!

In closing, here’s a picture of my housemate’s cat, Vinny, attacking my shoes while I brushed my teeth:

20200905_005907[1].jpg
Next
Next

Final Blog Post - Gold Sprints! Playtesting & Postmortem