godot-genre-stealth

安装量: 41
排名: #17562

安装

npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-genre-stealth
Genre: Stealth
Player choice, systemic AI, and clear communication define stealth games.
Available Scripts
stealth_ai_controller.gd
Expert AI controller with graduated detection, sound response, and alert state management.
Core Loop
Observe → Plan → Execute → Adapt → Complete
NEVER Do in Stealth Games
NEVER use instant binary detection
— Gradual 0-100% detection with visual feedback (filling meter). Binary "seen/not seen" removes player agency and feels unfair.
NEVER make guards see through walls
— Raycast-based vision with collision masks.
has_line_of_sight()
must check geometry. Wallhacks destroy stealth integrity.
NEVER use simple distance checks for sound
— Sound propagates along
NavigationServer3D
paths, NOT straight-line distance. Through-wall hearing breaks immersion.
-
NEVER make combat as viable as stealth
— If guns are easier than sneaking, players ignore stealth. Combat should be risky (outnumbered, limited ammo, loud alerts).
NEVER hide detection reasons from player
— Show WHY detected (light level high, made noise, in vision cone). "Gotcha" deaths frustrate, don't teach.
NEVER use single sample point for player visibility
— Sample multiple body parts (head, torso, feet). Hiding behind low cover should hide torso but expose head.
NEVER forget peripheral vision
— Humans have ~180° peripheral (less effective) + 60° focused vision. Single cone = unrealistic. Use composite shapes (Splinter Cell method).
Design Principles
From industry experts (Splinter Cell, Dishonored, Hitman developers):
Player Choice
Multiple valid approaches to every scenario
Systemic Design
Rules-based AI that players can learn and exploit
Clear Communication
Player always understands game state and threats
Fair Detection
No "gotcha" moments - threats visible before dangerous AI Detection System Vision Cone Implementation Based on Splinter Cell Blacklist GDC talk - realistic vision uses composite shapes : class_name EnemyVision extends Node3D @ export var forward_vision_range := 20.0

Main vision cone

@ export var peripheral_range := 10.0

Side vision

@ export var forward_fov := 60.0

Degrees

@ export var peripheral_fov := 120.0

Degrees

@ export var detection_speed := 1.0

How fast detection builds

var detection_level := 0.0

0-100

var target : Node3D = null func _physics_process ( delta : float ) -> void : var player := get_player_if_visible ( ) if player :

Detection rate varies by:

- Distance (closer = faster)

- Lighting on player

- Player movement (moving = more visible)

- In peripheral vs direct vision

var rate := calculate_detection_rate ( player ) detection_level = min ( 100 , detection_level + rate * delta ) else : detection_level = max ( 0 , detection_level - detection_speed * 0.5 * delta ) func get_player_if_visible ( ) -> Player : var player := get_tree ( ) . get_first_node_in_group ( "player" ) if not player : return null var to_player := player . global_position - global_position var distance := to_player . length ( ) var angle := rad_to_deg ( global_basis . z . angle_to ( - to_player . normalized ( ) ) )

Check forward cone

if angle < forward_fov / 2.0 and distance < forward_vision_range : if has_line_of_sight ( player ) : return player

Check peripheral (less effective)

elif angle < peripheral_fov / 2.0 and distance < peripheral_range : if has_line_of_sight ( player ) : return player return null func calculate_detection_rate ( player : Player ) -> float : var distance := global_position . distance_to ( player . global_position ) var distance_factor := 1.0 - ( distance / forward_vision_range ) var light_factor := player . get_light_level ( )

0.0 = dark, 1.0 = lit

var movement_factor := 1.0 if player . velocity . length ( )

0.5 else 0.3 return detection_speed * distance_factor * light_factor * movement_factor * 50.0 Sound Detection System Based on Thief/Hitman implementation - sounds propagate along navigation paths: class_name SoundPropagation extends Node

Sound travels through connected navigation points, not through walls

func propagate_sound ( origin : Vector3 , loudness : float , sound_type : String ) -> void : for enemy in get_tree ( ) . get_nodes_in_group ( "enemies" ) : var path := NavigationServer3D . map_get_path ( get_world_3d ( ) . navigation_map , origin , enemy . global_position , true ) if path . is_empty ( ) : continue

No path = sound blocked

var path_distance := calculate_path_length ( path ) var heard_loudness := loudness - ( path_distance * 0.5 )

Falloff

if heard_loudness

enemy . hearing_threshold : enemy . hear_sound ( origin , sound_type , heard_loudness ) func calculate_path_length ( path : PackedVector3Array ) -> float : var length := 0.0 for i in range ( 1 , path . size ( ) ) : length += path [ i ] . distance_to ( path [ i - 1 ] ) return length Player Light Level class_name LightDetector extends Node3D @ export var sample_points : Array [ Marker3D ]

