Showing posts with label game engine. Show all posts
Showing posts with label game engine. Show all posts

Wednesday, May 15, 2019

battleMETAL - And Yet, Somehow it all works - part 5 - Arming Menu

Load and lock…


On the heels of talking about the Deployment Menu, there’s another menu that I had a lot of fun building and consider a technical achievement: the Arming Menu. This menu is a staple of old vehicle combat games, and mech games in particular like in Battletech, it's called the ‘Mech Lab’. This is the menu where the player gets to exercise a level of creativity, creating a loadout of weapons and equipment that they are most comfortable with. In battleMETAL, every playable ‘Metal (mech) has a set of Equipment Slots. These slots may contain Weapons or Equipment but limited to a set of factors such as item size and the type of item. Here’s a few examples from old school games to showcase what my starting ideas were.

Mech Lab as seen in Mechwarrior 4: Mercenaries


Weapon Menu as seen in Earthsiege 2


These menus can be fairly daunting for a newer player or someone not used to this genre of game. A ton of information is thrown at the player all-at-once, and many decisions here will directly impact the player’s experience when they actually deploy to the game space. However, this is a menu that the game will return to many times, and Players are encouraged to spend all the time they want here testing out ideas. To that end, I tried to make the Arming Menu as straightforward as possible given the limited UI tools at hand.



Proof of Concept Arming Menu ~2016



...Ok, we can do a little better than that, heh. This image was the very first take on the Arming Menu. Although it lacks a lot of graphical icons, one can see the roots of the menu are here. Weapons and Equipment have a UID or unique ID that the game uses when looking up data for the item. When the Arming Menu is called, it grabs all relevant data for each UID, and the data is stored in separate .item files. These .item files are text files renamed to be better understood, and contain a JSON-style data language.



Add in data loaded for every mech in the game and the end result is a wonderful menu that looks like this:





Now this is looking pretty good. We can see the list of available equipment for the Player. We can see the hardpoints that the chosen mech possesses. There’s some other important panels here as well: the ‘Weapon Group’ panel in the lower left allows the Player to set their fire groups. A fire group is a collection of weapons bound to a single fire key. Next to this panel, is the ‘Energy Drain’ panel. This important panel ties into battleMETAL’s Player resource mechanics. In addition to health, Players must be aware of how much Energy their ‘Metal currently has. Energy is used to power weapons, shields (a rechargeable health pool), and sprinting. This panel tells the player how much energy their Weapon Groups are going to draw when used. This is helpful to Player if they’re trying to balance their equipment selections.


In the middle-right of the screen is a very important set of panels, the Weapon Info panels. The top one shows the statistics for the Weapon that a Player has chosen in the menu. The bottom panel is for the Weapon in the selected hardpoint. They’re over/under so that Players can compare directly, the stats of 2 different weapons before mounting any weapon to a hardpoint.


Now for some neat code notes. Every mech has a list of Hardpoints that can hold a piece of Equipment. The data for this is stored in a text file with a custom .unit extension. When the Player selects a mech in the Hangar Menu, that data is loaded into a set of global vars. These vars are not static, if the Player chooses a different mech, the globals are updated with the data of the next mech the Player chose. Part of the data loaded are the hardpoints, Quake C being more primitive means that hardpoint data is loaded into a series of Arrays[] dedicated to each data point. The index of the array is used as the UID of the Hardpoint:


float MECH_DATA_H_TYPE[10];denotes the allowable Types of equipment for the Hardpoint.

float MECH_DATA_H_SIZE[10]; maximum Size of the Equipment.

vector MECH_DATA_H_ARM[10];

vector MECH_DATA_H_HNG[10];


The two vector arrays are specifically for the UI. Currently, I hardcoded the screen-position of the Hardpoints when the Arming Menu needs to draw these on-screen. The Arming Menu code loops through all the hardpoints (game maximum of 9 possible hardpoints) and pulls the position for each Hardpoint from those vector arrays. When the Player clicks the mouse, the code checks to see if the mouse position is within the area near of each vector to determine if the Player has clicked on a Hardpoint. If the Player has, the code uses the UID of the Equipment in the Hardpoint to load the .item data file for that Equipment.


Another great feature I implemented recently was the ability for the Player to save up to 6 different configurations per ‘Metal. I realized that Players would prefer to be able to save their changes to the mechs for later use. Well if we’re going through the trouble of saving something once, then we now have the functionality to save anytime (isn’t programming grand?). The save feature is autonomic and triggered by natural Player interaction with the Arming Menu. Things like tabbing between configs, or switching from different menus and the Arming Menu. The save files are then stored in folders labelled via ‘Metal UID, and then their own number. The save files itself is a plaintext file, and is loaded on-demand. This means that savvy Players can noodle around with just the text file and have it load by simply changing configs in-game. I did put in some validations though, so no breaking a ‘Metal’s Hardpoint values by putting in larger-than-allowed weapons or such.


