925236118/alphaagent/godot-online-multiplayer

Claude Code and agent skill by 925236118/alphaagent

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Godot 4 多人游戏核心模式,包括场景管理、玩家生命周期、死亡复活机制。

Install

npx vskill@latest install 925236118/alphaagent/godot-online-multiplayer

SKILL.md

Godot Online Multiplayer

Godot 4 多人游戏核心模式,涵盖场景管理、玩家生命周期、死亡复活等机制。

When to Use This Skill

  • 构建多人在线游戏
  • 实现玩家生成与销毁
  • 处理多人游戏中的死亡与复活
  • 管理游戏流程状态机
  • 同步玩家数据

Core Concepts

多人游戏架构
MultiplayerHost (服务器/主机)
├── MultiplayerSpawner (自动生成节点)
├── MultiplayerSynchronizer (状态同步)
└── PlayerManager (玩家管理)
MultiplayerPeers (客户端/对等端)
├── NetworkedMultiplayerENet (网络连接)
└── Player instances (同步的玩家)

1. 场景结构

主游戏场景
gdscript
# main.tscn 场景结构
# Main (Node)
# ├── GameManager (Autoload)
# ├── MultiplayerSpawner (自动生成玩家)
# ├── World (Node2D)
# │ ├── SpawnPoints (Node)
# │ └── Enemies (Node)
# └── UI (CanvasLayer)
玩家场景
gdscript
# player.tscn 场景结构
# Player (CharacterBody2D)
# ├── Sprite2D
# ├── CollisionShape2D
# ├── HealthComponent
# ├── HitboxComponent
# ├── NameLabel (Label)
# └── HealthBar (ProgressBar)

2. MultiplayerSpawner 使用

gdscript
# PlayerSpawner.gd
class_name PlayerSpawner
extends MultiplayerSpawner
## 自动生成玩家节点
## 当对等端连接时自动生成玩家实例
signal player_spawned(player: Node, peer_id: int)
signal player_despawned(player: Node, peer_id: int)
@export var player_scene: PackedScene
func _ready() -> void:
# 设置生成路径
spawn_path = ^"/root/Main/Players"
# 连接信号
spawned.connect(_on_spawned)
despawned.connect(_on_despawned)
func _on_spawned(node: Node) -> void:
var peer_id := multiplayer.get_remote_sender_id()
print("玩家生成: %s (Peer ID: %d)" % [node.name, peer_id])
player_spawned.emit(node, peer_id)
func _on_despawned(node: Node) -> void:
var peer_id := multiplayer.get_remote_sender_id()
print("玩家销毁: %s (Peer ID: %d)" % [node.name, peer_id])
player_despawned.emit(node, peer_id)

3. 生成函数

gdscript
# spawn_manager.gd
class_name SpawnManager
extends Node
## 管理玩家生成逻辑
@export var player_scene: PackedScene
@export var spawn_points: Array[Marker2D] = []
var _spawn_points_container: Node2D
func _ready() -> void:
_spawn_points_container = $SpawnPoints
func get_spawn_position(peer_id: int) -> Vector2:
"""根据peer_id获取生成位置"""
if spawn_points.is_empty():
return Vector2.ZERO
# 使用 peer_id 哈希选择生成点,实现均衡分布
var index := peer_id % spawn_points.size()
var spawn_point := spawn_points[index]
if spawn_point is Marker2D:
return spawn_point.global_position
return spawn_point.global_position if spawn_point.has_node(".") else Vector2.ZERO
func spawn_player(peer_id: int, parent: Node) -> Node:
"""生成玩家节点"""
var player := player_scene.instantiate()
# 设置玩家名称为 peer_id
player.name = str(peer_id)
# 设置初始位置
player.global_position = get_spawn_position(peer_id)
# 配置玩家
if player.has_method("setup"):
player.setup(peer_id)
parent.add_child(player)
# 只在服务器上设置网络主人
if multiplayer.is_server():
player.set_multiplayer_authority(peer_id)
return player
func despawn_player(peer_id: int, parent: Node) -> void:
"""销毁玩家节点"""
var player := parent.get_node_or_null(str(peer_id))
if player:
player.queue_free()

4. 玩家加入流程

