Categories
Game Jams Learning Unity

A World in Pieces

So, over the weekend I participated in another game jam: Mini Jam 71, the latest installment of a biweekly game jam where each jam involves both an optional theme and a required limitation. This time the theme was “Puzzles”, and the limitation was that each entry had to combine two different game genres.

This, incidentally, seems to be the main thing I’m doing lately, game-development-wise: game jams. I haven’t made any progress lately in the courses on Unity Learn, nor have I started those free courses on Unreal Engine I recently found out about—or made any progress on the games I’d started. I do plan to do all those things when time permits, and I’ll blog about them when I do, but in the meantime participating in game jams is giving me more practice with Unity and maybe helping me feel a little better connected to some sort of game development community.

Anyway, as with the Discord Jam I participated in last week, I basically wasted a whole day deciding on an idea. For Thursday night and most of Friday, I’d more or less settled on one of the two genres for my game being an RPG, and waffling about what other genre to include—should I make an RPG-platformer? An RPG / puzzle game? The concept only crystallized for me and I finally started moving forward when I decided to ditch the RPG aspect after all and make an adventure game / platformer.

As for the “puzzles” theme, well, one straightforward way to incorporate that would have been to use “puzzle game” as one of the genres, and while I didn’t do that, adventure games do of course include puzzles and I guess maybe I could have counted that as fulfilling the theme (though again the theme—unlike the limitation—is optional in the Mini Jam, and entries don’t have to incorporate it at all), but I decided to take the theme more literally, and make the world out of jigsaw puzzle pieces, with the rationalization that some kind of unknown force is making the world come apart into pieces like a jigsaw puzzle, and you’ve traveled to the epicenter of the disaster to try to figure out what’s going on and how to reverse it. (This backstory is not explained at all in the game itself, though it would have been had I had the time to put in a title screen or even just some explanatory intro text… but time was of course limited, and there were other things that had higher priority.)

The puzzle pieces that made up the world were the first models I built for the game, and I ran into some problems initially; when I tried to import some of the pieces, I got an error message saying that Unity “Can’t calculate tangents, because mesh ‘BezierCurve’ doesn’t contain normals.” This was briefly confounding, but fortunately only briefly; it didn’t take me too long to realize that the reason for the error was that not everything in the model file was a mesh; when exporting some of the models from Blender, I’d accidentally included the light and camera as well as the mesh itself. Whoops. Well, that’s one error that I won’t be making at the future—or at least, if I do, I’ll recognize the error message now and know exactly what happened.

Six different kinds of puzzle piece made up the world, though I intentionally used more of some than of others.

(Despite the name, incidentally, ‘BezierCurve’ actually was a mesh. The mesh had that name because it started as a bezier curve outlining the piece that was then turned into a mesh and extruded, and of course the name of the object didn’t change in Blender when its type did. More specifically, the bezier curve outlined one side of the puzzle piece, and then each finished piece consisted of four joined copies of the converted and extruded curve. And if you don’t know anything about 3D modeling and don’t understand any of that uh, don’t worry about it; it’s not important.)

I gave the pieces a very simple texture, flat green on the top and bottom and flat brown on the sides. If I had time, I’d planned to give the texture more detail, making the top and bottom look like grass and the sides look like dirt, and/or make alternate textures for the pieces so different parts of the game world would have variant appearances—maybe in some places the puzzle pieces would look stony, or mechanical—but of course I didn’t have time for any of that, so all the pieces retained that same flat texture and the world looks fairly monotonous. Ah, well. Such is the nature of a game jam, I suppose.

Part of the finished game world, as seen from the player’s starting position.

Actually, aside from Unity, Blender is another thing this game jam gave me some good practice with. I ended up creating a lot of objects in Blender for this game—not just the puzzle pieces for the environment, but over a dozen inventory items (though admittedly a few of these were just combinations of other inventory items and didn’t require whole new models), as well as various other objects the player could interact with but not pick up. I think maybe the main thing I learned about Blender from doing this jam was that I gained a new appreciation for the Inset tool. A small thing, maybe, but it’s something.

Of course, with the limited time of a game jam, some of the models were a bit rushed (or a lot rushed), so some of them turned out better than others; there’s a notable, if perhaps not monotonic, decrease in quality in the models I created later in the jam as time got shorter. Consider this can opener, the first model I created for the jam aside from the puzzle pieces:

