
Godot Genre Sports
- 124 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Helps with ai & agent building tasks during AI-assisted development.
About
godot-genre-sports is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- godot-genre-sports
- AI & Agent Building
- AI-coding skill
Godot Genre Sports by the numbers
- 124 all-time installs (skills.sh)
- +9 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #3,744 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-genre-sportsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 124 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Genre: Sports
NEVER Do (Expert Anti-Patterns)
Physics & Ball Interaction
- NEVER parent the ball directly to a player Transform; strictly keep it a standalone
RigidBody3Dand useapply_central_impulse()for realistic dribble physics. - NEVER allow the ball to "Tunnel" through goals; strictly enable Continuous CD (
continuous_cd = true) on the ball's properties for high-velocity validation. - NEVER scale a
CollisionShape3Dnon-uniformly; strictly adjust the resource radius to preserve the internal moment of inertia. - NEVER apply impulses in
_process(); strictly use_physics_process()or_integrate_forces()to prevent visual jitter. - NEVER use a single collision shape for characters; strictly use layered shapes for Head, Torso, and Legs to enable headers and chest-traps.
Match & Team AI
- NEVER allow all AI to chase the ball ("Kindergarten Soccer"); strictly implement Formation Slots (Defense/Attack) where only the closest 1-2 players engage.
- NEVER use perfect goalkeeper reflexes; strictly add a Reaction Delay (0.2s-0.5s) and an "Error Rate" based on shot angle and velocity.
- NEVER ignore Root Motion for movement; strictly use
AnimationTreewith root motion to ensure momentum and turns are visually grounded. - NEVER trust client-side goal validations; strictly require the Authoritative Server to validate physics and score logic.
Implementation & Sync
- NEVER rely on the default physics tick rate (60 TPS) for fast-moving ballistics; strictly increase physics_ticks_per_second (e.g., to 120 or 240) to prevent tunneling.
- NEVER leave Physics Interpolation disabled if you want broadcast-quality smoothness; enable it in Project Settings to smooth ball transforms between ticks on high-refresh monitors.
- NEVER parent the ball directly to a player Transform; strictly keep it a standalone
RigidBody3Dand useapply_central_impulse()for realistic dribble physics. - NEVER skip vector normalization on joystick input; strictly normalize to prevent diagonal movement from being 1.4x faster.
- NEVER handle contextual buttons with
is_action_pressed(); strictly use a ContextManager to determine if Button A means "Pass", "Tackle", or "Switch". - NEVER evaluate an
Area3Dgoal trigger immediately; strictlyawait get_tree().physics_frameto allow the Physics Server to sync.
---
🛠 Expert Components (scripts/)
Original Expert Patterns
- sports_ball_physics.gd - High-fidelity Magnus effect and air drag model for ball-centric sports.
- team_manager.gd - Macro-behavior manager implementing Formation Slots and team strategy switching.
Modular Components
- sports_patterns.gd - Collection of utilities for physics-safe impulses and authoritative scoring.
---
Skill Chain
| Phase | Skills | Purpose |
|---|---|---|
| 1. Physics | physics-bodies, vehicle-wheel-3d | Ball bounce, friction, player collisions |
| 2. AI | steering-behaviors, godot-state-machine-advanced | Formations, marking, flocking |
| 3. Anim | godot-animation-tree-mastery | Blended running, shooting, tackling |
| 4. Input | input-mapping | Contextual buttons (Pass/Tackle share button) |
| 5. Camera | godot-camera-systems | Dynamic broadcast view, zooming on action |
Architecture Overview
1. The Ball (Physics Core)
The most important object. Must feel right.
# ball.gd
extends RigidBody3D
@export var drag_coefficient: float = 0.5
@export var magnus_effect_strength: float = 2.0
func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
# Apply Air Drag
var velocity = state.linear_velocity
var speed = velocity.length()
var drag_force = -velocity.normalized() * (drag_coefficient * speed * speed)
state.apply_central_force(drag_force)
# Magnus Effect (Curve)
var spin = state.angular_velocity
var magnus_force = spin.cross(velocity) * magnus_effect_strength
state.apply_central_force(magnus_force)2. Team AI (Formations)
AI players don't just run at the ball. They run to positions relative to the ball/field.
# team_manager.gd
extends Node
enum Strategy { ATTACK, DEFEND }
var current_strategy: Strategy = Strategy.DEFEND
var formation_slots: Array[Node3D] # Markers parented to a "Formation Anchor"
func update_tactics(ball_pos: Vector3) -> void:
# Move the entire formation anchor
formation_anchor.position = lerp(formation_anchor.position, ball_pos, 0.5)
# Assign best player to each slot
for player in players:
var best_slot = find_closest_slot(player)
player.set_target(best_slot.global_position)3. Match Manager
The referee logic.
# match_manager.gd
var score_team_a: int = 0
var score_team_b: int = 0
var match_timer: float = 300.0
enum State { KICKOFF, PLAYING, GOAL, END }
func goal_scored(team: int) -> void:
if team == 0: score_team_a += 1
else: score_team_b += 1
current_state = State.GOAL
play_celebration()
await get_tree().create_timer(5.0).timeout
reset_positions()
current_state = State.KICKOFFKey Mechanics Implementation
Contextual Input
"A" button does different things depending on context.
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("action_main"):
if has_ball:
pass_ball()
elif is_near_ball:
slide_tackle()
else:
switch_player()Steering Behaviors
For natural movement (Seek, Flee, Arrive).
func seek(target_pos: Vector3) -> Vector3:
var desired_velocity = (target_pos - global_position).normalized() * max_speed
var steering = desired_velocity - velocity
return steering.limit_length(max_force)Godot-Specific Tips
- NavigationServer3D: Essential for avoiding obstacles (other players/referee).
- AnimationTree (BlendSpace2D): Crucial for sports. You need smooth blending between Idle -> Walk -> Jog -> Sprint in all directions.
- PhysicsMaterial: Tune
bounceandfrictionon the Ball and Field colliders carefully.
Common Pitfalls
1. AI Bunching: All 22 players running at the ball (Kindergarten Soccer). Fix: Use Formation Slots. Only 1-2 players "Press" the ball; others cover space. 2. Magnetic Ball: Ball sticks to player too perfectly. Fix: Use a "Dribble" mechanic where the player kicks the ball slightly ahead physics-wise, rather than parenting it. 3. Unfair Goalies: Goalie reacts instantly. Fix: Add a "Reaction Time" delay and "Error Rate" based on shot speed/stats.
Advanced Sports Meta-Systems
Professional implementation of animation synchronization, spatial intelligence, and collision filtering.
1. Root-Motion-Transition (AnimationTree)
Utilize the AnimationMixer class (and its derivatives like AnimationTree) to extract root motion from complex animations. This ensures that the character's physical displacement is driven directly by the animation data, preventing "skating" and ensuring momentum is visually grounded during high-speed turns or shots.
class_name SportsCharacter extends CharacterBody3D
@onready var anim_tree: AnimationTree = $AnimationTree
func _physics_process(_delta: float) -> void:
# Extract root motion from the current animation state
var root_motion := anim_tree.get_root_motion_position()
# Apply to velocity for physics-synced movement
velocity = (global_transform.basis * root_motion) / _delta
move_and_slide()2. Contextual-Pass-Prediction (Raycasts)
To predict if a passing lane is clear, configure a PhysicsRayQueryParameters3D object and use PhysicsDirectSpaceState3D.intersect_ray(). This allows the AI or player assist to verify unobstructed paths to teammates before committing to an action.
class_name PassPredictor extends Node3D
func is_lane_clear(target_pos: Vector3) -> bool:
var space_state := get_world_3d().direct_space_state
var query := PhysicsRayQueryParameters3D.create(global_position, target_pos)
query.collision_mask = 1 # Environment/Opponents
var result := space_state.intersect_ray(query)
return result.is_empty() # Path is clear if no collision3. Layered-Hitbox Pattern
Configure Area3D nodes with specific collision_layer and collision_mask properties to filter interactions. By assigning different layers for the ball and specific body parts (Head, Torso, Legs), you can accurately detect contextual overlaps for headers, chest-traps, or slide tackles.
class_name BodyPartHitbox extends Area3D
enum Part { HEAD, TORSO, LEGS }
@export var part_type: Part
func _on_ball_entered(ball: RigidBody3D) -> void:
match part_type:
Part.HEAD:
apply_header_force(ball)
Part.TORSO:
apply_chest_trap(ball)
Part.LEGS:
apply_kick_force(ball)Expert Tip: For the "Root Motion" system, ensure the AnimationTree property deterministic is set to true to ensure consistent displacement across different hardware.
Reference
- Master Skill: godot-master
Reference
- Master Skill: godot-master
extends RigidBody3D
class_name MagnusBallPhysics
## Expert Physical Ball (Godot 4.6).
## Implements realistic bounce, friction, and the Magnus Effect (spin-lift).
@export var magnus_coefficient: float = 0.5
func _physics_process(delta: float) -> void:
# Magnus Effect: Force perpendicular to both velocity and spin
var spin = angular_velocity
var velocity = linear_velocity
# Expert Pattern: Cross product determines lift direction
var magnus_force = spin.cross(velocity) * magnus_coefficient
# Apply central force to simulate aerodynamic lift/curve
apply_central_force(magnus_force)
## [SKILL NOTICE]: Use 'PhysicsMaterial' on the RigidBody3D for
## friction and bounce. Use 'Magnus Effect' for realistic curve balls.
# skills/genre-sports/scripts/sports_ball_physics.gd
extends RigidBody3D
## Sports Ball Physics (Expert Pattern)
## Implements Magnus Effect (curve) and air drag for realistic ball flight.
class_name SportsBallPhysics
@export var drag_coefficient: float = 0.01
@export var magnus_strength: float = 0.5
@export var air_density: float = 1.2
func _ready() -> void:
# Ensure continuous collision detection for fast balls
custom_integrator = true
continuous_cd = true
contact_monitor = true
max_contacts_reported = 3
func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
var velocity = state.linear_velocity
var speed = velocity.length()
if speed < 0.1: return
# Drag Force: Fd = -0.5 * p * v^2 * Cd * A (simplified)
# Direction opposite to velocity
var drag_force = -velocity.normalized() * (0.5 * air_density * speed * speed * drag_coefficient)
state.apply_central_force(drag_force)
# Magnus Effect: Fm = S(w x v)
# Cross product of angular velocity and linear velocity
var angular_vel = state.angular_velocity
if angular_vel.length_squared() > 1.0:
var magnus_force = angular_vel.cross(velocity) * magnus_strength
state.apply_central_force(magnus_force)
## EXPERT USAGE:
## Attach to a RigidBody3D. Set Linear/Angular Damp to 0 in inspector
## (since we apply custom drag). Shoot ball with 'apply_impulse'.
# sports_patterns.gd
extends Node
# 1. Physics-Safe Ball Impulses
# EXPERT NOTE: Forces and impulses must be applied in the physics step to ensure stability.
func kick_ball(ball: RigidBody3D, force_vector: Vector3) -> void:
ball.apply_central_impulse(force_vector)
# 2. Custom Physics Materials (Bounciness)
# EXPERT NOTE: Set restitution programmatically for consistent bounce across surfaces.
func setup_ball_bounce(body: RigidBody3D, bounce: float) -> void:
var mat := PhysicsMaterial.new()
mat.bounce = bounce
body.physics_material_override = mat
# 3. Dynamic Joypad Assignment
# EXPERT NOTE: Safely detect and assign connected controllers for local multiplayer/team play.
func get_active_controllers() -> Array[int]:
return Input.get_connected_joypads()
# 4. Fast Distance Checking (Passing AI)
# EXPERT NOTE: Use distance_squared_to to bypass expensive square root calculations in loops.
func find_closest_teammate(me: Node3D, team: Array[Node3D]) -> Node3D:
return team.reduce(func(best, p):
return p if me.global_position.distance_squared_to(p.global_position) < \
me.global_position.distance_squared_to(best.global_position) else best
)
# 5. Authoritative Score RPC
# EXPERT NOTE: Client requests; server validates physics and position before updating score.
@rpc("any_peer", "call_local", "reliable")
func request_goal_validation(ball_rid: RID, pos: Vector3) -> void:
if multiplayer.is_server():
# Validate that the ball actually crossed the plane in physics space
print("Server validated goal at: ", pos)
# 6. Unreliable State Syncing
# EXPERT NOTE: Send raw bytes via UDP for minimum latency in fast sports movement.
func sync_player_movement(data: PackedByteArray) -> void:
multiplayer.send_bytes(data, 0, MultiplayerPeer.TRANSFER_MODE_UNRELIABLE)
# 7. Safe Controller Haptics
# EXPERT NOTE: Trigger vibration feedback for heavy tackles or successful shots.
func trigger_impact_vibration(device_id: int) -> void:
Input.start_joy_vibration(device_id, 0.6, 1.0, 0.2)
# 8. AI Rubber-Banding Speed
# EXPERT NOTE: Adjust AI agent speed dynamically based on player distance to maintain tension.
func apply_rubber_band(agent_rid: RID, player_dist: float, factor: float) -> void:
var speed := 5.0 + (player_dist * factor)
NavigationServer3D.agent_set_max_speed(agent_rid, speed)
# 9. Physics Server Body State Integration
# EXPERT NOTE: Manually calculate velocities for rigid bodies without breaking physics steps.
func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
# state.linear_velocity = custom_vector
pass
# 10. Normalizing Joystick Input
# EXPERT NOTE: Always normalize input to prevent diagonal movement from being faster.
func get_move_direction(device_id: int) -> Vector2:
var input := Vector2(
Input.get_joy_axis(device_id, JOY_AXIS_LEFT_X),
Input.get_joy_axis(device_id, JOY_AXIS_LEFT_Y)
)
return input.normalized() if input.length() > 0.1 else Vector2.ZERO
extends Node
class_name SportsUmpireLogic
## Expert Sports Umpire (Godot 4.6).
## Manages game state and scoring using a State Machine and Area3D signals.
enum State { PRE_GAME, ACTIVE, POST_GOAL, GAME_OVER }
var current_state: State = State.PRE_GAME
var score: int = 0
func _on_goal_area_body_entered(body: Node3D) -> void:
if current_state == State.ACTIVE and body is RigidBody3D:
_process_goal()
func _process_goal() -> void:
score += 1
current_state = State.POST_GOAL
# Expert Pattern: Use SceneTreeTimer for non-blocking delays
await get_tree().create_timer(2.0).timeout
current_state = State.ACTIVE
## [SKILL NOTICE]: Use 'Area3D' for spatial triggers (goals/bounds)
## and an 'enum' State Machine to manage rule logic.
extends Node
class_name StatModifierPowerup
## Expert Powerup System (Godot 4.6).
## Manages temporary stat buffs with automatic duration reverting.
var base_stats: Dictionary = { "speed": 10.0, "power": 5.0 }
var active_stats: Dictionary = base_stats.duplicate()
func apply_buff(stat_id: String, multiplier: float, duration: float) -> void:
if not active_stats.has(stat_id): return
active_stats[stat_id] = base_stats[stat_id] * multiplier
# Expert Pattern: Bind signal to pass context to the timeout callback
var timer = get_tree().create_timer(duration)
timer.timeout.connect(_revert_buff.bind(stat_id))
func _revert_buff(stat_id: String) -> void:
active_stats[stat_id] = base_stats[stat_id]
## [SKILL NOTICE]: Use 'bind()' on timer signals to pass specific
## stat IDs back to the callback for clean duration management.
# skills/genre-sports/scripts/team_manager.gd
extends Node
## Team AI Manager (Expert Pattern)
## Manages formations and assigns roles (Attacker, Defender) dynamically.
## Prevents "Kindergarten Soccer" (everyone chasing the ball).
class_name TeamManager
enum Strategy { ATTACK, DEFEND }
@export var formation_anchor: Node3D
@export var players: Array[Node3D] # AI Controllers
@export var ball: RigidBody3D
var current_strategy: Strategy = Strategy.DEFEND
var formation_slots: Array[Node3D] = []
func _ready() -> void:
# Collect formation slots (markers)
for child in formation_anchor.get_children():
if child is Marker3D:
formation_slots.append(child)
func _physics_process(delta: float) -> void:
if not ball: return
# 1. Determine Strategy
if _team_has_possession():
current_strategy = Strategy.ATTACK
else:
current_strategy = Strategy.DEFEND
# 2. Move Anchor
# Anchor generally follows ball but stays on team's side or moves upfield
var target_pos = ball.global_position
if current_strategy == Strategy.DEFEND:
target_pos.z *= 0.5 # Stay closer to goal
formation_anchor.global_position = formation_anchor.global_position.lerp(target_pos, delta)
# 3. Assign Roles
var best_player = _find_closest_player_to_ball()
for i in range(players.size()):
var player = players[i]
if player == best_player:
# Press the ball
player.set_target(ball.global_position)
player.set_state("CHASE")
else:
# Go to formation slot
if i < formation_slots.size():
player.set_target(formation_slots[i].global_position)
player.set_state("FORMATION")
func _team_has_possession() -> bool:
# Logic to check if any team member is controlling the ball
return false
func _find_closest_player_to_ball() -> Node3D:
var best: Node3D = null
var min_dist = INF
for p in players:
var d = p.global_position.distance_to(ball.global_position)
if d < min_dist:
min_dist = d
best = p
return best
## EXPERT USAGE:
## Setup Player AI with set_target/set_state methods.
## Create Formation Node with Marker3D children.