Finally, once the Player has tweaked the ‘Metal to their liking, they proceed to the Deployment Menu. When the Player hits the ‘Launch’ button, the CSQC takes all their selection data and pushes it up to the Server for parsing. The Player’s chosen mech UID is sent, along with Hardpoint data and Weapon Group options, the server than takes this data and builds the Player’s entity into that mech. Overall, this was a really fun feature to build for battleMETAL, and fairly radical for any Quake mod at all.

Monday, December 31, 2018

battleMETAL - Why that was a bad idea, a terrible idea - Part 6 - Game Logic

A series of events;
 
    An important part of any game is level design and the game play loop of the level. One of the reasons I chose Quake / Darkplaces is because of how mod-friendly the map making process is...and how well documented it is. Back in my high school days, before learning to code, I had dabbled with making maps for Quake II. iD Tech games all generally used a tool called GTKRadiant for level creation, this was an all-in-one map maker for their games. The tool allowed users to created the full 3D spaces of a Quake level, place map objects like weapons, monsters, and player start zones. It also is where the logic of a map was created. These logic map objects were under sections called trigger_ and func_. They are responsible for making doors that move, elevators, changing maps, etc.


    There’s an old joke about iD games using the map design called a ‘monster closet.’ A monster closet is a part of the map where, behind a seemingly normal piece of wall, a monster or two is just sitting there. When the player gets close, or triggers a specific event, the wall opens up surprising the player and dropping monsters on them. This is a joke because if you look at the wall, it doesn’t look like it should logically have any space behind it and what exactly were the monsters doing behind the wall? Taking a smoke break?


    For this article, let’s focus on the how of the monster closet. The user in GTKRadiant would create a space behind the wall. They would place monster entities into that space. Next they’d turn the ‘normal’ wall piece into a door. The door, like the monsters, is another entity. Rather than relying on a specified model though, the door entity uses the map geometry its assigned as its model. Then the map designer would create another map object; trigger_once, point it to the ‘wall’. The Quake C would then say, “when the player entity touches trigger_once, open the door.” In this regard, we can say that the map file holds a lot of the game state and logic. 


    I believed that at least for map design and creation, Quake was a good move. Creating custom entities that the editor can place was fairly straightforward. GTKRadiant uses a text file to define all the possible map entities that can be placed before compiling the map into a format that Quake can read. By adding my own battleMETAL objects to this text file, I found it was easier to create maps specifically for battleMETAL - including the really cool aspects like Nav Points and Objectives ( we’ll get to those in a bit). Unlike AI which I covered previously, the map logic schema for Quake is surprisingly flexible. The map editor GTKRadiant uses a series of ‘definition’ files containing text, usually called entities.def. This file contains the definitions for entities to be placed on the map.


    The definitions must link to function names in the Quake C code. For example, the definition for an enemy AI looks like this in the .def file:
   
/*QUAKED unit_human_mech_sniper (1 0 0) (-14 -14 -20) (14 14 20) <spawn flags>
        <description text here>
    */
   
    The “QUAKED” is the head tag for an entity definition, the next piece is the function name “unit_human_mech_sniper” which should be defined in the Quake C code exactly the same wording. From what I can tell, the map compiles the entities and converts the function name either to a reference or compares the function name to all the functions in the code to find a match. Once the game finds the right function, it creates an entity and then calls this function on that entity. Whatever code is written in unit_human_mech_sniper is then run immediately.

    The next series of arguments are as follows: color in GTKRadiant, and the bounding box size. The color is for color coding the entity in the map editor, making it easier to tell entities apart from each other. As for bounding box size, that is also shown in the editor, but will also be passed to the engine when the entity is spawned. Quake C code can override the bounding boxes if it is needed. Spawnflags are an important piece of data for both the editor and the engine. Flags are a data storage concept used in general coding. They hold a series of “yes or no” sets in a single variable usually by using binary math. For our purposes here, every entity has a spawnflags field which can set up certain “yes or no” choices when the entity is created. The exact wording is up to the coder, so one entity might have DROP_TO_FLOOR for a spawnflag while another has START_INACTIVE.  The map editor will render these as checkboxes.


    Finally there’s a the descriptor section, it is here that normal text will be rendered in the map editor but has no relation to code in the game. This section is for any relevant info about creating, placing , and using a map entity. Altogether this system is fairly easy to understand and extend for modding. Debugging is nice as well, if engine can’t parse the entity from the map, it simply removes the entry and logs the result into the console.


    Now that we have context for the maps, we can do quick overview of the game logic itself. Every gameplay instance in Quake takes place on one of these maps files. The map is loaded up when the server switches to the desired map file. In Quake C this before  the Main() function is run, the main is the entry function for the Quake C. StartFrame() is any code you want to run before the engine runs all other code in that slice of server time. From here the game instance if open-ended, where the ‘end’ of a map can be determined in a few different ways. Vanilla Quake used round timers, kill counts, and map entities to end a game instance and load the next map. For battleMETAL I needed something a bit more sophisticated, seeing as how the game is descended from different DNA.


    battleMETAL is inspired by mech sims, in which part of the simulation was of more authentic military scenarios. This meant that the player is given a set number of ‘objectives’ to achieve before the scenario is ended. The objectives sometimes were destroying things, but other times it was just visiting a location on the map or protecting a base of buildings from an attacking enemy. So, to achieve a similar setup as I created some custom map objects - the Objective, and the Nav Point. I also extended the game engine to load in a text file called a “mission file.” The world map entity would have a variable that pointed to a specified mission file that was loaded to the player’s view when they connect to a map. The code I wrote also sends a unique objective ID for each objective on the map. This is linked to a list of objectives in the mission file. Together, they communicate objectives to the player both during the briefing and game play.


    Of all the things I found to be difficult with battleMETAL, this was surprisingly not one of those things. The configuration of GTKRadiant is fairly well documented and extending the functionality of making maps for my specific game turned out to not be a lovecraftian nightmare, unlike other parts of the project….well except for those 3D terrain meshes…<sigh>.