That’s 292 polygons, baby! I… don’t actually know enough about 3d modeling to know whether that’s good.

Now, compare it to this… thing, which was one of the last:

“I really want to see that snake selling really nice objects while speaking BanjoKazooie” —ilPrinny on the Mini Jam Discord

(I did reuse one model I made for a previous game, namely the mango from the game I did for Discord Jam, Mutant Mango Mayhem. But that wasn’t so much done out of laziness as it was an intentional callback; I realized I wanted a large fruit to appear as an item in this game, and I figured I might as well use a mango to link it to my previous game, and if I was going to do that I might as well use the exact same model to make the link more explicit.)

There it is, a single gigantic mango growing at the end of a branch, just like in real life.

That’s not to say I didn’t learn anything about Unity, of course. I got a bit more practice with ScriptableObjects. I’d used them before, for the dialogue system that I originally created for Last Day to Live—and that I reused in “House of Secrets“, and also adapted to display messages to the player in this game. At some point, I still hope to flesh out that dialogue system, make it more user-friendly, and maybe put it up on the Unity Asset Store as a resource that others can use in their games, but I don’t know when I’m going to have the time for that. At any rate, for this Mini Jam entry, I also used ScriptableObjects for the inventory items. And it went pretty well, except for one thing that I’ve realized I should have done, and will do if faced with a similar situation in the future. If I wanted to refer in any script to a specific inventory item or dialogue node, I’d have to make sure that script had a reference to the relevant ScriptableObject, usually by creating a public variable to hold the reference and assigning it in the editor. It would have made a lot more sense to have some lookup table of the inventory items or dialogue nodes, a dictionary hashed by the item name or by some relevant keyword for the dialogue nodes, perhaps, so that to refer to any inventory items or dialogue nodes a script doesn’t need a reference to the ScriptableObjects themselves, but only to the lookup table—or better yet to some manager script that maintains the lookup table and provides public functions to fetch objects from it. Something I’ll add to the dialogue system when I get around to refining it, and something I’ll definitely use for my inventory system if I ever do another adventure game in Unity.

Actually, for this particular game, I probably shouldn’t have used the dialogue system at all; this game didn’t include any dialogue trees or player dialogue options, and I didn’t need most of the overhead involved in the system. True, the system did handle displaying the text in an appropriately resized UI window with a custom background and an optional portrait image, but it still might have been simpler to just cannibalize the part of the code that did that and write a function to display the text with the background and portrait that just takes a string parameter, rather than having to create a separate ScriptableObject for each separate bit of text that had to be displayed. Actually, come to think of it, that might be a good feature to add to the dialogue system, too, for situations where the game only needs to display a one-shot message without player response options and all the other bells and whistles that the dialogue node ScriptableObjects include.

But, maybe more so than learning to use any specific feature, I think I’m just getting more comfortable coding in Unity in general. There are a few things in this game that I was afraid would be a big hassle to implement that I ended up surprising myself with how easily I was able to code them up. There’s a system of energy beams that bounces off reflectors that the player can rotate to change their path; I was a bit apprehensive about trying to get that working, but it went surprisingly smoothly. Heck, the whole inventory system, with the items currently in the player’s inventory displaying in a UI window and with the cursor changing to match the item the player is currently using, didn’t turn out to be as hard to implement as I feared it would be… I did run into a bit of trouble at first getting the items to display in the right position in the window, but that didn’t take too long to resolve and turned out to be just a matter of a sign error.

I decided to make the energy beams pink, because… I don’t know.

But maybe the biggest surprise was the save system. I realized early on that a save system would be almost essential for a hybrid adventure game / platformer. There’s a reason that the old Sierra graphic adventures are widely lambasted for the frequent and often unexpected player character deaths and especially for the possibility of unknowingly getting into a dead-end no win situation, while the Lucasarts graphic adventures are praised for their more forgiving nature. In an adventure game, once you’ve solved a puzzle, especially a particularly involved one, you don’t want to keep having to solve it over and over; you definitely don’t want to have to restart the game from the beginning and have to slog through the motions of all the puzzles you’ve already solved. (I’m not trying to tear down Sierra here—they pioneered the graphic adventure genre, and there’s a lot that’s good to say about their games. But I don’t think many people nowadays would disagree that the sudden, frequent deaths and “dead man walking” situations were not among those good things.)

