The most difficult part of the project was finalizing the client and server architecture which took quite a bit of planning. After we were able to get the client-server network up, we had to pause some progress for half a week to refactor the code so it was maintainable as we added new classes, models, etc.
We expected that working with Bullet, the physics engine, would be difficult to integrate, and those expectations were easily met. While Bullet saved us from needing to implement the physics from scratch, it also introduced a lot of unexpected hurdles and bugs for Dan to figure out.
Implementing the user interface also proved to be difficult. Since there was no available framework for us to use, Sabit had to render the clickable buttons and text himself that needed to be responsive to the viewport.
Towards the end of the project, we ended up needing quite a few models from Lauren so work intensified in the last couple weeks for her. One huge aspect of the project that became much easier than expected was sending packets over the network between the clients and the server.Thanks to the framework that Rahul implemented, sending different packets became as easy as defining very modular functions that could easily be called by other parts of the video game.
Thomas was able to implement the graphics without much difficulty and the game ended up looking great. Gameplay, which Leland and Matt handled also turned out to be pretty straightforward, especially after Dan figured out the physics for projectiles.
First of all, we take a lot of pride in how the game turned out. The gameplay remained fairly close to expectation and in some ways, it came out better than we expected. The sea monster looks great thanks to Lauren's hard work. The skybox also looks stunning, especially when reflected on the shiny water graphics that Thomas added. Originally, we expected to have a flat water texture, especially since we thought waves would be too complicated to implement with little benefit. Adding the shimmery reflective effect water made the game look stunning without over-complicating the implementation.
Under the hood, we're proud of the software design of the client-server network and the measures we took with code review to preserve the architecture. Even trivial pieces of code were eliminated if they were unnecessary or any memory leak, no matter how large or small.
Total Lines: 7,971
Method: I copied the project into a new directory and removed all files that we did not write (e.g. external dependencies, .dll, .vcxproj, .obj, etc.) and called cloc on it.
[Lauren] All the models were created using Maya. Once the models were finished, they were exported as obj files and sent to Thomas to be imported into the game. Keeping the files small was important because if they were too big the initial start up of the game would be slowed significantly. Because of this, the models couldn't be very smooth, so we took a more angular design approach.
[Thomas] We did not use a library to load meshes, instead opting to code that manually. Textures were loaded using the SDL2_image library, version 2.0.1, so that we could support any image file format Lauren wanted to use without any difficulty. It's worth noting, however, that the textures loaded in this manner are flipped vertically when sent to OpenGL; in order to work around this, our shaders used 1-y for all texture lookups. While meshes and material data were loaded from the Wavefront OBJ format, we only supported use of a single material in a given model. In addition, we only supported meshes made entirely of triangles; we could not find any setting in Maya which would automatically triangulate meshes, so this required the additional step of manually triangulating each mesh before exporting.
[Rahul] We used Asio, the networking library that is included in Boost. It is important to note that Asio is its own standalone networking library, and does not need Boost to run. I used the standalone Asio library, without Boost. I would definitely use it again, it was extremely simple to set up the netcode for our game. It does have a gotcha around queueing writes but there are solutions on stackoverflow. It was a stable and simple implementation that only took me a day to finish, so it was definitely worth it.
[Dan] We used Bullet as our physics library. It had poor documentation and sometimes the documentation was outdated. However, the examples were very useful in learning specific Bullet features. After spending a day or two, it was relatively easy to use and would probably use it again. Bullet's collision detection was very easy to figure out.
Communication is very important in group dynamics. Often times, the team was on different pages about aspects of the game that were not decided on clearly. Other times, overlapping work would be done by two different members, resulting in wasted effort and time. But after the first few weeks, once we started to use Taiga.io and formally assigning tasks, it became clear that each member had their own general domain of the project (with the exception of Rahul who did most of the orchestrating and supervising in addition to his own work). Even though we abandoned Taiga.io, once these domains became a little more apparent, each member became self-sufficient and embarked on tasks independently with nothing more than a short notice needed to prevent overlapping work.
Code review became its own form of communication to keep everyone up-to-date on what the others were working on as well as informing the rest of the team of certain implementation details that might pertain to them.
It also became advantageous that we planned work time each week generously because when things come up, as they inevitably do, there's still enough to finish up the tasks for the week. Each week, we would meet on lecture days, usually leaving earlier than necessary, but it's better to plan for longer meetings and leave early than plan too short.
[Rahul] I think code-wise, we made good decisions. There aren't many immediately apparent things that I would change regarding the architecture or design of the client/server (in the context of this class). However, in terms of work distribution and planning, I think we could have done a better job. When we have 6 developers, it is a challenge to effectively parallelize the work, and I think we ran into that problem throughout the quarter. We stopped using Taiga.io for planning the tasks we needed to do every week, and that didn't really help the cause, because people didn't really know what to work on. I would definitely make sure that we continue planning our sprints and tasks and assigning work after a planning session so that we always have a direction and things for people to work on. Other things that I think we did great were code reviews and controlling our branches. I was stringent in making sure everyone forked the main repository and, for the most part, opened pull requests to add code to the master branch. These pull requests were then code reviewed, and we caught a lot of bugs and memory leaks that way, while also clarifying some features, improving, and optimizing code. It made sure that we had minimal bugs, and no one had to dig very deep to fix any problems. Doing pull requests also forced other developers to have some context regarding the work other people are doing.
[Leland] I wish I had been more involved in laying down the client-server architecture as that would have been valuable experience to pick up. Even though Rahul already had the expertise to complete it in just a weekend, I wish I had been more proactive in trying to help him as a learning exercise. Early on, I asked him a lot of questions, took notes, and drew diagrams to try and get a full and detailed picture of the network classes but before long, I became focused on writing my own gameplay code. Therefore,I just picked up what was necessary for my tasks, and deepening my understanding of the network was put on the backburner.
[Dan] Next time I think I want to be more involved in the design process as a learning experience. Like Rahul mentioned, I think we needed to assign tasks to people better. Many people did not have many things to do, and if we assign tasks more effectively I think the game would have turned out a lot better. The things that we did great were code reviews, branching, and helping each other out when needed.
[Rahul] I think CSE 167 helped me the most. A lot of the work that I did around the networking, client/server architecture, and general design I picked up outside of school, but CSE 167 is one of the few classes that require C++, so I was already familiar with it and I didn't run into any crazy problems while coding. CSE 167 was also obviously very relevant to the project since it taught me graphics, and even though I didn't directly work on the graphics code, I at least had some idea of what was going on in the dark depths of the client code.
[Leland] CSE 110 sticks out in my mind as a useful class as it taught me some of the design patterns that became useful for my code. A little daunted by the experience of the rest of my team, I put considerably more thought into each line of code that I wrote and I was excited to recognize where particular design patterns would be applicable and useful.
[Thomas] CSE 167 prepared me the most by teaching me the basics of meshes, although when I took it, it did not heavily use the modern OpenGL that allowed the fancier effects (water and glow) into the game. Even so, I would consider this class a necessity for those wishing to focus on graphics. However, I hear from friends currently taking the course that the course now uses modern OpenGL, and so I believe that it will definitely enable future classes to implement all of the fancy graphics of their dreams.
[Dan] I think CSE 131 helped me the most. I was responsible for the physics of the game. In CSE 131, we wrote the compiler in C++ which helped me learn the language better. CSE 131 also gave me experience in learning how to use poorly documents libraries, which I was able to apply to Bullet.
[Leland] Something I realized over the course of this project was that although I had always prided myself on my grades, they didn't do much to make me a better programmer. Just because I easily understood topics from a high-level did not necessarily mean I knew how to write the best code when actually getting down to it. I'm optimistic (and hopefully not naive in saying) that becoming a programmer is a matter of experience, so working with such a talented team has motivated me to continue to work hard in my career with a strong focus on refining my software engineering skills. Another smaller thing I noticed was that my interview prep showed me the actual application of learning the concepts behind those interview questions. For instance, functions and classes where I instinctively used a vector just to hold data, I realized would be better suited with an unordered_map. It was cool to see that there was some merit to refining interview skills and that they weren't just arbitrary and contrived problems.
[Rahul] I learned very early on about the importance of a singular game loop. I've done work on (game) servers before, but they've always been event-driven, the "loop" being the network threadpool polling for messages. This is the first time I've implemented a real game loop that drives the rest of the stuff, and it was interesting reading about why it's important to design high performance servers like that.
[Dan] From the beginning of the class, I wanted to learn from the more talented members of my group. I worked a lot with Rahul and learned a lot about good coding practices and design patterns. Rahul questioned a lot of the code I wrote and it forced me to improve the quality of my code.
Screenshots