top of page

Post-Mortem coming soon...

Introduction:

For this project my cohort was given a tile-based game template, that came with basic “movement” and “interaction” functionality, tilemap & related classes, as well as rudimentary shapes as a starter visuals.

The goal of this project was a.) to take this basic game template and design/implement a new mechanic for it, and b.) implement a peer’s mechanic alongside your mechanic, to then c.) design an overall puzzle incorporating the two mechanics.

The mechanic I designed and implemented was a teleporter that - when lined up with and facing towards it - will teleport the player to a position perpendicular to the player’s current position and as many tiles away from the teleporter as the player is when engaging the teleportation. Essentially teleporting in an L-shaped path.

The second mechanic I implemented was designed by Dalton SooHoo, which builds on the initial interaction functionality of “pushing” objects belonging to the ‘interactable’ class several tiles away (in a straight line in the direction the player is facing) from the player versus onto a single adjacent tile (in the direction the player is facing). 

 

What Went Right:

 

  1. Teleportation - The whole functionality of my teleporter consisted of multiple functions. The function that specifically changed the location of the player from their current position to a target position was fairly straightforward, and largely emulated the provided movement function.

  2. The “In-Line-with-Teleporter” Check - This function was a logic-driven stand-in for a raycast used to check if the player character was lined up with the teleporter (a condition necessary for teleportation). Basically it stepped through a series of “next” tiles - in a determined direction - within a loop that needed to meet a certain condition before exiting that loop (and setting variables along the way of course). It also incremented an integer variable that served as a counter to how many tiles away from the teleporter that the player was. Admittedly it was buggy from the onset (which I’ll get into in the below section of ‘What Went Wrong’), however the logic felt pretty straightforward in designing it and (broadly speaking) stayed the same even when debugging for the most part. When it worked as intended, it wasn’t difficult to then integrate it into the larger functionality of the teleportation itself, and later to relocate from the input action of teleporting to a continuous calling per movement (more on this in the 4th entry…).

  3. The Teleportation Configuration - After determining the “In-Line-with-Teleporter” check was working as intended, I then needed a function that would determine the target tile to be fed to my teleportation function. This function was similar to my “In-Line-with-Teleporter” check, reusing some of the logic and implementation. Where it differed was that it took the counter (int var) set in the “ILwT” function and used that against a new counter, using them being equal (==) as the condition to end the new stepping through “next” tiles loop. The final tile then being the “target tile” to set and therefore fed to the teleportation function.

  4. The Aiming Target / Feedback - After getting my mechanic to work, I wanted to make it so the player had visual feedback for when they were lined up with the teleporter and, more importantly, which tile they would teleport to should they activate the teleportation functionality. This required the “In-Line-with-Teleporter” check to occur continuously and update in realtime. I figured then that that would require me moving its call from the teleportation input action (where it was preceding both the configuration & teleportation function calls) to proceeding the movement function. This was actually fairly painless and worked as expected. I realized pretty soon that it would also need to be called after the “update player direction” function as well, since that would obviously require a new check as well. I didn’t lose much time making this change. Next was making a new function very similar to my “configuration” function, but instead of resulting in setting a teleportation target tile, it would instead activate a particle effect that would give feedback as to what tile is being targeted. After that it was a matter of deactivating the “aiming target tile”’s particle system on the onset of the call (which would deactivate the previous targeted tile’s particle effect, to serve as a continuous update). I figured this would throw error at the beginning of the game as no “aiming target tile” is initially set, so on began play I set that variable as empty.

  5. Assets Galore! - After getting my mechanic to work I decided to not only dress my game with some art assets, but also practice previous things my cohort and I learned in Unreal. I began not only replacing the basic visuals with 3D assets, but also implementing animation states for my character, as well as adding sound as both feedback and ambience, creating and implementing widgets for HUD UI as well as splash screens, etc. After implementing the second mechanic into the game, I continued to add and adjust these bells & whistles until the due date. It was a great refresher to things we were previously taught, as well as a great lesson for some new tricks and implementation approaches. Also it was just a lot of fun!

  6. Puzzle Design - Designing puzzles with my (& peer’s) mechanics was the most daunting aspect of this project to me initially. However, it ended up being not so bad and actually pretty fun. I just took it in steps, making adjustments as needed, until I took up most of the playable area with these puzzling steps at which time I just tagged it all with placing the win tile. It was relaxing too (surprisingly!). I think if I need a de-stresser any time soon I might just create more levels/puzzles for this game.

 