Now, there aren’t many ways for the player to die in my Mini Jam entry. In fact, there are only two: falling off the platforms or being hit by an energy beam. Still, having to start over all the way from the beginning any time one of those things happened would get really annoying, really quickly. I knew that I’d have to include some way for the player to save their progress, but I kind of put it off, and it ended up being one of the last things I programmed in the game. After all, it would mean storing what the player had in their inventory, what had changed about the gameworld, which puzzles had already been solved—it seemed like it would be very complicated to implement. And when I finally got around to implementing it… it wasn’t, at all. It didn’t take nearly as long to program as I’d feared it would, and the one or two errors that occurred were easily found and fixed.

Well, there was one that maybe wasn’t. There are a lot of moving platforms in the game, and one of the savepoints was in a place where it could only be accessed by a moving platform. (Well, in the final version of the game… but I’ll get back to that.) But this turned out to present a new problem. If the player saved at that savepoint, then if the player died they would be returned to where they were when they interacted with that savepoint… but of course when they interacted with the savepoint they were standing on a moving platform. Well, one of the things that hadn’t been saved with the player’s progress was the position of all the moving platforms at the time of the save, which meant that when the player was returned to that position there might no longer be a platform there, which means the player would immediately fall to their doom again… and then again, and again, until the platform happened to show up. This might sound amusing, but definitely didn’t make for good gameplay. But how to fix it? Well, the obvious fix would be to just… not have a savepoint near a moving platform. But was there a way to get it to work? After some thought, I came up with a solution that I thought might fix the problem with just a few lines of code. So I tried to put it in, not really expecting it to work on the first try, but… it did. Almost perfectly. Wow.

(The “almost” is because there was a single platform that kept throwing an error, but that turned out to be only because it was in a different place in the scene hierarchy than all the other platforms so the code (which took advantage of the scene hierarchy) didn’t work for it—but once I figured out what was going on and moved that platform to the right folder, all was well.)

Speaking of the platforms, that, I think, was the most tedious part of creating this game. Positioning all the platforms. There wasn’t code for that—each puzzle piece was positioned manually. Their vertical positions were varied, which was intentional; if two adjacent pieces were at the same height, then the seam between them wouldn’t be visible and it wouldn’t be obvious that they were separate pieces; I wanted the fact that the terrain was made up of puzzle pieces to be obvious. Horizontally, though, they were essentially placed on a regular grid. And their exact positioning was done by manually entering their coordinates in the inspector. Was there a faster way I could have done that? Almost certainly. But that probably would have required editor scripting, and that’s one feature of Unity I haven’t really gotten into. I think editor scripting will be involved in making my dialogue system more user-friendly, too, so at some point I really should look into it. Is there a course or tutorial on editor scripting on Unity Learn? Oh, hey! There is! Guess that’s one I should tackle some time soon, huh?

The full layout of the game as it currently stands, as seen from above in the editor. I haven’t counted exactly how many puzzle pieces there are in total, but… there are a lot.

As is usual for a game jam—or at least as has so far been usual for me in game jams—I didn’t get time to implement everything I’d wanted to. There are several puzzles I’d planned but didn’t have time to actually put in… including the central puzzle of the game! I say the “central puzzle” not because it would have been physically located near the center of the game area—though it would have—but because it was the first puzzle I came up with for this game, and most of the other puzzles came about because of it: either to provide the player with items they would need for this central puzzle, or to provide alternate uses of items needed for this puzzle (so that the fact these items would be used for this puzzle would be less obvious). But that meant most of these other puzzles would have to precede this central puzzle, which meant it made sense to implement them first, which meant when time ran low I had to leave out the puzzle that I’d designed most of the game around. Not that that was the only thing left out, of course; there were a couple of other puzzles as well, including an endgame puzzle where the player finally confronts and defeats the villain responsible for the world falling apart—a villain who, of course, doesn’t even appear in the game now.

(I had even already modeled in Blender an object that was going to be involved in the central puzzle—a cannon, for what it’s worth. In fact, I said above that the can opener was the first object I modeled for the game besides the puzzle pieces? Come to think of it, I think I modeled the cannon before the can opener. But because I ran out of time to implement the puzzle it was involved in, the cannon didn’t make it into the game.)

Okay, admittedly it wasn’t a complicated model…

