AgentSkillsCN

godot-genre-battle-royale

战斗 Royale 游戏的专家级蓝图,包括缩小区域/风暴机制(分阶段、伤害递减)、大规模网络连接(相关性、帧率优化)、部署系统(飞机、自由落体、降落伞)、战利品生成(权重表格、稀有度)、以及性能优化(LOD、遮挡剔除、对象池化)。适用于多人生存游戏或最后幸存者模式。触发关键词:battle_royale、zone_shrink、storm_damage、deployment_system、loot_spawn、networking_optimization、relevancy_system、snapshot_interpolation。

SKILL.md
--- frontmatter
name: godot-genre-battle-royale
description: "Expert blueprint for Battle Royale games including shrinking zone/storm mechanics (phase-based, damage scaling), large-scale networking (relevancy, tick rate optimization), deployment systems (plane, freefall, parachute), loot spawning (weighted tables, rarity), and performance optimization (LOD, occlusion culling, object pooling). Use for multiplayer survival games or last-one-standing formats. Trigger keywords: battle_royale, zone_shrink, storm_damage, deployment_system, loot_spawn, networking_optimization, relevancy_system, snapshot_interpolation."

Genre: Battle Royale

Expert blueprint for Battle Royale games with zone mechanics, large-scale networking, and survival gameplay.

NEVER Do

  • NEVER sync all 100 players every frame — Use relevancy system: only sync players within visual range. Far players update at 4Hz, nearby at 20Hz+.
  • NEVER make zone center fully random — New circle must overlap significantly with old circle, or players teleport. Limit offset to current_radius - target_radius.
  • NEVER use client-side hit detection — Client says "I shot at direction X", Server validates "Did it hit?". Prevents cheating.
  • NEVER spawn loot without pooling — 1000+ loot items cause GC spikes. Pool loot pickups and reuse instances.
  • NEVER forget VisibilityNotifier3D for distant players — Disable _process() and AnimationPlayer for players behind or far away. Saves 60-80% CPU.

Available Scripts

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

kill_feed_bus.gd

Global elimination signal bus with match stat tracking. Single emission point for UI/logging, sorted killer rankings for end-game summary.

storm_system.gd

Dynamic zone shrinking with damage interpolation. Tweens center/radius smoothly, scales damage by zone size for end-game intensity.


Core Loop

  1. Deploy: Player chooses a landing spot from an air vehicle.
  2. Loot: Player scavenges weapons and armor.
  3. Move: Player runs to the safe zone to avoid taking damage.
  4. Engage: Player fights others they encounter.
  5. Survive: Player attempts to be the last one standing.

Skill Chain

PhaseSkillsPurpose
1. Netgodot-multiplayer-networkingAuthoritative server, lag compensation
2. Mapgodot-3d-world-building, level-of-detailLarge terrain, chunking, distant trees
3. Itemsgodot-inventory-systemManaging backpack, attachments, armor
4. Combatshooter-mechanics, ballisticsProjectile physics, damage calculation
5. Logicgame-managerManaging the Storm/Zone state

Architecture Overview

1. The Zone Manager (The Storm)

Manages the shrinking safe area.

gdscript
# zone_manager.gd
extends Node

@export var phases: Array[ZonePhase]
var current_phase_index: int = 0
var current_radius: float = 2000.0
var target_radius: float = 2000.0
var center: Vector2 = Vector2.ZERO
var target_center: Vector2 = Vector2.ZERO
var shrink_speed: float = 0.0

func start_next_phase() -> void:
    var phase = phases[current_phase_index]
    target_radius = phase.end_radius
    # Pick new center WITHIN current circle but respecting new radius
    var random_angle = randf() * TAU
    var max_offset = current_radius - target_radius
    var offset = Vector2.RIGHT.rotated(random_angle) * (randf() * max_offset)
    target_center = center + offset
    
    shrink_speed = (current_radius - target_radius) / phase.shrink_time

func _process(delta: float) -> void:
    if current_radius > target_radius:
        current_radius -= shrink_speed * delta
        center = center.move_toward(target_center, (shrink_speed * delta) * (center.distance_to(target_center) / (current_radius - target_radius)))

2. Loot Spawner

Efficiently populating the world.

gdscript
# loot_manager.gd
func spawn_loot() -> void:
    for spawn_point in get_tree().get_nodes_in_group("loot_spawns"):
        if randf() < spawn_point.spawn_chance:
            var item_id = loot_table.roll_item()
            var loot_instance = loot_scene.instantiate()
            loot_instance.setup(item_id)
            add_child(loot_instance)

3. Deployment System

Transitioning from plane to ground.

gdscript
# player_controller.gd
enum State { IN_PLANE, FREEFALL, PARACHUTE, GROUNDED }

func _physics_process(delta: float) -> void:
    match current_state:
        State.FREEFALL:
            velocity.y = move_toward(velocity.y, -50.0, gravity * delta)
            move_and_slide()
            if position.y < auto_deploy_height:
                deploy_parachute()

Key Mechanics Implementation

Zone Damage

Checking if player is outside the circle.

gdscript
func check_zone_damage() -> void:
    var dist = Vector2(global_position.x, global_position.z).distance_to(ZoneManager.center)
    if dist > ZoneManager.current_radius:
        take_damage(ZoneManager.dps * delta)

Networking Optimization

You cannot sync 100 players every frame.

  • Relevancy: Only send updates for players within visual range.
  • Frequency: Update far-away players at 4Hz, nearby at 20Hz+ (Server Tick).
  • Snapshot Interpolation: Client buffers headers to play them back smoothly.

Godot-Specific Tips

  • MultiplayerSynchronizer: Use replication_interval to lower bandwidth for distant objects.
  • VisibilityNotifier3D: Critical. Disable _process and AnimationPlayer for players behind you or far away.
  • Occlusion Culling: Essential for large maps with buildings. Bake occlusion data.
  • HLOD: Use Hierarchical Level of Detail for terrain and large structures.

Common Pitfalls

  1. Too Main Loot: Too much loot causes lag. Fix: Use object pooling for loot pickups.
  2. Camping: Players hide forever. Fix: The Zone forces movement. Also, anti-camping mechanics like "scan reveals" (optional).
  3. Cheating: Client-side hit detection. Fix: Authoritative server logic. Client says "I shot at direction X", Server calculates "Did it hit?".

Reference