优化项目架构
This commit is contained in:
242
SproutFarm-Frontend/Components/GameBGMPlayer.gd
Normal file
242
SproutFarm-Frontend/Components/GameBGMPlayer.gd
Normal file
@@ -0,0 +1,242 @@
|
||||
extends Node
|
||||
|
||||
## 简单背景音乐播放器 - 支持多平台导出
|
||||
## 自动加载音乐文件,支持顺序和随机循环播放
|
||||
|
||||
# 播放模式
|
||||
enum PlayMode {
|
||||
SEQUENTIAL, # 顺序循环
|
||||
RANDOM # 随机循环
|
||||
}
|
||||
|
||||
# 配置
|
||||
@export var play_mode: PlayMode = PlayMode.SEQUENTIAL # 播放模式
|
||||
@export var auto_start: bool = true # 自动开始播放
|
||||
|
||||
# 预设音乐文件列表(用于导出版本)
|
||||
@export var music_files_list: Array[String] = [
|
||||
]
|
||||
|
||||
# 内部变量
|
||||
var audio_player: AudioStreamPlayer
|
||||
var music_files: Array[String] = []
|
||||
var current_index: int = 0
|
||||
var played_indices: Array[int] = [] # 随机模式已播放的索引
|
||||
|
||||
# 音量控制相关
|
||||
var current_volume: float = 1.0 # 当前音量 (0.0-1.0)
|
||||
var is_muted: bool = false # 是否静音
|
||||
var volume_before_mute: float = 1.0 # 静音前的音量
|
||||
|
||||
func _ready():
|
||||
# 创建音频播放器
|
||||
audio_player = AudioStreamPlayer.new()
|
||||
add_child(audio_player)
|
||||
audio_player.finished.connect(_on_music_finished)
|
||||
|
||||
# 从全局变量读取初始音量设置
|
||||
current_volume = GlobalVariables.BackgroundMusicVolume
|
||||
audio_player.volume_db = linear_to_db(current_volume)
|
||||
|
||||
# 加载音乐文件
|
||||
_load_music_files()
|
||||
|
||||
# 自动开始播放
|
||||
if auto_start and music_files.size() > 0:
|
||||
play_next()
|
||||
|
||||
func _load_music_files():
|
||||
"""加载音乐文件"""
|
||||
music_files.clear()
|
||||
|
||||
# 在编辑器中尝试动态加载文件夹
|
||||
if OS.has_feature("editor"):
|
||||
_load_from_folder()
|
||||
|
||||
# 如果没有找到文件或者是导出版本,使用预设列表
|
||||
if music_files.size() == 0:
|
||||
_load_from_preset_list()
|
||||
|
||||
func _load_from_folder():
|
||||
"""从文件夹动态加载(仅编辑器模式)"""
|
||||
var music_folder = "res://audio/music/"
|
||||
var dir = DirAccess.open(music_folder)
|
||||
|
||||
if dir:
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
|
||||
while file_name != "":
|
||||
if not dir.current_is_dir():
|
||||
var extension = file_name.get_extension().to_lower()
|
||||
# 支持常见音频格式
|
||||
if extension in ["mp3", "ogg", "wav"]:
|
||||
music_files.append(music_folder + file_name)
|
||||
print("动态加载音乐: ", file_name)
|
||||
file_name = dir.get_next()
|
||||
|
||||
print("动态加载了 ", music_files.size(), " 首音乐")
|
||||
|
||||
func _load_from_preset_list():
|
||||
"""从预设列表加载音乐"""
|
||||
for file_path in music_files_list:
|
||||
if ResourceLoader.exists(file_path):
|
||||
music_files.append(file_path)
|
||||
print("预设加载音乐: ", file_path.get_file())
|
||||
else:
|
||||
print("音乐文件不存在: ", file_path)
|
||||
|
||||
print("预设加载了 ", music_files.size(), " 首音乐")
|
||||
|
||||
func play_next():
|
||||
"""播放下一首音乐"""
|
||||
if music_files.size() == 0:
|
||||
print("没有音乐文件可播放")
|
||||
return
|
||||
|
||||
# 根据播放模式获取下一首音乐的索引
|
||||
match play_mode:
|
||||
PlayMode.SEQUENTIAL:
|
||||
current_index = (current_index + 1) % music_files.size()
|
||||
PlayMode.RANDOM:
|
||||
current_index = _get_random_index()
|
||||
|
||||
# 播放音乐
|
||||
_play_music(current_index)
|
||||
|
||||
func _get_random_index() -> int:
|
||||
"""获取随机音乐索引(避免重复直到所有歌曲都播放过)"""
|
||||
# 如果所有歌曲都播放过了,重置列表
|
||||
if played_indices.size() >= music_files.size():
|
||||
played_indices.clear()
|
||||
|
||||
# 获取未播放的歌曲索引
|
||||
var available_indices: Array[int] = []
|
||||
for i in range(music_files.size()):
|
||||
if i not in played_indices:
|
||||
available_indices.append(i)
|
||||
|
||||
# 随机选择一个
|
||||
if available_indices.size() > 0:
|
||||
var random_choice = available_indices[randi() % available_indices.size()]
|
||||
played_indices.append(random_choice)
|
||||
return random_choice
|
||||
|
||||
return 0
|
||||
|
||||
func _play_music(index: int):
|
||||
"""播放指定索引的音乐"""
|
||||
if index < 0 or index >= music_files.size():
|
||||
return
|
||||
|
||||
var music_path = music_files[index]
|
||||
var audio_stream = load(music_path)
|
||||
|
||||
if audio_stream:
|
||||
audio_player.stream = audio_stream
|
||||
audio_player.play()
|
||||
print("正在播放: ", music_path.get_file())
|
||||
else:
|
||||
print("加载音乐失败: ", music_path)
|
||||
|
||||
func _on_music_finished():
|
||||
"""音乐播放完成时自动播放下一首"""
|
||||
play_next()
|
||||
|
||||
# 公共接口方法
|
||||
func set_play_mode(mode: PlayMode):
|
||||
"""设置播放模式"""
|
||||
play_mode = mode
|
||||
played_indices.clear() # 重置随机播放历史
|
||||
print("播放模式设置为: ", "顺序循环" if mode == PlayMode.SEQUENTIAL else "随机循环")
|
||||
|
||||
func toggle_play_mode():
|
||||
"""切换播放模式"""
|
||||
if play_mode == PlayMode.SEQUENTIAL:
|
||||
set_play_mode(PlayMode.RANDOM)
|
||||
else:
|
||||
set_play_mode(PlayMode.SEQUENTIAL)
|
||||
|
||||
func get_current_music_name() -> String:
|
||||
"""获取当前播放的音乐名称"""
|
||||
if current_index >= 0 and current_index < music_files.size():
|
||||
return music_files[current_index].get_file()
|
||||
return ""
|
||||
|
||||
# 运行时添加音乐文件(用于用户自定义音乐)
|
||||
func add_music_file(file_path: String) -> bool:
|
||||
"""添加音乐文件到播放列表"""
|
||||
if ResourceLoader.exists(file_path):
|
||||
music_files.append(file_path)
|
||||
print("添加音乐: ", file_path.get_file())
|
||||
return true
|
||||
else:
|
||||
print("音乐文件不存在: ", file_path)
|
||||
return false
|
||||
|
||||
# ============================= 音量控制功能 =====================================
|
||||
|
||||
func set_volume(volume: float):
|
||||
"""设置音量 (0.0-1.0)"""
|
||||
current_volume = clamp(volume, 0.0, 1.0)
|
||||
if not is_muted:
|
||||
audio_player.volume_db = linear_to_db(current_volume)
|
||||
print("背景音乐音量设置为: ", current_volume)
|
||||
|
||||
func get_volume() -> float:
|
||||
"""获取当前音量"""
|
||||
return current_volume
|
||||
|
||||
func set_mute(muted: bool):
|
||||
"""设置静音状态"""
|
||||
if muted and not is_muted:
|
||||
# 静音
|
||||
volume_before_mute = current_volume
|
||||
audio_player.volume_db = -80.0 # 设置为最小音量
|
||||
is_muted = true
|
||||
print("背景音乐已静音")
|
||||
elif not muted and is_muted:
|
||||
# 取消静音
|
||||
audio_player.volume_db = linear_to_db(current_volume)
|
||||
is_muted = false
|
||||
print("背景音乐取消静音")
|
||||
|
||||
func toggle_mute():
|
||||
"""切换静音状态"""
|
||||
set_mute(not is_muted)
|
||||
|
||||
func is_music_muted() -> bool:
|
||||
"""获取静音状态"""
|
||||
return is_muted
|
||||
|
||||
func pause():
|
||||
"""暂停音乐"""
|
||||
if audio_player.playing:
|
||||
audio_player.stream_paused = true
|
||||
print("背景音乐已暂停")
|
||||
|
||||
func resume():
|
||||
"""恢复音乐"""
|
||||
if audio_player.stream_paused:
|
||||
audio_player.stream_paused = false
|
||||
print("背景音乐已恢复")
|
||||
|
||||
func stop():
|
||||
"""停止音乐"""
|
||||
if audio_player.playing:
|
||||
audio_player.stop()
|
||||
print("背景音乐已停止")
|
||||
|
||||
func is_playing() -> bool:
|
||||
"""检查是否正在播放"""
|
||||
return audio_player.playing and not audio_player.stream_paused
|
||||
|
||||
func get_current_position() -> float:
|
||||
"""获取当前播放位置(秒)"""
|
||||
return audio_player.get_playback_position()
|
||||
|
||||
func get_current_length() -> float:
|
||||
"""获取当前音乐总长度(秒)"""
|
||||
if audio_player.stream:
|
||||
return audio_player.stream.get_length()
|
||||
return 0.0
|
||||
1
SproutFarm-Frontend/Components/GameBGMPlayer.gd.uid
Normal file
1
SproutFarm-Frontend/Components/GameBGMPlayer.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ca2chgx5w3g1y
|
||||
106
SproutFarm-Frontend/Components/HTTPTextureRect.gd
Normal file
106
SproutFarm-Frontend/Components/HTTPTextureRect.gd
Normal file
@@ -0,0 +1,106 @@
|
||||
extends TextureRect
|
||||
class_name HTTPTextureRect
|
||||
|
||||
signal loading_started
|
||||
signal loading_finished(success: bool)
|
||||
|
||||
# HTTP请求节点
|
||||
var http_request: HTTPRequest
|
||||
|
||||
func _ready():
|
||||
# 创建HTTP请求节点
|
||||
http_request = HTTPRequest.new()
|
||||
add_child(http_request)
|
||||
|
||||
# 连接信号
|
||||
http_request.request_completed.connect(_on_request_completed)
|
||||
|
||||
# 从URL加载图像
|
||||
func load_from_url(url: String, custom_headers: Array = []) -> void:
|
||||
if url.is_empty():
|
||||
push_error("HTTPTextureRect: URL不能为空")
|
||||
loading_finished.emit(false)
|
||||
return
|
||||
|
||||
loading_started.emit()
|
||||
|
||||
# 发起HTTP请求
|
||||
var error = http_request.request(url, custom_headers)
|
||||
if error != OK:
|
||||
push_error("HTTPTextureRect: 发起HTTP请求失败,错误码: " + str(error))
|
||||
loading_finished.emit(false)
|
||||
|
||||
# HTTP请求完成的回调函数
|
||||
func _on_request_completed(result, response_code, headers, body):
|
||||
if result != HTTPRequest.RESULT_SUCCESS:
|
||||
push_error("HTTPTextureRect: HTTP请求失败,错误码: " + str(result))
|
||||
loading_finished.emit(false)
|
||||
return
|
||||
|
||||
if response_code != 200:
|
||||
push_error("HTTPTextureRect: HTTP请求返回非200状态码: " + str(response_code))
|
||||
loading_finished.emit(false)
|
||||
return
|
||||
|
||||
# 检查内容类型
|
||||
var content_type = ""
|
||||
for header in headers:
|
||||
if header.to_lower().begins_with("content-type:"):
|
||||
content_type = header.substr(13).strip_edges().to_lower()
|
||||
print("HTTPTextureRect: 内容类型: ", content_type)
|
||||
break
|
||||
|
||||
# 创建图像
|
||||
var image = Image.new()
|
||||
var error = ERR_INVALID_DATA
|
||||
|
||||
# 根据内容类型选择加载方法
|
||||
if content_type.begins_with("image/jpeg") or content_type.begins_with("image/jpg"):
|
||||
error = image.load_jpg_from_buffer(body)
|
||||
elif content_type.begins_with("image/png"):
|
||||
error = image.load_png_from_buffer(body)
|
||||
elif content_type.begins_with("image/webp"):
|
||||
error = image.load_webp_from_buffer(body)
|
||||
elif content_type.begins_with("image/bmp"):
|
||||
error = image.load_bmp_from_buffer(body)
|
||||
else:
|
||||
# 未知内容类型,尝试常见格式
|
||||
error = image.load_jpg_from_buffer(body)
|
||||
if error != OK:
|
||||
error = image.load_png_from_buffer(body)
|
||||
if error != OK:
|
||||
error = image.load_webp_from_buffer(body)
|
||||
if error != OK:
|
||||
error = image.load_bmp_from_buffer(body)
|
||||
|
||||
# 检查加载结果
|
||||
if error != OK:
|
||||
push_error("HTTPTextureRect: 无法加载图像,错误码: " + str(error))
|
||||
loading_finished.emit(false)
|
||||
return
|
||||
|
||||
# 创建纹理并应用
|
||||
var texture = ImageTexture.create_from_image(image)
|
||||
self.texture = texture
|
||||
print("HTTPTextureRect: 图像加载成功,尺寸: ", image.get_width(), "x", image.get_height())
|
||||
loading_finished.emit(true)
|
||||
|
||||
# 加载QQ头像的便捷方法
|
||||
func load_qq_avatar(qq_number: String) -> void:
|
||||
if not qq_number.is_valid_int():
|
||||
push_error("HTTPTextureRect: QQ号必须为纯数字")
|
||||
loading_finished.emit(false)
|
||||
return
|
||||
|
||||
# 使用QQ头像API
|
||||
#var url = "https://q.qlogo.cn/headimg_dl?dst_uin=" + qq_number + "&spec=640&img_type=png"
|
||||
var url = "http://q1.qlogo.cn/g?b=qq&nk="+qq_number+"&s=100"
|
||||
|
||||
# 添加浏览器模拟头
|
||||
var headers = [
|
||||
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||
"Accept: image/png,image/jpeg,image/webp,image/*,*/*;q=0.8"
|
||||
]
|
||||
|
||||
# 加载图像
|
||||
load_from_url(url, headers)
|
||||
1
SproutFarm-Frontend/Components/HTTPTextureRect.gd.uid
Normal file
1
SproutFarm-Frontend/Components/HTTPTextureRect.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://0d2j5m6j2ema
|
||||
1
SproutFarm-Frontend/Components/ToastShow.gd.uid
Normal file
1
SproutFarm-Frontend/Components/ToastShow.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://caly13tf4ni1d
|
||||
Reference in New Issue
Block a user