From the 22nd - 26th January I took part in the Global Game Jam 2024 with a team of friends. Our university opted to officially host it at one of their buildings and cancelled regular lessons in order to allow us to focus on it.
The theme was announced on the 20th and we all watched it live while in a call together. When it was first revealed as "Make Me Laugh", we were all very stunned. Such an abstract theme makes it hard to think of any possible idea to work with, but when we broke it down (and with some prompting from ChatGPT), we eventually came up with the idea of Stand-Up simulator.
The premise was simple:
You are a stand-up comedian performing at various clubs, you have to select 16 out of the total 64 available jokes to take in with you and perform (but you will only use max 10). Each club has a different audience with different tastes, so you have to gear your set of jokes to fit that. Each joke you tell will be judged by the audience on a scale of Excellent, Good, Mid & Bad and will affect an invisible score that determines your overall ranking at the end. So you have to play your cards right if you want to succeed.
Originally we were going to lock jokes behind progression rather than giving them all to you up front, but due to the time constraints that was quickly dropped.
Our team consisted of 4 members:
- Me (programmer)
- Em (producer & artist)
- Gaby (programmer)
- Grace (programmer)
Only one of us (Gaby) had strong knowledge in our chosen language and engine, that being C# with Unity, but we all needed to learn it anyway for our future Game Jam 4 that was starting soon, and I personally learn best by application & implementation (I basically need to do the thing in order to remember it).
So, 5 days to make a game. In an engine & language I've never used before. What could possibly go wrong?
Day 1 - The Power Cut
The first day of the jam (and first day back to uni for everyone else) began with my entire accommodation having lost all electricity & water. Wonderful!
Our team met up at the producer's accommodation, which was unaffected, and began planning the day ahead. We started work in the uni building at 11:30 and for the first 2 hours before lunch it mostly consisted of setting up the project with GitHub for source control. This was our first time working with any form of source control, as in previous projects we've each just resorted to hot potato (passing the files between each other and working on it one-at-a-time).
After lunch, each of us 3 programmers worked on different parts of the game. Grace handled the menu, Gaby worked on the clicking & dragging of cards for the deck builder screen, and I worked on the performance screen making the audience react to jokes by having them bounce up & down, like this:
The way I achieved this effect was first by applying gravity to the audience members. To stop them falling off the screen, they sit on an offscreen floor (as shown below). When a boolean variable is true, a loop is run wherein they are each given a randomised - within range - upward force IF they are touching the floor (to stop them just flying off the screen). The random element in the force results in each member having a different jump height, which in-turn leads to a desync as they touch the floor at different times. This effect is intended as it gives an organic feel to the audience. Because nobody laughs in unison, that would be creepy.
![]() |
The red line at the bottom is the off-screen floor that they stand on |
![]() |
The code each member has to make them jump |
All members of the audience are a child of a parent called "AudienceBody" which at this point in time just cycled through and toggled each member on or off at once, so that they didn't need to each be manually activated and could instead be grouped under one boolean switch. However, one of the lecturers saw this and challenged me to get more complicated by only activating some of them each time, rather than the 'all or nothing' solution that was currently present.
Challenge excepted.
Day 2 - Spending way too much time on the Audience
For most of Tuesday I spent my time programming the audience body to randomly select a specific amount of members to 'laugh'. At first, I had 4 boolean values labelled "Excellent", "Good", "Mid" and "Bad" in which a different number of audience members would get picked based on those 4 tiers. However, this was problematic for 2 reasons.
Firstly, the 'update' method runs every tick (which you can consider a frame - so if the game runs at 60fps, 60 frames/ticks are run every second). This meant that every time one of the above boolean values was true, the method that randomly selected x members would rerun infinitely, leading to all of the members getting chosen after a few passthroughs. Having multiple booleans as the condition made it really hard to ensure it was only run once, and so I simplified (and at the same time made it more modular) by only having two boolean switches for separate tasks that immediately disabled themselves once their methods had run (so that it only runs once when activated). The first is called 'on' and it turns the members on, and the other is 'off' which - naturally - turns them back off. The on button is accompanied by an integer value called 'loop' which determines how many members get called (so if loop = 4, then 4 random members would laugh).
![]() |
AudienceControl main program |
The way the members were selected was using the included 'random in range' function, which picked a number at random and activated the corresponding child when using that number as the index for the GetChild() instruction. This was rather robust...except for one issue.
There could be duplicates! Meaning if 8 were requested, there was nothing to stop audience member #6 getting called twice, leading to only 7 individual members laughing. My solution to this was to add an array of all the possible numbers, put the RNG (random number generator) into a while loop, and each time, check & remove the number. Because in C#, removing a value from an array returns a boolean. So my logic was: if the value had not been picked before, the operation would successfully find and remove the number, returning true and allowing it to proceed. But if the same number gets asked to be removed again, false will be returned because the number is not there to be removed, and the operation will fail. As this is inside a while loop, a number keeps getting drawn until it's unique.
![]() |
Bounce function that selects the member and activates it |
This took the entirety of Tuesday to complete, but by the end I was very happy with the result:
Later in the evening, I also added the spotlight you see in the video above using Unity's rendering pipeline and the keyframed animation that makes it move slightly. While not shown above, when you first enter the scene, there is a delay before it comes on to give the feeling of the show 'starting' before your eyes. To achieve this, I had the spotlight deactivate itself on Start and used the Invoke command to schedule a function that re-enabled it to be played after 2 seconds.
![]() |
Spotlight code |
Day 3 - Work from home day
On Wednesday, each of us was unavailable at a different point in the day, so we made the decision to just work from home. The morning was spent in a Discord call coming up with all the "jokes" we needed for our comedian to use. Since none of us are inherently funny, we decided to use the jschlatt approach of saying totally random and bonkers 'punchlines' without the question. This is, in fact, a very effective method as it allowed us to right literally whatever we wanted and the left field nature of it makes it arguably funnier than an actually clever joke.
Things such as "This is joke number 32", "Laugh please" and "Han shot first" are just some of the many "jokes" we created. Given that we had to make 64 of these aswell, using this approach was actually a huge time saver.
Later in the day, I was in charge of the Callouts. These being the words "Excellent", "Good" and "Bad" which were supposed to be presented to the user after telling a joke to clearly display the feedback (you can see them in the first video). Think of it like Wii Sports bowling, where you'd hear "Strike!" or "Nice shot" after each attempt.
I achieved the effect by putting the images under a parent called "Callout_Manager" who held 4 boolean values, one for each tier. At the start, all the children were disabled so that they are hidden. When any of the booleans became true, the manager would check to see which it was and enable the corresponding child and play its attached animation that I also keyframed like the Spotlight. After 1.4 seconds (the length of the animation), the Callout manager disables that child again and resets all the booleans to false, ready to be triggered again.
![]() |
Callout Manager functions and Start method |
![]() |
Callout Manager main code |
Later that evening I did a small experiment on an idea I had to add a transition between scenes using more keyframed animations. My idea was to have a square UI 'Raw image' at the bottom of the canvas (so that it appeared above all other elements) on either sides of the screen, just outside the camera view. When a scene change was triggered, the animation would play to make the image cover the screen before switching.
On the next scene, the animation would be played mirrored and in reverse to hide the fact that the scene had in fact changed and instead give the illusion that the player moved seamlessly. A demonstration of it can be seen below:
Unfortunately, when other elements of the game were added from the other coders, it became difficult to ensure the image was on top of all other UI elements as some were created during runtime. The menu's are also not split into different scenes either, leading to more issues trying to make it work there aswell. Coupled with these problems and time constraints, the idea was cut entirely. While great in it's controlled environment, it was simply too hard to naturally impliment into the whole game. However, I never got a chance to remove the halves from the Performance screen and so it still partly plays in a rather ugly way.
Still, I'm glad I gave it a shot as I will likely re-use the principle in future projects.
Day 4 - Audio & Merging
Today was merging day. Up until this point, we had each worked on a seperate branch of the project using GitHub and so now was the point where they all had to merge into one, complete game, and hope all our code worked together.
The thing was though, GitHub had been a bit of annoyance up until this point, and Gaby was not a fan of it, so she resorted to ol' reliable: copy & paste everything.
So while she mentally deteriated dealing with that task, I looked into adding sound effects to my branch as it would be really easy to redo in Gaby's copy once I understood what to do.
The sound effects themselves had already been procured by Grace so it was just a matter of adding them. But here lied the first hurdle: there are 4 response tiers, each should have atleast 1 unique sfx, so what would be the best way to do that?
My solution was to have 4 arrays of audio clips - one for each tier - and a 5th array to be used as the "main" array. Upon being activated, AudeinceControl now looked at an object called LogicManager to find out what the tier was and used it to define how many members should laugh as well as assign one of the 4 populated arrays to the main array.
![]() |
AudioClip arrays |
![]() |
Tier_Select method added to AudienceControl |
After running the loop to select members to laugh, AudienceControl then selected a random clip from the main array and played it. All the clips were made to be the same length, and so using Callout Manager's Invoked reset method (which now reset the booleans inside Logic Manager rather than its own), the Audience are switched on/off in sync with the Callouts.
![]() |
Updated AudienceControl with sfx code |
By the end of today, we had a very promising looking game. There were still things to be done and bugs to fix, sure, but it could all be managed before the deadline tomorrow. What could possibly go wrong?
Day 5 - Murphy's Law
For most of the morning Gaby was fighting a problem she thought she'd fixed the night before, but apparently not. I worked breiefly on adding the music to the game and also the murmering sfx at the start of the performance scene before the spotlight comes on. This pretty much consisted of finding the right object to stick this line of code into their script for each scene:
The soft deadline was 3pm, at which point an expo would begin until 5pm for everyone to showcase what they had done. As Gaby franticaly tried to fix the bug that was preventing the game from being playable and simultaneoulsy adding a review screen for after your performance, the rest of us more or less just had to wait or work on areas around the game like the submission page.
At 2:30pm Gaby successfully fixed the issue and the game was playable! Now, Em and I just had to fill in the bits we had worked on in the meantime and at 2:15pm the game was ready to be built!
So we built the project....and it was broken.
We tried again - could've just been a bad build - aaand still broken.
By 3pm we were unable to figure out why the project broke during the build process (Unity did sometimes report an error in the console but it was so generic we did not know why it was happening and intermittently) So we had to submit as is. Luckily, the game ran fine in-engine and so for the expo we just fullscreened the game window and let everyone play it inside Unity.
After the expo, we celebrated with another team at Mcdonald's. Despite it not being publicly playable, I was still incredibly proud of what we had achieved in only 5 days. It was perhaps the most progress I think I'd ever witnessed in such a short timeframe. Between the concept, the functionality and the polish, I feel this is one of the best projects that I have had the pleasure of working on so far.
And it's not a sad ending either, because as I said, 3pm was the soft deadline. The actual point at which submissions closed was 10pm. And the thing about programmers is that, when they want to do something, they won't stop until it's done.
At 9:30, Gaby managed to fix the issue and create a fully working build! While you will need to see her Devlog for the details, I believe the issue was related to the csv file containing the jokes not being properly compiled into the builds by Unity.
And so, with about 10 minutes to go before the real deadline, the working game was uploaded!
And that's it. The Global Game Jam 2024 is over. I really enjoyed my time with it overall. Having entered with absolutely no C# or Unity experience I learnt so much from this event. My team was an absolute blast to work with and I'm really happy with what we accomplished.
The GGJ submission page can be found here and the itch.io page can be found here (go follow the producer, their last jams did not go as well as mine but they are really talented and hard working. They'll make some great things in the future you don't want to miss).
- JDM
Comments
Post a Comment