Download
OpenGL 32-Bit: Download Here
OpenGL 32-Bit: Download Here
Because I wasn't in class, I recorded my Presentation.
I had originally proposed that for this project, I would make a multiplayer Pachinko game. The goal of the game would have been to give the players a little bit of freedom to move before their character falls, and then leave the rest to collisions and fate. The winner(s) would have been the people whose objects fell into the highest score-zone.
This idea was inspired by stream-integrated games, which don't require much input from the chatters. However, because my game would exist entirely in a context outside of Streaming, I realized - and this was reinforced when I started testing this game idea - that it would be pretty boring. I know the goal of this project wasn't to make a work of art, but I decided to change direction.
Instead, what I ended up making was a synchronous competitive Doodle Jump game.
The goal of the game is to climb higher than your opponent. You can see the other player's position, but you can't block each other or use their platforms.
The sides of the screen wrap around back to the center.
If you touch the underside of a platform, it will still send you upwards.
Client-side only, the background starts as a light blue and gradates towards black as your altitude increases. There are two thresholds: the atmosphere threshold, which is a dark blue, and the space threshold, which is black. Once you've reached these, your client retains their minimum state even if you fall back to the bottom.
The player's controls are ArrowKeyLeft to move Left and ArrowKeyRight to move Right. Each player can reset their platform by using Backspace so that they can resume climbing if they fall. Each player also has two Rocket charges to try and recover if they miss their jump. By inputting Down-Left-Up or Down-Right-Up, the player can use a charge. Each charge can only be used once, unless the player Resets their platform.
I chose the Doodle Jump angle to make something which was fun and interactive while having a very simple core. During this semester in my Studio class, one of our Professors advised us to play Astro Bot as reference and inspiration for the game we were working on. I took that advice, and was reminded how fun a game with a very simple foundation can be. Because I normally prefer to play Fighting Games and MMORPGs, I'm used to having a lot of buttons and complicated overlapping mechanics. It was a good reminder!
For this game, I wanted to capture the floaty, bouncy feel of Doodle Jump. When I was first trying to nail this, it was not working out. Between my gravity and the drag factor I'd added to Rigidbodies, the character fell far too quickly immediately after a bounce. Even if I added a huge amount of velocity, the player would almost appear to teleport off-screen and then rocket back down like a missile. Making Gravity lower and even restricting my Terminal Velocity variable weren't making it work the way I wanted - in those cases, the player just fell very slowly.
I'll talk about this more later, but Yan's Collision System was handling that situation incredibly well! My player character was dropping at meteoric speeds and still wouldn't phase through a plane.
I ended up making two changes: Removing Drag, and giving the player a propulsion period after the bounce. During the propulsion period, Gravity is not applied. By doing this, I was able to recreate the slow, floaty bounces that Doodle Jump had, where the Player gets time after their bounce to rise up and figure out where they're going next.
An emergent gameplay style came out of the way I'd programmed the reset. The player could just keep resetting to keep recovering their Boost charges and infinitely fly up into space. While this is incredibly easy to fix, I decided to leave it in because it creates another way to play my game and I found the button mashing fun.
I didn't deviate much from the assignments, so it fit in the Engine insofar as it's a game using custom-exported 3D models. My GameObject class was made to bundle up the Mesh and Rigidbody together, which ended up working very well for manipulating my Player objects.
Because my System is an Input System, the sheer fact that the player uses Keyboard controls necessitates the use of inputs. Although the more advanced parts of my System are tailored for implementation in Fighting Games, I used the Sequence class to add some more complex inputs to what was otherwise a game with two directional controls. When I was recording my gameplay for my presentation, I put up a keyboard monitoring tool so the cycling keypresses were visible.
One of the advantages I predicted my Input System having is, because it uses the Windows Event Callback loop, it only receives and processes inputs when the game window has focus. I was very pleased that this is exactly how it works! When I was testing multiplayer with two clients on my machine, I could independently control one without inputs being read by the other. This was very helpful both for debugging and for testing.
My System was very easy to use, and I'm happy with how I designed it - minus one issue. As I discussed in my presentation, I needed to declare my KeyboardData variable as "extern" in order to get my HandleKeypressEvents function to not throw errors. My simple but critical oversight was not declaring my HandleKeypressEvents function as "static." It may have also been better to associate it with the KeyboardData class.
Because it is a function associated with the namespace, it worked fine for me when I was testing my Input System on an independent program. I learned the hard way that the non-static approach lacked portability for larger projects where I wanted to use it across multiple linked files. While I consider this a mistake and it resulted in my System Documentation missing some critical information, the troubleshooting I needed to do in order to understand why it wasn't working helped me to better understand these C++ practices.
The bulk of what I needed to do to integrate the Collision Manager
Networking demo
I chose to use Roberto Reynoso's Networking System and Yan Liu's Collision System in my game.
Because it was crucial for my bouncing to work, I implemented Yan's system first. It was very easy to integrate into my system; I simply added the file to my solution and all of the necessary classes were ready to go.
Because the Canvas link didn't link to Yan's write-up and documentation, I started by trying to figure out how to use the system as-is. If I ran into any problems, I would message Yan. However, that wasn't necessary. The way his code was written, it was immediately evident that I needed to create a Collision Manager, assign Collider objects to it in order for it to check collisions between them, constrain the movement of my GameObjects to the resulting positions of TryMoveTo(), and update the CollisionManager via my update loop. It really was that simple - I was both relieved and impressed! This isn't necessarily something I learned from using his System, but rather from his presentation: I hadn't heard about the BVH algorithm before. However, seeing how it produced effective real-time results for my game, it's something I would like to look more into.
For Roberto's Networking System, it was similarly easy for me to implement. In this case, he provided a lot of handles which could be used for Networking. I wasn't immediately sure which handles I would have needed for my use-case, but his Write-up was thorough and with working examples. Because of that, I had no trouble adding the system to my game and transmitting the location of the other player remotely. I had several conversations with Roberto during his time developing the system, and I found the topic very interesting. As a result, he and I are planning on taking a Graduate-Level Networking course together for our final elective.
Overall, I believe the goal of the assignments are the following: getting meaningful experience in working in an unfamiliar codebase, working on small parts within a much larger project, working with other peoples' code and programming behaviors, and overall, gaining a better understanding about the design and architecting of projects and the systems that might go into a project.
We began the semester being thrust into a simple pre-made game engine. There were too many files to simply read through all of them and understand the entire workings of all of the code in the engine, but that was the point: in a large project or codebase, you almost certainly will not have that luxury. Furthermore, in a well-architected project, you shouldn't have to know about how everything works in order to add parts or modify a small existing portion.
One point I believe we were expected to learn was about separation of concerns, and how we ought to future-proof code we work on (and not introduce tech debt) by making sure what we write is complete and readable - for others, but also for ourselves to come back to later.
Something else I found important from this class was that we should look outside of ourselves - what if we want to build for 32-bit? What if we want to target linux? By having the foresight to create a structure for other formats to be plugged in later without having to completely overhaul the code we write, we introduce far more flexibility.
On top of that, there was plenty to learn from the Assignments themselves, as well as from the code provided to us. I'd never used LUA until this class; we used it to parse our custom 3D Model formats. I'd never worked in such a large Solution before, but I learned many different things about the linking of individual projects, files, and outside packages. I gained a better understanding of things that had once been question marks to me in C++. In my opinion, the code provided to us was written at a professional standard; I've tried to learn from and emulate the formatting and the practices I've seen.
In class, Tony gave us professional insight and valuable tips. The conversation in class about the different use-cases for OptiX and RTCore was particularly enlightening. I'd never heard of those or CUDA before, but we got a live demo into what they look like, why they're used, and what advantages they provide (or gaps they fill) compared to the Graphics libraries that we were using. Of the Engineering courses I've had so far, this one is my favorite.
This class also required us to provide write-ups for our assignments. Thinking in the context of Dev Blogs and documentation-writing, I hadn't had practice with this until now. I'd never even posted information to a website before!
When it comes to designing projects and systems, I prefer to spend my time up-front, trying to preemptively consider what issues the system ought to address, different ways a user might want to interface with the system, and what costs/benefits a particular implementation might have compared to another.
If I'm designing things as-I-go, it's either because something became apparent during implementation (which is fine), or because I'm trying to get it done in a hurry (not so fine). I believe you have to take the best of both approaches - you can't possibly come up with every situation ahead of time, so you must dive in and discover the use-cases. However, a decent amount of preparation can provide solid foundation which reduces the amount of time one might have to go back and refactor.