feat(Dialogue): 实现基本的对话系统
This commit is contained in:
155
Assets/Script/Gameplay/Player/PlayerDialogueController.cs
Normal file
155
Assets/Script/Gameplay/Player/PlayerDialogueController.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Script.Gameplay.Interface;
|
||||
using Script.Gameplay.Input;
|
||||
using UI;
|
||||
|
||||
namespace Script.Gameplay.Player
|
||||
{
|
||||
public class PlayerDialogueController : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private bool isEnablePlayerDialogue = true; // 是否启用玩家对话功能
|
||||
[SerializeField] private FirstPersonRaycaster firstPersonRaycaster; // 第一人称射线检测器
|
||||
private bool isReadingDialogue = false;
|
||||
public event Action OnPlayerBeginDialogue;
|
||||
public event Action OnPlayerEndDialogue;
|
||||
public event Action<GameObject> OnGazeEnterDialogue;
|
||||
public event Action<GameObject> OnGazeExitDialogue;
|
||||
private Queue<string> dialogueQueue = new Queue<string>();
|
||||
private PlayerDialogueViewer playerDialogueViewer;
|
||||
|
||||
private IDialogue previousDialogue;
|
||||
private IDialogue currentDialogueTarget;
|
||||
public IDialogue CurrentDialogueTarget
|
||||
{
|
||||
get => currentDialogueTarget;
|
||||
set
|
||||
{
|
||||
previousDialogue = currentDialogueTarget;
|
||||
currentDialogueTarget = value;
|
||||
if (previousDialogue != currentDialogueTarget)
|
||||
{
|
||||
if (currentDialogueTarget != null)
|
||||
{
|
||||
OnGazeEnterDialogue?.Invoke((currentDialogueTarget as MonoBehaviour)?.gameObject);
|
||||
}
|
||||
if (previousDialogue != null)
|
||||
{
|
||||
OnGazeExitDialogue?.Invoke((previousDialogue as MonoBehaviour)?.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (firstPersonRaycaster == null)
|
||||
firstPersonRaycaster = GetComponent<FirstPersonRaycaster>() ??
|
||||
GetComponentInChildren<FirstPersonRaycaster>();
|
||||
if (firstPersonRaycaster == null)
|
||||
firstPersonRaycaster = FindObjectOfType<FirstPersonRaycaster>();
|
||||
if (firstPersonRaycaster == null)
|
||||
Debug.LogWarning("FirstPersonRaycaster not found! Please assign or add it to the player.");
|
||||
|
||||
var input = InputManager.Instance.Input;
|
||||
input.Player.Interact.performed += ctx =>
|
||||
{
|
||||
if (!isEnablePlayerDialogue) return;
|
||||
if (CurrentDialogueTarget == null) return;
|
||||
if (isReadingDialogue) return;
|
||||
BeginDialogue();
|
||||
};
|
||||
input.Player.ShowNextDialogue.performed += ctx =>
|
||||
{
|
||||
if (!isEnablePlayerDialogue) return;
|
||||
if (CurrentDialogueTarget == null) return;
|
||||
if (!isReadingDialogue) return;
|
||||
PassNextDialogue();
|
||||
};
|
||||
|
||||
ControllerLocator.Instance.Register(this);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
DetectDialogue();
|
||||
}
|
||||
|
||||
private void DetectDialogue()
|
||||
{
|
||||
if (!isEnablePlayerDialogue || firstPersonRaycaster == null) return;
|
||||
GameObject lookAtObj = firstPersonRaycaster.CurrentLookAtObject;
|
||||
IDialogue hitDialogue = lookAtObj != null ? lookAtObj.GetComponent<IDialogue>() : null;
|
||||
CurrentDialogueTarget = hitDialogue;
|
||||
}
|
||||
|
||||
private void BeginDialogue()
|
||||
{
|
||||
if (CurrentDialogueTarget != null)
|
||||
{
|
||||
isReadingDialogue = true;
|
||||
OnPlayerBeginDialogue?.Invoke();
|
||||
CurrentDialogueTarget.OnBeginDialogue?.Invoke(true);
|
||||
dialogueQueue = SplitDialogue(CurrentDialogueTarget.DialogueContent);
|
||||
PassNextDialogue();
|
||||
}
|
||||
}
|
||||
|
||||
private void PassNextDialogue()
|
||||
{
|
||||
if (CurrentDialogueTarget != null)
|
||||
{
|
||||
if (dialogueQueue.Count == 0)
|
||||
{
|
||||
EndDialogue();
|
||||
return;
|
||||
}
|
||||
|
||||
string nextDialogue = dialogueQueue.Dequeue();
|
||||
if (playerDialogueViewer != null)
|
||||
{
|
||||
playerDialogueViewer.ReceiveDialogue(nextDialogue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EndDialogue()
|
||||
{
|
||||
if (CurrentDialogueTarget != null)
|
||||
{
|
||||
isReadingDialogue = false;
|
||||
OnPlayerEndDialogue?.Invoke();
|
||||
CurrentDialogueTarget.OnEndDialogue?.Invoke(true);
|
||||
if (playerDialogueViewer != null)
|
||||
{
|
||||
playerDialogueViewer.ClosePanel();
|
||||
}
|
||||
// 可扩展:隐藏UI、恢复输入等
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<string> SplitDialogue(string content)
|
||||
{
|
||||
// 按换行分段
|
||||
return new(content.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
|
||||
public void RegisterDialogueViewer(PlayerDialogueViewer viewer)
|
||||
{
|
||||
playerDialogueViewer = viewer;
|
||||
}
|
||||
|
||||
public void SetPlayerDialogueEnabled(bool isEnabled)
|
||||
{
|
||||
isEnablePlayerDialogue = isEnabled;
|
||||
if (!isEnabled)
|
||||
{
|
||||
CurrentDialogueTarget = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user