Thursday, August 20, 2015

Inspectors

Finally decided to get down to something that I had been putting off for awhile: custom inspector editors. This has no bearing on the actual game itself, and in fact, will not even be accessible by anyone playing the game. However, for us, these are invaluable tools as they allow us to 'visually edit' different parameters belonging to different game components, which will reduce the amount of time we are editing scripts directly.

So, what the hell does this mean? I don't know, I just like to throw around buzzwords. To explain how useful these custom inspectors are, I'll talk about how we were previously doing things, which is somewhat shameful. So, each character has a set of attributes, as is standard in RPGs. The old way literally used a gigantic statically allocated array that stored these parameters. So, if I wanted to change Bodom's maximum HP, I'd have to dig out ole' AttributeStruct.cs, find the particular character array, find the particular attribute I wanted to modify, change it, then save and recompile.

Unfortunately, I wasn't just satisfied with starting down the wrong path for attributes, solely. This was also the case for skills and items. A lot of this can be summed up by saying that these classes (Item, Skill, et cetera) were not extended from actual Unity MonoBehaviors. This means that they could not be added to scripts as components. This means that their parameters could not be edited via a Unity inspector. There were good and bad reasons for doing this:

 The bad:

-Wastes a bunch of time searching for the things I want to edit
-Is error-prone--oh, I edited his HP when I meant to edit his SP?
-Can't be changed while the game is running
-Adding a new character/enemy was a huge pain. Imagine you had to copy and paste about 100 lines of code every time you wanted to make a new character. Then you had to individually change each entry. In a sense, this is like having a bunch of spreadsheets for each character, item, skill, etc. except that the spreadsheets are buried in a giant file. And the spreadsheets aren't formatted into proper rows and columns. And you can't actually save your spreadsheets because you accidentally put a comma somewhere it shouldn't have gone.

The good:

- Quick and dirty--allowed us to test whether the basic functionality (damage formulas, equipment assignments, skill usage) actually worked.
-Certain parameters were not able to be well-represented (enumeration types, dictionaries, etc) in an inspector without having to write custom inspector code. Since I didn't want to jump into that just yet, this way seemed preferable.
-Uh... that's about it. (But if you didn't actually read this, it would appear as if there were three good reasons, no?)

The ugly:

-Me

So, anyhow, what the hell is an inspector? Here is a picture of one from the Unity editor:

Who needs nostrils?
Er... that's not right. While we're on the subject though, Inspector Gadget was a real dumbass. OK, moving on...



This is a custom inspector designed for skills. Contrast this with the previous method of doing things:



The previous two pictures are essentially equivalent in functionality, but keep in mind, the second picture only shows TWO of Bodom's skills. And that's not even all of the code required to actually register the skills with Bodom. For some reason, I insisted on having skills be placed in a Dictionary. That was dumb. I am dumb.

But yeah, clearly, one is better than the other. Also, that is a 16-parameter constructor. I should honestly have my degree revoked for even having thought about doing that in the first place.

But why stop there? After re-hauling one thing, you might as well keep going. Items were pretty trivial, so I won't even show that. Attributes, on the other hand, were the true target here.

To recap, enemies scale with character level. We plan on having all characters max out at level 100, (easily changed if necessary). Each character currently has 17 different attributes, which are floats. So, to further illustrate how painful this would have been had we continued with the original method, we would have had something like 1700 values to change for every character for every level. To be fair, that's still the case, but you no longer have to edit them in a file (like the previous picture).

The idea was that we needed to be able to edit the current attributes of the character, edit ALL potential attributes of the character for every level and have a way to save and load these from files,

So, this is what we came up with:



This will essentially allow us to modify any character's attributes, whether the game is actively playing or not. This is insanely valuable. You could almost say it's invaluable. Essentially, I can test any set of attributes at any level for any character and then save them  back into the attribute map once they are correctly tuned.

The attribute map then is basically a file that exists for every character that contains the attribute information for that character. "Load to Attribute Map" will take the current character's attributes and save them to file so that they are not lost.

The attribute map inspector, then, looks something like this:


