Hello, and welcome back to another Grippy Golf devlog!
When we last left off, I had finally gotten my game into a state where I was comfortable with putting up a store page. However, there were still a number of core features missing, and a lot of what was there was in a bit of rough shape. So, this past month has been dedicated to filling in some of those gaps, and polishing up some existing features to make sure they shine. Let’s get to it.
If you watched my old trailer, you may have noticed something missing from it: sound. In the past, sound effects have been one of the last things I add to a game. This is not because they aren’t important – sound is a huge part of game feel. Rather, it’s a part of game design I’m just not particularly confident in. However, it felt super weird to have a trailer with no sound at all, and it really started to bother me.
So, I set out to add some audio. My typical approach is to record my own effects when practical, and fill in any gaps with sounds from the amazing FreeSound.org. Then, I make whatever tweaks are necessary over in Audacity, filtering noise, adjusting reverb, changing the pitch, etc. Next, I bring the files into Unreal, where I’ve been experimenting with the new MetaSounds system. I’m still fairly new to it, but it offers a lot of control over when and how a sound is played. For example, with this setup I can adjust the pitch of a firework based on how it spirals, and also trigger new sounds when it explodes.
As I was doing this, I discovered that Unreal has this wonderful class called PhysicsCollisionHandler, which theoretically receives every collision event from the physics system. The idea is that you can handle all your collision sound effects in a single place, rather than splitting that logic across various classes. Sounds great, right?
Well, turns out it just, um, doesn’t work? The function is called, but the input parameters are just blank, so it’s completely useless. By the way, here’s a forum post from 6 years ago describing this very issue, which I guess was never fixed.
That was disappointing, but I decided to just make my own instead. So, my newly dubbed “CollisionSFXManager” is notified whenever a collision occurs, looks at the objects involved, and plays the corresponding sound effect. I came up with a simple but effective priority system, where softer materials will have priority over harder ones. This way, if a stone falls in the grass, you get a nice soft rustle and not a sharp clack.
It still needs some tweaks. In particular, right now it only accommodates head-on collisions, so if two objects are sliding across each other it doesn’t really work. But for now it’s good enough, and I’ve structured my code so that adding that functionality should be straightforward.
As I was working on this, I realized that April Fools was coming up. So, I grabbed a couple friends and had them help me with this gem:
Funnily enough, I actually got a bunch of request to keep this in the game, so now you can go to the audio settings and turn it on, if that’s something you want.
Recording and setting up all the various sound effects took most of a week, at which point I looked up and realized that it was almost Easter. I had been planning to add moving obstacles for a while, and this seemed like the perfect opportunity. So, I decided to add bunnies to my game. That should tell you everything you need to know about my planning.
I started by getting some reference images and modeling up a robot bunny in Blender. Why a robot? Well, it occurred to me that some people might have an issue with running over bunnies with a giant snowball of random objects, so I figured it would be better if they weren’t actually alive. I’m not really sure how well that logic tracks, but whatever, moving on.
The animations for this one were actually pretty fun, and I’m pleased with how they turned out. Now though, I needed to create a system that would let the bunny run around, but also get stuck to the ball.
My initial plan was to use a basic box or capsule collider for all the physics behavior, since my early experiments with ragdoll physics were a bit… twitchy. However, the shape colliders also behaved very strangely, so I ended up just using an invisible static mesh. Not sure why there’s any difference there, but it works so I won’t look too deeply into it.
I then made it so that the bunny will follow the path laid out by a spline actor, pausing briefly at each spline point and stopping if it encounters any obstacles. Just a few tweaks to connect the animation, and I’d say it looks pretty good. In the future, this same system should be useable for cars, pedestrians, and any other “animate objects” that I desire.
Now, a common question I’ve been getting is “how many boosts can you use?”. The answer is one, but you can refresh it by interacting with certain obstacles, like in Celeste. However, up until now the player had no real way of knowing this, aside from just messing about and experimenting.
So to fill that information gap, I created a HUD for the player. The first two features I added were icons, one that tells you when you can boost, and one that tells you what, if any, powerup the player currently has. I’ve also added a mass readout that tells you how much stuff is stuck to your ball, and some bonus objective widgets that I will explain in a little bit.
I’ll be the first to admit that the design of the HUD isn’t terribly inspirational at the moment: graphic design is definitely another area I’d say I’m weak in. But’s it’s workable for a first pass, and I like how information will fade in and out to avoid cluttering up the screen too much.
Since I keep making comparisons to Celeste, there’s something else worth noting: Celeste doesn’t really have a HUD. Instead, Madeline’s hair changes color to reflect the number of dashes remaining. This is a much more elegant approach, since it lets you keep your eyes on the character and keeps the screen nice and clear.
At first, I was reluctant to do something similar for my game, since I was worried that the effect would either be too distracting or wouldn’t be visible when lots of objects are stuck to the ball. That’s something I have to deal with a lot, scaling up particles and camera shake and other effects as the camera zooms out.
However, as I’m putting this post together, I’m starting to change my mind about that. It should definitely be doable to add some glow or other effect that is visible but not distracting, so look forward to that in the future.
Ok, so in the last post I briefly mentioned how I created some powerups for the game. Let’s look at those a little more closely.
My original design for pickups was… not amazing. I was trying to go for a Mario Kart-esq breakable container, but the end result wasn’t terribly appealing and didn’t stand out enough against the background. It also doesn’t help that this was from before I narrowed down the color palette.
So my first step was to come up with a better design. This ended up being surprisingly difficult, as the pickup needs to be clearly visible against all sorts of backgrounds, as well as readable from far away and potentially from any angle. In the end, I kept things very simple, just adding a glowing border around the powerup itself. The contrast between the bright purple and the darker color of the powerup is easy to see, and I quite like it. There was a slight issue where the border would get too thin when the pickup was far away, but I solved that by expanding the border material based on distance to the camera. From there, I just added some simple particle effects and a dash of screen shake, and we were good to go.
My next target was the magnet powerup. First off, at some point during development I had accidentally broken it, so I needed to go back and refactor the code. Once that was done, I went through and juiced up the game feel, adding more particles, screen shake, and audio. It’s admittedly more “black hole” than “magnet” nowadays, but I just love the way it looks. I’m especially pleased with the wormhole at the center, which looks super cool but is actually quite basic.
After that came the firework powerup. This one still worked fine, though I did modify it so that if the firework collided with an obstacle, it would explode early. Again, I ran a polish pass to get the visuals up to date, and you’ve already seen how I connected the pitch of the audio to the spiraling trajectory. To be honest, I’m a little concerned that this one might be overpowered with just how far the player can travel while using it, but that’s a problem for future, game-balance me.
This last one isn’t really a powerup, but I’ll put it here anyways. The idea behind the barrier is that the ball will bounce off if you don’t have sufficient mass, but will instantly shatter if you do. The old design had this neat little ripple effect when it was struck, but it was pretty hard to see. After some fiddling, I realized that connecting the ripple to the material’s refraction index gave much better results.
As for when the barrier is broken, I completely scrapped the old approach, which used Unreal’s Destructible Mesh system but somehow didn’t really feel punchy enough for me. Instead, now there’s a burst of shards, a hearty screen shake, and a ripple across the barrier as it collapses. I’m especially pleased with the audio for this one, check it out:
Everything discussed so far has been largely cosmetic in nature: necessary, certainly, but not impacting the underlying structure of the game. This last topic is different.
You see, there’s something of a disconnect right now in the gameplay. On one hand, you have the sticking mechanic, which is super unique and appealing, but is inherently difficult to control and tends to slow the ball down. On the other hand, my previous playtesting showed that it is really fun to move quickly and dynamically, chaining together these complicated combos. On the face of it, these two systems feel somewhat unrelated, maybe even opposed to each other.
So how can these two halves be brought together? Enter the bonus objective system.
The plan is to have multiple tiers of completion for each level. The first, default completion is simply to get to the goal, in however many strokes it takes. This is for when the player is still new to the level or is experimenting. The second, more advanced completion is to get to the goal in a single stroke, to get a hole in one. This is to push players towards faster, more dynamic, more skilled movement. The third and final completion is to get a hole in one while also completing all bonus objectives. This, simply put, is to incentivize mastery. Players will have to use the level layout in new, creative ways, and they’ll have to respond dynamically to the curveballs thrown at them by the sticking mechanic. Or at least, that’s the plan.
So, I created a new class called the BonusObjectiveManager, which will let me specify objectives for each level. Right now, I have the framework set up for going over or staying under a certain mass, sticking to or avoiding certain objects, using or avoiding certain powerups, and staying under a certain number of boosts. I’m sure I will be adding more in the future as they come to me.
The objectives are then displayed during the intro cinematic, and will pop in whenever they are fulfilled or failed, as well as at the end of the game. I want to avoid overloading the screen if possible, so a lot of this behavior will probably be tweaked.
That’s it for now! Looking back on my progress, it can sometimes be hard to see if I’ve accomplished a lot or nothing at all, especially when there’s so much left to do. Still, I’m happy with the work so far.
One feature you might have noticed was missing in this post was Multiplayer. Don’t worry, it’s still in the plan, I just don’t have much to show off just yet. In fact, I’m calling it right now that for the next month’s post, multiplayer will be the primary focus! So look forward to that.
Until next time,