Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
thedivergentai avatar

Godot Genre Fighting

  • 135 installs
  • 454 repo stars
  • Updated July 28, 2026
  • thedivergentai/gd-agentic-skills

Use godot-genre-fighting for development tasks

About

godot-genre-fighting: A skill for development. This provides functionality for development workflows.

  • godot-genre-fighting

Godot Genre Fighting by the numbers

  • 135 all-time installs (skills.sh)
  • +10 installs in the week ending Jul 27, 2026 (Skillselion tracking)
  • Ranked #2,658 of 4,347 Backend & APIs 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-fighting

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs135
repo stars454
Last updatedJuly 28, 2026
Repositorythedivergentai/gd-agentic-skills

What it does

Use godot-genre-fighting for development tasks

Files

SKILL.mdMarkdownGitHub ↗

Genre: Fighting Game

Expert blueprint for 2D/3D fighters emphasizing frame-perfect combat and competitive balance.

NEVER Do (Expert Anti-Patterns)

Frame-Data & Logic

  • NEVER use variable framerates; strictly lock logic to a Deterministic Fixed Loop (using _physics_process with a frame-counter) and call `reset_physics_interpolation()` on teleport.
  • NEVER use standard Physics for hit detection; strictly use `PhysicsDirectSpaceState.intersect_shape()` to query hitboxes instantly without Area2D signal lag.
  • NEVER skip Damage Scaling; strictly apply 10% reduction per hit in a combo to prevent infinite matches.
  • NEVER make all moves safe on block; strictly ensure high-reward moves have Recovery Windows where the attacker is punishable.
  • NEVER rely on Area2D.get_overlapping_areas(); strictly use `intersect_shape()` for immediate, frame-perfect resolution.
  • NEVER forget Hitbox Proximity (Proximity Guard); strictly trigger guard states when a hitbox enters a nearby zone, even if it hasn't landed.

Character & Animation

  • NEVER use simple parenting (scale.x = -1) for character flip; strictly adjust the dedicated Visuals node while managing hitbox offsets programmatically.
  • NEVER use string-based animation triggers; strictly use AnimationMixer with ADVANCE_MANUAL for frame-synced playback.
  • NEVER use yield or await for frame-critical logic; strictly use Integer Frame Counting within state machines to manage recovery/startup windows perfectly.
  • NEVER store frame data in raw scripts; strictly use `Resource` files (.tres) with delegated logic for damage scaling, cancels, and combo-state tracking.
  • NEVER use deep node hierarchies for character parts; strictly keep skeletons shallow to reduce transformation overhead.

Input & Networking

  • NEVER skip Input Buffering; strictly implement a 5-10 frame buffer to ensure lenient, responsive execution for the player.
  • NEVER leave Input.use_accumulated_input enabled; strictly disable it to preserve sub-frame timing for precise combo links.
  • NEVER use client-side hit detection for netplay; strictly use rollback netcode or server validation to prevent desyncs.
  • NEVER use standard TCP for multiplayer; strictly use UDP/ENet to avoid head-of-line blocking during latency spikes.
  • NEVER rely on the SceneTree for fighter transforms in netplay; strictly manage positions in a serializable data buffer.

---

🛠 Expert Components (scripts/)

Original Expert Patterns

  • fighting_input_buffer.gd - Frame-locked input engine (60fps) with motion command fuzzy matching (QCF/DP).
  • hitbox_component.gd - Professional hitbox/hurtbox utility with layered collision zones (High/Low/Throw).

Modular Components

  • deterministic_physics_loop.gd - Custom loop pattern for frame-perfect game state progression.
  • direct_hitbox_query.gd - PhysicsServer shape-casting for immediate collision resolution.
  • hit_stop_controller.gd - Dynamic time-scale manipulation for "impact" feel.
  • manual_animation_advancer.gd - Frame-synced animation control via manual delta processing.
  • rollback_state_serializer.gd - Serialization logic for managing discrete game state snapshots.
  • bitwise_state_flags.gd - High-performance bitwise flags for fighter state tracking.
  • input_accumulation_control.gd - Toggle for disabling Godot's input accumulation for sub-frame timing.
  • raw_byte_network_sync.gd - UDP-based state synchronization for netplay efficiency.
  • string_name_optimization.gd - Pattern for using pointer-level StringName comparisons in AI states.
  • round_timer_logic.gd - Logic for frame-synced match timers and timeout triggers.

---

Core Loop

Neutral Game → Confirm Hit → Execute Combo → Advantage State → Repeat

Skill Chain

godot-project-foundations, godot-characterbody-2d, godot-input-handling, animation, godot-combat-system, godot-state-machine-advanced, multiplayer-lobby

---

Frame-Based Combat System

Fighting games operate on frame data - discrete time units (typically 60fps).

Frame Data Fundamentals

class_name Attack
extends Resource

@export var name: String
@export var startup_frames: int  # Frames before hitbox becomes active
@export var active_frames: int   # Frames hitbox is active
@export var recovery_frames: int # Frames after hitbox deactivates
@export var on_hit_advantage: int # Frame advantage when attack hits
@export var on_block_advantage: int # Frame advantage when blocked
@export var damage: int
@export var hitstun: int  # Frames opponent is stunned
@export var blockstun: int # Frames opponent is in blockstun

func get_total_frames() -> int:
    return startup_frames + active_frames + recovery_frames

func is_safe_on_block() -> bool:
    return on_block_advantage >= 0

Frame-Accurate Processing

extends Node

var frame_count: int = 0
const FRAME_DURATION := 1.0 / 60.0
var accumulator: float = 0.0

func _process(delta: float) -> void:
    accumulator += delta
    while accumulator >= FRAME_DURATION:
        process_game_frame()
        frame_count += 1
        accumulator -= FRAME_DURATION

func process_game_frame() -> void:
    # All game logic runs here at fixed 60fps
    for fighter in fighters:
        fighter.process_frame()

---

Input System

Input Buffering

Store inputs and execute when valid:

class_name InputBuffer
extends Node

const BUFFER_FRAMES := 8  # Industry standard: 5-10 frames
var buffer: Array[InputEvent] = []

func add_input(input: InputEvent) -> void:
    buffer.append(input)
    if buffer.size() > BUFFER_FRAMES:
        buffer.pop_front()

func consume_input(action: StringName) -> bool:
    for i in range(buffer.size() - 1, -1, -1):
        if buffer[i].is_action(action):
            buffer.remove_at(i)
            return true
    return false

Motion Input Detection (Quarter Circle, DP, etc.)

class_name MotionDetector
extends Node

const QCF := ["down", "down_forward", "forward"]  # Quarter Circle Forward
const DP := ["forward", "down", "down_forward"]   # Dragon Punch
const MOTION_WINDOW := 15  # Frames to complete motion

var direction_history: Array[String] = []

func add_direction(dir: String) -> void:
    if direction_history.is_empty() or direction_history[-1] != dir:
        direction_history.append(dir)
    # Keep last N directions
    if direction_history.size() > 20:
        direction_history.pop_front()

func check_motion(motion: Array[String]) -> bool:
    if direction_history.size() < motion.size():
        return false
    # Check if motion appears in recent history
    var recent := direction_history.slice(-MOTION_WINDOW)
    return _contains_sequence(recent, motion)

func _contains_sequence(haystack: Array, needle: Array) -> bool:
    var idx := 0
    for dir in haystack:
        if dir == needle[idx]:
            idx += 1
            if idx >= needle.size():
                return true
    return false

---

Hitbox/Hurtbox System

class_name HitboxComponent
extends Area2D

enum BoxType { HITBOX, HURTBOX, THROW, PROJECTILE }

@export var box_type: BoxType
@export var attack_data: Attack
@export var owner_fighter: Fighter

signal hit_confirmed(target: Fighter, attack: Attack)

func _ready() -> void:
    monitoring = (box_type == BoxType.HITBOX or box_type == BoxType.THROW)
    monitorable = (box_type == BoxType.HURTBOX)
    connect("area_entered", _on_area_entered)

func _on_area_entered(area: Area2D) -> void:
    if area is HitboxComponent:
        var other := area as HitboxComponent
        if other.box_type == BoxType.HURTBOX and other.owner_fighter != owner_fighter:
            hit_confirmed.emit(other.owner_fighter, attack_data)

---

Combo System

Hit Confirmation and Combo Counter

class_name ComboTracker
extends Node

var combo_count: int = 0
var combo_damage: int = 0
var in_combo: bool = false
var damage_scaling: float = 1.0

const SCALING_PER_HIT := 0.9  # 10% reduction per hit

func start_combo() -> void:
    in_combo = true
    combo_count = 0
    combo_damage = 0
    damage_scaling = 1.0

func add_hit(base_damage: int) -> int:
    combo_count += 1
    var scaled_damage := int(base_damage * damage_scaling)
    combo_damage += scaled_damage
    damage_scaling *= SCALING_PER_HIT
    return scaled_damage

func drop_combo() -> void:
    in_combo = false
    combo_count = 0
    damage_scaling = 1.0

Cancel System

enum CancelType { NONE, NORMAL, SPECIAL, SUPER }

func can_cancel_into(from_attack: Attack, to_attack: Attack) -> bool:
    # Normal → Special → Super hierarchy
    match to_attack.cancel_type:
        CancelType.NORMAL:
            return from_attack.cancel_type == CancelType.NONE
        CancelType.SPECIAL:
            return from_attack.cancel_type in [CancelType.NONE, CancelType.NORMAL]
        CancelType.SUPER:
            return true  # Supers can cancel anything
    return false

---

Character States

enum FighterState {
    IDLE, WALKING, CROUCHING, JUMPING,
    ATTACKING, BLOCKING, HITSTUN, BLOCKSTUN,
    KNOCKDOWN, WAKEUP, THROW, THROWN
}

class_name FighterStateMachine
extends Node

var current_state: FighterState = FighterState.IDLE
var state_frame: int = 0

func transition_to(new_state: FighterState) -> void:
    exit_state(current_state)
    current_state = new_state
    state_frame = 0
    enter_state(new_state)

func is_actionable() -> bool:
    return current_state in [
        FighterState.IDLE,
        FighterState.WALKING,
        FighterState.CROUCHING
    ]

---

Netcode Considerations

Rollback Essentials

class_name GameState
extends Resource

# Serialize complete game state for rollback
func save_state() -> Dictionary:
    return {
        "frame": frame_count,
        "fighters": fighters.map(func(f): return f.serialize()),
        "projectiles": projectiles.map(func(p): return p.serialize())
    }

func load_state(state: Dictionary) -> void:
    frame_count = state["frame"]
    for i in fighters.size():
        fighters[i].deserialize(state["fighters"][i])
    # Reconstruct projectiles...

---

Balance Guidelines

ElementGuideline
Health10,000-15,000 for ~20 second rounds
Combo damageMax 30-40% of health per touch
Fastest moves3-5 frames startup (jabs)
Slowest moves20-40 frames (supers, overheads)
Throw rangeShort but reliable
Meter gainFull bar in ~2 combos received

---

Common Pitfalls

PitfallSolution
Infinite combosImplement hitstun decay and gravity scaling
Unblockable setupsEnsure all attacks have counterplay
Lag input dropsRobust input buffering (8+ frames)
Desync in netplayDeterministic physics, rollback netcode

---

Godot-Specific Tips

1. Use `_physics_process` sparingly - implement your own frame-based loop 2. AnimationPlayer: Tie hitbox activation to animation frames 3. Custom collision: May need custom hitbox system rather than physics engine 4. Save/Load for rollback: Keep state serializable

Advanced Fighting Game Meta-Systems

Professional implementation of move-set management, editor-side debugging, and roster balance.

1. Command-List JSON Schema (Move-set Definitions)

Decouple move-sets from character logic by using an external JSON schema. This allows designers to iterate on frame data and inputs without modifying GDScript.

class_name MoveSetLoader extends Node

var move_data: Dictionary = {}

func load_from_json(path: String) -> void:
    var file := FileAccess.open(path, FileAccess.READ)
    if file:
        var json_string := file.get_as_text()
        var result = JSON.parse_string(json_string)
        if result is Dictionary:
            move_data = result
        file.close()

# Example JSON Schema structure:
# {
#   "hadouken": {
#     "input": ["down", "down_forward", "forward", "punch"],
#     "startup": 12,
#     "active": 3,
#     "recovery": 25,
#     "damage": 800
#   }
# }

2. Visual Frame Advancer (Editor Debugger)

Use @tool scripts to create editor-side debugging tools that allow scrubbing through animation frames to verify hitbox alignment.

@tool
class_name FrameAdvancer extends Node

@export var current_frame: int = 0:
    set(value):
        current_frame = value
        if Engine.is_editor_hint():
            _sync_animation_to_frame()

@export var step_forward: bool = false:
    set(value):
        if value:
            current_frame += 1
            step_forward = false # Reset toggle

func _sync_animation_to_frame() -> void:
    var anim_player: AnimationPlayer = get_node_or_null("../AnimationPlayer")
    if anim_player:
        anim_player.seek(current_frame * (1.0/60.0), true)
        # Force a redraw of debug shapes
        get_parent().queue_redraw()

3. Character-Specific Scaling (Balance Resources)

Encapsulate roster balance variables in Resource files. This allows for profile-based scaling (e.g., HeavyWeight vs. GlassCannon) that can be swapped instantly.

class_name FighterBalanceProfile extends Resource

@export_group("Damage Scaling")
@export var base_damage_mult: float = 1.0
@export var combo_proration_rate: float = 0.9 # Lower means faster damage drop-off

@export_group("Movement Scaling")
@export var walk_speed_mult: float = 1.0
@export var dash_distance_mult: float = 1.0

@export_group("Defense Scaling")
@export var max_health: int = 10000
@export var guts_threshold: float = 0.3 # Damage reduction kicks in at 30% HP

# Usage in Fighter script:
# @export var balance_profile: FighterBalanceProfile
# func take_damage(amount: int) -> void:
#     health -= int(amount * balance_profile.damage_reduction_curve)

Anti-Pattern: NEVER hardcode balance numbers in the Fighter base class. Strictly use delegated Resource profiles to maintain a clean, maintainable roster.

Reference

  • Master Skill: godot-master

Related skills

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.