当前位置: 首页 > news >正文

Unity功能模块一对话系统(5)-完善对话流程及功能

       现在我们的文本仍然是单句显示的,这还不是我们想要的效果,本期让我们完善对话流程,并使用SO(ScriptableObject)来进行一段对话序列的配置和动态加载。

最终效果功能展示

功能一:场景内可根据资源序号ID来动态加载对话数据

功能二:按空格进行下一句,如果当前对话未打印完毕,按下空格则快进直接显示全部文本。

功能三:支持对话中可委托触发

这里碍于篇幅我会分析几个类中的部分关键点,关键的功能源码在后文。

一.功能分析

1.DialogueDataSequence

对话数据SO类

职能

创建SO文件,易于配置对话数据。

注意类前需要使用特性[CreateAssetMenu()]

示例代码

[CreateAssetMenu(menuName = "编辑器界面的创建目录", fileName = "创建出的文件的名称")]

using System;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "Data/DialogueDataLine", fileName = "DialogueDataLine")]
public class DialogueDataSequence : ScriptableObject
{
    [Header("对话线ID")]
    public int ID;
    [Header("自动下一句")]
    public bool canAutonNext;
    [Header("可以快进")]
    public bool canQuickShow;
    [Header("打字机效果")]
    public bool needTyping;
    [Header("当前话语序号")]
    public int currentIndex;
    [Header("淡入时间")]
    public float fadeDuration;
    [Header("对话线")]
    public List<DialogueData> dialogueLine = new List<DialogueData>();
}

[Serializable]
public class DialogueData
{
    [Header("讲述者")]
    public string speaker;
    [Multiline]
    [Header("内容文本")]
    public string content;
}

2.DialoguePanel 

对话框面板

对话框面板无需考虑当前是哪个对话序列,只接收显示的必要信息。

职能

显示一句对话文本信息

关键字段

  [Header("高级文本")]
  public AdvancedText displayText;
  [Header("文本讲述者")]
  public TMP_Text speakerNameText;

  //可以快速显示
  bool _canQuickShow;
  //自动显示下一句
  bool _canAutonNext;

关键函数

  /// <summary>
  /// 显示对话框
  /// </summary>
  /// <param name="speaker">讲述者</param>
  /// <param name="content">内容</param>
  /// <param name="needTyping">使用打字机效果</param>
  /// <param name="fadeDuration">打印间隔</param>
  /// <param name="canQickShow">快速显示</param>
  /// <param name="canAutonNext">自动下一句</param>

  public void ShowDialogue(string speaker, string content, bool needTyping = true, float fadeDuration = 0.2f, bool canQickShow = true, bool canAutonNext = false) 
  {
      speakerNameText.text = speaker;
      speakerNameText.GetComponent<Animator>().SetTrigger("Show");
      
      if (displayText.text != "") 
      {
          displayText.TextDisAppear();
      }

      _canQuickShow = canQickShow;
      _canAutonNext = canAutonNext;

      if (needTyping)
          StartCoroutine(displayText.ShowText(content,E_DisplayType.Typing,fadeDuration));
      else
          StartCoroutine(displayText.ShowText(content,E_DisplayType.Fading,fadeDuration));
  }

3.DialogueManager

对话管理器

职能

1.持有对当前对话序列SO文件的引用。通过外部传入的序列号加载目标对话序列。

2.开启和关闭对话框面板。

3.操作当前的对话序列(如添加对话委托)

关键字段

  [Header("当前进行的对话序列")]
  public DialogueDataSequence currentDialogueSq;

  DialoguePanel D_Panel;//对话面板

功能函数

1.BeginDialogueSequence()

暴露开始对话及开始下一句对话接口,若下一句已经是最后一句,则关闭对话框。

  /// <summary>
  /// 根据对话线的ID开启一段对话
  /// </summary>
  /// <param name="ID">对话线序号</param>
  /// <param name="action">对话委托</param>
  public void BeginDialogueSequence(int ID, Action action = null)
  {
       //加载目标对话序列,获得其引用
       currentDialogueSq = Resources.Load<DialogueDataSequence>("StaticData/DialogueLine/" + ID);
  currentDialogueSq.currentIndex = 0;

      action?.Invoke();//接收对话委托
      UIManager.Instance.ShowPanel<DialoguePanel>(panel =>
      {
//开启对话框,从对话序列的开始显示对话文本内容
          panel.ShowDialogue(currentDialogueSq.dialogueLine[currentDialogueSq.currentIndex].speaker,                                          
                   currentDialogueSq.dialogueLine[currentDialogueSq.currentIndex].content,
                   currentDialogueSq.needTyping, currentDialogueSq.fadeDuration,
                   currentDialogueSq.canQuickShow, currentDialogueSq.canAutonNext);
           D_Panel = panel;
      });
      Debug.Log(currentDialogueSq.currentIndex);
      ActionCheck();
  }


  /// <summary>
  /// 每结束一句对话时检查是否有委托需要执行
  /// </summary>
  void ActionCheck()
  {
      for (int i = 0; i < currentDialogueSq.eventList.Count; i++)
      {
          if (currentDialogueSq.eventList[i].eventIndex == currentDialogueSq.currentIndex)
          {
              currentDialogueSq.eventList[i].MyEvent?.Invoke();
              Debug.Log("检测到对话事件触发!");
          }
      }
  }