So does that mean those missing puzzles will never be implemented, or will I maybe one day finish the game and put them in? Eh… well, actually, I just had an idea for what I might eventually do about my unfinished game jam games in general. But I won’t get into that now. Stay tuned.

Oh, and speaking of running out of time, I had a bit of a stressful situation when it came time to submit my game. So, I had a working game, and I was ready to submit, so I hit the”Build” button and waited. The build took a long time, as I had expected, but eventually it finished, and… the directory that the game was supposed to have been built to was empty. Oh, silly me; I’d forgotten to add the scene to the build! So I did that, and tried the build again, and… still empty. And again. At this point I was getting worried. I still had a bit of time left—I’d anticipated that something might go wrong, so I’d started the submission process an hour and a half before the deadline, but still, that wasn’t that much time.

Was it maybe because my computer was out of memory? Maybe I ought to reboot. So I did, but first I copied the game to an external drive so I could try it on a different computer while my laptop was rebooting. Only the other computer turned out not to have the right version of Unity installed, and its internet connection was for some reason not working so I couldn’t install it, and I didn’t manage to get that situation resolved before my laptop had finished rebooting, so I went ahead an reopened the game on my laptop and tried the build again, and… whew. This time it worked. So I got my submission in with time to spare.

Enough time to spare, in fact, that I had time to fix an issue that had been bothering me. As it was, the game allowed the player to interact with an object as long as it was visible on the screen, no matter how far the player was from it. Not only did this not make sense, but it allowed several puzzles to be easily bypassed. (No need to figure out how to get past the monster when you can pick up the object behind the monster from the other end of the map!) So I wanted to make it so the player had to be within a certain distance of an object to interact with it. Now, how to do this? The first solution that came to mind was just calculating the distance between the player’s position and the object’s position. But that could present issues with objects like, most notably, the energy beams, which are very long; the player could be right next to the beam, but very far from the center of the beam, and so the distance would be very large between the player’s position and the beam’s position. No, what I wanted was to calculate the distance from the player to the nearest part of the object’s collider. Was there an easy way to do that? Searching turned up a function to do just that—but for 2D colliders. Had I searched a bit harder, I might have found that Unity does in fact include a function to find the closest point on a (3D) collider that would have helped, but I, uh, didn’t find out about that till just now. Instead, I used a different solution: surrounding the player with a big spherical collider with IsTrigger set, and only allowing the player to interact with objects that had entered (and not exited) the trigger. It took some trial and error to get it working, but eventually it did. I’m not sure that was the simplest way to do it—I’ve already mentioned the ClosestPoint function I just found, and it occurred to me after the submission deadline that I also could have used a Raycast—but it worked.

In fact, I got that fixed and the game resubmitted in time to fix another minor issue—when the player clicked to close a dialogue window, if there was an interactable object behind that dialogue window the game registered the click on that object as well. By now I only had about fifteen or twenty minutes left, but this was an easy fix; all I had to do was set a flag when the player closed a dialogue window that prevented that click from being counted in the mouse manager script (and that was then automatically unset later in that script’s Update function). It took five or ten minutes for me to make the fix, another ten minutes or so for Unity to rebuild the game, and then I got the doubly fixed version submitted less than a minute before the deadline. Phew.

Granted, the game might still have other minor issues I didn’t find and fix in time, like the fact that I accidentally placed this monster facing away from the player so you only see the back of its head. Oops.

So anyway, what are my next steps? Well… all the things I mentioned in the second paragraph of this post are still things I want to do: continue with Unity Learn; start learning Unreal Engine; keep working on some of the games I’ve started. But also, I’m going to keep participating in game jams; they’re good practice and a good learning experience, and, of course, they’re fun (albeit stressful). There are a few upcoming game jams I’ve got my eye on, including of course the next installment of the Mini Jam in two weeks (theme and limitation of course currently TBA). So, of course, any and all of these occurrences I’ll blog about when they happen.

Oh… and come to think of it, I really ought to play and rate other entries in the Discord Jam and Mini Jam 71. So, uh… I guess that’s probably what my next post here is going to be about.

Oh, yeah, and of course you can play my Mini Jam 71 entry, “Pieces”, on itch.io.

By March Miskin

Hi, I'm March Miskin, and this is my blog, so if you want to know more about me... read the blog, I guess.

Leave a Reply

Your email address will not be published. Required fields are marked *