Finishing the Final Game Jam

Intro

It was the final month before the deadline, and there was still a lot of work to do.

My main contribution, the movement system, was in a finished state. However, the rest of the game still needed polish. Most importantly, the cutscene system, which would allow for dialogue to be displayed to the player. This system will be used for the start and end portions of the game, as well as the story's exposition.

Cutscenes

Since there was a simple story within the game, as well as no tutorial level planned, the team thought that a cutscene system would be the perfect place for exposition.

They would be inspired by the cutscene system in Persona 3, with features like:

Persona 3 dialogue

There were only going to be two cutscenes, one at the start and one at the end. This gave me the option to just hardcode two cutscenes, potentially saving time and making life easier. However, this also meant the cutscenes would be much harder to alter and expand, and also meant it would be very hard to ever add additional cutscenes since they would also require code to be copied and pasted. Just all-around terrible practice, and I knew I was better than that.

So instead, I went for a far more modular system that would only utilise one set of scripts and one prefab, which can then be placed in whatever scene and only needs the text that appears in the boxes and the avatars to be changed. This meant if I wanted to change how cutscenes work, I wouldn't have to change all instances. I would only have to alter the main scripts. It also made it incredibly easy to add new cutscenes, which we even ended up doing, so in the long run going for this system was certainly a good call.


It works by having a main "Cutscene" object in each scene, which handles the logic for the dialogue section. All the dialogue boxes are stored in an array within a dialogue object. This allows the cutscene to have one coroutine, which loops through the array and runs a "write" function for each box. 

Having them stored in an array is incredibly helpful as it allows for any amount of entries without having to alter the code. This means that once the code is written, the entire dialogue can be created just within the editor by adding each dialogue box entry to the array. This is not only far quicker, but results in non-programmers being able to customise the cutscenes as they don't need knowledge in C# programming, so it's great for accessibility.

On top of this, each Dialogue object within the array is part of a DialogueBox class. This class doesn't have any methods, but it does have one important attribute; an avatar. This allows each dialogue box to have its own attached avatar, yet again making the mode much more modular as all the data is stored in the dialogue box object rather than hardcoded in the script. It also again is more accessible for non-programmers. The DialogueBox also has its own text assigned to the object, although this is done with the Text component rather than an attribute.

All this data in the dialogue box makes the writing function's job much easier. First, it stores the text in a variable and then clears it so that the box starts empty visually, and also sets the avatar to be visible. It then loops through every letter in the text and displays them one by one in the box with a slight delay. It then waits for the player to click and returns back to display the next box. I used this YouTube video for help coming up with the text display system.

All this combined results in a cool final result.

One alternative I had for displaying the text was to have a mask object gradually reveal the text. After consideration, though, this would be way more work and would likely need to be manually set up for each box. The original system prevents this by having it work dynamically for any length of text since the for loop uses the text length for its stop value.


I'm very happy with my implementation of this, it made life much easier when adding dialogue, and it didn't take long at all since I ran into very few issues. Despite this, though, I think there are definitely improvements I could make.

Mainly, better support for multiple cutscenes in one scene. Upon reflection, I think the current system I have is weird. It could easily be simplified into just one script attached to the cutscene object, with the dialogue objects as a child. The system I have now though does have one benefit, and it's that I could quite easily add functionality to allow for multiple dialogues.

Instead of storing one singular dialogue object, I could make it an array instead and have as many Dialogues as I want. Then at any point within the code, I can call a function with the index of the dialogue I want, and it can run that specific one. This would be a far more logical and modular system since currently, the code would require a lot of janky rewriting to accommodate extra dialogues, so I wish I could have implemented it knowing this. This change though is not completely necessary since there was only a maximum of one cutscene within a scene. Nonetheless, I still believe it would have been worth doing just in case.

Another improvement I should have made was the ability to skip dialogue. Right now you have to wait for the whole box to finish typing before moving on to the next, and this feels incredibly slow, and frustrating if you have already watched it and are trying to replay the level. Instead, I should have done an additional left-click check while the text is being delayed to skip it, allowing for much faster cutscenes if people desire.

Overall, though, the cutscene system was great, and I'm super proud.

This was the last major feature I had to add, now it was finally onto polishing the game

Improvements

It had been quite a while since I implemented all my previous features, and since then I had improved much as a programmer and others had found bugs, so naturally there was a lot I had to do.

The first thing I wanted to fix was power-up stacking. My current implementation works by just multiplying the player's speed or jump height by a hardcoded number. This makes sense to do, however it is actually quite a poor implementation as it means that any other factor that can affect the player's speed or jump height will affect the bonus provided by the power-up. Most things affected weren't major, however, there was one interaction that was problematic and that was collecting multiple of the same power-up. This is because by multiplying the attribute of the player, it would increase exponentially, causing a huge increase over time. 