2.NextDialogue()

当前对话序列内索引自增,显示下一句对话

  /// <summary>
  /// 用于隐藏当前文本并显示下一句文本,用于外部调用(如面板或按钮)
  /// </summary>
  public IEnumerator NextDialogue()
  {
      if (currentDialogueSq.currentIndex + 1 < currentDialogueSq.dialogueLine.Count)
      {
          currentDialogueSq.currentIndex++;
          D_Panel.ShowDialogue(currentDialogueSq.dialogueLine[currentDialogueSq.currentIndex].speaker,
                                                         currentDialogueSq.dialogueLine[currentDialogueSq.currentIndex].content,
                                                        currentDialogueSq.needTyping, currentDialogueSq.fadeDuration,
                                                        currentDialogueSq.canQuickShow, currentDialogueSq.canAutonNext);
          Debug.Log(currentDialogueSq.currentIndex);
          ActionCheck();
      }
      else
      {
          EndDialogueSquence();//结束对话
      }

      yield return null;
  }


  /// <summary>
  /// 结束一段对话序列
  /// </summary>
  void EndDialogueSquence()
  {
      currentDialogueSq?.eventList.Clear();
      currentDialogueSq = null;
      UIManager.Instance.HidePanel<DialoguePanel>();
  }

//退出游戏重置对话状态
private void OnApplicationQuit()
{
    currentDialogueSq?.eventList.Clear();
    currentDialogueSq = null;
}

       由于我个人框架的实现与UI部分联系密切的问题,考虑给出完整源码文章会过于冗余且侧重点失衡,上面分享的是我实现过程中的关键功能片段,需要朋友们结合实际应用适配到自己的项目中,原理的大致通用的,当然上述代码某些地方肯定是能够进一步优化拓展的,也欢迎大佬不吝赐教。

二.实验结果测试

1.测试脚本

using UnityEngine;
public class TMPTest : MonoBehaviour
{
    [Header("对话序号")]
    public int dialogueID;


    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.E))
        {
            DialogueManager.Instance.BeginDialogueSequence(dialogueID, () =>
            {
                DialogueManager.Instance.AddDialogueEvent(1, 2, T1);
                DialogueManager.Instance.AddDialogueEvent(1, 4, T2);
            });
        }
    }

    void T1()
    {
        Debug.Log("对话内事件1触发");
    }
    void T2()
    {
        Debug.Log("对话内事件2触发");
    }
}

2.数据配置

这里我挂载了测试脚本,并配置了一段对话数据,序号为1

三.实验结果展示

打印效果:

对话中事件触发效果:

三.后文

       由于我个人框架的实现与UI部分联系密切的问题,考虑给出完整源码文章会过于冗余且侧重点失衡,上面分享的是我实现过程中的关键功能片段,需要朋友们结合实际应用适配到自己的项目中,原理的大致通用的,当然上述代码某些地方肯定是能够进一步优化拓展的,也欢迎大佬不吝赐教。

本篇完!

http://www.dtcms.com/a/98394.html

相关文章:

  • Python针对大规模数据使用”sys模块加速I/O操作“:
  • 21天Python计划:函数简单介绍
  • PCBB印刷电路板缺陷检测YOLO数据集分享
  • LeetCode hot 100—两两交换链表中的节点
  • Redis场景问题1:缓存穿透
  • 用python压缩图片大小
  • 算法-广度优先搜索
  • WSL系统找不到指定的文件
  • 接口自动化——初识pytest
  • (头歌作业—python)3.2 个人所得税计算器(project)
  • 智能发光斑马线:点亮城市道路安全之光
  • DDR(Double Data Rate)详解
  • 云边端协同
  • vue 脚手架解决跨域问题
  • 2022年12月青少年软件编程(图形化)等级考试三级编程题
  • 在树莓派5(8G版)上运行Ollama + LLM
  • NG-ZORRO中tree组件的getCheckedNodeList怎么使用
  • 26考研——排序_选择排序_选择排序的基本思想 简单选择排序(8)
  • 每天一篇目标检测文献(六)——Part One
  • 小林coding-12道Spring面试题
  • 自然语言处理(17:(第五章2.)梯度消失和LSTM(详细拆解))
  • WebSocket通信的握手阶段
  • 基于 WebAssembly 的 Game of Life 交互实现
  • ArrayList和LinkedList比较
  • 使用 Python 进行链上数据监控:让区块链数据触手可及
  • 深入解析音频:格式、同步及封装容器
  • SQL Server:sys.dm_hadr_physical_seeding_stats
  • 2023第十四届蓝桥杯大赛软件赛国赛C/C++ 大学 B 组(真题题解)(C++/Java题解)
  • [项目]基于FreeRTOS的STM32四轴飞行器: 十六.激光测距定高功能
  • 【Web前端】解锁JavaScript中Web存储API的秘密