mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
076d27a564
Playing with a gamepad did not work: all six move_* actions had no joypad event at all, so a pad could yaw/pitch/roll/turbo but could not translate. Nothing caught it because every action existed and the game booted fine — no assertion checked that an action is reachable on *both* devices. Controller layout, on the 6DOF convention (left stick aims, right stick translates), using all six of the pad's analog axes for the ship's six degrees of freedom: left stick yaw + pitch right stick strafe + vertical LB / RB roll RT / LT forward / back L3 turbo R3 ball camera Input is now read with Input.get_axis instead of is_action_pressed, so triggers and sticks are proportional. Keyboard values are unchanged. Three rotation bugs found by measuring a real Ship rather than reading the code: - apply_torque() is world-space and the torque was never rotated into the hull's frame (unlike thrust, which uses -ship_basis.z). Roll input became pitch after a 90 degree turn and inverted at 180, so the controls were correct flying up-field and backwards flying back. - ship.tscn's inertia is Vector3(7, 1, 7) but a flat torque was applied to every axis, giving yaw 7x the angular acceleration of pitch and roll (172 deg/s vs 52). Torque is now scaled per-axis by inertia, so rotation_acceleration means rad/s^2 and all three axes match. Yaw is unchanged. - pitch_down pitched the nose UP: get_axis's arguments were reversed, so the I/K keys and the stick each did the opposite of their label. Menus were unusable on a pad for a separate reason: Godot 4.7 gives ui_up/down/left/right joypad events by default but leaves ui_accept and ui_cancel with none (verified against a pristine project), so a controller could move the highlight and never press anything. A confirms and B goes back. Gameplay exits on a new leave_gameplay action (Escape / Start) rather than ui_cancel, so carrying B for menus cannot abandon a live match. Bindings for both devices are rebindable in Settings -> Controls, persisted to user://input.cfg — a separate file from settings.cfg because VideoSettings.save() rewrites that file wholesale and would drop any section it does not know about. project.godot stays the source of truth for defaults; overrides are only ever a delta on top of a boot-time snapshot. Verified: 268 unit tests, the ENet integration gate, and a 16-sample before/after comparison of networked prediction residuals showing the physics change does not regress them (median 0.083m -> 0.065m). Note for follow-up: every policy in Game/bots/ was trained against the old sluggish, world-axis rotation and will over-rotate until retrained.
50 lines
2.0 KiB
GDScript
50 lines
2.0 KiB
GDScript
class_name PlayerShipController
|
|
extends ShipController
|
|
|
|
# Drives a Ship from the local player's input actions (see project.godot
|
|
# [input] and FLIGHT_MANUAL.md).
|
|
|
|
var _action := ShipAction.new()
|
|
|
|
|
|
func get_action() -> ShipAction:
|
|
# Full overwrite per axis (not +=/-=): _action is reused across ticks, so
|
|
# fields must not depend on starting from a fresh Vector3.ZERO each call.
|
|
#
|
|
# Input.get_axis(negative, positive) is strength(positive) -
|
|
# strength(negative), so these keep the exact sign conventions the digital
|
|
# version had while becoming proportional on a controller:
|
|
# get_action_strength() returns a flat 1.0 for a held key but the
|
|
# normalised past-deadzone deflection for an InputEventJoypadMotion. A
|
|
# half-pulled trigger is therefore half thrust, and keyboard flight is
|
|
# unchanged down to the value.
|
|
|
|
# Forward/Backward thrust (main engines)
|
|
_action.thrust.z = Input.get_axis("move_back", "move_forward")
|
|
|
|
# Strafe thrusters (left/right)
|
|
_action.thrust.x = Input.get_axis("move_left", "move_right")
|
|
|
|
# Vertical thrusters (up/down)
|
|
_action.thrust.y = Input.get_axis("move_down", "move_up")
|
|
|
|
# Yaw (turn left/right around Y axis)
|
|
_action.rotation.y = Input.get_axis("turn_right", "turn_left")
|
|
|
|
# Pitch (nose up/down around X axis). Positive rotation.x is nose-UP:
|
|
# torque about local +X rotates the ship's up vector toward its tail by the
|
|
# right-hand rule, which lifts the nose (measured, not assumed). The
|
|
# argument order here used to be reversed, so "pitch_down" pitched up and
|
|
# the I/K keys were each labelled as the opposite of what they did.
|
|
# The default binding then gives flight-sim polarity — right stick forward
|
|
# is pitch_down is nose down — and InputSettings holds the player's
|
|
# preference for flipping that.
|
|
_action.rotation.x = Input.get_axis("pitch_down", "pitch_up") * InputSettings.pitch_sign()
|
|
|
|
# Roll (bank left/right around Z axis)
|
|
_action.rotation.z = Input.get_axis("roll_right", "roll_left")
|
|
|
|
_action.turbo = Input.is_action_pressed("turbo")
|
|
|
|
return _action
|