Hey all! Today I'm going to revisit a topic covered in one of our older episodes, protocols! That episode was a fun one to prepare for, as I spent most of the week leading up to it building out a (small) game engine. The code can be found here. That's right, we used to have a Github that we uploaded code to for a little bit. Maybe we'll get back to that in the future. We actually had a misstep in that episode that we have yet to repeat since recording it. We read nearly that entire playground. It wasn't fun for us and we don't think it provided our listeners with good content. I still wanted to talk about it though, and now that we've got the blog....why not do it here??? I'm envisioning the Playing with Protocols group of blog posts to cover creating a game engine using protocols, from design to implementation, using the old playground I wrote as a template.
Before I get started on any project I like to design the parts of my system. I view it as the foundation on which the project will be built, and in all honesty, it's one of my favorite parts of writing software. It's a time to show off your creativity, and build something beautifully. You can set yourself up for success if you design your project correctly. The flip side of that is also true. Hasty designs or ill-considered assumptions lead to flawed projects where you spend more time fixing bugs than you do implementing features, and no one wants that!
Step One: Begin at the beginning
You would think this goes without saying, but in practice it can be somewhat hard to do. Decide what you want to build, then figure out how you are going to build it. The goal at the end of this series of posts is to have a game engine for a role-playing game (RPG). For those who may be unfamiliar with what an RPG is, it is generally a game in which the player will have some grand objective but initially lacks the skills to complete that objective. Sound familiar? To me it's analogous to my coding journey. When I first sat down to code I didn't know the difference between a stack and a queue. When I started learning Swift I couldn't tell you how to create a dictionary, much less make a call to a backend database such as Firebase, and then parse the results. All of this I learned with time. Sean Allen does a great job of explaining it in his video here.
Where does that leave us now?: Starting is usually the hardest part of any project for me, and I don't think I'm alone in that. The trick is to break your idea down to it's most atomic parts and then turn those into features. What happens in an RPG? A player will usually choose between a set of specializations for their character to be, such as Wizard, Warrior, Archer, etc. The player will guide the character through smaller objectives while fighting enemies (or not) to collect experience. As the character gains experience it will become stronger and be able to defeat stronger enemies and clear tougher objectives. Repeat this process until the character is strong enough to tackle the final objective. Some companies have made literally billions of dollars off of this model (looking at you Blizzard).
Have we accomplished anything yet?: It may not feel that way now, but by defining our goal even in abstract terms as above, we start to get a clearer picture of what we need to build for our project. There will be a character, enemies, items and objectives. Let's take a look at these individually:
What makes a character? Let's say our character has these attributes: a name, health, experience, strength, agility, and intelligence. We also know that a player can choose a type for a character to be. Of course that's not all a character is. It would be a pretty boring game if the character sat on the screen the entire time unable to do anything! Each character will also be associated with a group of actions. A character can fight, heal, loot an enemy, and (hopefully not) die.
We need to have some opposition! Enemies will be of a certain type and will have a name, health, strength, agility, intelligence, and will give the player some amount of experience after they are defeated. They too can fight and heal. In addition to those actions an enemy can also flee. We're already starting to see similarities between our characters and the opponents that they will face. Things are shaping up!
If a player can loot an enemy and an enemy can be looted, then we need to have items to facilitate this action. What adventurer doesn't like a pile of glittery treasure? Items should definitely be of more than one type, and should also have stats on them. It feels as if things are getting complicated again, so let's break it all down one more time. An item could be a piece of armor or a weapon. Depending on type, it will have different stats.
Objectives will have certain goals for characters to meet before they can be completed. They will reward characters with experience and possibly an item.
Step Two: Abstractualize
I'm pretty sure that's a word. Now we start getting into the protocol portion of the post. Look at every possible object we've broken down so far. Extract commonalities to the most abstract protocol and then refine from there. Both characters and enemies have names, health, experience and basic stats. They also share the fight, heal, and die actions. If we create an abstract protocol that contains these traits and actions then we will be forced to define similar functionality for the structs which implement them. Items are broken down into weapons and armor, but again, there are shared traits between them such as the types of stats present. In the current design objectives stand on their own.
Step Three: Visualize
This is when each of the designs covered so far become more concrete. Simply the act of getting the design out of your head and onto something visual helps you decide where to take your application. There are plenty of times when designing a program one way of doing things may seem to make sense up until the point the ideas flow from your head and onto (possibly metaphorical) paper. I used draw.io to create the following Unified Modeling Language (UML) diagram. What is UML? It's basically a picture of your program's architecture.
These diagrams are the blueprints of the application. Reading these make sense once you've walked through a few them. Each box is broken down into three sections with the topmost being the name, the middle being the attributes, and the final being the functions. A diagram may omit the second or third section (as the Enemy diagram has done with the second section), but must always have the first. Let's look at what we've got here. There's an abstract protocol named BasicCharacter. It has the attributes we mentioned earlier in the second section, with the function signatures in the third section. This protocol is inherited by two other protocols, the Hero, and the Enemy. In addition to the what's defined in the BasicCharacter, these two protocols will force you to build out additional functionality based on whether you adhere to the Hero or the Enemy.
The item UML diagram is very similar to that of the character UML diagram. All items will share the basic strength, agility, intelligence, durability, and isBroken traits. An item is also responsible for breaking down and repairing. We will have two flavors of item in our game engine, weapons and armor. There's a need to define two protocols specific to each type of item because they will fill very specific roles. It wouldn't make sense (in most cases) to have a weapon that also had some armor value, just as armor should not have damage attributes (again in most cases) (yes I know getting punched with a gauntlet wouldn't be enjoyable). The final attribute in each protocol will be an enumeration I'll cover next article.
The final protocol I've defined is for objectives. These are going to be straight forward in that they just hold attributes for what a player would get if he or she completed the objective. The reward is an optional object that conforms to the Item protocol because not all objectives will have an associated reward.
These protocols are great, but they don't do much on their own. Literally. There's no implementation at all. Next up we're going to tackle writing out our protocols and creating the structs that adhere to our them.
I hope you enjoyed my first post as much as I enjoyed writing it! Are there flaws in my design? Most definitely! That's part of the process and it's very important to keep it in mind. Don't become rigid in your first design because it can (and most likely should) change. With that being said, let me know what you think of it so far in the comments below. These posts aren't planned ahead so you're getting an inside look at building this engine out first hand. It will get ugly at times. Again, that's part of the process. There are very few (read: NONE) developers I know that can sit at a computer, fire up an IDE, and write a flawless program from start to finish. All of the great developers do share one trait though; they absolutely love the process. Writing code isn't as romantic as it's portrayed in pop culture. Keep working at it and you'll become great as well.