Game inputs and float observation vector
This document describes what data we take from the game (TMInterface / SimStateData), how it maps to our float observation vector, and what the game provides but we do not use. It is the single reference for “are we using everything?” and for extending the observation space.
Sources
TMInterface 2 (socket API): we use
get_simulation_state()→ returnsSimStateData(from packagetminterface).Float vector is built in
trackmania_rl.float_inputs:build_float_vector()andstate_dict_from_sim_state().RL rollout:
game_env_backend.py/game_instance_manager.pycallget_simulation_state(), thenstate_dict_from_sim_state(), thenbuild_float_vector().
Float vector layout (dimension and indices)
Formula (see config_loader.py):
float_input_dim = 27 + 3 * n_zone_centers_in_inputs
+ 4 * n_prev_actions_in_inputs
+ 4 * n_contact_material_physics_behavior_types
+ 1
With defaults (n_zone=40, n_prev=5, n_contact=4): 184.
Order (same as in float_inputs.build_float_vector()):
Index |
Count |
Description |
Source |
|---|---|---|---|
0 |
1 |
Temporal: time left in mini-race (overwritten in buffer collate) |
Config / rollout |
1–20 |
20 |
Previous actions: [accel, brake, left, right] × 5 steps |
Our action buffer (not from SimStateData) |
21–52 |
32 |
gear_and_wheels: 4×is_sliding, 4×has_ground_contact, 4×damper_absorb, gearbox_state, gear, actual_rpm, counter_gearbox_state, 4×4 contact_material one-hot per wheel |
sim_state.scene_mobil, sim_state.simulation_wheels |
53–55 |
3 |
Angular velocity (car frame) |
dyna.current_state.angular_speed × orientation |
56–58 |
3 |
Velocity (car frame); 56=lateral, 58=forward |
dyna.current_state.linear_speed × orientation |
59–61 |
3 |
y_map (up in world, in car frame) |
orientation @ [0,1,0] |
62–181 |
120 |
Zone centers in car frame (40 × 3) |
Map VCP + position/orientation (not raw from game) |
182 |
1 |
Margin to finish (meters) |
Zone math (not raw from game) |
183 |
1 |
is_freewheeling |
scene_mobil.is_freewheeling |
Indices used elsewhere: buffer_management.py and reward shaping use e.g. 25:29 (ground contact), 56:59 (velocity), 62:65 and 65:68 (first two zone centers). If you add/remove/reorder features, update those indices and float_input_dim.
What we use from SimStateData
dyna.current_state: position, rotation (orientation), linear_speed, angular_speed.
scene_mobil: engine.gear, engine.actual_rpm, gearbox_state, is_freewheeling.
simulation_wheels (×4): real_time_state.is_sliding, has_ground_contact, damper_absorb, contact_material_id (mapped via
contact_materials.pyto physics behavior categories).sim_state.race_time: used for progress and finish detection (not in the float vector).
We do not read previous_state / temp_state from dyna; we only use current_state.
What the game provides but we do NOT use
Below is what is available on SimStateData (and related structs) but is not fed into the float vector or used in rollout logic. Adding any of these would require extending FloatStateDict, state_dict_from_sim_state(), build_float_vector(), config (e.g. float_input_dim, normalization), and any code that indexes by position (e.g. buffer_management reward logic).
dyna (HmsDynaStruct)
previous_state, temp_state (full previous/temp physics state).
add_linear_speed, force, torque, inverse_inertia_tensor.
quat (we use rotation matrix only).
unknown, not_tweaked_linear_speed, owner.
scene_mobil (SceneVehicleCar)
input_gas, input_brake, input_steer (current inputs; we use our own prev_actions from the action buffer).
max_linear_speed, quality, block_flags.
prev_sync_vehicle_state, sync_vehicle_state, async_vehicle_state, prev_async_vehicle_state (speed_forward, speed_sideward, rpm, input_steer, input_gas, input_brake, is_turbo, gearbox_state — game-internal view; we use dyna velocity and engine instead).
has_any_lateral_contact, last_has_any_lateral_contact_time.
water_forces_applied, turning_rate, turbo_boost_factor, last_turbo_type_change_time, last_turbo_time, turbo_type, roulette_value.
is_sliding (car-level; we use per-wheel is_sliding), wheel_contact_absorb_counter, burnout_state.
current_local_speed, total_central_force_added, is_rubber_ball, saved_state.
simulation_wheels (per wheel)
Everything except real_time_state: steerable, surface_handler (position, rotation, etc.), offset_from_vehicle, prev_sync_wheel_state, sync_wheel_state, async_wheel_state, contact_relative_local_distance, etc.
real_time_state: we use is_sliding, has_ground_contact, damper_absorb, contact_material_id only; we do not use field_4, field_8, field_12, field_48, field_84, field_108, relative_rotz_axis, nb_ground_contacts, field_144, rest.
SimStateData top-level
version, context_mode, flags, timers.
cp_data (checkpoint data): we use our own zone/VCP math and do not feed raw checkpoint state into the float vector.
TMInterface API
get_inputs(): returns replay input script as a string (used in validation/capture scripts), not the current simulation state. We do not use it for the RL observation.
Outputs: commands we send to the car (binary vs analog)
What we use today: binary only.
We send commands via TMInterface.set_input_state(left, right, accelerate, brake) in tminterface2.py. All four arguments are booleans (packed as uint8). So in RL we only send one of the 12 discrete actions from config.inputs.actions (e.g. left+accel, right+brake, coast) — no analog steering or throttle.
What TMInterface supports in script format:
steer — analog. Integer in
[-65536, 65536](negative = left, positive = right, 0 = no steer). Extended range withextended_steer. Used in replay/script files (e.g."8.43 steer 13292").gas — analog in script, but TMNF does not use the value: “TMNF/TMUF do not support analog acceleration.” So gas is effectively binary in TMNF.
press / rel — binary (up, down, left, right).
When we convert replays to TMInterface scripts we convert analog steer to binary left/right (deadzone); we do not preserve analog in our RL action space.
Socket API we have: Our client only implements C_SET_INPUT_STATE with 4 bytes (left, right, accelerate, brake). There is no set_steer_value(int) in tminterface2.py. So for real-time control we are limited to binary. To use analog steer we would need a new socket message type (if the game plugin supports it) or per-step execute_command("steer ...") (brittle).
Summary
Dimension: 184 with default config; layout is fixed in
float_inputs.build_float_vector()and must matchfloat_input_dimand state normalization.Used from game: current dyna state (position, orientation, linear/angular speed), mobil engine/gearbox/freewheeling, and per-wheel sliding/contact/damper/contact_material.
Not used: previous/temp dyna state, forces/torques, car-level lateral contact/turbo/burnout, game’s internal vehicle states (sync/async), wheel geometry/sync state, replay input string, and raw checkpoint data. Adding any of these would require extending the float vector and all dependent code (config, normalization, reward indices).