-
Notifications
You must be signed in to change notification settings - Fork 79
Input and Output Data
The base agent has some useful values that we can read from in order to get some basic information about the car that we are controlling, the team our car is on, and even the name of our car in the game.
These values can be accessed at any time from the base agent using the self
parameter:
def initialize_agent(self):
# Print the bot's team
print(self.team)
def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
# Get our car
my_car = packet.game_cars[self.index]
Some useful values to know are:
self.team = 0 # The team that our bot is on
self.name = "ExampleBot" # Our bot's in-game name
self.index = 2 # The index of the car our agent is controlling
Every tick, your bot will receive a GameTickPacket from the framework. The packet will contain all the raw values from the game (such as car and ball locations). Your bot receives the packet in the get_output(self, packet)
function and you should return a ControllerState which describes what your bot's response is.
Getting values from the packet is simple:
def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
# You can get values out of the packet like this:
x_location_of_first_car = packet.game_cars[0].physics.location.x;
NOTE: Some structs/lists in the GameTickPacket has a fixed length. So make sure you use the num_cars
and the num_boosts
values as lengths if you iterate through the associated lists.
packet: {
'game_cars': [
{
'physics': {
'location': {'x': 0.0, 'y': 0.0, 'z': 0.0},
'rotation': {'pitch': 0.0, 'yaw': 0.0, 'roll': 0.0},
'velocity': {'x': 0.0, 'y': 0.0, 'z': 0.0},
'angular_velocity': {'x': 0.0, 'y': 0.0, 'z': 0.0}
},
'is_demolished': False,
'has_wheel_contact': True,
'is_super_sonic': False,
'is_bot': True,
'jumped': False,
'double_jumped': True,
'name': 'Jupiter',
'team': 0,
'boost': 48.0,
'hitbox': {'length': 118, 'width': 84, 'height': 36},
'hitbox_offset': {'x': 13.88, 'y': 0.0, 'z': 20.75},
'score_info': {
'score': 340,
'goals': 2,
'own_goals': 0,
'assists': 1,
'saves': 1,
'shots': 3,
'demolitions': 1
}
},
{ ... }
],
'num_cars': 2,
'game_boosts': [
{
'is_active': True,
'timer': 0.0
},
{ ... }
],
'num_boost': 36,
'game_ball': {
'physics': {
'location': {'x': 0.0, 'y': 0.0, 'z': 0.0},
'rotation': {'pitch': 0.0, 'yaw': 0.0, 'roll': 0.0},
'velocity': {'x': 0.0, 'y': 0.0, 'z': 0.0},
'angular_velocity': {'x': 0.0, 'y': 0.0, 'z': 0.0}
},
'latest_touch': {
'player_name': 'Beavis',
'time_seconds': 120.63,
'hit_location': {'x': 0.0, 'y': 0.0, 'z': 0.0},
'hit_normal': {'x': 0.0, 'y': 0.0, 'z': 0.0},
'team': 0,
'player_index': 0
},
'drop_shot_info': {
'damage_index': 0,
'absorbed_force': 0,
'force_accum_recent': 0
},
'collision_shape': {
'type': 1,
'box': {'length': 153.0, 'width': 153.0, 'height': 153.0},
'sphere': {'diameter': 184.0},
'cylinder': {'diameter': 184.0, 'height': 30.0}
}
},
'game_info': {
'seconds_elapsed': 405.12,
'game_time_remaining': 34.0,
'is_overtime': False,
'is_unlimited_time': False,
'is_round_active': True,
'is_kickoff_pause': False,
'is_match_ended': False,
'world_gravity_z': -650.0,
'game_speed': 1.0,
'frame_num': 3923
},
'teams': [
{
'team_index': 0,
'score': 7
},
{ ... }
],
'num_teams': 2,
}
A ControllerState is the controller input the bot should perform. Import ControllerState like this: from rlbot.agents.base_agent import SimpleControllerState
, and create a new instance with SimpleControllerState()
Example:
from rlbot.agents.base_agent import BaseAgent, SimpleControllerState
from rlbot.utils.structures.game_data_struct import GameTickPacket
class ExampleBot(BaseAgent):
def initialize_agent(self):
pass
def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
# Make the bot go forwards by setting throttle to 1
controller = SimpleControllerState()
controller.throttle = 1
return controller
The ControllerState has the following attributes:
{
throttle:float; /// -1 for full reverse, 1 for full forward
steer:float; /// -1 for full left, 1 for full right
pitch:float; /// -1 for nose down, 1 for nose up
yaw:float; /// -1 for full left, 1 for full right
roll:float; /// -1 for roll left, 1 for roll right
jump:bool; /// true if you want to press the jump button
boost:bool; /// true if you want to press the boost button
handbrake:bool; /// true if you want to press the handbrake button
use_item:bool; /// true if you want to use a rumble item
}
A few values are constant, like the locations of boosts and goals. Some of these can be found in the FieldInfo data. FieldInfo contains the following:
field_info: {
'boost_pads': [
{
'location': Vector3,
'is_full_boost': boolean
},
{ ... }
],
'num_boosts': int,
'goals': [
{
'team_num': int,
'location': Vector3,
'direction': Vector3,
'width': float,
'height': float
},
{ ... }
],
'num_goals': int
}
Note: Dropshot tiles can be found in FieldInfo as GoalInfo objects.
Note2: Boost pads and Dropshot tiles will be sorted according to y * 100 + x
, just like their counterparts (game_boosts
and dropshot_tiles
that contain their current state) in the game tick packet.
In Python, you can retrieve this information by calling get_field_info()
on the BaseAgent:
from rlbot.agents.base_agent import BaseAgent, SimpleControllerState
from rlbot.utils.structures.game_data_struct import GameTickPacket
class ExampleBot(BaseAgent):
def initialize_agent(self):
pass
def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
# Constant values can be found the the FieldInfo:
info = self.get_field_info()
# Manually construct a list of all big boost pads
# info.boost_pads has a fixed size but info.num_boosts is how many pads there actually are
big_pads = []
for i in range(info.num_boosts):
pad = info.boost_pads[i]
if pad.is_full_boost:
big_pads.append(pad)
Mutators can only be set before the game starts by a human.
To get the current mutators, do something like this:
from rlbot.messages.flat.BoostOption import BoostOption
def initialize_agent(self):
# See all of the match settings at https://github.com/RLBot/RLBot/blob/master/src/main/flatbuffers/rlbot.fbs#L784
match_settings = self.get_match_settings()
mutators = match_settings.MutatorSettings()
# Examples
# Game mode
game_mode = (
"soccer",
"hoops",
"dropshot",
"hockey",
"rumble",
"heatseeker"
)
self.gamemode = game_mode[match_settings.GameMode()]
# View all mutator options at https://github.com/RLBot/RLBot/blob/master/src/main/flatbuffers/rlbot.fbs#L753
# Boost amount
if mutators.BoostOption() == BoostOption.Unlimited_Boost:
# Do whatever
# Boost acceleration
# Leverage some known constants to get an acceleration value based on the setting.
# These values are from the Physics section of the Useful Game Values wiki -> https://github.com/RLBot/RLBot/wiki/Useful-Game-Values#physics
base_boost_accel = 991 + (2/3)
boost_accel = (
base_boost_accel, # Default
base_boost_accel * 1.5, # Strength x 1.5
base_boost_accel * 2, # Strength x 2
base_boost_accel * 10 # Strength x 10
)
self.boost_accel = boost_accel[mutators.BoostStrengthOption()]
You can find descriptions of the tricky values in this file:
https://github.com/RLBot/RLBot/blob/master/src/main/flatbuffers/rlbot.fbs
You will recognize some of the previous mentioned classes as tables. I.e. packet.score_info
is a ScoreInfo table.