Here's a gameplay video that shows the result of my efforts:
The rest of the post will provide some narrative about how the project came about.
Inspiration
When my wife saw what I was working on, she said, "This used to be the only kind of game." It's true that my inspiration for this project draws from games of my youth: the 1980s and the Commodore 64.
One of my favorite games on the C64 was Krakout. Check out this video of a chap playing the first several levels of the game, and tell me if that's not some of the best SID music you have ever heard. It amazes me what some of these guys could do with three-voice polyphony. On top of that, Krakout had the trippy option to scroll the background with the ball and, of course, some kind of destructible floating heads. I didn't go back to watch the video until after the jam, when I was writing this post. Now I have that music running in the background, and I'm digging it.
I thought about it a bit Friday night and got up Saturday ready to dig in. I decided to use Unreal Engine 4 for a few reasons. One reason was that there were some specific engine features I wanted to explore. Another was I was hoping to encounter some ideas to wrap up my semester's work on tutorial videos that explore Computer Science concepts in UE4. That project has taken a back seat to just trying to keep the lights on this semester. Fortunately, my playlists exist outside of the semester timeframe, so I can continue to explore the project beyond this semester.
What Didn't Make It In
A Little English
One of the ideas I wanted to explore from a game design point of view was the effect of torque or spin on the ball. I have never seen a breakout-style game that does this, so I thought it would be fun. My hope was that I could get this all done using UE4's physics engine. I tinkered with it for maybe two hours Saturday morning and made little headway, but here's some of what I learned.
I was able to get a ball launched so that when it bounced off of a wall, it was given appropriate spin. However, this also slowed it down. It slowed down because of restitution and friction. Restitution was easy to change without hindrance, but when I removed the friction from the surfaces, the ball no longer gained any spin. In retrospect, this is sensible: of course it is the friction of the collision surfaces that would impart a rotation. In a pure, frictionless, inelastic collision, we would expect no other kind of force to be involved. I could not reconcile the desire for spin and the lack of friction, and I had no gameplay at all at this point, and so I abandoned this line of research.
Later in the day, I read about the potential of using a physics motor to drive the forward momentum of the ball, perhaps in a way that I could increase its energy on each bounce instead of reduce it. However, at this time I had decided I could not afford any more effort along these lines.
I had switched instead to writing my own collision logic. I have always had a hard time remembering fundamentals of linear algebra: I feel like I am always learning it from scratch. I was able to do some research to discover the formulae I needed for bouncing off of arbitrary surfaces, and I found this StackOverflow answer to be particularly useful.
After programming the basic bounce logic, I jumped back into trying to get some spin on the ball. I spent too long on this without yielding any fruit, but I do want to share a small success. At one point, I determined that the spin I wanted to add was determined by how the original vector and the resulting bounce vector intersected. Dot product was being used to determine the new angle, but how could I sort out if I should impart a clockwise or counterclockwise spin? Cross product! Yes, friends, I remembered something from linear algebra and deployed it successfully. Huzzah! Unfortunately, I was sinking way too much time into this little bit of polish, and at lunchtime still didn't have a playable game, and so this all went into a dead branch on git.
Destructible Meshes
I had read about UE4's destructible meshes but never used them, and I thought this would be a fun way to use the 3D engine to enhance an essentially 2D gameplay experience. Making destructible bricks was not too difficult, and I quickly had them exploding nicely into shards. Unfortunately, these shards were then bouncing off of the gameplay bounds, but only sometimes: some shards passed through, and some bounced off at very high speeds. I fiddled with the collision settings so that the bricks only collided with the ball but not the bounds, but then the result was that, even if I turned off collision at the time of explosion, some still collided with the ball. It seems to me that the handling of collision on these shards is inconsistent. I followed some advice about tweaking the size settings so that they would not collide at all, but I did not find the engine simulation to match the suggestion. Of course, I was doing destructible meshes in an unconventional way, and so the best I can figure is that what I wanted to do is not what this technology was designed to do.
I looked into the new Chaos destruction engine, which I had seen mentioned in a livestream months ago. I knew plugins were available for it, but I didn't realize that you have to do a 4.23 source build for them to work. That put a mercifully quick end to this exploration.
This was another Saturday morning exploration of polish that left me a little smarter but without any kind of playable game. Over lunch, I expressed my frustration over not following the advice to get something working first, and then add polish. My justification is that the whole project was really just an exploration of polish: of course I knew I could make a breakout game, but the crux of it was making it with these particular ideas in place. My wife said, "Learning is slow." I responded with such vigor that she thought I was mad, but I was really just emphatic about how right she was. Her perspective was that perhaps a game jam was not time to learn something new but to deploy what you already know. Well, maybe next jam I'll try that approach, but I do tend to use them as a timeboxed exploration of something new.
Level Sequences
For all the time I've spent in UE4, I've never really designed levels. My major UE4 release, Kaiju Kaboom, uses procedurally-generated levels. I've never used the level sequencer or matinee to create in-game cinematic effects, and I thought this would be fun. In Goofball, I did explore this for doing some fun camera work, but in the end, everything I was trying to accomplish I could do better with other techniques. For the camera, it was a simple Set View Target with Blend node. I was hoping to make the main game camera do a sort of figure-eight movement that gets more intense with each level, but I ran out of time before I could explore that.
Spline Meshes
I originally had simple stretched cubes to establish the game area, and as I was replacing placeholders with real assets, I realized that this might be a good opportunity to explore spline meshes. When I first learned about spline meshes—probably years ago—I was blown away. I had never really thought about how I would model something like a highway before. Again, because I don't really do any level design as such, I never came across a need for them. It got me thinking, what if I designed the boundary of the game with a spline rather than a static model?
This was driven by whimsy: unlike torque, destructible meshes, and level sequences, I did not come into the jam wanting to explore spline meshes. I was able to get a simple and ugly spline mesh laid down, but what really surprised me was that I was getting collision from the path of the control lines rather than the rendered path of the geometry. I am still puzzled by this, but I realized pretty quickly that this was not core to the project and just dropped it. Perhaps I can take a closer look at this in a future project.
Gameplay Cues and Decorators
Along the lines of finding topics to pursue in a tutorial video, I had hoped to get Goofball to the point where I could explore either simple gameplay cues (as I wrote about a few weeks ago) or the use of the Decorator pattern for stacking powerups. Unfortunately, neither worked out. I never got beyond simple ad hoc handling of visual effects, and without any powerups at all, there was no need for decorators. Still, I think both are interesting topics, and I need to consider whether it's worth making a video tutorial on these. I would have to spend a few more post-LD46 hours with the code to see if I've left it in an amenable state.
What did make it in
Now, let's turn our attention to some of the cool things that are in the game.
Fisheye Lens
One of the main drivers for this project was to play with visual effects, and chief among these was the idea of a fisheye lens effect. You know what I'm talking about. Waughmp Waughmp. I didn't have any idea how to do this, so I started a-Googling. There were a few mentions of FOV tweaking, but I saw some cautions against that and never tried it. I went right for what seemed to be the consensus approach: a post-processing volume. This was exciting to me since this was another thing I had never done.
Emre Karabacak's tutorial was helpful in getting me set up with a minimum working material. My next step was to consider how to stack the effects. Timelines are a useful way in UE4 to play a canned animation, such as running the fisheye intensity up and down over half a second or so. What I wanted was something more like curve addition or scaling. While the intensity is rising, the ball may hit another brick, which should raise the ceiling and extend the curve. I looked into UE4 curve assets (again, something I had never used), but I did not see a way to operate on them, only to evaluate them.
I ended up using the magic of FInterpTo with a pair of values. The target intensity indicates the peak of the curve, and every time there's a ball collision, that intensity increases. That target also falls toward zero. The interpolated intensity will always move toward the target intensity. The result is exactly what I wanted: a peak that can raise arbitrarily high, but falls off, and a visual effect that tracks it.
The standard tutorial code one finds online for a fisheye material has it centered in the camera, but as I played with my game, I realized it could be interesting to track the ball's position with the effect. This also presented me with a good challenge: had I learned enough of the UE4 material editor, and did I understand the fisheye demo code well enough, to make these changes? It took little time and it looked great, so I was happy when this all came together. The basic idea was to get the ball's position, project it to screen space, and then normalize this to put it into UV space. This is then a modification to the RadialGradientExponent's center parameter.
I knew early on in the project that I wanted the profusion of visual effects to be the main source of difficulty escalation in the game. Most breakout games do this with level design, but I wanted to tinker with technology rather than clever arrangements of blocks. One of the last features I added was that, as the player clears boards, the amount of intensity added by ball bounces increases by 50%. This is really disorienting to me as a player, and I think it delivers on the "goofball" promise. (Here's a treat for my readers: Numpad * kills all the blocks on the screen. Use this a few times in a row to ramp up the level, and then check out the insane visuals. Enjoy!)
Having dropped the physics engine and the destructible meshes, I do wonder whether I could have done this effect with reasonable performance in Godot and built the game for HTML5. This certainly would have made it easy to share. I barely know the syntax of GLSL shader programming, but I wonder if my time spent in the UE4 material editor would give me a good grounding for getting into it. That's a challenge for another day.
Music!
The last time I composed any music on my computer was for Please, Sir, my entry to Ludum Dare 28 in (yikes!) 2013. It seems I did not write a blog post about it, but I found my GitHub repository. I remember writing the soundtrack in Rosegarden, which I chose in part because it was most like the Cakewalk composition software I used back in the 1990s to record an album of MIDI tunes. I used to spend a lot more time making music than I do these days, and I miss it sometimes. You can't do everything... but you can do game jams!
One of the things I love about Ludum Dare is that it pushes you to be a renaissance man. The Ludum Dare compo requires you to make the whole thing yourself: programming, design, art, music. A few years ago, they added the Ludum Dare jam, which allows you to work in teams and use others' assets. Given that there's basically infinity jams on the Internet going on now, I don't know why someone would want to come to Ludum Dare to do that. While the Ludum Dare site describes the compo as "hard mode," to me, that's really what makes it stand apart from the rest.
In any case, I was excited to get into LMMS and figure it out. I mentioned before that this was the tool that I recently learned about and then coached my son into using for our March FamJam. Here was my chance to write something for it myself.
One aspect of the tool that surprised me is that every beat/bassline track has the same instruments. This is not at all obvious to me from the interface: it looks to me that my song can have any number of beat/basslines, so why should they be different? This cost me some time as I worked on one beat, set it aside, worked on another beat, and removed instruments from it. Then, back to the original beat, and things are missing.
After making better sense of the interface, I laid down a beat and a single repeating melody, which was just a simple doodle in A minor. My plan was to come back to it and add some more panache, but I ran out of time. Well, what actually happened was that the time I was going to use to add some more audio interest was instead spent helping my son publish his own Ludum Dare entry, but that was a good use of my time.
The number of samples and synths in LMMS is overwhelming. However, it also became a great source of sound effects. All of the sounds in Goofball are pulled from LMMS. My original plan was to tinker with something like bfxr, but as I worked with LMMS, it seemed crazy not to use it.
Color Palette
I did not have a particular look in mind for the game going into it. While the rest of the family was engaged with Ludum Dare participation, my wife was working on a puzzle:
The pieces were spread across the kitchen table, and the color scheme caught my eye. I started replacing placeholder materials with some drawn from this palette, and I dropped the background to pure black. I'm not sure what colors I would have ended up with if my wife hadn't been working on this puzzle, but I was pleased to find such convenient inspiration.
Family
I wasn't the only one working on a Ludum Dare 46 project. As I mentioned above, #1 Son made his first official entry to the jam. He's a teenager with his own GitHub account, and so I hope this was a good experience for him. There doesn't seem to be any minimum age for Ludum Dare accounts, but the infrastructure around it benefits from things like email and Web-based repositories. #2 Son also worked on a game in Construct, although he said he did not want to actually submit it to the Compo. However, this morning, he mentioned that he wouldn't mind sharing it with family and friends, so folks in those categories can watch my Facebook page for links later. #3 Son also made an entry using Kodu. He really wanted to share it, but I don't think it's quite the right time for him to do so, and I'm not sure I can export a game out of Kodu anyway. Still, I love his spirit. #4 Son spent the weekend mostly watching his brothers make games. I suspect that one of these days his interest in reading will kick in, and then there will be no stopping him.
That's the story of Ludum Dare 46 from my perspective. I hope you found something interesting to take away from it. Thanks for reading!
No comments:
Post a Comment