There were a few ways I could fix this, firstly I could just make sure that the levels were designed without it being possible to collect the same power-up consecutively. This is a terrible and lazy fix however, as it forces unnecessary restrictions in level design, and is also completely unpredictable as there can be countless factors to consider such as speed runners being really fast or altering the power-up duration that could mess with the fix.

Another fix could be just preventing players from collecting multiple of the same power-up. This is not too bad and could be implemented fairly easily, however, I wasn't a fan of it just because of the player experience. If a player collected a power-up and felt no effect, they might be confused as to why this happened or just unsatisfied. This is what made the more complex, but better-designed fix so appealing.

The final fix would instead increase the player's movement speed linearly, rather than exponentially. This means that the attribute is increased by the same value for each collected power-up, resulting in a far more predictable outcome.

The way this works is very simple. Rather than multiplying the player's speed by 2 to increase it, I would instead add the default speed value every time. This way, it would increase by a constant value each time, rather than gradually increasing by larger and larger values.

This system is much more consistent and will prevent any confusion or absurdly high effects.

Linear

Exponential

Looking back, I definitely should have designed the system this way originally, but I kinda rushed the code and made it carelessly thinking I was likely going to redo it anyway as they were only prototype effects which I thought would have their functionality changed. That didn't happen, however, so I had to waste time going back and fixing the poor system. So putting in more effort to create the most robust systems I can will save me time in the long run.

Another aspect I wanted to improve was the rolling. With a now-built level, I was able to test a lot of the mechanics in a proper environment, and the one which stood out the most as being clunky was the roll. I found it would feel very disorientating, and would often be very hard to control and play off of, causing it to often be a burden and unsatisfying to use.

I managed to track down these issues to two causes:

Firstly, the player's speed would remain equal during the roll, so they would be rolling extremely fast without being able to see where they were going.

And secondly, the 360 spin of the roll was completely linear, meaning they only spent a tiny portion of the animation being able to see in front. It also meant the camera felt very snappy at the end, which made it feel hard to track.

The first issue seemed incredibly simple to change as I had created similar temporary slowness effects for other systems such as the roll failing. However, no matter what I did, the player's speed would never be changed until the end of the roll...

I spent AGES debugging this to try to figure out what was causing it. At first, I thought it could somehow be related to the coroutine I was using, so I translated the roll function into the update function. This didn't fix anything though and was a waste of time. I eventually figured out what was causing it, and it was very silly. 

All it was, was that the player was sliding when they hit the ground at the same time as rolling. This causes the sliding speed to take effect, overwriting the normal roll speed. To fix this, I just had to make sure that the player can't slide during the roll, and just that like this painful issue was fixed.

My debugging process on this was very poor, and I immediately assumed the issue was something random to do with coroutines, rather than an actual logic issue I had made. I should have trusted my knowledge and investigated other cases where I could have made mistakes, rather than doubting myself. This could have saved a lot of valuable time because the fix itself was so simple. Hopefully, as I improve as a programmer in the future, I will become more confident in myself.

The second thing I wanted to change was the linear spin. If I used an animation curve to make the spin linger towards the end, it would result in a much less disorientating spin.

And the final result, I found to be way more fluid and satisfying, with no major cost to efficiency or code readability since both changes were so minor.

The Day of Reckoning

The time had finally come, and it was the night before the deadline.

The game was in a fairly finished state, but there was still quite a lot to be done to help polish it. These included:

With only 24 hours left, Avery and I got to work finalising the game for release.

My GitHub contributions during the final day

24 hours later, with no sleep and a lot of caffeine, the game was finished and submitted.

In hindsight, as fun as crunching the game in the final 24 hours was, this was definitely a bad idea. I should have spent more time throughout the week focusing on the game. I was making a lot more silly mistakes the more tired I got, so having time to actually rest and recharge my brain would have been beneficial.

Anyway, with that, the game was finally finished and published onto itch.io.

Conclusion

I'm very proud of my work on the game over the past few months. 

I've learnt so much in regard to what goes into making a proper game and how to use unity. The final product is one that is super fun to play, and I received lots of compliments from friends about the fluid movement system, which was incredibly gratifying. For the first few days after release, there were also many people playing and trying their best to get the fastest score and get high on the leaderboard, which was so cool and see and makes me feel like as a team we did a very good job.

Current leaderboard as I'm writing this post