Multiple points on player body

func get_light_level ( ) -> float : var total := 0.0 var space := get_world_3d ( ) . direct_space_state for point in sample_points : for light in get_tree ( ) . get_nodes_in_group ( "lights" ) : var dir := light . global_position - point . global_position var query := PhysicsRayQueryParameters3D . create ( point . global_position , light . global_position ) var result := space . intersect_ray ( query ) if result . is_empty ( ) :

Not blocked

total += light . light_energy / dir . length_squared ( ) return clamp ( total / sample_points . size ( ) , 0.0 , 1.0 ) AI Alert States Three-phase system (industry standard): enum AlertState { IDLE , SUSPICIOUS , ALERTED , COMBAT } class_name EnemyAI extends CharacterBody3D var alert_state := AlertState . IDLE var suspicion_point : Vector3 var search_timer := 0.0 signal alert_state_changed ( new_state : AlertState ) func transition_to ( new_state : AlertState ) -> void : alert_state = new_state alert_state_changed . emit ( new_state ) match new_state : AlertState . SUSPICIOUS : play_animation ( "suspicious" ) speak_dialogue ( "what_was_that" ) AlertState . ALERTED : speak_dialogue ( "who_goes_there" )

Other guards in range hear and become suspicious

alert_nearby_guards ( ) AlertState . COMBAT : speak_dialogue ( "intruder" ) trigger_alarm ( ) Visual Feedback (Critical!) class_name AlertIndicator extends Node3D @ export var idle_icon : Texture2D @ export var suspicious_icon : Texture2D

"?"

@ export var alerted_icon : Texture2D

"!"

@ export var detection_meter : ProgressBar

Shows filling detection

func update_indicator ( state : AlertState , detection : float ) -> void : detection_meter . value = detection match state : AlertState . IDLE : icon . texture = idle_icon detection_meter . visible = false AlertState . SUSPICIOUS : icon . texture = suspicious_icon detection_meter . visible = true AlertState . ALERTED : icon . texture = alerted_icon detection_meter . visible = false Player Abilities Five categories of stealth tools (per Mark Brown's analysis): 1. Movement Alteration

Crouch, crawl, run (noisy vs quiet)

func calculate_noise_level ( ) -> float : if is_crouching : return 0.2 elif is_running : return 1.0 else : return 0.5 2. Information Gathering

Peek, scout, mark enemies

func activate_detective_vision ( ) -> void : for enemy in get_tree ( ) . get_nodes_in_group ( "enemies" ) : enemy . show_outline ( ) enemy . show_vision_cone ( ) 3. AI Manipulation

Throw distractions

func throw_distraction ( target_position : Vector3 ) -> void : var rock := distraction_scene . instantiate ( ) rock . global_position = target_position add_child ( rock ) SoundPropagation . propagate_sound ( target_position , 30.0 , "impact" ) 4. Space Control

Shoot out lights, create hiding spots

func shoot_light ( light : Light3D ) -> void : light . visible = false

Update light level for area

  1. Enemy Elimination func perform_takedown ( enemy : EnemyAI , lethal : bool ) -> void : if enemy . alert_state == AlertState . COMBAT : return

Can't stealth kill alert enemy

if lethal : enemy . die ( ) else : enemy . knockout ( )

Body becomes interactable

spawn_body
(
enemy
)
Level Design
Outpost Design (Open Areas)
[Safe perimeter for observation]
|
[Sparse guards at edges - isolatable]
|
[Dense center with objective]
|
[Multiple entry points/routes]
Limited Encounter Design (Corridors)
Enemies visible 8+ meters before engagement
Multiple paths through
Cover objects and hiding spots
Emergency escape routes
UI Communication
Based on Thief's "light gem" innovation:
class_name
StealthHUD
extends
Control
@
onready
var
visibility_meter
:
TextureProgressBar
@
onready
var
sound_meter
:
TextureProgressBar
@
onready
var
minimap
:
Control
func
_process
(
_delta
:
float
)
->
void
:
visibility_meter
.
value
=
player
.
get_light_level
(
)
*
100
sound_meter
.
value
=
player
.
current_noise_level
*
100
Common Pitfalls
Pitfall
Solution
Instant detection
Use gradual detection with clear feedback
Guards see through walls
Raycast-based vision with proper collision
Unfair patrol patterns
Make patterns learnable, with tells
Two games (stealth + combat)
Either commit to stealth or make combat risky
Unclear detection
Always show WHY player was detected
Godot-Specific Tips
Raycasts for vision
Use
PhysicsRayQueryParameters3D
with collision masks
NavigationAgent3D
For patrol routes and pathfinding
Area3D
For sound propagation zones and trigger areas
AnimationTree
Blend between alert state animations Reference Master Skill: godot-master
返回排行榜