For the most part, these are extremely similar. The key difference being is that we can set up the attributes for any level. Additionally, different level attributes can be specified in a relative manner, meaning that you simply supply the difference in attributes from one level to the next, which is also a huge deal in terms of productivity and not having to remember what the previous level's attributes were.

Just for completeness, here is the enemy attribute map, which has a few extra parameters that are enemy-specific, such as EXP, AP and MONEY awarded by the enemy upon defeat:


Anyhow, just wanted to give a more behind-the-scenes look at our own processes for adding and editing different game components. Now that I've gotten this (mostly) out of the way, I can begin to focus on the next big target: Enemy AI. Thanks for reading!

'Murica.

Friday, August 14, 2015

Enemy spawns / Game data persistence

Since the last update, there has been a substantial amount of progress made on two different, but related fronts. Specifically, the enemy spawning system has been more or less finished (that's programmer parlance for "I finished 80% of it, but I don't know what the hell to do with the other 20%" - also known as the Pareto Principle.. or a modified version of it, anyhow). Additionally, we have the framework in place to save/load game data, which actually turned out to be quite simple once one gets past the initial fear of having to implement something so crucial to the overall game. Rather, the fear of badly messing it up.

So, first, I want to explain enemy spawning mechanics, starting with the old method. Previously, we would literally drag the enemy prefabs into the scene. Then, one (or both of us) would prepare an arrangement of five or six scented candles in a crude pentagram-like fashion with its centerpoint being my laptop. Depending on how we lit the candles, sometimes the scene would work. Other times, Lucifer himself would appear in the corner of the room for a brief moment, and we'd be like, 'clean rebuild and try again'.

Now I know you're all like, "this didn't happen, it's clearly embellished. There is no way they made a pentagram with six candles." And you would be so, so wrong.

The new method is much better and does not, at the moment, rely on any kind of ritual. The first step was to break up all scene elements into two categories: those that were scene-dependent and those that would persist through scene loading. We already had something like this, but it was highly unorganized. We began shuffling around objects that clearly did not need to be duplicated--characters, for instance, never need to be duplicated. Previously, we had each playable character present in each scene within the Unity editor itself, but this was not the best idea, and was mainly to avoid issues that we didn't quite understand at the time. Also, we were really coked out in June/July, but we've cut (get it?) back a lot.

A similar fate befell the cameras that we are using--no need to duplicate these in every scene. Additionally, each enemy object contained its own battle scene transition, which is completely needless, considering that every transition would essentially take you to the same place. Much better to have a single transition object and activate it once battle collisions occur.

As for enemy spawns, these were previously non-existent. What I mean by this, is that we would simply add the enemy prefab to the scene (as stated before prior to the bullshit segue about incantations and whatnot). This enemy prefab contained, among other things, its own spawn parameters and its own waypoint collection. This would be fine and dandy, but we elected ultimately to have a random enemy spawn system. You can see the issue there, when you want to spawn a spider that only chases you for 10 meters, or doesn't chase you at all, et cetera. This meant that we would have to modify the actual object within the scene itself and pray to god that we never applied the prefab. To explain, this would have overwritten every other spider's parameter. The point being, it was a bad idea.

If you recall the update regarding the world map battle zone spawning (you don't and neither do I, so it's fine), the idea was to randomly spawn an encounter once a certain number of steps had elapsed. The great thing about this is that we could pretty much reuse this same framework--instead of spawning a battle group randomly (the term for the collection of enemies that you actually engage when the battle transition occurs), we could spawn a bunch of them within the scene itself, with an extra layer of logic sitting on top that actually just instantiates the enemy prefab at the location. World map battle encounters do not need to bother with this step since enemies are not visible.

Essentially, the relevant parameters were then copied outside of the enemy prefab itself and into the battle spawn object. Much cleaner, and much more flexible (like my fourth wife). The next goal was to actually persist the list of enemy spawns through battles. This means that they would retain their previous locations and spawn status upon leaving a battle (were they defeated in battle? did you flee from them?). Additionally, they would not need to be re-instantiated each time (this is really the big one). For a scene with a few enemies, sure, this is trivial, but for more involved scenes, this will save a lot of time.

We then realized that it isn't just enemies that would benefit from this, but treasure chests/resource nodes as well. Not sure if we want to handle NPCs like this yet, but we'll find out soon enough. What happens then is that these nodes are actually placed under a special node within the scene called LevelManager. The nice thing about this is that we can actually inherit from this class if we want to add level-specific callbacks. For instance, when you first enter Cephaline, it's dark, but it never is again after that. Currently we are handling this in an... uh... hacky way. But no longer, damn it.

Then when you enter a new scene (not the world map or battle scenes, they's special) we graft the nodes from the non-persistent root (LevelManager) to the persistent root (GameManager) and instantiate the enemies accordingly. This has the added benefit of allowing the scene objects to be specified within the level itself. This also has the benefit of putting these persistent objects in a nice centralized location, which is useful for serialization.

As for the enemy spawn mechanics themselves, these are still being finalized, but the basic idea is this: every scene has a number of potential spawn points. When the scene is loaded for the first time, we iterate through these spawn points and check whether or not an enemy should be created at this location. This is mostly time-based, but can be conditional-based as well. Imagine ole' mama spider only popping up after gettin' pissed that our intrepid adventurers decapitated ten of her children.

Now, I know you're thinking, "spiders have millions of babies. Who gives a shit about ten dead spiders?" You be quiet with that type of logic.

Once an enemy is defeated, depending on whether it is a one-time spawn (like mama spider up there, although you could have grandma spider show her ass once ten mama spiders get killed, ad infinitum, all the way back to amoeba), it will be marked as inactive until its spawn timer comes up again. This should be pretty familiar to anyone who has played (MMO)RPGs with similar systems.

The big question then, is how to persist this across multiple reloads. As it turns out, C# makes it fairly easy and straightforward to serialize and encrypt any object. As it also turns out, Unity doesn't and is like, "hell naw, whatchu tryna do here boy?" Unity asks us this multiple times per day, which is why we both wear headphones.

So, even though it's overkill, it would have been extremely nice to have just taken the entire GameManager (the persistent node) and serialized it once a scene was exited and deserialized it once a scene was entered. But again, that would have meant that we would have saved off every attribute (even the ones we did't want saved). Also, it wasn't possible without paying some amount of money for an extension script. I wish I were kidding.

The problem is that C#'s serializer can only serialize primitive types... which is to be expected. Furthermore, it requires you to wrap any attributes that you want to serialize under a special tag. Generally, the idea is to wrap an entire class or structure within this. Unfortunately, all game objects (including GameManager) inherits from Monobehaviour which cannot be serialized.

Thus, the solution, as much as I hate it, is to serialize a nested structure within the class itself. Normally, this would involve duplicating all of your variables. For instance, you would have the forward-facing variables that are exposed to the rest of the game (and most importantly, the Unity inspector). and then you would have a separate 'chunk' that are able to be serialized. Whenever you serialize or deserialize, these would be copied back and forth each time in an explicit step that individually sets/gets variables. Puke.

The trick to get around this was to use properties, including setters and getters that redirect to the internal serializable data structure. The only real issue with this is that you lose access to these variables within the Unity inspector. Thankfully, since they are not meant to be modified, you shouldn't really see them there anyhow. Furthermore, we plan on having custom inspector scripts at some point (any volunteers out there?) that could optionally expose them anyhow.

You know what, talk is cheap. Let me show you what a directory looks like before we save data and afterwards. Put your sunglasses on.



Looks like a pretty normal, empty directory, huh?

I know it's hard to believe, but that is that very same directory AFTER saving game data. Pretty sweet, huh?

All joking (and badly cropped pictures of nothing) aside, we do have a functioning saving/loading system, which is a pretty big f'ing deal. (I get one f word per blog post, right?) I could go on and on about this, but all that matters is that it works.

Next up: revamping the skill/inventory system. *shudder*











Saturday, August 8, 2015

Tearin' down the house

OK, yes, it has been a long time. As our good friend Conway Twitty would say, "It's beeeeen aaaa looonnngg timmmme." Sorry about that. He is not my friend.

So, let's see.. when the hell did we last update this thing? 2013? Good god, it has been awhile. Let me look back at that post and see what we were even doing at that point... (brief interlude) OK, that was quite a long time ago.

For the last few weeks, we have focused our efforts solely on Molsha. If you have not watched this short, short video, you can check it out here: https://www.youtube.com/watch?v=w7OU7TY4qR8. Molsha, besides being a weirdass name, in many ways, is the culmination of many of the different pieces of the Bevontule development effort, and I'm not gonna lie: setting it up was a bitch.

But, through that particular struggle, we learned a lot. Basically, if nothing else, Molsha taught us that we literally have to start the project over again under a different title. OK, not really, but we did learn a lot. Here is a brief list:

-You literally almost need a dedicated machine in order to bake lightmaps. Briefly, lightmaps are, in a sense, a huge runtime optimization applied at the cost of many, many hours of preprocessing. Lightmaps are often used in lieu of having a dedicated, dynamic light. Essentially, dynamic lighting has to look at every object (actually, the vertices and pixels themselves) in the scene and decide, what is the overall amount of light received by this object? Without going too deep into the calculations, this is a somewhat costly process. Since the position of the Sun in Molsha does not actually change, you could say that the lighting applied to each and every object does not actually change either unless something is obscuring it (aka, the player, but we use something else to handle that). A lightmap, then, is basically a giant texture applied to the entire scene that encodes all of the light contributions without having to compute them every frame. Pretty cool, huh...

-...But this didn't mean shit ultimately, as building the .exe itself caused the lightmap to not be applied at all. This is the reason we had to run the demo within the Unity editor itself. Shameful.

-Recording the actual video was a huge pain, not because we suck at talking, but because FRAPS (video-recording software) hiccuped a lot. Granted, we took precautions to reduce this, but ultimately, all we needed to do (somehow) was restart Andy's computer. Seriously. His computer sucks. I should have suspected that this would be a problem when, one day, we were just programming away, and a piece of fresh toast popped out of what I had previously assumed was his disk drive. We ate the toast; it was good.

-Particle systems use a LOT of computational power. I mean, yeah, this is obvious, rendering thousands of tiny objects is going to reduce the framerate. Fortunately, many of the particle system prefabs were ridiculously overtuned (using 5000 particles over the life cycle of the system, instead of 500 or so.) Reducing these particle emitter counts helped the framerate considerably.

 -We implemented a hack to remove the enemy from the scene after you had defeated it in combat. The hack was simple--store the enemy's name before battle, and call SetActive on the enemy with that name once battle has concluded. What could possibly go wrong with this?? I'll tell ya! We were even careful not to have multiple enemies with the same name (that's why each enemy encounter is with a differently named enemy). But, unfortunately, the actual Mesh object parented under the GameObject itself shared the same name. (Why did I do that). In essence, after battle, this disabled the actual mesh and not the gameobect. Hilarity ensued as we wondered why we were able to re-engage with what should've been a non-existent enemy. We suck. We fixed it.

But more than anything, this has taught us that we need to refactor. Imagine that the Bevontule code is a house and we are both the architects AND the people that are building the house. Typically, this is not the case, which sucks in its own way.

So, we started with this:


And then it turned into this:



But, ultimately, we want it to look like this:


Basically, that's programming summed up in a few images. Actually, you don't even need the last image except to display an excessively idealized version of what you want your code to one day look like. (That's what the architect shows you)

The point being, we are going to be quite busy (moreso than before) as we rewrite large portions of our code to better handle the kinds of situations that we are going to encounter. In a nutshell, here is a basic roadmap:

-Rewrite the enemy spawning/saving/loading code to be more efficient, and to anticipate the other problems that will inevitably arise when we get into actual save file writing/reading. Much of this has already been started.

-Rewrite our scene loading code to be more efficient. This will likely involve investigation of other Unity concepts such as asynchronous and additive loading.

-Investigate new lighting/rendering/modeling/batching techniques to get the most mileage out of the scenes we have created. For instance, Cephaline, as it stands, runs at a pathetic 30 frames per second. There are a lot of different reasons for this, most of falling under the category of "trying to walk before you learn how to be zygote" Molsha, on the other hand, ran at around 100 FPS, and is honestly, a much more complicated scene. So, we're learnin. But there is a lot more to be done.

-Vote for Donald Trump