Writing these dev diaries has also been massively beneficial towards my growth as a programmer, as I was able to properly reflect on all my actions and then improve parts of my code. It was through these dev diaries that I noticed issues I had made, and massive potential improvements, which has all been great. I'd never written a dev diary before, so at first it was quite daunting, and I struggled a lot to really reflect on the things I had done. But throughout the year I feel as though I got better and better, which allowed me to create far beefier and detailed entries with proper critical thinking of my performance.


Looking back, there are definitely certain aspects I can still improve upon.

Firstly, is the obvious one of time management. While throughout the majority of the project I was actually on top of a lot of things and working very quickly, I definitely found myself slipping towards the end, leading to the massive crunch during the final day. Better time management would have given me more time to further improve the game and help maintain a consistent quality, since now a few days after finishing I can think of a bunch of things I wish I could have added. For example, I wish I had added more of Max's 2D art that he had created, like pop-ups when a power-up is active or more immersive loading screens.

Planned main menu

It also resulted in my vaulting and grappling mechanics being scrapped from the final game as there was no place for them in the only level, which was a shame. If I had more time, I could have helped create more levels that could utilise these mechanics.

One way I could have improved my time management could be through properly tracking the hours I spent each week, and actually dedicating a set time. Doing this would have allowed me to evenly allocate my time to the project, so I could remain consistently up to speed and if I was slacking I would be immediately aware. This also could have been done with other modules to ensure I was properly balancing my time because these past few weeks have been incredibly jam-packed as a result of this poor management.

Alongside tracking hours, using SMART goals to give me proper and obtainable targets would have definitely given me better motivation to work on the project, as at times I felt unmotivated and overwhelmed by how much there was left to do. Goals would have allowed me to focus on specific parts and not get bogged down by how much there was left.

Another aspect to improve is putting more effort into making my code cleaner and easier to work with. Since this was the first proper time I've worked on code with multiple people, it meant a lot of the time people were working with code I had written. Due to my eagerness to just finish stuff and not really caring about how well it was written, this made their jobs a lot harder.

One way I should have fixed this was through proper documentation. Earlier on in my dev diary, I acknowledged that I needed to comment more. However, I ended up still not doing it and as a result, a lot of the code I ended up writing was extremely messy and confusing. I should have spent time writing these comments while I was coding, as while it would have taken additional time, it would have made my code far easier to work with for other people. This is because the comments would explain how sections of the code work, and why certain parts exist without the person looking at it having to do their own investigation, saving a lot of time.

Long mess of code that could have really benefited from comments

I also should have spent time refactoring code after I had written it, as I've noticed I have a habit of finishing code, making sure it's functional, and then just leaving it forever without considering any possible improvements to the way I had written it. These improvements could have allowed for much better readability, as I could have cleaned up potentially confusing parts by reducing the complexity. They also could have allowed for more efficient code, since I could potentially notice poorly optimised methods and clear them up, improving performance as well as just reducing the amount of bloated code. As such a new developer, it makes it far more likely I would spot improvements later on since my skills are developing really quickly. I should have used this to my advantage by improving the old code I had made when I was less experienced.

I hope that in the future I'm able to improve upon these flaws. I know I'll be more conscious of them throughout my next projects. Also, by having them be major issues to this project, I'll be far more likely to not repeat them again. 


It wasn't all negative, however, there are also parts I think I did very well.

Firstly, I think my independent research and problem-solving was done very well. Through YouTube tutorials and forum posts, I very rarely found myself asking for help and assistance from teammates, most things I was able to implement myself. Being able to create things independently is not only much faster, but also makes it far easier to improve as a programmer and leads to me learning far more than I would have if I was hand-held through everything. I think it may have also led to me being slightly stubborn to ask for help when I maybe should have. This balance will be improved over time though, through the more projects I develop.

I also believe my overall contribution to the project was more than enough, and I rarely found myself slacking and not doing any work. While my time management wasn't great, I always made sure I was doing at least something every so often, which allowed me to finish the whole movement system in a nice polished state.  Doing this resulted in the game being properly finished in time, as I helped pick up dead weight left by other members and members that had left the group during the project. This overall led to me getting a high number on the final peer review score, which was nice to have and will help assist the grade I receive.

All-in-all, though, creating Don't Miss The Bell was a super enjoyable experience which I'm incredibly happy with. I really liked my teammates, and I found the overall gameplay to be super creative and engaging to create for. I hope that I did a good job reflecting upon my performance and was able to properly evaluate my positive and negative aspects, as this will all assist me in becoming the best game developer I can be.

I am looking forward to whatever game I make next.

Media