refactor:实现新的项目结构组织
- 添加 _Core/components/ 和 _Core/utils/ 目录 - 重新组织 scenes/ 目录结构,按功能分类 - 迁移 StringUtils.gd 到新的 _Core/utils/ 位置 - 迁移 AuthScene.gd 到新的 scenes/ui/ 位置 - 添加 AI 文档支持目录 docs/AI_docs/ - 添加开发参考文档 claude.md
This commit is contained in:
617
docs/AI_docs/templates/managers.md
Normal file
617
docs/AI_docs/templates/managers.md
Normal file
@@ -0,0 +1,617 @@
|
||||
# 🎯 管理器模板集合
|
||||
|
||||
> AI编程助手专用:各类管理器的标准化代码模板
|
||||
|
||||
## 🎮 游戏管理器模板
|
||||
|
||||
### 基础游戏管理器
|
||||
```gdscript
|
||||
# _Core/managers/GameManager.gd
|
||||
extends Node
|
||||
|
||||
## 游戏状态管理器
|
||||
## 负责管理游戏的全局状态、玩家数据和游戏流程
|
||||
|
||||
# 信号定义
|
||||
signal game_state_changed(old_state: GameState, new_state: GameState)
|
||||
signal player_data_updated(data: Dictionary)
|
||||
signal game_paused()
|
||||
signal game_resumed()
|
||||
|
||||
# 游戏状态枚举
|
||||
enum GameState {
|
||||
LOADING,
|
||||
MENU,
|
||||
PLAYING,
|
||||
PAUSED,
|
||||
GAME_OVER,
|
||||
SETTINGS
|
||||
}
|
||||
|
||||
# 常量定义
|
||||
const SAVE_FILE_PATH: String = "user://game_save.dat"
|
||||
const CONFIG_FILE_PATH: String = "res://Config/game_config.json"
|
||||
|
||||
# 导出变量
|
||||
@export var debug_mode: bool = false
|
||||
@export var auto_save_interval: float = 30.0
|
||||
|
||||
# 公共变量
|
||||
var current_state: GameState = GameState.LOADING
|
||||
var is_paused: bool = false
|
||||
var game_time: float = 0.0
|
||||
|
||||
# 玩家数据
|
||||
var player_data: Dictionary = {
|
||||
"level": 1,
|
||||
"experience": 0,
|
||||
"coins": 100,
|
||||
"health": 100,
|
||||
"max_health": 100,
|
||||
"energy": 100,
|
||||
"max_energy": 100
|
||||
}
|
||||
|
||||
# 私有变量
|
||||
var _auto_save_timer: Timer
|
||||
var _game_config: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
_initialize_manager()
|
||||
_setup_auto_save()
|
||||
_load_game_config()
|
||||
change_state(GameState.MENU)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if current_state == GameState.PLAYING and not is_paused:
|
||||
game_time += delta
|
||||
|
||||
## 改变游戏状态
|
||||
func change_state(new_state: GameState) -> void:
|
||||
if current_state == new_state:
|
||||
return
|
||||
|
||||
var old_state = current_state
|
||||
_exit_state(old_state)
|
||||
current_state = new_state
|
||||
_enter_state(new_state)
|
||||
|
||||
game_state_changed.emit(old_state, new_state)
|
||||
|
||||
if debug_mode:
|
||||
print("[GameManager] State changed: %s -> %s" % [old_state, new_state])
|
||||
|
||||
## 暂停游戏
|
||||
func pause_game() -> void:
|
||||
if current_state != GameState.PLAYING:
|
||||
return
|
||||
|
||||
is_paused = true
|
||||
get_tree().paused = true
|
||||
game_paused.emit()
|
||||
|
||||
## 恢复游戏
|
||||
func resume_game() -> void:
|
||||
if not is_paused:
|
||||
return
|
||||
|
||||
is_paused = false
|
||||
get_tree().paused = false
|
||||
game_resumed.emit()
|
||||
|
||||
## 更新玩家数据
|
||||
func update_player_data(key: String, value) -> void:
|
||||
if not player_data.has(key):
|
||||
push_warning("Unknown player data key: %s" % key)
|
||||
return
|
||||
|
||||
player_data[key] = value
|
||||
player_data_updated.emit(player_data)
|
||||
|
||||
if debug_mode:
|
||||
print("[GameManager] Player data updated: %s = %s" % [key, value])
|
||||
|
||||
## 获取玩家数据
|
||||
func get_player_data(key: String = ""):
|
||||
if key.is_empty():
|
||||
return player_data.duplicate()
|
||||
|
||||
return player_data.get(key, null)
|
||||
|
||||
## 保存游戏数据
|
||||
func save_game() -> bool:
|
||||
var save_data = {
|
||||
"player_data": player_data,
|
||||
"game_time": game_time,
|
||||
"current_state": current_state,
|
||||
"timestamp": Time.get_unix_time_from_system()
|
||||
}
|
||||
|
||||
var file = FileAccess.open(SAVE_FILE_PATH, FileAccess.WRITE)
|
||||
if file == null:
|
||||
push_error("Failed to open save file for writing")
|
||||
return false
|
||||
|
||||
file.store_string(JSON.stringify(save_data))
|
||||
file.close()
|
||||
|
||||
if debug_mode:
|
||||
print("[GameManager] Game saved successfully")
|
||||
|
||||
return true
|
||||
|
||||
## 加载游戏数据
|
||||
func load_game() -> bool:
|
||||
if not FileAccess.file_exists(SAVE_FILE_PATH):
|
||||
if debug_mode:
|
||||
print("[GameManager] No save file found")
|
||||
return false
|
||||
|
||||
var file = FileAccess.open(SAVE_FILE_PATH, FileAccess.READ)
|
||||
if file == null:
|
||||
push_error("Failed to open save file for reading")
|
||||
return false
|
||||
|
||||
var json_text = file.get_as_text()
|
||||
file.close()
|
||||
|
||||
var json = JSON.new()
|
||||
var parse_result = json.parse(json_text)
|
||||
if parse_result != OK:
|
||||
push_error("Failed to parse save file JSON")
|
||||
return false
|
||||
|
||||
var save_data = json.data
|
||||
|
||||
# 恢复玩家数据
|
||||
if save_data.has("player_data"):
|
||||
player_data = save_data.player_data
|
||||
player_data_updated.emit(player_data)
|
||||
|
||||
# 恢复游戏时间
|
||||
if save_data.has("game_time"):
|
||||
game_time = save_data.game_time
|
||||
|
||||
if debug_mode:
|
||||
print("[GameManager] Game loaded successfully")
|
||||
|
||||
return true
|
||||
|
||||
# 私有方法
|
||||
func _initialize_manager() -> void:
|
||||
# 设置进程模式为总是处理(即使暂停时也能工作)
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
|
||||
func _setup_auto_save() -> void:
|
||||
_auto_save_timer = Timer.new()
|
||||
add_child(_auto_save_timer)
|
||||
_auto_save_timer.wait_time = auto_save_interval
|
||||
_auto_save_timer.timeout.connect(_on_auto_save_timeout)
|
||||
_auto_save_timer.start()
|
||||
|
||||
func _load_game_config() -> void:
|
||||
if not FileAccess.file_exists(CONFIG_FILE_PATH):
|
||||
push_warning("Game config file not found")
|
||||
return
|
||||
|
||||
var file = FileAccess.open(CONFIG_FILE_PATH, FileAccess.READ)
|
||||
if file == null:
|
||||
push_error("Failed to open game config file")
|
||||
return
|
||||
|
||||
var json_text = file.get_as_text()
|
||||
file.close()
|
||||
|
||||
var json = JSON.new()
|
||||
var parse_result = json.parse(json_text)
|
||||
if parse_result != OK:
|
||||
push_error("Failed to parse game config JSON")
|
||||
return
|
||||
|
||||
_game_config = json.data
|
||||
|
||||
func _enter_state(state: GameState) -> void:
|
||||
match state:
|
||||
GameState.LOADING:
|
||||
# 加载游戏资源
|
||||
pass
|
||||
GameState.MENU:
|
||||
# 显示主菜单
|
||||
pass
|
||||
GameState.PLAYING:
|
||||
# 开始游戏逻辑
|
||||
_auto_save_timer.start()
|
||||
GameState.PAUSED:
|
||||
# 暂停游戏
|
||||
_auto_save_timer.stop()
|
||||
GameState.GAME_OVER:
|
||||
# 游戏结束处理
|
||||
save_game()
|
||||
GameState.SETTINGS:
|
||||
# 显示设置界面
|
||||
pass
|
||||
|
||||
func _exit_state(state: GameState) -> void:
|
||||
match state:
|
||||
GameState.PLAYING:
|
||||
# 退出游戏时自动保存
|
||||
save_game()
|
||||
|
||||
func _on_auto_save_timeout() -> void:
|
||||
if current_state == GameState.PLAYING:
|
||||
save_game()
|
||||
```
|
||||
|
||||
## 🌐 网络管理器模板
|
||||
|
||||
### HTTP请求管理器
|
||||
```gdscript
|
||||
# _Core/managers/NetworkManager.gd
|
||||
extends Node
|
||||
|
||||
## 网络请求管理器
|
||||
## 负责处理所有HTTP请求和网络通信
|
||||
|
||||
# 信号定义
|
||||
signal request_completed(request_id: String, success: bool, data: Dictionary)
|
||||
signal connection_status_changed(is_connected: bool)
|
||||
|
||||
# 常量定义
|
||||
const BASE_URL: String = "https://api.example.com"
|
||||
const TIMEOUT_DURATION: float = 10.0
|
||||
const MAX_RETRIES: int = 3
|
||||
|
||||
# 请求状态枚举
|
||||
enum RequestStatus {
|
||||
PENDING,
|
||||
SUCCESS,
|
||||
FAILED,
|
||||
TIMEOUT,
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
# 公共变量
|
||||
var is_connected: bool = false
|
||||
var active_requests: Dictionary = {}
|
||||
|
||||
# 私有变量
|
||||
var _http_client: HTTPClient
|
||||
var _request_counter: int = 0
|
||||
|
||||
func _ready() -> void:
|
||||
_initialize_network()
|
||||
_check_connection()
|
||||
|
||||
## 发送GET请求
|
||||
func send_get_request(endpoint: String, headers: Dictionary = {}) -> String:
|
||||
return _send_request(HTTPClient.METHOD_GET, endpoint, "", headers)
|
||||
|
||||
## 发送POST请求
|
||||
func send_post_request(endpoint: String, data: Dictionary, headers: Dictionary = {}) -> String:
|
||||
var json_data = JSON.stringify(data)
|
||||
return _send_request(HTTPClient.METHOD_POST, endpoint, json_data, headers)
|
||||
|
||||
## 发送PUT请求
|
||||
func send_put_request(endpoint: String, data: Dictionary, headers: Dictionary = {}) -> String:
|
||||
var json_data = JSON.stringify(data)
|
||||
return _send_request(HTTPClient.METHOD_PUT, endpoint, json_data, headers)
|
||||
|
||||
## 发送DELETE请求
|
||||
func send_delete_request(endpoint: String, headers: Dictionary = {}) -> String:
|
||||
return _send_request(HTTPClient.METHOD_DELETE, endpoint, "", headers)
|
||||
|
||||
## 取消请求
|
||||
func cancel_request(request_id: String) -> bool:
|
||||
if not active_requests.has(request_id):
|
||||
return false
|
||||
|
||||
var request_data = active_requests[request_id]
|
||||
if request_data.http_request:
|
||||
request_data.http_request.cancel_request()
|
||||
|
||||
_cleanup_request(request_id, RequestStatus.CANCELLED)
|
||||
return true
|
||||
|
||||
## 检查网络连接状态
|
||||
func check_connection() -> void:
|
||||
_check_connection()
|
||||
|
||||
# 私有方法
|
||||
func _initialize_network() -> void:
|
||||
_http_client = HTTPClient.new()
|
||||
|
||||
func _send_request(method: HTTPClient.Method, endpoint: String, data: String, headers: Dictionary) -> String:
|
||||
var request_id = _generate_request_id()
|
||||
var full_url = BASE_URL + endpoint
|
||||
|
||||
# 创建HTTP请求节点
|
||||
var http_request = HTTPRequest.new()
|
||||
add_child(http_request)
|
||||
|
||||
# 设置请求超时
|
||||
http_request.timeout = TIMEOUT_DURATION
|
||||
|
||||
# 连接完成信号
|
||||
http_request.request_completed.connect(_on_request_completed.bind(request_id))
|
||||
|
||||
# 准备请求头
|
||||
var request_headers = ["Content-Type: application/json"]
|
||||
for key in headers:
|
||||
request_headers.append("%s: %s" % [key, headers[key]])
|
||||
|
||||
# 存储请求信息
|
||||
active_requests[request_id] = {
|
||||
"http_request": http_request,
|
||||
"method": method,
|
||||
"url": full_url,
|
||||
"status": RequestStatus.PENDING,
|
||||
"retry_count": 0,
|
||||
"start_time": Time.get_time_dict_from_system()
|
||||
}
|
||||
|
||||
# 发送请求
|
||||
var error = http_request.request(full_url, request_headers, method, data)
|
||||
if error != OK:
|
||||
push_error("Failed to send HTTP request: %d" % error)
|
||||
_cleanup_request(request_id, RequestStatus.FAILED)
|
||||
return ""
|
||||
|
||||
return request_id
|
||||
|
||||
func _generate_request_id() -> String:
|
||||
_request_counter += 1
|
||||
return "req_%d_%d" % [Time.get_time_dict_from_system().hour * 3600 + Time.get_time_dict_from_system().minute * 60 + Time.get_time_dict_from_system().second, _request_counter]
|
||||
|
||||
func _on_request_completed(request_id: String, result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
if not active_requests.has(request_id):
|
||||
return
|
||||
|
||||
var request_data = active_requests[request_id]
|
||||
var success = false
|
||||
var response_data = {}
|
||||
|
||||
# 解析响应
|
||||
if response_code >= 200 and response_code < 300:
|
||||
success = true
|
||||
var body_text = body.get_string_from_utf8()
|
||||
|
||||
if not body_text.is_empty():
|
||||
var json = JSON.new()
|
||||
var parse_result = json.parse(body_text)
|
||||
if parse_result == OK:
|
||||
response_data = json.data
|
||||
else:
|
||||
response_data = {"raw_response": body_text}
|
||||
else:
|
||||
# 处理错误响应
|
||||
var body_text = body.get_string_from_utf8()
|
||||
response_data = {
|
||||
"error": "HTTP Error %d" % response_code,
|
||||
"response_code": response_code,
|
||||
"raw_response": body_text
|
||||
}
|
||||
|
||||
# 发送完成信号
|
||||
request_completed.emit(request_id, success, response_data)
|
||||
|
||||
# 清理请求
|
||||
_cleanup_request(request_id, RequestStatus.SUCCESS if success else RequestStatus.FAILED)
|
||||
|
||||
func _cleanup_request(request_id: String, status: RequestStatus) -> void:
|
||||
if not active_requests.has(request_id):
|
||||
return
|
||||
|
||||
var request_data = active_requests[request_id]
|
||||
|
||||
# 移除HTTP请求节点
|
||||
if request_data.http_request:
|
||||
request_data.http_request.queue_free()
|
||||
|
||||
# 从活动请求中移除
|
||||
active_requests.erase(request_id)
|
||||
|
||||
func _check_connection() -> void:
|
||||
# 简单的连接检查(可以改为ping服务器)
|
||||
var old_status = is_connected
|
||||
is_connected = true # 这里可以实现实际的连接检查逻辑
|
||||
|
||||
if old_status != is_connected:
|
||||
connection_status_changed.emit(is_connected)
|
||||
```
|
||||
|
||||
## 🎵 音频管理器模板
|
||||
|
||||
### 音频系统管理器
|
||||
```gdscript
|
||||
# _Core/managers/AudioManager.gd
|
||||
extends Node
|
||||
|
||||
## 音频管理器
|
||||
## 负责管理游戏中的音乐和音效播放
|
||||
|
||||
# 信号定义
|
||||
signal music_finished()
|
||||
signal sound_effect_finished(sound_name: String)
|
||||
|
||||
# 音频类型枚举
|
||||
enum AudioType {
|
||||
MUSIC,
|
||||
SOUND_EFFECT,
|
||||
UI_SOUND,
|
||||
VOICE
|
||||
}
|
||||
|
||||
# 常量定义
|
||||
const MUSIC_PATH: String = "res://assets/audio/music/"
|
||||
const SOUND_PATH: String = "res://assets/audio/sounds/"
|
||||
const VOICE_PATH: String = "res://assets/audio/voice/"
|
||||
|
||||
# 导出变量
|
||||
@export var master_volume: float = 1.0
|
||||
@export var music_volume: float = 0.7
|
||||
@export var sfx_volume: float = 0.8
|
||||
@export var voice_volume: float = 0.9
|
||||
|
||||
# 音频播放器
|
||||
var music_player: AudioStreamPlayer
|
||||
var sfx_players: Array[AudioStreamPlayer] = []
|
||||
var voice_player: AudioStreamPlayer
|
||||
|
||||
# 当前播放状态
|
||||
var current_music: String = ""
|
||||
var is_music_playing: bool = false
|
||||
var active_sounds: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
_initialize_audio_players()
|
||||
_load_audio_settings()
|
||||
|
||||
## 播放背景音乐
|
||||
func play_music(music_name: String, fade_in: bool = true) -> void:
|
||||
var music_path = MUSIC_PATH + music_name + ".ogg"
|
||||
|
||||
if not ResourceLoader.exists(music_path):
|
||||
push_warning("Music file not found: %s" % music_path)
|
||||
return
|
||||
|
||||
# 停止当前音乐
|
||||
if is_music_playing:
|
||||
stop_music(fade_in)
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
|
||||
# 加载并播放新音乐
|
||||
var audio_stream = load(music_path)
|
||||
music_player.stream = audio_stream
|
||||
music_player.volume_db = linear_to_db(music_volume * master_volume)
|
||||
|
||||
if fade_in:
|
||||
_fade_in_music()
|
||||
else:
|
||||
music_player.play()
|
||||
|
||||
current_music = music_name
|
||||
is_music_playing = true
|
||||
|
||||
## 停止背景音乐
|
||||
func stop_music(fade_out: bool = true) -> void:
|
||||
if not is_music_playing:
|
||||
return
|
||||
|
||||
if fade_out:
|
||||
_fade_out_music()
|
||||
else:
|
||||
music_player.stop()
|
||||
is_music_playing = false
|
||||
current_music = ""
|
||||
|
||||
## 播放音效
|
||||
func play_sound_effect(sound_name: String, volume_override: float = -1.0) -> void:
|
||||
var sound_path = SOUND_PATH + sound_name + ".ogg"
|
||||
|
||||
if not ResourceLoader.exists(sound_path):
|
||||
push_warning("Sound file not found: %s" % sound_path)
|
||||
return
|
||||
|
||||
# 获取可用的音效播放器
|
||||
var player = _get_available_sfx_player()
|
||||
if player == null:
|
||||
push_warning("No available sound effect player")
|
||||
return
|
||||
|
||||
# 加载并播放音效
|
||||
var audio_stream = load(sound_path)
|
||||
player.stream = audio_stream
|
||||
|
||||
var final_volume = volume_override if volume_override > 0 else sfx_volume
|
||||
player.volume_db = linear_to_db(final_volume * master_volume)
|
||||
|
||||
player.play()
|
||||
active_sounds[sound_name] = player
|
||||
|
||||
## 设置主音量
|
||||
func set_master_volume(volume: float) -> void:
|
||||
master_volume = clamp(volume, 0.0, 1.0)
|
||||
_update_all_volumes()
|
||||
|
||||
## 设置音乐音量
|
||||
func set_music_volume(volume: float) -> void:
|
||||
music_volume = clamp(volume, 0.0, 1.0)
|
||||
if is_music_playing:
|
||||
music_player.volume_db = linear_to_db(music_volume * master_volume)
|
||||
|
||||
## 设置音效音量
|
||||
func set_sfx_volume(volume: float) -> void:
|
||||
sfx_volume = clamp(volume, 0.0, 1.0)
|
||||
_update_sfx_volumes()
|
||||
|
||||
# 私有方法
|
||||
func _initialize_audio_players() -> void:
|
||||
# 创建音乐播放器
|
||||
music_player = AudioStreamPlayer.new()
|
||||
add_child(music_player)
|
||||
music_player.finished.connect(_on_music_finished)
|
||||
|
||||
# 创建多个音效播放器(支持同时播放多个音效)
|
||||
for i in range(8):
|
||||
var sfx_player = AudioStreamPlayer.new()
|
||||
add_child(sfx_player)
|
||||
sfx_players.append(sfx_player)
|
||||
|
||||
# 创建语音播放器
|
||||
voice_player = AudioStreamPlayer.new()
|
||||
add_child(voice_player)
|
||||
|
||||
func _get_available_sfx_player() -> AudioStreamPlayer:
|
||||
for player in sfx_players:
|
||||
if not player.playing:
|
||||
return player
|
||||
return null
|
||||
|
||||
func _fade_in_music() -> void:
|
||||
music_player.volume_db = linear_to_db(0.01)
|
||||
music_player.play()
|
||||
|
||||
var tween = create_tween()
|
||||
tween.tween_method(_set_music_volume_db, 0.01, music_volume * master_volume, 1.0)
|
||||
|
||||
func _fade_out_music() -> void:
|
||||
var tween = create_tween()
|
||||
tween.tween_method(_set_music_volume_db, music_volume * master_volume, 0.01, 1.0)
|
||||
tween.tween_callback(_stop_music_after_fade)
|
||||
|
||||
func _set_music_volume_db(volume: float) -> void:
|
||||
music_player.volume_db = linear_to_db(volume)
|
||||
|
||||
func _stop_music_after_fade() -> void:
|
||||
music_player.stop()
|
||||
is_music_playing = false
|
||||
current_music = ""
|
||||
|
||||
func _update_all_volumes() -> void:
|
||||
if is_music_playing:
|
||||
music_player.volume_db = linear_to_db(music_volume * master_volume)
|
||||
_update_sfx_volumes()
|
||||
|
||||
func _update_sfx_volumes() -> void:
|
||||
for player in sfx_players:
|
||||
if player.playing:
|
||||
player.volume_db = linear_to_db(sfx_volume * master_volume)
|
||||
|
||||
func _load_audio_settings() -> void:
|
||||
# 从配置文件加载音频设置
|
||||
pass
|
||||
|
||||
func _on_music_finished() -> void:
|
||||
is_music_playing = false
|
||||
current_music = ""
|
||||
music_finished.emit()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**🎯 使用说明**:
|
||||
1. 选择合适的管理器模板
|
||||
2. 根据具体需求修改类名和功能
|
||||
3. 确保在project.godot中配置为AutoLoad
|
||||
4. 遵循项目的命名规范和代码风格
|
||||
5. 添加必要的错误处理和日志记录
|
||||
Reference in New Issue
Block a user