Files
agame/scripts/player/voice_chat.gd
T
2026-09-13 02:09:22 +08:00

56 lines
2.1 KiB
GDScript

extends Node
## 最简近距离语音:按住 C(talk)采集麦克风 → 广播给所有对端 →
## 在说话者位置的 RaytracedAudioPlayer3D 回放。
## 距离衰减/声像由 3D 音频完成,墙体闷音、环境混响由 raytraced_audio 插件完成。
## 依赖:default_bus_layout.tres 的 mic 总线(静音 + AudioEffectCapture)、
## RaytracedReverb/RaytracedAmbient 总线(插件用)、player.tscn 根节点下的 Voice 节点。
@export var max_distance := 25.0
var _capture: AudioEffectCapture
var _voice_3d: RaytracedAudioPlayer3D
var _playback: AudioStreamGeneratorPlayback
func _ready() -> void:
_capture = AudioServer.get_bus_effect(AudioServer.get_bus_index("mic"), 0)
_voice_3d = RaytracedAudioPlayer3D.new()
var gen := AudioStreamGenerator.new()
gen.mix_rate = 48000.0 # 与 audio/driver/mix_rate 一致,否则变速变调
_voice_3d.stream = gen
_voice_3d.position = Vector3(0, 1.5, 0) # 嘴部高度
_voice_3d.max_distance = max_distance
add_child(_voice_3d)
if is_multiplayer_authority() and DisplayServer.get_name() != "headless":
var mic := AudioStreamPlayer.new()
mic.stream = AudioStreamMicrophone.new()
mic.bus = "mic"
add_child(mic)
mic.play()
# 监听器只建在本地玩家身上(多个监听器会互相抢 current)
var listener := RaytracedAudioListener.new()
listener.position = Vector3(0, 1.6, 0) # 头部高度
add_child(listener)
listener.owner = get_parent() # 插件的 ambient 计算会读 owner,代码创建的节点必须手动指定
else:
_voice_3d.play()
_playback = _voice_3d.get_stream_playback()
func _process(_delta: float) -> void:
if not is_multiplayer_authority() or _capture == null:
return
if not Input.is_action_pressed("talk"):
_capture.clear_buffer() # 松开时丢弃积压,避免下次开口带旧音频
return
var frames := _capture.get_buffer(_capture.get_frames_available())
if not frames.is_empty():
receive_voice.rpc(frames)
@rpc("any_peer", "call_remote", "unreliable_ordered", 1)
func receive_voice(frames: PackedVector2Array) -> void:
if not _playback:
_playback = _voice_3d.get_stream_playback()
_playback.push_buffer(frames)