120 lines
3.7 KiB
C#
120 lines
3.7 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using Cielonos.Core.Interaction;
|
||
using Cielonos.MainGame.Narrative;
|
||
using Sirenix.OdinInspector;
|
||
using SLSUtilities.FunctionalAnimation;
|
||
using SLSUtilities.Narrative;
|
||
using UnityEngine;
|
||
|
||
namespace Cielonos.MainGame.Interactions
|
||
{
|
||
/// <summary>
|
||
/// NPC 交互基类。
|
||
/// 场景中的 NPC 挂载此脚本,交互时会自动将自身的 storyId 投递给 StoryDirector。
|
||
/// </summary>
|
||
public class NpcBase : InteractableObjectBase, IFuncAnimExecutor
|
||
{
|
||
#region Fields & Properties
|
||
|
||
[SerializeField]
|
||
protected CharacterData characterData;
|
||
|
||
[TitleGroup("Story Settings", "剧情与对话路由配置", Alignment = TitleAlignments.Centered)]
|
||
[SerializeField]
|
||
[Tooltip("对应 NarrativeEntry 剧情路由表中的 storyId (如 'OldMan')")]
|
||
protected string storyId;
|
||
|
||
/// <summary>NPC 的唯一故事 ID。</summary>
|
||
public string StoryId => storyId;
|
||
|
||
[TitleGroup("Components & References", "组件与动作数据引用", Alignment = TitleAlignments.Centered)]
|
||
public Animator animator;
|
||
public AnimatorOverrideController animatorOverride;
|
||
public FuncAnimDataCollection fullBodyFuncAnims;
|
||
public VFXData vfxData;
|
||
|
||
/// <summary>NPC 专属动画播放子模块</summary>
|
||
[HideInInspector]
|
||
public NpcFuncAnimSubmodule funcAnimSm;
|
||
|
||
/// <summary>全局活跃 NPC 查找注册表 (通过 StoryId 和 GameObject 名字进行双重映射查找)</summary>
|
||
|
||
#endregion
|
||
|
||
#region IFuncAnimExecutor Implementation
|
||
|
||
public Transform Transform => transform;
|
||
public Vector3 CenterPosition => transform.position;
|
||
public Animator Animator => animator;
|
||
|
||
#endregion
|
||
|
||
#region Lifecycle Callbacks
|
||
|
||
protected virtual void OnEnable()
|
||
{
|
||
StoryDirector.ActiveNpcs[characterData.nameKey] = this;
|
||
}
|
||
|
||
protected virtual void OnDisable()
|
||
{
|
||
StoryDirector.ActiveNpcs.Remove(characterData.nameKey);
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
funcAnimSm = new NpcFuncAnimSubmodule(this);
|
||
if (fullBodyFuncAnims != null && fullBodyFuncAnims.animDataList != null)
|
||
{
|
||
foreach (FuncAnimData animData in fullBodyFuncAnims.animDataList)
|
||
{
|
||
funcAnimSm.Add(animData);
|
||
}
|
||
}
|
||
}
|
||
|
||
protected virtual void Update()
|
||
{
|
||
// 推进时间、检测非循环动作结束以及执行重置
|
||
funcAnimSm?.Update(Time.deltaTime);
|
||
}
|
||
|
||
protected virtual void LateUpdate()
|
||
{
|
||
// 在物理表现计算完毕后分发每帧的特效/音效等事件
|
||
funcAnimSm?.UpdateEvents();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Interaction Logic
|
||
|
||
protected override void InitializeChoices()
|
||
{
|
||
choices.Add(new InteractionChoice("Talk", Talk));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发对话。NPC 并不感知具体播放哪个 Yarn 节点,完全委托给 StoryDirector 进行状态评估。
|
||
/// </summary>
|
||
public virtual void Talk()
|
||
{
|
||
if (string.IsNullOrEmpty(storyId))
|
||
{
|
||
Debug.LogWarning($"[NpcBase] {gameObject.name} 未配置 storyId,无法启动对话。");
|
||
return;
|
||
}
|
||
|
||
if (StoryDirector.Instance == null)
|
||
{
|
||
Debug.LogError("[NpcBase] 启动对话失败:场景中未找到 StoryDirector 实例!");
|
||
return;
|
||
}
|
||
|
||
StoryDirector.Instance.PlayStory(storyId);
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |