[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70K3B3MFE9RZPSYXGEKTY7G":3,"skill-same:01M70K3B3MFE9RZPSYXGEKTY7G":68},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":49,"ownerHtmlUrl":50,"ownerBlogUrl":51,"ownerDescription":52,"updatedAt":53,"install":54,"resources":67},"01M70K3B3MFE9RZPSYXGEKTY7G","godot-gdscript-patterns",null,"Master Godot 4 GDScript patterns including signals, scenes, state machines, and optimization. Use when building Godot games, implementing game systems, or learning GDScript best practices.","shengdabai\u002FTony-Claude-Code-Skills","shengdabai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F116417419?v=4","01M70F6D2WY5PQV5A92BKYJQEW","Tony Sheng","01M70F6D3GAAAYZ5H4CC1G8VXM",3,"MIT","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002F_collections\u002Fwshobson-agents\u002Fplugins\u002Fgame-development\u002Fskills\u002Fgodot-gdscript-patterns\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"gdscript patterns","patterns","godot gdscript","godot gdscript patterns","godot","gdscript","systems","state","best practices","use","scenes","learning","games","Optimization","master","signals","state machines","game","when","implementing","2026-11-10 10:12:30",false,"---\nname: godot-gdscript-patterns\ndescription: Master Godot 4 GDScript patterns including signals, scenes, state machines, and optimization. Use when building Godot games, implementing game systems, or learning GDScript best practices.\n---\n\n# Godot GDScript Patterns\n\nProduction patterns for Godot 4.x game development with GDScript, covering architecture, signals, scenes, and optimization.\n\n## When to Use This Skill\n\n- Building games with Godot 4\n- Implementing game systems in GDScript\n- Designing scene architecture\n- Managing game state\n- Optimizing GDScript performance\n- Learning Godot best practices\n\n## Core Concepts\n\n### 1. Godot Architecture\n\n```\nNode: Base building block\n├── Scene: Reusable node tree (saved as .tscn)\n├── Resource: Data container (saved as .tres)\n├── Signal: Event communication\n└── Group: Node categorization\n```\n\n### 2. GDScript Basics\n\n```gdscript\nclass_name Player\nextends CharacterBody2D\n\n# Signals\nsignal health_changed(new_health: int)\nsignal died\n\n# Exports (Inspector-editable)\n@export var speed: float = 200.0\n@export var max_health: int = 100\n@export_range(0, 1) var damage_reduction: float = 0.0\n@export_group(\"Combat\")\n@export var attack_damage: int = 10\n@export var attack_cooldown: float = 0.5\n\n# Onready (initialized when ready)\n@onready var sprite: Sprite2D = $Sprite2D\n@onready var animation: AnimationPlayer = $AnimationPlayer\n@onready var hitbox: Area2D = $Hitbox\n\n# Private variables (convention: underscore prefix)\nvar _health: int\nvar _can_attack: bool = true\n\nfunc _ready() -> void:\n    _health = max_health\n\nfunc _physics_process(delta: float) -> void:\n    var direction := Input.get_vector(\"left\", \"right\", \"up\", \"down\")\n    velocity = direction * speed\n    move_and_slide()\n\nfunc take_damage(amount: int) -> void:\n    var actual_damage := int(amount * (1.0 - damage_reduction))\n    _health = max(_health - actual_damage, 0)\n    health_changed.emit(_health)\n\n    if _health \u003C= 0:\n        died.emit()\n```\n\n## Patterns\n\n### Pattern 1: State Machine\n\n```gdscript\n# state_machine.gd\nclass_name StateMachine\nextends Node\n\nsignal state_changed(from_state: StringName, to_state: StringName)\n\n@export var initial_state: State\n\nvar current_state: State\nvar states: Dictionary = {}\n\nfunc _ready() -> void:\n    # Register all State children\n    for child in get_children():\n        if child is State:\n            states[child.name] = child\n            child.state_machine = self\n            child.process_mode = Node.PROCESS_MODE_DISABLED\n\n    # Start initial state\n    if initial_state:\n        current_state = initial_state\n        current_state.process_mode = Node.PROCESS_MODE_INHERIT\n        current_state.enter()\n\nfunc _process(delta: float) -> void:\n    if current_state:\n        current_state.update(delta)\n\nfunc _physics_process(delta: float) -> void:\n    if current_state:\n        current_state.physics_update(delta)\n\nfunc _unhandled_input(event: InputEvent) -> void:\n    if current_state:\n        current_state.handle_input(event)\n\nfunc transition_to(state_name: StringName, msg: Dictionary = {}) -> void:\n    if not states.has(state_name):\n        push_error(\"State '%s' not found\" % state_name)\n        return\n\n    var previous_state := current_state\n    previous_state.exit()\n    previous_state.process_mode = Node.PROCESS_MODE_DISABLED\n\n    current_state = states[state_name]\n    current_state.process_mode = Node.PROCESS_MODE_INHERIT\n    current_state.enter(msg)\n\n    state_changed.emit(previous_state.name, current_state.name)\n```\n\n```gdscript\n# state.gd\nclass_name State\nextends Node\n\nvar state_machine: StateMachine\n\nfunc enter(_msg: Dictionary = {}) -> void:\n    pass\n\nfunc exit() -> void:\n    pass\n\nfunc update(_delta: float) -> void:\n    pass\n\nfunc physics_update(_delta: float) -> void:\n    pass\n\nfunc handle_input(_event: InputEvent) -> void:\n    pass\n```\n\n```gdscript\n# player_idle.gd\nclass_name PlayerIdle\nextends State\n\n@export var player: Player\n\nfunc enter(_msg: Dictionary = {}) -> void:\n    player.animation.play(\"idle\")\n\nfunc physics_update(_delta: float) -> void:\n    var direction := Input.get_vector(\"left\", \"right\", \"up\", \"down\")\n\n    if direction != Vector2.ZERO:\n        state_machine.transition_to(\"Move\")\n\nfunc handle_input(event: InputEvent) -> void:\n    if event.is_action_pressed(\"attack\"):\n        state_machine.transition_to(\"Attack\")\n    elif event.is_action_pressed(\"jump\"):\n        state_machine.transition_to(\"Jump\")\n```\n\n### Pattern 2: Autoload Singletons\n\n```gdscript\n# game_manager.gd (Add to Project Settings > Autoload)\nextends Node\n\nsignal game_started\nsignal game_paused(is_paused: bool)\nsignal game_over(won: bool)\nsignal score_changed(new_score: int)\n\nenum GameState { MENU, PLAYING, PAUSED, GAME_OVER }\n\nvar state: GameState = GameState.MENU\nvar score: int = 0:\n    set(value):\n        score = value\n        score_changed.emit(score)\n\nvar high_score: int = 0\n\nfunc _ready() -> void:\n    process_mode = Node.PROCESS_MODE_ALWAYS\n    _load_high_score()\n\nfunc _input(event: InputEvent) -> void:\n    if event.is_action_pressed(\"pause\") and state == GameState.PLAYING:\n        toggle_pause()\n\nfunc start_game() -> void:\n    score = 0\n    state = GameState.PLAYING\n    game_started.emit()\n\nfunc toggle_pause() -> void:\n    var is_paused := state != GameState.PAUSED\n\n    if is_paused:\n        state = GameState.PAUSED\n        get_tree().paused = true\n    else:\n        state = GameState.PLAYING\n        get_tree().paused = false\n\n    game_paused.emit(is_paused)\n\nfunc end_game(won: bool) -> void:\n    state = GameState.GAME_OVER\n\n    if score > high_score:\n        high_score = score\n        _save_high_score()\n\n    game_over.emit(won)\n\nfunc add_score(points: int) -> void:\n    score += points\n\nfunc _load_high_score() -> void:\n    if FileAccess.file_exists(\"user:\u002F\u002Fhigh_score.save\"):\n        var file := FileAccess.open(\"user:\u002F\u002Fhigh_score.save\", FileAccess.READ)\n        high_score = file.get_32()\n\nfunc _save_high_score() -> void:\n    var file := FileAccess.open(\"user:\u002F\u002Fhigh_score.save\", FileAccess.WRITE)\n    file.store_32(high_score)\n```\n\n```gdscript\n# event_bus.gd (Global signal bus)\nextends Node\n\n# Player events\nsignal player_spawned(player: Node2D)\nsignal player_died(player: Node2D)\nsignal player_health_changed(health: int, max_health: int)\n\n# Enemy events\nsignal enemy_spawned(enemy: Node2D)\nsignal enemy_died(enemy: Node2D, position: Vector2)\n\n# Item events\nsignal item_collected(item_type: StringName, value: int)\nsignal powerup_activated(powerup_type: StringName)\n\n# Level events\nsignal level_started(level_number: int)\nsignal level_completed(level_number: int, time: float)\nsignal checkpoint_reached(checkpoint_id: int)\n```\n\n### Pattern 3: Resource-based Data\n\n```gdscript\n# weapon_data.gd\nclass_name WeaponData\nextends Resource\n\n@export var name: StringName\n@export var damage: int\n@export var attack_speed: float\n@export var range: float\n@export_multiline var description: String\n@export var icon: Texture2D\n@export var projectile_scene: PackedScene\n@export var sound_attack: AudioStream\n```\n\n```gdscript\n# character_stats.gd\nclass_name CharacterStats\nextends Resource\n\nsignal stat_changed(stat_name: StringName, new_value: float)\n\n@export var max_health: float = 100.0\n@export var attack: float = 10.0\n@export var defense: float = 5.0\n@export var speed: float = 200.0\n\n# Runtime values (not saved)\nvar _current_health: float\n\nfunc _init() -> void:\n    _current_health = max_health\n\nfunc get_current_health() -> float:\n    return _current_health\n\nfunc take_damage(amount: float) -> float:\n    var actual_damage := maxf(amount - defense, 1.0)\n    _current_health = maxf(_current_health - actual_damage, 0.0)\n    stat_changed.emit(\"health\", _current_health)\n    return actual_damage\n\nfunc heal(amount: float) -> void:\n    _current_health = minf(_current_health + amount, max_health)\n    stat_changed.emit(\"health\", _current_health)\n\nfunc duplicate_for_runtime() -> CharacterStats:\n    var copy := duplicate() as CharacterStats\n    copy._current_health = copy.max_health\n    return copy\n```\n\n```gdscript\n# Using resources\nclass_name Character\nextends CharacterBody2D\n\n@export var base_stats: CharacterStats\n@export var weapon: WeaponData\n\nvar stats: CharacterStats\n\nfunc _ready() -> void:\n    # Create runtime copy to avoid modifying the resource\n    stats = base_stats.duplicate_for_runtime()\n    stats.stat_changed.connect(_on_stat_changed)\n\nfunc attack() -> void:\n    if weapon:\n        print(\"Attacking with %s for %d damage\" % [weapon.name, weapon.damage])\n\nfunc _on_stat_changed(stat_name: StringName, value: float) -> void:\n    if stat_name == \"health\" and value \u003C= 0:\n        die()\n```\n\n### Pattern 4: Object Pooling\n\n```gdscript\n# object_pool.gd\nclass_name ObjectPool\nextends Node\n\n@export var pooled_scene: PackedScene\n@export var initial_size: int = 10\n@export var can_grow: bool = true\n\nvar _available: Array[Node] = []\nvar _in_use: Array[Node] = []\n\nfunc _ready() -> void:\n    _initialize_pool()\n\nfunc _initialize_pool() -> void:\n    for i in initial_size:\n        _create_instance()\n\nfunc _create_instance() -> Node:\n    var instance := pooled_scene.instantiate()\n    instance.process_mode = Node.PROCESS_MODE_DISABLED\n    instance.visible = false\n    add_child(instance)\n    _available.append(instance)\n\n    # Connect return signal if exists\n    if instance.has_signal(\"returned_to_pool\"):\n        instance.returned_to_pool.connect(_return_to_pool.bind(instance))\n\n    return instance\n\nfunc get_instance() -> Node:\n    var instance: Node\n\n    if _available.is_empty():\n        if can_grow:\n            instance = _create_instance()\n            _available.erase(instance)\n        else:\n            push_warning(\"Pool exhausted and cannot grow\")\n            return null\n    else:\n        instance = _available.pop_back()\n\n    instance.process_mode = Node.PROCESS_MODE_INHERIT\n    instance.visible = true\n    _in_use.append(instance)\n\n    if instance.has_method(\"on_spawn\"):\n        instance.on_spawn()\n\n    return instance\n\nfunc _return_to_pool(instance: Node) -> void:\n    if not instance in _in_use:\n        return\n\n    _in_use.erase(instance)\n\n    if instance.has_method(\"on_despawn\"):\n        instance.on_despawn()\n\n    instance.process_mode = Node.PROCESS_MODE_DISABLED\n    instance.visible = false\n    _available.append(instance)\n\nfunc return_all() -> void:\n    for instance in _in_use.duplicate():\n        _return_to_pool(instance)\n```\n\n```gdscript\n# pooled_bullet.gd\nclass_name PooledBullet\nextends Area2D\n\nsignal returned_to_pool\n\n@export var speed: float = 500.0\n@export var lifetime: float = 5.0\n\nvar direction: Vector2\nvar _timer: float\n\nfunc on_spawn() -> void:\n    _timer = lifetime\n\nfunc on_despawn() -> void:\n    direction = Vector2.ZERO\n\nfunc initialize(pos: Vector2, dir: Vector2) -> void:\n    global_position = pos\n    direction = dir.normalized()\n    rotation = direction.angle()\n\nfunc _physics_process(delta: float) -> void:\n    position += direction * speed * delta\n\n    _timer -= delta\n    if _timer \u003C= 0:\n        returned_to_pool.emit()\n\nfunc _on_body_entered(body: Node2D) -> void:\n    if body.has_method(\"take_damage\"):\n        body.take_damage(10)\n    returned_to_pool.emit()\n```\n\n### Pattern 5: Component System\n\n```gdscript\n# health_component.gd\nclass_name HealthComponent\nextends Node\n\nsignal health_changed(current: int, maximum: int)\nsignal damaged(amount: int, source: Node)\nsignal healed(amount: int)\nsignal died\n\n@export var max_health: int = 100\n@export var invincibility_time: float = 0.0\n\nvar current_health: int:\n    set(value):\n        var old := current_health\n        current_health = clampi(value, 0, max_health)\n        if current_health != old:\n            health_changed.emit(current_health, max_health)\n\nvar _invincible: bool = false\n\nfunc _ready() -> void:\n    current_health = max_health\n\nfunc take_damage(amount: int, source: Node = null) -> int:\n    if _invincible or current_health \u003C= 0:\n        return 0\n\n    var actual := mini(amount, current_health)\n    current_health -= actual\n    damaged.emit(actual, source)\n\n    if current_health \u003C= 0:\n        died.emit()\n    elif invincibility_time > 0:\n        _start_invincibility()\n\n    return actual\n\nfunc heal(amount: int) -> int:\n    var actual := mini(amount, max_health - current_health)\n    current_health += actual\n    if actual > 0:\n        healed.emit(actual)\n    return actual\n\nfunc _start_invincibility() -> void:\n    _invincible = true\n    await get_tree().create_timer(invincibility_time).timeout\n    _invincible = false\n```\n\n```gdscript\n# hitbox_component.gd\nclass_name HitboxComponent\nextends Area2D\n\nsignal hit(hurtbox: HurtboxComponent)\n\n@export var damage: int = 10\n@export var knockback_force: float = 200.0\n\nvar owner_node: Node\n\nfunc _ready() -> void:\n    owner_node = get_parent()\n    area_entered.connect(_on_area_entered)\n\nfunc _on_area_entered(area: Area2D) -> void:\n    if area is HurtboxComponent:\n        var hurtbox := area as HurtboxComponent\n        if hurtbox.owner_node != owner_node:\n            hit.emit(hurtbox)\n            hurtbox.receive_hit(self)\n```\n\n```gdscript\n# hurtbox_component.gd\nclass_name HurtboxComponent\nextends Area2D\n\nsignal hurt(hitbox: HitboxComponent)\n\n@export var health_component: HealthComponent\n\nvar owner_node: Node\n\nfunc _ready() -> void:\n    owner_node = get_parent()\n\nfunc receive_hit(hitbox: HitboxComponent) -> void:\n    hurt.emit(hitbox)\n\n    if health_component:\n        health_component.take_damage(hitbox.damage, hitbox.owner_node)\n```\n\n### Pattern 6: Scene Management\n\n```gdscript\n# scene_manager.gd (Autoload)\nextends Node\n\nsignal scene_loading_started(scene_path: String)\nsignal scene_loading_progress(progress: float)\nsignal scene_loaded(scene: Node)\nsignal transition_started\nsignal transition_finished\n\n@export var transition_scene: PackedScene\n@export var loading_scene: PackedScene\n\nvar _current_scene: Node\nvar _transition: CanvasLayer\nvar _loader: ResourceLoader\n\nfunc _ready() -> void:\n    _current_scene = get_tree().current_scene\n\n    if transition_scene:\n        _transition = transition_scene.instantiate()\n        add_child(_transition)\n        _transition.visible = false\n\nfunc change_scene(scene_path: String, with_transition: bool = true) -> void:\n    if with_transition:\n        await _play_transition_out()\n\n    _load_scene(scene_path)\n\nfunc change_scene_packed(scene: PackedScene, with_transition: bool = true) -> void:\n    if with_transition:\n        await _play_transition_out()\n\n    _swap_scene(scene.instantiate())\n\nfunc _load_scene(path: String) -> void:\n    scene_loading_started.emit(path)\n\n    # Check if already loaded\n    if ResourceLoader.has_cached(path):\n        var scene := load(path) as PackedScene\n        _swap_scene(scene.instantiate())\n        return\n\n    # Async loading\n    ResourceLoader.load_threaded_request(path)\n\n    while true:\n        var progress := []\n        var status := ResourceLoader.load_threaded_get_status(path, progress)\n\n        match status:\n            ResourceLoader.THREAD_LOAD_IN_PROGRESS:\n                scene_loading_progress.emit(progress[0])\n                await get_tree().process_frame\n            ResourceLoader.THREAD_LOAD_LOADED:\n                var scene := ResourceLoader.load_threaded_get(path) as PackedScene\n                _swap_scene(scene.instantiate())\n                return\n            _:\n                push_error(\"Failed to load scene: %s\" % path)\n                return\n\nfunc _swap_scene(new_scene: Node) -> void:\n    if _current_scene:\n        _current_scene.queue_free()\n\n    _current_scene = new_scene\n    get_tree().root.add_child(_current_scene)\n    get_tree().current_scene = _current_scene\n\n    scene_loaded.emit(_current_scene)\n    await _play_transition_in()\n\nfunc _play_transition_out() -> void:\n    if not _transition:\n        return\n\n    transition_started.emit()\n    _transition.visible = true\n\n    if _transition.has_method(\"transition_out\"):\n        await _transition.transition_out()\n    else:\n        await get_tree().create_timer(0.3).timeout\n\nfunc _play_transition_in() -> void:\n    if not _transition:\n        transition_finished.emit()\n        return\n\n    if _transition.has_method(\"transition_in\"):\n        await _transition.transition_in()\n    else:\n        await get_tree().create_timer(0.3).timeout\n\n    _transition.visible = false\n    transition_finished.emit()\n```\n\n### Pattern 7: Save System\n\n```gdscript\n# save_manager.gd (Autoload)\nextends Node\n\nconst SAVE_PATH := \"user:\u002F\u002Fsavegame.save\"\nconst ENCRYPTION_KEY := \"your_secret_key_here\"\n\nsignal save_completed\nsignal load_completed\nsignal save_error(message: String)\n\nfunc save_game(data: Dictionary) -> void:\n    var file := FileAccess.open_encrypted_with_pass(\n        SAVE_PATH,\n        FileAccess.WRITE,\n        ENCRYPTION_KEY\n    )\n\n    if file == null:\n        save_error.emit(\"Could not open save file\")\n        return\n\n    var json := JSON.stringify(data)\n    file.store_string(json)\n    file.close()\n\n    save_completed.emit()\n\nfunc load_game() -> Dictionary:\n    if not FileAccess.file_exists(SAVE_PATH):\n        return {}\n\n    var file := FileAccess.open_encrypted_with_pass(\n        SAVE_PATH,\n        FileAccess.READ,\n        ENCRYPTION_KEY\n    )\n\n    if file == null:\n        save_error.emit(\"Could not open save file\")\n        return {}\n\n    var json := file.get_as_text()\n    file.close()\n\n    var parsed := JSON.parse_string(json)\n    if parsed == null:\n        save_error.emit(\"Could not parse save data\")\n        return {}\n\n    load_completed.emit()\n    return parsed\n\nfunc delete_save() -> void:\n    if FileAccess.file_exists(SAVE_PATH):\n        DirAccess.remove_absolute(SAVE_PATH)\n\nfunc has_save() -> bool:\n    return FileAccess.file_exists(SAVE_PATH)\n```\n\n```gdscript\n# saveable.gd (Attach to saveable nodes)\nclass_name Saveable\nextends Node\n\n@export var save_id: String\n\nfunc _ready() -> void:\n    if save_id.is_empty():\n        save_id = str(get_path())\n\nfunc get_save_data() -> Dictionary:\n    var parent := get_parent()\n    var data := {\"id\": save_id}\n\n    if parent is Node2D:\n        data[\"position\"] = {\"x\": parent.position.x, \"y\": parent.position.y}\n\n    if parent.has_method(\"get_custom_save_data\"):\n        data.merge(parent.get_custom_save_data())\n\n    return data\n\nfunc load_save_data(data: Dictionary) -> void:\n    var parent := get_parent()\n\n    if data.has(\"position\") and parent is Node2D:\n        parent.position = Vector2(data.position.x, data.position.y)\n\n    if parent.has_method(\"load_custom_save_data\"):\n        parent.load_custom_save_data(data)\n```\n\n## Performance Tips\n\n```gdscript\n# 1. Cache node references\n@onready var sprite := $Sprite2D  # Good\n# $Sprite2D in _process()  # Bad - repeated lookup\n\n# 2. Use object pooling for frequent spawning\n# See Pattern 4\n\n# 3. Avoid allocations in hot paths\nvar _reusable_array: Array = []\n\nfunc _process(_delta: float) -> void:\n    _reusable_array.clear()  # Reuse instead of creating new\n\n# 4. Use static typing\nfunc calculate(value: float) -> float:  # Good\n    return value * 2.0\n\n# 5. Disable processing when not needed\nfunc _on_off_screen() -> void:\n    set_process(false)\n    set_physics_process(false)\n```\n\n## Best Practices\n\n### Do's\n- **Use signals for decoupling** - Avoid direct references\n- **Type everything** - Static typing catches errors\n- **Use resources for data** - Separate data from logic\n- **Pool frequently spawned objects** - Avoid GC hitches\n- **Use Autoloads sparingly** - Only for truly global systems\n\n### Don'ts\n- **Don't use `get_node()` in loops** - Cache references\n- **Don't couple scenes tightly** - Use signals\n- **Don't put logic in resources** - Keep them data-only\n- **Don't ignore the Profiler** - Monitor performance\n- **Don't fight the scene tree** - Work with Godot's design\n\n## Resources\n\n- [Godot Documentation](https:\u002F\u002Fdocs.godotengine.org\u002Fen\u002Fstable\u002F)\n- [GDQuest Tutorials](https:\u002F\u002Fwww.gdquest.com\u002F)\n- [Godot Recipes](https:\u002F\u002Fkidscancode.org\u002Fgodot_recipes\u002F)\n","# Godot GDScript Patterns\n\nProduction patterns for Godot 4.x game development with GDScript, covering architecture, signals, scenes, and optimization.\n\n## When to Use This Skill\n\n- Building games with Godot 4\n- Implementing game systems in GDScript\n- Designing scene architecture\n- Managing game state\n- Optimizing GDScript performance\n- Learning Godot best practices\n\n## Core Concepts\n\n### 1. Godot Architecture\n\n```\nNode: Base building block\n├── Scene: Reusable node tree (saved as .tscn)\n├── Resource: Data container (saved as .tres)\n├── Signal: Event communication\n└── Group: Node categorization\n```\n\n### 2. GDScript Basics\n\n```gdscript\nclass_name Player\nextends CharacterBody2D\n\n# Signals\nsignal health_changed(new_health: int)\nsignal died\n\n# Exports (Inspector-editable)\n@export var speed: float = 200.0\n@export var max_health: int = 100\n@export_range(0, 1) var damage_reduction: float = 0.0\n@export_group(\"Combat\")\n@export var attack_damage: int = 10\n@export var attack_cooldown: float = 0.5\n\n# Onready (initialized when ready)\n@onready var sprite: Sprite2D = $Sprite2D\n@onready var animation: AnimationPlayer = $AnimationPlayer\n@onready var hitbox: Area2D = $Hitbox\n\n# Private variables (convention: underscore prefix)\nvar _health: int\nvar _can_attack: bool = true\n\nfunc _ready() -> void:\n    _health = max_health\n\nfunc _physics_process(delta: float) -> void:\n    var direction := Input.get_vector(\"left\", \"right\", \"up\", \"down\")\n    velocity = direction * speed\n    move_and_slide()\n\nfunc take_damage(amount: int) -> void:\n    var actual_damage := int(amount * (1.0 - damage_reduction))\n    _health = max(_health - actual_damage, 0)\n    health_changed.emit(_health)\n\n    if _health \u003C= 0:\n        died.emit()\n```\n\n## Patterns\n\n### Pattern 1: State Machine\n\n```gdscript\n# state_machine.gd\nclass_name StateMachine\nextends Node\n\nsignal state_changed(from_state: StringName, to_state: StringName)\n\n@export var initial_state: State\n\nvar current_state: State\nvar states: Dictionary = {}\n\nfunc _ready() -> void:\n    # Register all State children\n    for child in get_children():\n        if child is State:\n            states[child.name] = child\n            child.state_machine = self\n            child.process_mode = Node.PROCESS_MODE_DISABLED\n\n    # Start initial state\n    if initial_state:\n        current_state = initial_state\n        current_state.process_mode = Node.PROCESS_MODE_INHERIT\n        current_state.enter()\n\nfunc _process(delta: float) -> void:\n    if current_state:\n        current_state.update(delta)\n\nfunc _physics_process(delta: float) -> void:\n    if current_state:\n        current_state.physics_update(delta)\n\nfunc _unhandled_input(event: InputEvent) -> void:\n    if current_state:\n        current_state.handle_input(event)\n\nfunc transition_to(state_name: StringName, msg: Dictionary = {}) -> void:\n    if not states.has(state_name):\n        push_error(\"State '%s' not found\" % state_name)\n        return\n\n    var previous_state := current_state\n    previous_state.exit()\n    previous_state.process_mode = Node.PROCESS_MODE_DISABLED\n\n    current_state = states[state_name]\n    current_state.process_mode = Node.PROCESS_MODE_INHERIT\n    current_state.enter(msg)\n\n    state_changed.emit(previous_state.name, current_state.name)\n```\n\n```gdscript\n# state.gd\nclass_name State\nextends Node\n\nvar state_machine: StateMachine\n\nfunc enter(_msg: Dictionary = {}) -> void:\n    pass\n\nfunc exit() -> void:\n    pass\n\nfunc update(_delta: float) -> void:\n    pass\n\nfunc physics_update(_delta: float) -> void:\n    pass\n\nfunc handle_input(_event: InputEvent) -> void:\n    pass\n```\n\n```gdscript\n# player_idle.gd\nclass_name PlayerIdle\nextends State\n\n@export var player: Player\n\nfunc enter(_msg: Dictionary = {}) -> void:\n    player.animation.play(\"idle\")\n\nfunc physics_update(_delta: float) -> void:\n    var direction := Input.get_vector(\"left\", \"right\", \"up\", \"down\")\n\n    if direction != Vector2.ZERO:\n        state_machine.transition_to(\"Move\")\n\nfunc handle_input(event: InputEvent) -> void:\n    if event.is_action_pressed(\"attack\"):\n        state_machine.transition_to(\"Attack\")\n    elif event.is_action_pressed(\"jump\"):\n        state_machine.transition_to(\"Jump\")\n```\n\n### Pattern 2: Autoload Singletons\n\n```gdscript\n# game_manager.gd (Add to Project Settings > Autoload)\nextends Node\n\nsignal game_started\nsignal game_paused(is_paused: bool)\nsignal game_over(won: bool)\nsignal score_changed(new_score: int)\n\nenum GameState { MENU, PLAYING, PAUSED, GAME_OVER }\n\nvar state: GameState = GameState.MENU\nvar score: int = 0:\n    set(value):\n        score = value\n        score_changed.emit(score)\n\nvar high_score: int = 0\n\nfunc _ready() -> void:\n    process_mode = Node.PROCESS_MODE_ALWAYS\n    _load_high_score()\n\nfunc _input(event: InputEvent) -> void:\n    if event.is_action_pressed(\"pause\") and state == GameState.PLAYING:\n        toggle_pause()\n\nfunc start_game() -> void:\n    score = 0\n    state = GameState.PLAYING\n    game_started.emit()\n\nfunc toggle_pause() -> void:\n    var is_paused := state != GameState.PAUSED\n\n    if is_paused:\n        state = GameState.PAUSED\n        get_tree().paused = true\n    else:\n        state = GameState.PLAYING\n        get_tree().paused = false\n\n    game_paused.emit(is_paused)\n\nfunc end_game(won: bool) -> void:\n    state = GameState.GAME_OVER\n\n    if score > high_score:\n        high_score = score\n        _save_high_score()\n\n    game_over.emit(won)\n\nfunc add_score(points: int) -> void:\n    score += points\n\nfunc _load_high_score() -> void:\n    if FileAccess.file_exists(\"user:\u002F\u002Fhigh_score.save\"):\n        var file := FileAccess.open(\"user:\u002F\u002Fhigh_score.save\", FileAccess.READ)\n        high_score = file.get_32()\n\nfunc _save_high_score() -> void:\n    var file := FileAccess.open(\"user:\u002F\u002Fhigh_score.save\", FileAccess.WRITE)\n    file.store_32(high_score)\n```\n\n```gdscript\n# event_bus.gd (Global signal bus)\nextends Node\n\n# Player events\nsignal player_spawned(player: Node2D)\nsignal player_died(player: Node2D)\nsignal player_health_changed(health: int, max_health: int)\n\n# Enemy events\nsignal enemy_spawned(enemy: Node2D)\nsignal enemy_died(enemy: Node2D, position: Vector2)\n\n# Item events\nsignal item_collected(item_type: StringName, value: int)\nsignal powerup_activated(powerup_type: StringName)\n\n# Level events\nsignal level_started(level_number: int)\nsignal level_completed(level_number: int, time: float)\nsignal checkpoint_reached(checkpoint_id: int)\n```\n\n### Pattern 3: Resource-based Data\n\n```gdscript\n# weapon_data.gd\nclass_name WeaponData\nextends Resource\n\n@export var name: StringName\n@export var damage: int\n@export var attack_speed: float\n@export var range: float\n@export_multiline var description: String\n@export var icon: Texture2D\n@export var projectile_scene: PackedScene\n@export var sound_attack: AudioStream\n```\n\n```gdscript\n# character_stats.gd\nclass_name CharacterStats\nextends Resource\n\nsignal stat_changed(stat_name: StringName, new_value: float)\n\n@export var max_health: float = 100.0\n@export var attack: float = 10.0\n@export var defense: float = 5.0\n@export var speed: float = 200.0\n\n# Runtime values (not saved)\nvar _current_health: float\n\nfunc _init() -> void:\n    _current_health = max_health\n\nfunc get_current_health() -> float:\n    return _current_health\n\nfunc take_damage(amount: float) -> float:\n    var actual_damage := maxf(amount - defense, 1.0)\n    _current_health = maxf(_current_health - actual_damage, 0.0)\n    stat_changed.emit(\"health\", _current_health)\n    return actual_damage\n\nfunc heal(amount: float) -> void:\n    _current_health = minf(_current_health + amount, max_health)\n    stat_changed.emit(\"health\", _current_health)\n\nfunc duplicate_for_runtime() -> CharacterStats:\n    var copy := duplicate() as CharacterStats\n    copy._current_health = copy.max_health\n    return copy\n```\n\n```gdscript\n# Using resources\nclass_name Character\nextends CharacterBody2D\n\n@export var base_stats: CharacterStats\n@export var weapon: WeaponData\n\nvar stats: CharacterStats\n\nfunc _ready() -> void:\n    # Create runtime copy to avoid modifying the resource\n    stats = base_stats.duplicate_for_runtime()\n    stats.stat_changed.connect(_on_stat_changed)\n\nfunc attack() -> void:\n    if weapon:\n        print(\"Attacking with %s for %d damage\" % [weapon.name, weapon.damage])\n\nfunc _on_stat_changed(stat_name: StringName, value: float) -> void:\n    if stat_name == \"health\" and value \u003C= 0:\n        die()\n```\n\n### Pattern 4: Object Pooling\n\n```gdscript\n# object_pool.gd\nclass_name ObjectPool\nextends Node\n\n@export var pooled_scene: PackedScene\n@export var initial_size: int = 10\n@export var can_grow: bool = true\n\nvar _available: Array[Node] = []\nvar _in_use: Array[Node] = []\n\nfunc _ready() -> void:\n    _initialize_pool()\n\nfunc _initialize_pool() -> void:\n    for i in initial_size:\n        _create_instance()\n\nfunc _create_instance() -> Node:\n    var instance := pooled_scene.instantiate()\n    instance.process_mode = Node.PROCESS_MODE_DISABLED\n    instance.visible = false\n    add_child(instance)\n    _available.append(instance)\n\n    # Connect return signal if exists\n    if instance.has_signal(\"returned_to_pool\"):\n        instance.returned_to_pool.connect(_return_to_pool.bind(instance))\n\n    return instance\n\nfunc get_instance() -> Node:\n    var instance: Node\n\n    if _available.is_empty():\n        if can_grow:\n            instance = _create_instance()\n            _available.erase(instance)\n        else:\n            push_warning(\"Pool exhausted and cannot grow\")\n            return null\n    else:\n        instance = _available.pop_back()\n\n    instance.process_mode = Node.PROCESS_MODE_INHERIT\n    instance.visible = true\n    _in_use.append(instance)\n\n    if instance.has_method(\"on_spawn\"):\n        instance.on_spawn()\n\n    return instance\n\nfunc _return_to_pool(instance: Node) -> void:\n    if not instance in _in_use:\n        return\n\n    _in_use.erase(instance)\n\n    if instance.has_method(\"on_despawn\"):\n        instance.on_despawn()\n\n    instance.process_mode = Node.PROCESS_MODE_DISABLED\n    instance.visible = false\n    _available.append(instance)\n\nfunc return_all() -> void:\n    for instance in _in_use.duplicate():\n        _return_to_pool(instance)\n```\n\n```gdscript\n# pooled_bullet.gd\nclass_name PooledBullet\nextends Area2D\n\nsignal returned_to_pool\n\n@export var speed: float = 500.0\n@export var lifetime: float = 5.0\n\nvar direction: Vector2\nvar _timer: float\n\nfunc on_spawn() -> void:\n    _timer = lifetime\n\nfunc on_despawn() -> void:\n    direction = Vector2.ZERO\n\nfunc initialize(pos: Vector2, dir: Vector2) -> void:\n    global_position = pos\n    direction = dir.normalized()\n    rotation = direction.angle()\n\nfunc _physics_process(delta: float) -> void:\n    position += direction * speed * delta\n\n    _timer -= delta\n    if _timer \u003C= 0:\n        returned_to_pool.emit()\n\nfunc _on_body_entered(body: Node2D) -> void:\n    if body.has_method(\"take_damage\"):\n        body.take_damage(10)\n    returned_to_pool.emit()\n```\n\n### Pattern 5: Component System\n\n```gdscript\n# health_component.gd\nclass_name HealthComponent\nextends Node\n\nsignal health_changed(current: int, maximum: int)\nsignal damaged(amount: int, source: Node)\nsignal healed(amount: int)\nsignal died\n\n@export var max_health: int = 100\n@export var invincibility_time: float = 0.0\n\nvar current_health: int:\n    set(value):\n        var old := current_health\n        current_health = clampi(value, 0, max_health)\n        if current_health != old:\n            health_changed.emit(current_health, max_health)\n\nvar _invincible: bool = false\n\nfunc _ready() -> void:\n    current_health = max_health\n\nfunc take_damage(amount: int, source: Node = null) -> int:\n    if _invincible or current_health \u003C= 0:\n        return 0\n\n    var actual := mini(amount, current_health)\n    current_health -= actual\n    damaged.emit(actual, source)\n\n    if current_health \u003C= 0:\n        died.emit()\n    elif invincibility_time > 0:\n        _start_invincibility()\n\n    return actual\n\nfunc heal(amount: int) -> int:\n    var actual := mini(amount, max_health - current_health)\n    current_health += actual\n    if actual > 0:\n        healed.emit(actual)\n    return actual\n\nfunc _start_invincibility() -> void:\n    _invincible = true\n    await get_tree().create_timer(invincibility_time).timeout\n    _invincible = false\n```\n\n```gdscript\n# hitbox_component.gd\nclass_name HitboxComponent\nextends Area2D\n\nsignal hit(hurtbox: HurtboxComponent)\n\n@export var damage: int = 10\n@export var knockback_force: float = 200.0\n\nvar owner_node: Node\n\nfunc _ready() -> void:\n    owner_node = get_parent()\n    area_entered.connect(_on_area_entered)\n\nfunc _on_area_entered(area: Area2D) -> void:\n    if area is HurtboxComponent:\n        var hurtbox := area as HurtboxComponent\n        if hurtbox.owner_node != owner_node:\n            hit.emit(hurtbox)\n            hurtbox.receive_hit(self)\n```\n\n```gdscript\n# hurtbox_component.gd\nclass_name HurtboxComponent\nextends Area2D\n\nsignal hurt(hitbox: HitboxComponent)\n\n@export var health_component: HealthComponent\n\nvar owner_node: Node\n\nfunc _ready() -> void:\n    owner_node = get_parent()\n\nfunc receive_hit(hitbox: HitboxComponent) -> void:\n    hurt.emit(hitbox)\n\n    if health_component:\n        health_component.take_damage(hitbox.damage, hitbox.owner_node)\n```\n\n### Pattern 6: Scene Management\n\n```gdscript\n# scene_manager.gd (Autoload)\nextends Node\n\nsignal scene_loading_started(scene_path: String)\nsignal scene_loading_progress(progress: float)\nsignal scene_loaded(scene: Node)\nsignal transition_started\nsignal transition_finished\n\n@export var transition_scene: PackedScene\n@export var loading_scene: PackedScene\n\nvar _current_scene: Node\nvar _transition: CanvasLayer\nvar _loader: ResourceLoader\n\nfunc _ready() -> void:\n    _current_scene = get_tree().current_scene\n\n    if transition_scene:\n        _transition = transition_scene.instantiate()\n        add_child(_transition)\n        _transition.visible = false\n\nfunc change_scene(scene_path: String, with_transition: bool = true) -> void:\n    if with_transition:\n        await _play_transition_out()\n\n    _load_scene(scene_path)\n\nfunc change_scene_packed(scene: PackedScene, with_transition: bool = true) -> void:\n    if with_transition:\n        await _play_transition_out()\n\n    _swap_scene(scene.instantiate())\n\nfunc _load_scene(path: String) -> void:\n    scene_loading_started.emit(path)\n\n    # Check if already loaded\n    if ResourceLoader.has_cached(path):\n        var scene := load(path) as PackedScene\n        _swap_scene(scene.instantiate())\n        return\n\n    # Async loading\n    ResourceLoader.load_threaded_request(path)\n\n    while true:\n        var progress := []\n        var status := ResourceLoader.load_threaded_get_status(path, progress)\n\n        match status:\n            ResourceLoader.THREAD_LOAD_IN_PROGRESS:\n                scene_loading_progress.emit(progress[0])\n                await get_tree().process_frame\n            ResourceLoader.THREAD_LOAD_LOADED:\n                var scene := ResourceLoader.load_threaded_get(path) as PackedScene\n                _swap_scene(scene.instantiate())\n                return\n            _:\n                push_error(\"Failed to load scene: %s\" % path)\n                return\n\nfunc _swap_scene(new_scene: Node) -> void:\n    if _current_scene:\n        _current_scene.queue_free()\n\n    _current_scene = new_scene\n    get_tree().root.add_child(_current_scene)\n    get_tree().current_scene = _current_scene\n\n    scene_loaded.emit(_current_scene)\n    await _play_transition_in()\n\nfunc _play_transition_out() -> void:\n    if not _transition:\n        return\n\n    transition_started.emit()\n    _transition.visible = true\n\n    if _transition.has_method(\"transition_out\"):\n        await _transition.transition_out()\n    else:\n        await get_tree().create_timer(0.3).timeout\n\nfunc _play_transition_in() -> void:\n    if not _transition:\n        transition_finished.emit()\n        return\n\n    if _transition.has_method(\"transition_in\"):\n        await _transition.transition_in()\n    else:\n        await get_tree().create_timer(0.3).timeout\n\n    _transition.visible = false\n    transition_finished.emit()\n```\n\n### Pattern 7: Save System\n\n```gdscript\n# save_manager.gd (Autoload)\nextends Node\n\nconst SAVE_PATH := \"user:\u002F\u002Fsavegame.save\"\nconst ENCRYPTION_KEY := \"your_secret_key_here\"\n\nsignal save_completed\nsignal load_completed\nsignal save_error(message: String)\n\nfunc save_game(data: Dictionary) -> void:\n    var file := FileAccess.open_encrypted_with_pass(\n        SAVE_PATH,\n        FileAccess.WRITE,\n        ENCRYPTION_KEY\n    )\n\n    if file == null:\n        save_error.emit(\"Could not open save file\")\n        return\n\n    var json := JSON.stringify(data)\n    file.store_string(json)\n    file.close()\n\n    save_completed.emit()\n\nfunc load_game() -> Dictionary:\n    if not FileAccess.file_exists(SAVE_PATH):\n        return {}\n\n    var file := FileAccess.open_encrypted_with_pass(\n        SAVE_PATH,\n        FileAccess.READ,\n        ENCRYPTION_KEY\n    )\n\n    if file == null:\n        save_error.emit(\"Could not open save file\")\n        return {}\n\n    var json := file.get_as_text()\n    file.close()\n\n    var parsed := JSON.parse_string(json)\n    if parsed == null:\n        save_error.emit(\"Could not parse save data\")\n        return {}\n\n    load_completed.emit()\n    return parsed\n\nfunc delete_save() -> void:\n    if FileAccess.file_exists(SAVE_PATH):\n        DirAccess.remove_absolute(SAVE_PATH)\n\nfunc has_save() -> bool:\n    return FileAccess.file_exists(SAVE_PATH)\n```\n\n```gdscript\n# saveable.gd (Attach to saveable nodes)\nclass_name Saveable\nextends Node\n\n@export var save_id: String\n\nfunc _ready() -> void:\n    if save_id.is_empty():\n        save_id = str(get_path())\n\nfunc get_save_data() -> Dictionary:\n    var parent := get_parent()\n    var data := {\"id\": save_id}\n\n    if parent is Node2D:\n        data[\"position\"] = {\"x\": parent.position.x, \"y\": parent.position.y}\n\n    if parent.has_method(\"get_custom_save_data\"):\n        data.merge(parent.get_custom_save_data())\n\n    return data\n\nfunc load_save_data(data: Dictionary) -> void:\n    var parent := get_parent()\n\n    if data.has(\"position\") and parent is Node2D:\n        parent.position = Vector2(data.position.x, data.position.y)\n\n    if parent.has_method(\"load_custom_save_data\"):\n        parent.load_custom_save_data(data)\n```\n\n## Performance Tips\n\n```gdscript\n# 1. Cache node references\n@onready var sprite := $Sprite2D  # Good\n# $Sprite2D in _process()  # Bad - repeated lookup\n\n# 2. Use object pooling for frequent spawning\n# See Pattern 4\n\n# 3. Avoid allocations in hot paths\nvar _reusable_array: Array = []\n\nfunc _process(_delta: float) -> void:\n    _reusable_array.clear()  # Reuse instead of creating new\n\n# 4. Use static typing\nfunc calculate(value: float) -> float:  # Good\n    return value * 2.0\n\n# 5. Disable processing when not needed\nfunc _on_off_screen() -> void:\n    set_process(false)\n    set_physics_process(false)\n```\n\n## Best Practices\n\n### Do's\n- **Use signals for decoupling** - Avoid direct references\n- **Type everything** - Static typing catches errors\n- **Use resources for data** - Separate data from logic\n- **Pool frequently spawned objects** - Avoid GC hitches\n- **Use Autoloads sparingly** - Only for truly global systems\n\n### Don'ts\n- **Don't use `get_node()` in loops** - Cache references\n- **Don't couple scenes tightly** - Use signals\n- **Don't put logic in resources** - Keep them data-only\n- **Don't ignore the Profiler** - Monitor performance\n- **Don't fight the scene tree** - Work with Godot's design\n\n## Resources\n\n- [Godot Documentation](https:\u002F\u002Fdocs.godotengine.org\u002Fen\u002Fstable\u002F)\n- [GDQuest Tutorials](https:\u002F\u002Fwww.gdquest.com\u002F)\n- [Godot Recipes](https:\u002F\u002Fkidscancode.org\u002Fgodot_recipes\u002F)\n",{"name":5,"description":7},"main","7850740bd837adb32f0a109ab2af82482196c6f2","skills\u002F_collections\u002Fwshobson-agents\u002Fplugins\u002Fgame-development\u002Fskills\u002Fgodot-gdscript-patterns",1,"https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills","https:\u002F\u002Fzturnsgo.com","https:\u002F\u002Fgithub.com\u002Fshengdabai","https:\u002F\u002Fzturnsgo.com\u002F","AI × education creator — producer, not consumer. Taught 6,000+ learners; shipping learning tools & daily essays with Claude Code, practicing @xiaolai's way 🥋","2026-11-01 21:28:23",{"source":8,"skillName":5,"commands":55,"prompt":65,"zipAvailable":66},[56,59,62],{"tool":57,"command":58},"npx","npx skills add shengdabai\u002FTony-Claude-Code-Skills --skill godot-gdscript-patterns",{"tool":60,"command":61},"bunx","bunx skills add shengdabai\u002FTony-Claude-Code-Skills --skill godot-gdscript-patterns",{"tool":63,"command":64},"pnpm","pnpm dlx skills add shengdabai\u002FTony-Claude-Code-Skills --skill godot-gdscript-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002F_collections\u002Fwshobson-agents\u002Fplugins\u002Fgame-development\u002Fskills\u002Fgodot-gdscript-patterns\u002FSKILL.md",true,[],[69,88,122,147,169,189],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":80,"publishedAt":39},"01M70P17DAD4C1C47NPH6A96EM","xiaolai-methodology","以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 \"李笑来\"、\"笑来\"、\"笑来老师\"、\"扮演李笑来\"、\"找笑来聊聊\"、\"笑来怎么看\"、\"xiaolai\"、\"把时间当作朋友\"、\"通往财富自由之路\"、\"定投改变命运\"、\"韭菜的自我修养\"、\"自学是门手艺\"、\"让时间陪你慢慢变富\"、\"七年就是一辈子\"、\"元认知\"、\"活在未来\"、\"复利\u002F长期主义\"、\"践行\"、\"痴迷改进\"、\"个人商业模式\u002F出售时间\"、\"睡后收入\"、\"定投\"、\"财富自由\"、\"操作系统升级\"、\"最少必要知识\"、\"北极星提示词\"、\"用 AI 该怎么学\u002F思考\" 等任一触发词,或当用户带着关于创业\u002F赚钱\u002F投资\u002F学习\u002F职业\u002F重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fxiaolai-methodology\u002FSKILL.md",[75],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",[81,82,83,84,85,86,87],"methodology","xiaolai","xiaolai methodology","朋友","投资","writing","收入",{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":102,"publishedAt":39},"01M70P13NB9YP7ACJR1S861CDS","zread","Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `.\u002F.zread\u002Fwiki\u002F`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module\u002Fpackage explanations, or \"what does this repo do\"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread \u002F zread.ai directly. Also use it proactively before diving into a large unknown repo — if `.\u002F.zread\u002Fwiki\u002Fcurrent` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word \"zread\".","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fzread\u002FSKILL.md",[94,97],{"categoryNo":95,"categoryName":96,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":98,"categoryName":99,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[90,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121],"knowledge","wiki","explore","walkthrough","project","get","knowledge base","run","generate","intent","cli","map","documentation","project wiki","disk","architecture","code","unknown","generated",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":132,"publishedAt":39},"01M70P10S4SWMZQ8BJAFBFDRW3","youtube","Launch the YouTube Creator Agent to auto-generate videos.\nUse when: user types \u002Fyoutube, wants to create YouTube videos,\ngenerate video content, or upload to YouTube.\nKeywords: youtube, video, 视频, 创作视频, 发布视频","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube\u002FSKILL.md",[128,129],{"categoryNo":95,"categoryName":96,"taxonomyType":78,"taxonomyTypeDesc":79},{"categoryNo":130,"categoryName":131,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000036","内容创作者",[124,133,134,135,136,137,138,139,111,140,141,142,37,28,143,144,145,146],"user","content","视频","creator","generate video","videos","launch","video content","create","upload","auto","types","Video","创作",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":154,"publishedAt":39},"01M70P0YE95D8BACZCGV8BK90K","youtube-script-sync","Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新\u002F同步\u002F优化 Youtube视频逐字稿知识库”, or says “更新\u002F同步\u002F优化该知识库” while this knowledge base is the active context.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-sync\u002FSKILL.md",[153],{"categoryNo":95,"categoryName":96,"taxonomyType":78,"taxonomyTypeDesc":79},[155,156,124,157,158,145,159,160,161,162,163,164,165,166,109,167,37,28,103,168],"sync","script","youtube script sync","recording","shorts","first","base","person","update","active","child","optimize","idempotent","long",{"skillNo":170,"name":171,"nameZh":6,"description":172,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":173,"categories":174,"keywords":179,"publishedAt":39},"01M70P0WMV5XT0EEC0N445PW07","writing-skills","How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-skills\u002FSKILL.md",[175,176,177,178],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},{"categoryNo":95,"categoryName":96,"taxonomyType":78,"taxonomyTypeDesc":79},{"categoryNo":98,"categoryName":99,"taxonomyType":100,"taxonomyTypeDesc":101},{"categoryNo":130,"categoryName":131,"taxonomyType":100,"taxonomyTypeDesc":101},[180,86,181,182,183,184,119,37,28,185,186,187,188],"writing skills","how","teach","write skill","write","claude code skills","files","how to","trigger",{"skillNo":190,"name":191,"nameZh":6,"description":192,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":193,"categories":194,"keywords":197,"publishedAt":39},"01M70P0SJFR4YVJQBGMHKGSKXT","writing-rules","How to write .claude\u002Frules\u002F files that Claude actually follows. Use when creating, improving, or reviewing project rules.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-rules\u002FSKILL.md",[195,196],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},{"categoryNo":130,"categoryName":131,"taxonomyType":100,"taxonomyTypeDesc":101},[198,86,199,107,200,186,37,181,184,28,187],"Rules","writing rules","project rules"]