Skip to content

Input and Output Data

Tangil edited this page Dec 20, 2021 · 6 revisions

Input and output data are controlled by this specification: https://github.com/RLBot/RLBot/blob/master/src/main/flatbuffers/rlbot.fbs

Based on that file, classes are auto-generated in the rlbot.flat namespace, e.g. rlbot.flat.GameTickPacket.

Using internal data structures

As in the C# example, it is strongly recommended that you convert structures like the GameTickPacket into your own internal data structures. Here are some reasons to do so:

  • It is generally wise in software engineering to translate to your own business objects. If the framework changes the data format, you will only need to alter your code in one place (the translation), and your complicated bot logic can stay the same.
  • The classes in the rlbot.flat namespace are not particularly useful. They have no functions and their read access is a bit slow because the data is stored in a way that you might not expect.
  • By using your own version of the structures, you get to avoid the displeasing .Value syntax (nullables) that is generated through FlatBuffers.

The example bot already comes with internal data structures that you can use. These processed structures can be extended to fit your needs, or you can throw them away entirely.

Bot class

The Bot class (the class that your own bot should inherit) 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 are public readonly properties in the Bot class. Some useful values to know are:

this.Team;          // The team that our bot is on
this.Name;          // Our bot's in-game name
this.Index;         // The index of the car our agent is controlling

Note that you don't need to write this unless there is name shadowing. It was added above to clarify that it is part of the Bot class.

GameTickPacket

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 GetOutput(GameTickPacket packet) method and you should return a Controller which describes what your bot's response is. Since GetOutput is an abstract method in the Bot class, you must override it.

Getting values from the packet is simple:

public override Controller GetOutput(rlbot.flat.GameTickPacket packet)
{
    // Get the location of our car
    // It is strongly recommended that you convert rlbot.flat.GameTickPacket into your own internal structure before
    // using any game data, or use the processed versions as shown in the example.
    rlbot.flat.Vector3 carLocation = rawPacket.Players(this.index)?.Physics?.Location.Value;

    ...
}

NOTE: Some properties in the GameTickPacket don't implement IEnumerable as you may expect. For example, GameTickPacket.Players is not an IEnumerable but instead a method. To get the length of one of these structures, use the property that specifies the length (e.g. PlayersLength). This is one of the reasons you should convert everything you use from the GameTickPacket into your own internal data structure! The internal data structure in the example bot uses arrays so IEnumerable is already implemented for those fields.

Sample GameTickPacket

Here is a sample GameTickPacket presented in a JSON-like format for clarity.

packet:
{
    Players:
    [
        {
            Physics:
            {
                Location: {X: 0f, Y: 0f, Z: 0f},
                Rotation: {Pitch: 0f, Yaw: 0f, Roll: 0f},
                Velocity: {X: 0f, Y: 0f, Z: 0f},
                AngularVelocity: {X: 0f, Y: 0f, Z: 0f}
            },
            IsDemolished: false,
            HasWheelContact: true,
            IsSupersonic: false,
            IsBot: true,
            Jumped: false,
            DoubleJumped: true,
            Name: CSharpExample,
            Team: 0,
            Boost: 48f,
            Hitbox: {Length: 118f, Width: 84f, Height: 36f}
        },
        { ... }
    ],
    PlayersLength: 2,
    BoostPadStates:
    [
        {
            IsActive: true,
            Timer: 0f
        },
        { ... }
    ],
    BoostPadStatesLength: 36,
    Ball:
    {
        Physics:
        {
            Location: {X: 0f, Y: 0f, Z: 0f},
            Rotation: {Pitch: 0f, Yaw: 0f, Roll: 0f},
            Velocity: {X: 0f, Y: 0f, Z: 0f},
            AngularVelocity: {X: 0f, Y: 0f, Z: 0f}
        },
        LatestTouch:
        {
            PlayerName: CSharpExample (2),
            GameSeconds: 120.63f,
            Location: {X: 0f, Y: 0f, Z: 0f},
            Normal: {X: 0f, Y: 0f, Z: 0f},
            Team: 0,
            PlayerIndex: 0
        },
        DropShotInfo:
        {
            DamageIndex: 0,
            AbsorbedForce: 0f,
            ForceAccumRecent: 0
        }
        ShapeType:
        {
            (enum)
            NONE = 0,
            BoxShape = 1,
            SphereShape = 2,
            CylinderShape = 3
        }
    },
    GameInfo:
    {
        SecondsElapsed: 405.12f,
        GameTimeRemaining: 34f,
        IsOvertime: false,
        IsUnlimitedTime: false,
        IsRoundActive: true,
        IsKickoffPause: false,
        IsMatchEnded: false,
        WorldGravityZ: -650f,
        GameSpeed: 1f
    },
    Teams:
    [
        {
            TeamIndex: 0,
            Score: 7
        },
        { ... }
    ],
    TeamsLength: 2,
}

Controller

A Controller is the controller input the bot should perform.

Example:

public override Controller GetOutput(rlbot.flat.GameTickPacket rawPacket)
{
    // Return an empty controller with default values
    return new 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
  UseItem: bool; // true if you want to use a rumble item
}

Field Info

A few values are constant, like the locations of boosts and goals. Some of these can be found in the FieldInfo data.

You can retrieve this information by calling GetFieldInfo() in your class that inherits the Bot class:

public override Controller GetOutput(rlbot.flat.GameTickPacket rawPacket)
{
    // Get the locations of all the big boost pads
    rlbot.flat.FieldInfo fieldInfo = GetFieldInfo();

    Vector3[] boostPads = new Vector3[fieldInfo.BoostPadsLength];

    for (int i = 0; i < fieldInfo.BoostPadsLength; i++)
        if (fieldInfo.BoostPads(i).Value.IsFullBoost)
            boostPads[i] = fieldInfo.BoostPads(i).Value.Location.Value.ToVector3();

    ...
}

.ToVector3() can be an extension method that converts rlbot.flat.Vector3 objects to your own Vector3. Here's an example:

public static Vector3 ToVector3(this rlbot.flat.Vector3 vector)
{
    return new Vector3(vector.X, vector.Y, vector.Z);
}

FieldInfo contains the following:

FieldInfo:
{
    BoostPads:
    [
        {
            Location: {X: -3584f, Y: 0f, Z: 73f},
            IsFullBoost: true
        },
        { ... }
    ],
    BoostPadsLength: 50,
    Goals: [
        {
            TeamNum: 0,
            Location: {X: 0f, Y: 1f, Z: 0f},
            Direction: {X: 0f, Y: -5120f, Z: 312f},
        },
        { ... }
    ],
    Goals: 2
}

Dropshot data

See the central wiki here: https://github.com/RLBot/RLBot/wiki/Dropshot