So now's the point of the project where we roll up our sleeves and really start to dig in. I talked about all the ways in which our design space can go in last week, and about constraining your execution as much as possible. But even so, we just have a heck of a lot to go and build. I've got a lot of ground to cover in this blog alone, and we're still only about half way through implementing a single weapon. So let's get to it!
Data Driving Your Design
This topic could be an entire blog in and of itself. It's a fundamental concept that every professional game of any scale has to have. And yet, surprisingly enough, there's no real built in support for it in Unity. And few game tutorials or showcases even talk about it. But I'm going to drill it in here: plan on building a database for your game. Think about it - in just about any game you build, there's going to be some collection of some similar units, with shared characteristics. This could be the players, mobs, weapons, spells, abilities - whatever. You'll need a way to easily manage the shared data associated with those objects, independently of modifying the objects themselves. So what you want to do is put all of the static, shared data into separate records - which you can then serialize in a table, and then create instances of those object based on their base record. There's no real agreed upon industry term for this process, but at Ensemble (and now at Robot) we called that separating the protodata from the instanced data. Let's walk through an example.
In our game, we have mechs. Now we want a variety of mechs, with different characteristics. Things like health, maximum speed, turn rate, heat dissipation, etc. So we set up protodata for the mechs, where each protomech record defines those characteristics for a single class of mech:
Those records can then be serialized in an XML table, for instance, or stored in a database. When we instantiate a mech, we give the mech a protoID to use. It will look up the protorecord for that ID, and use the characteristics for that record. In that way, we can share the static data among every instance of our units. And furthermore, the static data can be edited by designers, or yourself, independently of the instance prefab. I did a similar thing for weapons, so now each weapon has a protodata record assoicated with it, which defines how much damage it will do, whether or not it is a triggered or channeled weapon, etc.
Note - In my case, I ended up just attaching the protorecords directly to the prefab itself. That's why they're derived from monobehavior, so they can be components. I did this for two reasons. One, in this game, I'm the only designer, so creating a "table driven method" of editing the protodata is not as important. Two, the mech prefabs themselves are actually protodata, when you think about it. The problem with this is that now a copy of the protodata lives on every instance of a mech in our game. In a game where I'm unlikely to ever have more than 8 units in game at any one time, this is probably okay. If these units were say, villagers in an RTS though, or mobs in an MMO, this would not be okay. Never be afraid to modify your conventions for the realities of your situation.
|The new Dagger mech, with Protodata|
With the protodata firmly established, it was time to figure out exactly how a weapon works. Before I wrote a single more line of code, I actually pulled up OneNote, and started typing out my architecture design in stream of consciousness notes form. This is something I do every time I'm tackling a new system. I want to know exactly what all the pieces are, and how they connect. I spent several hours on Saturday doing this. Walking through the system, trying to account for everything in my design that I knew I wanted to support. I wanted to handle channeled weapons (where the weapon fires as long as you hold the trigger down, and stops when you let up - like a flamethrower) and triggered weapons. I wanted to handle AOE damage and DOT damage. I want weapons to work both as projectiles and instant raycast. And it all has to work in multiplayer. And most importantly, we want to separate the effects associated with the weapon from the simulation part of the weapon. Effects should only happen on the client, where simulation changes (damage, etc) have to happen on the server. This last point was the most key piece of the architecture.
I'm not actually going to detail the entire architecture here. This blog is going to be long enough just describing the process. I will try to get the OneNote book that I'm keeping notes in shared, for those of you that are sufficiently masochistic as to want to peer even further into my thoughts. For the blog though, suffice it to say that I ended up with an architecture that seemed like it would do what I wanted. It would work in multiplayer, handle all the design prerequisites, and keep the effects separate from the simulation.
Along Came Multiplayer
I was of course, completely wrong. With the architecture in place (and in writing!), I spent most of the rest of Saturday actually writing the code to support the design. I set up the various classes - Mech, ProtoMech, Weapon, ProtoWeapon, WeaponEffect - and wired them up at least enough for me to go through a simple fire process and see if the pieces worked. And, unfortunately, they did not. Not even a little bit. I immediately sat about debugging, and rummaging quite a bit through the Unity Multiplayer manuals, trying to figure out which pieces I had wrong. Unfortunately, by this time it was late Saturday, I was tired, and I went to bed unhappy with the state of things.
Sunday, in the light of a new day, I backed out my pieces, and started re-establishing them, one by one, until I finally figured out the problems. Out of my troubles though, comes a really technical, but really important, Unity Multiplayer Gem. Which I will share with you!
Unity Multiplayer Gem - All network behaviors must live on the root object of an object that is spawned by the networking manager. They cannot live on subobjects.
See? Already you know more than you did coming in. Once I understood that fundamental truth, I was able to clean up my system and get it back on track. Well that's not entirely true - I did make a brief foray down a dark path where I attempted to spawn the weapons separately from the mech. This was not.. how to say it - a good solution.
The Effects Problem
We've already gone on long enough for this morning's blog, but there's one more piece I spent a lot of time on, and so feel like it too deserves a mention. Again, this is a topic that could well garner an entire blog onto itself. Managing effects collections - things like sounds, lights, particle effects, animations, etc - can be a huge system. And your solution can vary from just straight up hardcoding the effects for each individual weapon, to building some general "effects manager" that allows you to handle a wide variety of effects in a generic fashion. And I spent quite a bit of time Sunday struggling with which end of the spectrum my solution would fall on. Every part of me wanted to build a really robust, full featured, effects manager. But I also knew that could be a really big system that would be put in place to handle what is most likely only going to end up being a half dozen or so types of weapons.
In the end, I came up with a compromise that I feel pretty good about. I have the notion of a WeaponEffect, which is a collection of effects to be used in any given situation. So for instance, the muzzle flash of a gun might be a WeaponEffect that contains the gun's fire animation, the sound effect, the light, the particle effect, and maybe a mesh to show the muzzle flash. Each of these individual effects will live on child objects to the parent weapon effect, and will have an EffectActivator component attached to it. The EffectActivator component allows for independent delays and durations for each effect. In this way, even though the entire muzzle flash effect may be 1 second, the mesh activation flare may be only 0.05 seconds, the animation can be 0.8 seconds, the sound effect can be 0.5 seconds, and start a half second late in the effect. In this way, I should be able to build relatively complex effect systems for individual effects at any given time. Remember - we have to support explosions at some point!
Note - The effects themselves. One thing I really did not want to do was get in the business of actually building particle effects. Let me tell you - good particle effects are an art form in and of themselves. I went to the Asset Store and purchased a collection of Sci-Fi weapon effects for $35.00 that I think has pretty much all of the effects I need for my game. It's a really nice, robust collection - I'll link it below. However, like many of these assets, it comes with a pretty large collection of code that is their own "ready to use" weapon system. If you were so inclined, you could take that system and probably integrate it into your game. That was not my desire at all. So again, I imported the package unto a separate project to test and evaluate, then imported in only the particle effect prefabs and shaders into my project.
So that's about it. I spent a lot off time working on the systems this weekend, and by the end, I literally had one weapon, halfway working. That is, it plays effects - but doesn't actually shoot anything yet. But most importantly, we now have a system in place. We know how the pieces work, how they relate to each other. I still have lots of code to write to finish the system, and to build out all our weapons - but I'm no longer wondering how it will all work. And that, is how games get made.
Questions? Comments? Be sure and mention them below.
Sci-Fi Effects 1.15 by Forge3D
This is the Sci-Fi Effects package I downloaded. These guys have now provided all of my effects and sound effects for my game. Maybe I should contact them about some nice environmental pieces.
Unity Multiplayer Manual
I've linked it before but I spent so much time here this weekend it bears listing again.
At Home Project Design Notes
The OneNote notebook I'm using to keep my design thoughts in as I work on the project. Not really fit for human consumption, but feel free to peruse if you wish. A Microsoft account will probably be necessary to view it.
The Unity3D At Home Project
The main index page of the At Home Project. If you'd like to catch up from the very beginning, this is where to go.