As part of my bachelor thesis on matchmaking in online video games, I implemented a player-specific skill rating in Tropical Gunfight. This means that each player is rewarded with ranking points based on his behavior. If the player makes sure to behave in a class-specific way, he will receive a higher multiplier on the points. These points are then added to his existing matchmaking points.
To allow testing, I configured the system in a decentralized way, rather than using databases as is typical in the industry. All information that the player achieves in the course is stored locally in a file. This includes his MMR points, K/D as well as kills and deaths. If the player deletes this file, his progress is reset.
Besides the standard class, I implemented another class, the sniper. Each class has its gadget. The Soldier gets one grenade per round, while the Sniper gets one mine. Furthermore, the Soldier rewards the player for seeking close combat, measuring how long the player aims at the enemy at close range.
The Sniper class is different, here the player has the option to right-click to get into the gun's aimer. Here, it is measured how long the player aims at the opponent via the aiming device, whereby the distance does not play a role.
I implemented the two classes because I want players to be able to get more MMR points through class-specific behavior.
I'll talk about the number of points and the multipliers later on.
The controls of the game can be read in the lobby. To use a gadget of the respective class, you need to press the F key.
In the following, I will explain a small part of the game, so that in the further course of the documentation certain terms are more understandable.
PlayerController: The PlayerController is created for each player and is not replicated. Only the server and the owning client have access to the class. In it, functions are called that are responsible for controlling the player or the HUD, for example.
GameState: The GameState class is replicated for the server and all clients. It is used to determine game information such as round winners or to instantiate items that spawn in the level.
PlayerState: Just like the GameState class, the PlayerState class is also replicated. It contains player information like team affiliation or player class.
GameInstance: The GameInstance class runs on the server and client and persists through all levels of the game without being destroyed. This class is not replicated and serves as the cornerstone, so to speak, for every game.
GameMode: For completeness, I also mention the GameMode class here. It is created exclusively on the server and this is also the only one who has access to it. It contains for example the functions which are responsible for the spawning of the players.
The collection of telemetry data is always performed by the server only per game. Four files were created in the project for this purpose:
TelemetryStats_EN: The enumerator where the names of the stored statistics are stored.
Telemetry_FL: The function library contains different functions for storing and reading telemetry data.
Telemetry_BP: Core class for storing the variables for the telemetry data.
Variables include:
NOTE: Team A is equivalent to the red team and Team B is equivalent to the green team. In addition, in the entire project, a string was used instead of an enumerator for the teams. Since a change would have too strong an impact on the entire project, I have left it at that.
Telemetry_DA: DataAsset where the telemetry data is stored.
As already mentioned, the Telemetry_FL contains a library of functions to collect or read telemetry data. To collect the telemetry data the function "AddStat" is executed.
This function can be used at any point. Figure 3 shows an example of the use of the "AddStat" function when recording the time for which the player has been aiming at the opponent as a soldier. After a timer has been started in the Soldier class, the macro from Figure 3 is called. In it, the PlayerState is checked in the base class to see in which team the player is playing and it is passed to the function as a parameter. In addition, the telemetry name or stat name and the value are passed. Since the timer is configured in 0.1 seconds, the same value is passed at this point. It is important to mention here that only the server saves the value and not the client. This applies to all captured telemetry data.
In it, the functions from figure 4 are used.
The function "Calculate Total" calculates the total number of ranking points for the respective team. The "Set New MMR" function saves the calculated values to the local save file or adds the value to the existing MMR points. The kills and deaths of the players are also added (see Figure 5). used
At this point, it should be mentioned that in the course of the project phase as well as in the practical work, the biggest challenge for me was to ensure the successful replication of the data. This is because many actions may only be performed by the server to avoid cheating, such as reloading the weapon. However, not everything may be replicated to keep the network load as low as possible.
This was also the case at the point where the MMR is stored. Because although the values are determined by the server, it must not write the information into the local file. Here it must be exactly paid attention to the fact that this information carries out only the client itself.
Figure 6 shows the area responsible for calculating the total score.
The most important part of the work is located in the macro framed in red (see Figure 6).
Here, all telemetry data is read out and calculated centrally in one place. This gives the game designer, among others, the possibility to configure the multipliers in one place to make the game more balanced. The calculated value is then saved. Figure 7 shows an excerpt from the macro when calculating the headshots that the player has taken in the course of the game.
The red-framed nodes are again functions from Telemetry_FL. The value that was saved for headshots is read and saved with a multiplier of two as ranking points.
As already mentioned, the telemetry data is collected and calculated by the server. So that the values can be output at the end on the client, the result is passed over the Node "MultiFillRPData" to both PlayerControllers. These in turn store the values in member variables so that the values can be displayed at the end of the game.
For integrity reasons, the telemetry data is executed by the server. But so that each client can see the values on the screen at the end of the game, each client has member variables that are filled with the values of the server.
Figure 8 shows that each client executes the ClientShowGameWon event at the end of the game, showing each client the final screen with the telemetry data. Here, each client queries for the values of Team A and Team B.
The red-framed node from Figure 9 is examined in more detail in Figure 10.
In the event "TelemetryReadStats" you can see how an enumerator can be used to query the values for each class individually (see Figure 11).
This can be done individually per class. Here, the values can also be exchanged at any time or a new class can be implemented in the game.
As can be seen in Figure 9, after the values have been queried, this information is brought from the member variable to the so-called widget.
Figure 12 shows a section in which the values for team A are read from the member variable for the widget.
To make the whole thing clearer, Figure 13 shows how the widget obtains the class information from the member variable.
Here, the value is searched for from the "m_classTeamA" dictionary variable with the "TeamA" key. This value is entered in the "GreenClassText" text field.
Note: I have worked in the widget with the teams red and green to immediately recognize that this is a widget and not a variable in another class.
Figure 14 shows how each client now sees the values on its screen.
Figure 15 shows the MMR value in the lobby, as well as the Kills and Deaths and K/D of the players.
Based on my implementation of player abilities through class-specific behavior, I was able to show that it is indeed possible to evaluate players individually. This technique could be implemented similarly in team-based games to evaluate players more accurately. The developers can make appropriate configurations, which provide for a balance of the values. By this, I mean that the multiplier in my project on the gadget can also be configured higher to reward the player even more for behaving in a class-specific manner. This would move away from the conventional method, whereby players only get points by winning or losing, and ensure that player skills can be assessed more individually.
Unreal Engine 4.26.2
Link to the repository: https://christian-brueckl.de:8443/svn/TropicalGunfightBA/
_________________________
Username: showcase
Password: showcase
If you can't find a game over the internet, you should check the following steps: