跳到主要内容
知仓学习社ZHICANG

godot-game-loop-waves

Expert patterns for managing combat waves, difficulty scaling, and automated enemy spawning in Godot 4. Use when building wave-based shooters, tower…

不碰外部(只输出文字)无严重或高危命中thedivergentai/GD-Agentic-Skills

它会碰到什么

扫了多少3 个文本文件,17 KB
它会碰到什么不碰外部(只输出文字)
命中总数0 处
命中统计严重 0 · 高 0 · 中 0 · 低 0

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

Architectural Thinking: The "Wave-State" Pattern

A Master implementation treats waves as Data-Driven Transitions. Instead of hardcoding spawn counts, use a WaveResource to define "Encounters" that the WaveManager processes sequentially.

Core Responsibilities

  • Manager: Orchestrates the timeline. Handles delays between waves and tracks "Victory" conditions (all enemies dead).
  • Spawner: Decoupled nodes that provide spatial context for where enemies appear (wave_spawner.gd / wave_weighted_spawner.gd).
  • Resource: Immutable data containers that allow designers to rebalance the game without touching code.

Density Decision Tree (pick scale before coding)

| Live density | Approach | MANDATORY loads |

| :--- | :--- | :--- |

| Under ~80 SceneTree enemies | Node manager + Marker spawners | [wave_manager.gd](scripts/wave_manager.gd), [wave_spawner.gd](scripts/wave_spawner.gd), [wave_resource.gd](scripts/wave_resource.gd) |

| Swarm visuals / hundreds | MultiMesh + weighted composition | [wave_loop_patterns.gd](scripts/wave_loop_patterns.gd) (MultiMesh / async path), [wave_weighted_spawner.gd](scripts/wave_weighted_spawner.gd) |

| ~10k bodies | PhysicsServer / NavigationServer RIDs (no per-mob Node) | [wave_loop_patterns.gd](scripts/wave_loop_patterns.gd) server-RID patterns; do not scale wave_manager node spawns |

wave_manager.gd is the SceneTree golden path (deferred add_child, group/signal clear counts, optional pool). Treat it as prototype→mid-scale — for RID swarms, follow wave_loop_patterns.gd instead of instantiating thousands of nodes.

Composition Golden Path

  1. Author a [wave_resource.gd](scripts/wave_resource.gd) composition table.
  2. Place [wave_spawner.gd](scripts/wave_spawner.gd) Markers (or [wave_weighted_spawner.gd](scripts/wave_weighted_spawner.gd) when variety weights matter).
  3. Point [wave_manager.gd](scripts/wave_manager.gd) spawner at that Marker; manager defers spawn and clears via &"enemies" group + signals.
  4. When weights replace fixed counts, call WaveWeightedSpawner.spawn_enemy() from the composition loop (or set manager spawner to the weighted node).

Expert Code Patterns

1. The Async Wave Trigger

Use await timers in [wave_manager.gd](scripts/wave_manager.gd) — MANDATORY read before writing a custom timeline. Spawns use call_deferred(&"add_child", …); clear via signals/groups (not get_children() scans).

2. Composition-Based Spawning

Define variety in [wave_resource.gd](scripts/wave_resource.gd); place units with [wave_spawner.gd](scripts/wave_spawner.gd) / [wave_weighted_spawner.gd](scripts/wave_weighted_spawner.gd). Do not hardcode scene paths in the manager.

Master Decision Matrix: Progression

| Pattern | Best For | Logic |

| :--- | :--- | :--- |

| Linear | Story missions | Hand-crafted list of WaveResource. |

| Endless | Survival modes | Code-generated WaveResource with multiplier math. |

| Triggered | RPG Encounters | Wave starts only when player enters an Area3D. |