What Went Wrong:

  1. The “In-Line-with-Teleporter” Check - As I said above, the overall logical structure of what I was trying to implement stayed rather consistent throughout, and it worked swimmingly when finished, however at the beginning I was stuck at one portion of it. That portion was the ‘While’ loop necessary for this function to work. I might’ve been confused whether the ‘While’ node was entry or exit controlled (as UE has no ‘Do…While’ node as far as I know). I wondered if maybe I needed another type of looping node with a break, or just whatever it was I didn’t know that I didn’t know. I was so frustrated, pseudo-coded again and again, was at the point of tracing the wires on screen with my finger, and then finally pleading to the node to fire off the ‘completed’ code as I knew the condition was being met. It also kept creating infinite loops, requiring me to turn UE off & on several times while debugging. The executed code also branched off into some additional conditional checks as well (for example, when the line-check would eventually hit a wall, which would be the bounds of the line-check), so I thought maybe all those alternate routes weren’t being handled somewhere (thus the infinite loops). The some false or true pins to these conditional checks I left blank, but I thought that was ok to do (works in other places in the code). I started to wonder if all routes needed to end somewhere (even if just to a print screen node), within a while loop for whatever reason. To be honest, I don’t even remember how I eventually solved it, but I did finally get it to work without having to scrap the ‘while’ loop altogether.

  2. Another Dang ‘While’ (Infinite) Loop - After solving the infinite loop issue with my first while loop I thought such a thing wouldn’t happen again. However, some of my later functions also needed a ‘While’ loop node, and sure enough I managed another infinite loop in implementing one of these functions. I forget if it was the ‘Configuration’ function or the ‘Aiming’ function, but one of these functions resulted in an infinite loop much to my dismay. Not again! However, unlike the previous incident (which I lost an embarrassing amount of hours to) I was able to get it to work properly after only 15-30 minutes or so… much to my relief.

  3. Pick-Up Item - Wanting to keep my game as “data-driven” as possible, utilizing the existing functionality of the game template and subsequent data derived from the tile system, I decided not to use an overlap event to “destroy actor” for my pick-up item (as is the more conventional approach). It was the same reasoning behind not using a raycast in making my “In-Line-with-Teleporter” function, but unlike the “ILwT” function this implementation seemed a simple enough thing. All I really needed to do was call “destroy actor” when the player entered the tile that contained the item. What I failed to consider was that the tile still contained that particular interactable, from a functional point of view. This might never have been a practical issue however, as I made it so that the player could always enter these tiles, and also my puzzles never worked out that the player would need to “push” the teleporter (another child of the interactable class) to a tile that previously contained a pick-up item (which would’ve probably thrown an error when trying to set the tile to a new interactable, or just erase the previous; not sure which). Also if the player tried to engage the input action designated to interacting with that tile again (after the pick-up item was “gone”), they’d be all the more ignorant that the interactable still existed (in behind-the-scenes logic) as the main result of consuming this item was to enter a “Super-Charged” state. If you are already in this state, nothing would happen if you try to enter it again. HOWEVER, there is an alternative use / conditional dependent functionality of consuming this interactable. If the player is in a “weak” state (based on a disparity of necessary “stamina” (an integer value), the item does not put them in the “Super-Charged” state but instead increases their stamina by a small amount (+15). But again, I probably still wouldn’t have realized these tiles still contained “invisible” interactables following their respective 3D actors being destroyed, as normally after consuming the item the player would more than likely move on from that tile (and not have any reason to attempt to interact with it again). I only (finally) discovered this “bug” when I accidentally spammed one of these tiles (while in the “weak” state) and noticed my stamina increased by 30 (not 15). I tried again and sure enough I could increase my stamina indefinitely until I was able to reach first the regular “Idle” state and then the “Super-Charged” state. Luckily I figured out what the reasoning was before I finished uttering “wtf” (I think all I got out was “wt-”). Of course there’s still an interactable there. I only destroyed the 3D asset upon completion, but the tile needs its “current interactable” var to be set to empty. Quick fix, but that one almost slipped through the cracks.

  4. 3D Model in Widget - For my splash screens I wanted to see if I could somehow convert my 3D models (player character, teleporter, and pickup item) into images to use for both a title & instructional page. A google search brought up a “How to Render a 3D Model in UMG” tutorial. It was even better than I expected! The end result is something like a livestream of a placed 3D model being “filmed” by a SceneCapture2D camera component. Therefore if your model has an idle animation or a particle effect (as my character and teleporter did respectively), those would be animated within the widget! I was excited. This could be used in a HUD, but I was just using it for a splash screen so I made a designated level for each screen/widget. The first one worked out perfectly, looking even better than expected. The second however, was wilding out for reasons (still) unknown. Not only did I lose the lighting on the “filmed” 3D models being fed to the widget (I think I know why that happened actually), but for some strange reason a semi-translucent portion of my main character’s face filled up the entire top area of the screen covering up most of the widget, bouncing about in the manner of its idle animation. This was really bizarre because none of these SceneCapture2D components were pointed at a placed model of my character (that was in another designated level used for an entirely different widget). This widget was just “filming” and using my teleporter and pick-up item. It was such a strange occurrence that I quickly gave up trying to debug and ended up just making a brand new level, placing brand new models, with brand new captures for the widget instead. So the “title page” widget worked just fine, but the “instructional page” needed two drafts.

  5. The Mysterious Errors (that don’t actually interrupt gameplay) - The game works as intended (at least it seems to anyway), but ever since moving the “ILwT” function to the player movement & update direction functions (to make the check continuously in order to facilitate the teleportation aiming target feedback), I get errors after each playtest session. Mind you, these errors don’t affect/interrupt gameplay (nor does any functionality seem to be not in order), but they persist all the same. The error doesn’t actually point to the “ILwT” function, but I think it exists because that function is continually in use. It actually occurs on a “set next aiming tile” node in my “aiming configuration” function. For the sake of time, I didn’t spend much time debugging, but I think it might have something to do with handling the condition of not finding a tile to set. Which actually doesn’t really make sense or else one would imagine an infinite loop would take place (much like in the “ILwT” function if I didn’t have it stop at finding a “wall tile”). So to be honest, I’m not sure what’s causing it. I tried to put an isValid check before the node in question, but that led to infinite loops (I guess I should’ve seen that coming). I still don’t know what is causing that error, but as it’s not really affecting the game I’m not overly in a rush to try to debug it.

So in conclusion, I had a lot of fun with this project! It’s really the first time I felt a sense of “making a game” even if it was just three miniscule levels, not the most uniform in terms of art direction, and messy (mostly uncommented) code that could definitely be cleaned up and refactored. I felt like I got great practice in scripting, a refresher in a few other areas of Unreal, as well as learned brand new things altogether! I look forward to other similar projects, and might even continue developing this game for practice, my own enjoyment, and as something to add to my (currently non-exist) portfolio.

bottom of page