gdscript
# game_manager.gd (Autoload)
extends Node
signal player_joined(peer_id: int)
signal player_left(peer_id: int)
signal game_started
signal game_ended(winner_id: int)
enum GameState { WAITING, PLAYING, GAME_OVER }
var game_state: GameState = GameState.WAITING
var players: Dictionary = {} # peer_id -> PlayerData
var _min_players: int = 1
var _max_players: int = 4
func _ready() -> void:
# 设置网络回调
multiplayer.peer_connected.connect(_on_peer_connected)
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
# 如果是服务器,启动游戏循环
if multiplayer.is_server():
_check_start_game()
func _on_peer_connected(peer_id: int) -> void:
print("玩家连接: %d" % peer_id)
# 创建玩家数据
var player_data := PlayerData.new()
player_data.peer_id = peer_id
player_data.name = "Player_%d" % peer_id
player_data.is_alive = true
player_data.score = 0
players[peer_id] = player_data
# 通知所有客户端有新玩家加入
rpc("_sync_player_joined", peer_id, player_data.to_dict())
player_joined.emit(peer_id)
# 检查是否可以开始游戏
_check_start_game()
func _on_peer_disconnected(peer_id: int) -> void:
print("玩家断开: %d" % peer_id)
# 清理玩家数据
var player_data := players.get(peer_id)
if player_data:
player_data.free()
players.erase(peer_id)
# 通知所有客户端
rpc("_sync_player_left", peer_id)
player_left.emit(peer_id)
# 检查游戏是否结束
if game_state == GameState.PLAYING and players.size() < 2:
_end_game(0) # 剩余玩家获胜
@rpc(call_local = true)
func _sync_player_joined(peer_id: int, data: Dictionary) -> void:
if not players.has(peer_id):
var player_data := PlayerData.new()
player_data.from_dict(data)
players[peer_id] = player_data
@rpc(call_local = true)
func _sync_player_left(peer_id: int) -> void:
var player_data := players.get(peer_id)
if player_data:
player_data.free()
players.erase(peer_id)
func _check_start_game() -> void:
if game_state == GameState.WAITING and players.size() >= _min_players:
start_game()
func start_game() -> void:
game_state = GameState.PLAYING
game_started.emit()
# 重置所有玩家状态
for peer_id in players.keys():
var data := players[peer_id]
data.is_alive = true
data.lives = 3
data.score = 0
rpc("_sync_game_state", GameState.PLAYING)
@rpc(call_local = true)
func _sync_game_state(state: GameState) -> void:
game_state = state
func _end_game(winner_id: int) -> void:
game_state = GameState.GAME_OVER
game_ended.emit(winner_id)
rpc("_sync_game_state", GameState.GAME_OVER)
rpc("_sync_winner", winner_id)
@rpc(call_local = true)
func _sync_winner(winner_id: int) -> void:
pass # UI显示胜利者
# PlayerData.gd
class_name PlayerData
extends RefCounted
var peer_id: int
var name: String
var is_alive: bool
var lives: int = 3
var score: int = 0
func to_dict() -> Dictionary:
return {
"peer_id": peer_id,
"name": name,
"is_alive": is_alive,
"lives": lives,
"score": score
}
func from_dict(data: Dictionary) -> void:
peer_id = data.get("peer_id", 0)
name = data.get("name", "")
is_alive = data.get("is_alive", true)
lives = data.get("lives", 3)
score = data.get("score", 0)

5. 断开处理

gdscript
# connection_manager.gd
class_name ConnectionManager
extends Node
signal connection_failed
signal connection_succeeded
signal server_disconnected
const DEFAULT_PORT := 8765
const MAX_PLAYERS := 8
func _ready() -> void:
# 显示主菜单UI
pass
func create_server(port: int = DEFAULT_PORT) -> bool:
"""创建服务器"""
var peer := ENetMultiplayerPeer.new()
var error := peer.create_server(port, MAX_PLAYERS)
if error != OK:
push_error("无法创建服务器: %s" % error)
return false
multiplayer.multiplayer_peer = peer
# 连接信号
peer.peer_connected.connect(_on_peer_connected)
peer.peer_disconnected.connect(_on_peer_disconnected)
print("服务器创建成功,端口: %d" % port)
connection_succeeded.emit()
return true
func join_server(ip: String, port: int = DEFAULT_PORT) -> bool:
"""加入服务器"""
var peer := ENetMultiplayerPeer.new()
var error := peer.create_client(ip, port)
if error != OK:
push_error("无法连接服务器: %s" % error)
connection_failed.emit()
return false
multiplayer.multiplayer_peer = peer
# 连接信号
peer.connection_succeeded.connect(_on_connection_succeeded)
peer.connection_failed.connect(_on_connection_failed)
peer.server_disconnected.connect(_on_server_disconnected)
print("正在连接 %s:%d..." % [ip, port])
return true
func _on_peer_connected(peer_id: int) -> void:
print("对等端连接: %d" % peer_id)
func _on_peer_disconnected(peer_id: int) -> void:
print("对等端断开: %d" % peer_id)
# GameManager 处理玩家离开逻辑
func _on_connection_succeeded() -> void:
print("连接成功!")
connection_succeeded.emit()
func _on_connection_failed() -> void:
print("连接失败!")
connection_failed.emit()
func _on_server_disconnected() -> void:
print("服务器断开")
server_disconnected.emit()
# 返回主菜单
get_tree().change_scene_to_file("res://scenes/menu.tscn")
func disconnect_from_server() -> void:
if multiplayer.multiplayer_peer:
multiplayer.multiplayer_peer.close()
multiplayer.multiplayer_peer = null

6. 死亡与复活

gdscript
# player.gd
class_name Player
extends CharacterBody2D
signal died(peer_id: int, killer_id: int)
signal respawned(peer_id: int)
@export var respawn_time: float = 3.0
@export var invincibility_after_spawn: float = 2.0
var peer_id: int = 0
var is_alive: bool = true
var killer_id: int = 0
var _respawn_timer: Timer
var _invincibility_timer: Timer
var _health_component: HealthComponent
func _ready() -> void:
# 获取生命组件
_health_component = $HealthComponent
if _health_component:
_health_component.died.connect(_on_died)
# 创建计时器
_respawn_timer = Timer.new()
_respawn_timer.one_shot = true
_respawn_timer.timeout.connect(_on_respawn_timer_timeout)
add_child(_respawn_timer)
_invincibility_timer = Timer.new()
_invincibility_timer.one_shot = true
_invincibility_timer.timeout.connect(_on_invincibility_timeout)
add_child(_invincibility_timer)
func setup(p_id: int) -> void:
peer_id = p_id
name = str(p_id)
# 设置网络权限
set_multiplayer_authority(p_id)
# 在所有客户端上设置可见性
.rpc("_sync_setup", p_id)
@rpc
func _sync_setup(p_id: int) -> void:
peer_id = p_id
name = str(p_id)
func _on_died(source_peer_id: int) -> void:
if not is_alive:
return
is_alive = false
killer_id = source_peer_id
died.emit(peer_id, killer_id)
# 通知服务器
rpc("_notify_death", peer_id, source_peer_id)
@rpc(call_local = true)
func _notify_death(victim_id: int, killer_id: int) -> void:
died.emit(victim_id, killer_id)
# 服务器处理死亡逻辑
if multiplayer.is_server():
GameManager.on_player_died(victim_id, killer_id)
# 开始复活计时
_start_respawn_timer()
func _start_respawn_timer() -> void:
_respawn_timer.start(respawn_time)
func _on_respawn_timer_timeout() -> void:
if is_networked():
rpc("_request_respawn", peer_id)
else:
_do_respawn()
@rpc
func _request_respawn(p_id: int) -> void:
if multiplayer.is_server():
_do_respawn_for_player(p_id)
func _do_respawn_for_player(p_id: int) -> void:
# 服务器检查是否可以复活
var player_data := GameManager.players.get(p_id)
if player_data and player_data.lives > 0:
rpc("_perform_respawn", p_id)
@rpc(call_local = true)
func _perform_respawn(p_id: int) -> void:
if peer_id != p_id:
return
# 重置状态
is_alive = true
_health_component.current_health = _health_component.max_health
# 移动到生成点
global_position = SpawnManager.get_spawn_position(p_id)
# 无敌时间
_invincibility_timer.start(invincibility_after_spawn)
respawned.emit(peer_id)
func _on_invincibility_timeout() -> void:
# 恢复碰撞
pass
func take_damage(amount: int, source_peer_id: int) -> void:
if not is_alive:
return
if _health_component:
_health_component.take_damage(amount, get_node_or_null(str(source_peer_id)))

7. 游戏结束检查

gdscript
# game_manager.gd 继续

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
103
Repository forks
14
7-day trend score
23

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/925236118/alphaagent/godot-online-multiplayer/badge)](https://verified-skill.com/skills/925236118/alphaagent/godot-online-multiplayer)

Something wrong with this skill or its page? Tell us.