NEVER Do

  • NEVER iterate through get_children() to find all enemies — This is extremely slow. Always add enemies to an "enemies" group and use get_tree().get_nodes_in_group(&"enemies") for efficient access.
  • NEVER constantly instantiate() and queue_free() hundreds of enemies — This causes garbage collection stutters. Use an object pool to reuse existing enemy instances.
  • NEVER spawn thousands of separate MeshInstance3D nodes for swarms — This will tank your draw calls. Use MultiMeshInstance3D to batch thousands of meshes into a single GPU call.
  • NEVER calculate pathfinding for hundreds of agents on the main thread — This will freeze your game. Enable use_async_iterations on your navigation regions or use NavigationServer3D.query_path().
  • NEVER forget to check is_inside_tree() before adding a child — If the spawner is queued for deletion, adding a child will crash. Always verify the spawner is still active in the tree.
  • NEVER assign a preloaded resource (like stats.tres) directly to spawned mobs — They will all share the exact same health/stats. Always call base_stats.duplicate_deep() to give each mob its own unique data.
  • NEVER use standard strings for high-frequency group calls — Always use StringName (&"enemies", &"take_damage") for optimal hash performance and to avoid unnecessary string allocations.
  • NEVER spawn entities directly inside physics callbacks synchronously — Instantiating nodes during physics steps can corrupt the physics state. Always use call_deferred(&"add_child", enemy).
  • NEVER leave CollisionShapes on dead enemies active — Corpses will block towers and navigation. Use set_deferred("disabled", true) immediately upon death.
  • NEVER synchronize complex Object types via MultiplayerSynchronizer — It only supports primitive types. For complex data, sync a UID or ID and look up the data locally on the client.
  • NEVER auto-start waves without player feedback — Always provide a UI countdown, a visual "Wave Incoming" effect, or a start button to maintain player agency.
  • NEVER hardcode spawn positions at (0,0,0) — Use Marker3D nodes in the editor so you can visually adjust spawn points without digging into code.
  • NEVER check wave completion by counting children every frame — It's too expensive. Maintain a local counter or use a signal-based system to track active enemy counts.
  • NEVER use the same navigation map for every entity type — If you have flying and walking enemies, use separate navigation maps to prevent pathing issues.
  • NEVER scale collision shapes non-uniformly for spawners — This breaks the collision detection math. Adjust the shape resource properties instead.

Available Scripts

> MANDATORY: Read the appropriate script before implementing the corresponding pattern.

[wave_loop_patterns.gd](scripts/wave_loop_patterns.gd)

10 Expert patterns: MultiMesh swarms, async pathfinding, background preloading, and server-side physics mobs.

[wave_manager.gd](scripts/wave_manager.gd)

Orchestrates the timeline, delays between waves, and tracks clear via group counts + signals. Uses call_deferred add_child; optional pool via use_pool / recycle_enemy.

[wave_resource.gd](scripts/wave_resource.gd)

Data containers for wave compositions and difficulty settings.

[wave_spawner.gd](scripts/wave_spawner.gd)

Marker3D spatial portal — get_spawn_position() with optional radius jitter. Wire as WaveManager.spawner.

[wave_weighted_spawner.gd](scripts/wave_weighted_spawner.gd)

Weighted random enemy selection at a Marker. Use when composition variety is probability-driven rather than fixed counts.


Expert Wave Patterns

1. Occlusion Culling for Swarms

To optimize performance with hundreds of enemies, enable Occlusion Culling.

  • Setup: Add an OccluderInstance3D to your arena and bake it.
  • Result: Enemies completely hidden behind walls/pillars won't be processed by the GPU, significantly boosting FPS.

2. Wave UI Architecture

Decouple your wave data from the UI using a CanvasLayer and signals.

  • Wave Counter: Display current/total waves.
  • Health Bars: Use a TextureProgressBar on a CanvasLayer for bosses, or Sprite3D with a viewport texture for individual enemy health bars.

Expert knowledge (on demand)

> LLM-ignorance rule: If a general agent would not know it before reading, load the reference — never delete expert deltas.

  • [wave-expert-patterns.md](references/wave-expert-patterns.md) — restored baseline pedagogy (architecture, WHY, implementation depth)

Reference

> Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.

Official Documentation

  • ResourcesWaveResource compositions and delays stay designer-editable without hardcoding spawn tables in managers.
  • Nodes and scene instancesPackedScene.instantiate() plus add_child / call_deferred is the safe spawn path for wave enemies.
  • Groups — track live mobs with StringName groups and get_node_count_in_group instead of scanning children every frame.
  • Idle and Physics Processing — keep pacing on timers/await; never instantiate mid-physics callback without deferring.
  • SceneTreeTimer — pre-wave delays and spawn-rate gaps via create_timer without a forever _process countdown.
  • Using signalswave_started / wave_cleared / all_waves_complete decouple UI, audio, and combat from the manager timeline.
  • Background loadingResourceLoader.load_threaded_request bosses/heavy waves so first spawn does not hitch.
  • Random number generation — weighted composition and spawn jitter with RandomNumberGenerator.rand_weighted.
  • Using MultiMesh — batch swarm visuals when hundreds of minions would explode draw calls.
  • Occlusion culling — hide off-camera arena mobs so dense waves stay GPU-affordable.
  • Navigation introduction (3D) — async agent paths and separate maps for flying vs walking wave units.
  • Using Servers — PhysicsServer3D/NavigationServer3D RID swarms when SceneTree nodes cannot scale.

Related Skills

Prerequisites

Complements

Downstream / consumers

Master

  • godot-master — library router and mirrored module entry for cross-skill discovery.

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。