Loading...
Loading...
Use when building gameplay systems in Godot 4.7: enemy AI that patrols, chases and attacks, state machines, behavior trees (LimboAI), NavigationAgent3D pathfinding and avoidance for 100 to 1000 enemies, 'agents stack up' or 'path is empty', hitbox and hurtbox combat, fast bullets going through walls, save and load, inventory, quests, dialogue (Dialogue Manager), input remapping, procedural dungeons, or measuring agent counts and frame time headless.
npx skill4agent add scenario-labs/skills scenario-godot-gameplay--fixed-fps 60npx skills add scenario-labs/skills --skill <name>gd_runreferences/procedures.mdtests/code/godot-gameplay/--fixed-fps 60low_processor_usage_mode_sleep_usecPerformance.TIME_*max_neighborsregion_get_iteration_idnav_tools.wait_appliedcontinuous_cd.tresCACHE_MODE_IGNOREtarget_positionsafe_velocityget_next_path_position()is_navigation_finished()path_desired_distancetime_horizon_agentstime_horizon_obstaclesmax_neighbors| Input | Why it changes the plan | Default |
|---|---|---|
| Agent count on screen | nodes vs server agents, think budget | 200 |
| Frame budget for gameplay | the gate for every benchmark | a quarter of 16.67 ms (4.2 ms) at 60 fps [added] |
| 2D or 3D | NavigationAgent2D vs 3D, Area2D vs 3D | 3D |
| Who authors behavior | code (FSM, enum) vs designers (LimboAI) | code |
| Save trust | own files only vs shared or modded saves | shared: scan or JSON |
| Platform | input devices, CPU budget | desktop, keyboard and mouse plus pad |
gd_env.base_project("3d", P)application/config/nameuser://gg.install(P)3d/physics_engine="Jolt Physics"gd_run.check_all(P)nav_sourcemake_navmesh(parsed colliders, source group)bakewait_appliedmap_get_pathvelocity_computedmove_and_slidegg.crowd_bench(P, n)gg.scaling_verdictPerformance.add_custom_monitor(&"ai/thinks", callable)OBJECT_ORPHAN_NODE_COUNTNode.new()gg.budget_sharedecide(state, percept)VisibleOnScreenNotifier3D.is_on_screen()bullet_poolmigrate()save_io.save_resource_atomicload_resource_checked.dialoguegg.check_dialogue_text_unhandled_inputdungeon_gentest_gameplay_live.pyreferences/critique.md| Value | Measured | Relative to |
|---|---|---|
| 200 bodies with avoidance | 1.98 ms per frame, 0 overlaps | M5 Max, fixed fps 60, 60 m arena with 14 obstacles |
| Scaling with avoidance | 50 agents: 1.07 ms, 200: 2.00, 800: 7.31 (about 9 µs per agent) | same |
| 200 server agents plus MultiMesh | 0.26 ms | same |
| Think cost, 200 enemies | budget 0.40 ms, every tick 0.78, none 0.33 | 5 Hz, 40 per tick |
| First navmesh sync | 3 physics frames; rebake applied after 2 to 16 | async iterations on (default) |
| Bake | 5 ms, 180 polygons | 60 m arena, cell 0.25 |
| 1000 bullets | data 0.35 ms; RigidBody3D with CCD 1.05; without CCD 1.00 (45% miss walls) | 60 m/s, 2 s life |
| Jolt speed clamp | 500 m/s | |
| Dungeon generate plus validate | 88 µs median, 1000 of 1000 valid | 14-room main path, 4 branches |
| NavigationAgent3D defaults | radius 0.5, max_neighbors 10, neighbor_distance 50, max_speed 10, avoidance off | 4.7.2 ClassDB |
scaling_verdictbudget_sharegd_run.run_testscheck_allgd_review.image_checks| Mistake | What it looks like | Fix |
|---|---|---|
| Benchmarking plain headless | about 7 ms per "frame" whatever the load | |
| Path queried right after assigning the mesh | empty path, agents idle at spawn | |
| Default bake (Both from root) with NPCs inside | holes round spawn points | group plus static colliders |
| Avoidance off "for speed" | 3108 overlapping pairs, enemies merge | avoidance on: it costs little at 200 |
| Vision as one Area3D per enemy | broadphase churn | distance, cone, one ray [added] |
| Trusting a ray hit on a teleported body | enemy "sees" through a wall | stale-hit distance check |
| Hit areas on layer 1 and mask 1 | self-hits, triple damage | mask or layer only; HitLog |
| "Function blocked during in/out signal" | |
| Bullet bodies on the default layer | 1000 bodies collide: 110 to 170 ms per frame | own layer, mask world and targets |
| No CCD on fast bodies | bullets pass walls at 30 m/s | |
Loading a downloaded | its script runs | |
Default | old values come back | |
| runtime error or a silent no-op through | |
Preloaded inventory | one chest's loot shows in all | |
| Always-succeeding first child in a Selector | the attack branch never runs (Chap.C E_FIy2dTkNc 00:17:27) | gate first, BTProbability or AlwaysFail |
| Logical keycodes in actions | WASD breaks on AZERTY | physical keycodes |
| Teleport with physics interpolation on | the body is drawn sliding across the jump [added, not measured] | |
| no crash in 4.7.2; every later access is a "previously freed" error reading 0 (docs say crash) | never free one; reset its state instead |
nav_sourcestate_changeddecide()region_bake_navigation_meshparse_source_geometry_databake_from_source_geometry_dataregion_get_iteration_idquery_path(params, result, callback)custom_aabbgeometry_parsed_geometry_typeduplicate(true)duplicate_deep(DEEP_DUPLICATE_ALL)JSON.from_nativeto_nativeInputEventKey.as_text()DEVICE_ID_KEYBOARDreferences/procedures.mdexpert-notes.mdcritique.mdgui-paths.mdsources.mdscripts/gd_gameplay.pyrun_jobcrowd_benchscaling_verdictbudget_sharescripts/agentkit/gameplay/