Tuesday, August 14, 2018

battleMETAL - Why that was a bad idea, a terrible idea - Part 2 - Model Mishaps

Compromises were made
Models are a good enough place to start about things that went awry. The most complicated models so far in battleMETAL are the star units themselves, the metals. The game requires that certain game units have multiple hit locations that can be destroyed before the entire unit is killed. In the case of the mechs, the locations are the center torso, left and right torsos, left and right arms, and the legs. 5 locations total for every mech unit. To better communicate damage to players, it was decided that arms and torsos should be ‘blown off’ when the health of those areas reach zero. This translates to each mech piece its own disconnect mesh, like a lego piece.

Most FPS games don’t deal with complex vehicles, they are scaled to people-sized players and monsters fighting. The models for each of this units is usually 1 solid, animated mesh. Despite complexity of the model, the fact that model is 1 contiguous piece of geometry makes working with them fairly easy. Having five sub-models that make up one complete mech model gets heavy very quickly. It increases the number of files per game unit, makes coding for game units a bit more complex. More modern game engines can handle this complication using techniques like bone-based modeling. Quake was made slightly before skeletal animation became an industry standard. Half-Life is credited with bringing skeletal animation to the mainstream and Half-Life’s engine is a heavily modified version of the Quake 1 engine.

Darkplaces does allow for skeletal animation but it comes with some severe catches. The quake c code for Darkplaces can utilize bones but only on the client-side. Now why is this a problem? I think I mentioned client-side prediction as an issue in previous posts. The snag with this approach is that the bones are only rendered on the client’s side of the game code. If the bones are only on the client then the location in game-space of those bones becomes an issue due to discrepancies between server and client game states. If Darkplaces came with out-of-the-box client-side prediction for programmers to work with, then this might be less technical debt. As it stands, client-side prediction was left up to the individual coder for some...reason...and not every programmer is up to the task of solving a such a complex issue.

The other possible option for skeletal animation is found in the Quake 3 model format called md3. This model format was made for Quake 3, itself a multiplayer-centric, fast-paced, arena FPS from late 1999. Quake 3 also made an attempt to fix client-side issues of skeletal animation but John Carmack went about it in his own unique way. MD3 models can be given little ‘tag’ triangles (a triangle being made up of 3 vertices in the model). Each ‘tag’ would then be given a unique name within the model for later reference. The technical debt of this approach is how tags are organized. When making a model, the tags have to be made, pointing in the desired direction of orientation for the mesh that will be attached there. The tags themselves are not saved when the model is exported to md3, thus any subsequent work on the mesh will require remaking the tags in their specific location.

For Quake 3 models that only ever had 1 or 2 tags per model, the ‘tag’ method was an ok solution. battleMETAL however, breaks this through complexity of the models in the game. The solution I settled on isn’t exactly performant for multiplayer gaming (thankfully battleMETAL is more a single-player game), but it does work. Remember how every model is an entity in quake c? Entities can be given specified ‘movement’ types to reflect, in the engine, how the entity is supposed to move. Player entities have MOVETYPE_WALK which is for handling player movement and physics. Darkplaces expands this functionality by adding a new MOVETYPE_FOLLOW. This movement mode is for objects that cannot collide with anything, and is for entities that, well, follow another entity.

General use-case for movetype_follow in the context of Quake is things like, having the gun model that a player is holding reflected externally by having a gun entity set to movetype_follow. Using this, battleMETAL treats all mech model pieces as separate entities with the movetype_follow. The locations of the pieces are set in relation to the controlling player’s origin point. In this way, the requirement of having hit locations, and visible damage have been satisfied without resorting to time consuming or costly programming quirks. The code uses the Factory Pattern to turn a mech data file into a mech, including the construction of the model component entities.

battleMETAL was able to use some animation for the mech models in the end. The leg models for every mech are animated in the model creation program and exported to the md3 format when animations are complete. The quake c code then controls the playback of those animations. Although this means animations are comparatively ‘stiff’ when put next to modern games, they are sufficient for the project.