Advanced Customizable Character Controller
A key feature of any FPS is how the character moves around in the world. The default Heroengine ACCC is setup well for a mmorpg character but not for a FPS character. The first step to that goal was that a new character model and animation set were developed. Then we overrode certain server scripts to make sure characters start with a FPS character controller rather than the default one.
The character controller then needed to turn inputs from the user in to specific animations and behaviors for the new character model. The basic ACCC was gutted of most of its features to be more streamlined with our needs. In some cases this just meant deleting large sections of code that just were not relevant to FPS,for example swimming logic was taken out. There is no water in our game design so there is no need for the character controller to know how to swim. In other cases code need to be added for new behaviors. One of the things that needed to be added was a tracking of which limbs you are missing. The major hook of our game was that missing limbs would result in your character controlling differently so the ACCC needed to be keep track of which ones you had or did not.
Another major concept that the character controller needed to handle was the fact that character rotation would no longer be dictated by the direction the character was moving. In the HeroEngine default controller depending on where your camera is facing your character will animate and turn toward that direction. In a FPS movement almost never controls where you are directly aiming. This took handling of the character rotation out of the controllers hands.
In most cases you do not want the ACCC to be involved in game logic. The ACCC should sit between your game logic and animating the character. This means that the ACCC should react to commands from your game logic and interpret all the different signals its getting in to one final action. There are cases of where the FPS ACCC does not do this. While it technically works it means that there is tight coupling between different game systems and the character controller.
Server - Client Communication
Weapons and Attacking
Weapons and Attacking Weapons in FPS Games can have many different behaviors and properties. That is why weapon objects are created from the Spec System. It allows the fundamental behavior of a weapon to be varied. The creation of these complex objects is handled in the Spec System. Weapons may contain many properties but most of them are immutable which fits well with the Spec System.
Weapons are created as non-persistent nodes and added to a player's Weapon Inventory, which is replicated to the client. The fields on a Weapon are set for reverse replication which makes the client authoritative over the behavior of the Weapon.
There are two types of Weapons: Ranged and Melee. The way they attack is what seperates the two types of weapons. Both types of weapons use client side Raycasting to determine what nodes were hit by an attack.
All Raycasting used for weapon attacking uses the external function
Raycats3D is used because the weapon may not always raycast in a direction perpendicular to the viewport.
Raycast3D is also used because it can return the name of a dynamic character shape name for the node that was hit. The start position of the raycast is the active camera's node position. The direction of the ray is determined by the camera node's rotation. The length or distance of the the ray is determined by the Weapon Spec.
Modern FPS games track stats for many different actions. Those things can range from bullets fired, accuracy, steps taken, head shots, and the list goes on. In this implementation for simplicity stats are not persisted and we only track kills and deaths. We centralized stat tracking in to a system node that would get updates on the server for players various actions. The stats system could be easily extended to include more stats via the observer/listener pattern and adding in more fields.
The way it works right now is when a player joins the match they also register themselves with the stats system. Once registered their account ID is added in to a lookuplist on a statnode on the server. The stat node is tracked by the stats system and replicated to all clients registered with the stat system. When updates to the node happen the stat system is notified and then sends out messages to any systems that care about the update. Most notably the leaderboard on the client tracks stats and displays the proper information in real time.
The main drawback with the current implementation of the stat system is that it is not persisted. Stats are tracked for the duration of a player being in a match and then discarded if the player leaves the game ever. To implement a system that will permanently keep stats the server would need to write out files to the repository or make a arbitrary root stat node that is associated to the account root node. Since a system like that was beyond the scope of our demo we did not go with that implementation and kept it simple.
The camera in any FPS needs to feel smooth and responsive to allow for good accuracy. The rotating of the camera was kept very basic. As the player moves the mouse the camera is rotated based on the user's mouse sensitivity settings. This does make the camera feel very responsive, but it does mean that small corrections in aim can be hard to do. Some form of mouse acceleration would need to be used to combat lower DPI mouses and inaccuracies with mouse movement.
New command layers were added in to the gamekeybindings.ini file in the repository to handle mouse movement. This allowed us to turn on and off mouse camera control as needed. Also the external functions GetIgnoreCursor() and SetIgnoreCursor( ignore as Boolean ) Allowed us to lock the mouse to the center of the screen. With out using that external function another method of moving and locking the cursor would have been needed. In most cases SetIgnoreCursor will be what you want to use to keep the mouse in the viewport for a fps.
Lastly we added in a death cam for when a player dies. This leverages the external functions involving a camera to provide a cinematic feel. Again we kept it simple just to show how a death camera could be done. In its current implementation the death camera will position its self exactly where the FPS camera is. Once it is positioned the camera will pan down and move up to show the player their exploding robot. If a death cam was not put in the player's view would be be locked in to the same position as the robot producing a weird view.
Pick-Ups allow the player to regain health and ammo while playing. We oped to go with a simple a pick up system. On the server it queries for all trigger volumes in the area. It then checks every 30 seconds if there is a pick up class glommed on to it. If there is a pick up class glommed on it does nothing otherwise it gloms on a new pickup class. Triggers each have a preset polling period to check if a player is "inside" it. The poll rate on our triggers is set low so triggers are responsive. Based on our characters movement speed and the triggers polling rate it should be impossible to go through a trigger with out it detecting you. Depending on what type of pick-up class is glommed on to the trigger the callback method for that class will be called and perform pickup specific behavior.
This is not a robust example of how to do pick-ups because it uses all trigger volumes in a level to generate pick up locations. An alternate way to do a more robust pick up system would to leverage the spacial awareness system in conjunction with the prop system. This would make adding new pick-up spawner easy for world builders. Using trigger volumes instead limit how useful trigger volumes are in a more robust game.