Unity 实现一个简易可拓展性的对话系统

        本人能力有限,一切实现仅供参考,如有不足还请斧正

        起因是我看到学校社团内有人做了对话系统的分享,我想了想之前没写过这种东西,而Fungus插件教程太老了,NodeCanvas插件学习成本又比较高,我就干脆寻找资料 加上自己迭代一下,花了一天时间完成了这个对话系统

目录

1.介绍

2.核心脚本

对话管理器

对话事件

对话配置脚本

对话节点脚本 

3.使用指北

路径配置

关于特性

关于接口 

关于UI 

其余内容请自行查看源码


     Github:  Haki-sheep/Haki-sheep-UnityTools at DialogTools-dev1.0 

        演示视频:

Unity一个简易可拓展的对话系统

1.介绍

         这个对话系统并不是可视化编辑节点(像是NodeCanvas插件那种),但也支持一键将Excel表转为So文件,通过配表的方式轻量化这一过程

        首先,算上DEMO一共632行,去掉以后可能 不到四百行 所以十分轻巧

        但是由于代码量摆在那,所以目前本对话系统只支持小玩具, 今后我说不定会将其拓展为课编辑节点的系统,当然,目前我个人使用起来还是比较方便的,毕竟是自己编写的系统

        其次 Base只涉及到了Odin插件EPPLUS 以及一个单例基类 无需其他支持

2.核心脚本

对话管理器

        对话流程如下:

using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;public class DialogManager : SingltonMono<DialogManager>
{#region 基础配置//配置相关private DialogConfig curDialogConfig;private int nodeIndex;public bool nodeNotOver => nodeIndex < curDialogConfig.nodeList.Count - 1;//角色相关private string characterName;private Sprite characterAvatar;//外部 可做替换public Player player;public DialogMainUI dialogMainUI;public SelectUI selectUI;#endregion#region 对话流程 /// <summary>/// 开始对话/// </summary>/// <param name="dialogConfig">想要对话角色的配置</param>/// <param name="nodeIndex">从第几个节点开始对话</param>public void StartDialog(DialogConfig dialogConfig, int nodeIndex = 0){if (curDialogConfig == dialogConfig) return;//不要重复对话curDialogConfig = dialogConfig;this.nodeIndex = nodeIndex;characterName = curDialogConfig.characterName;characterAvatar = curDialogConfig.characterAvatar;StartCoroutine(PlayNode(curDialogConfig.nodeList[nodeIndex]));}public void CeckCharacterInfo(DialogNode node, Image ui_characterAvator, Text ui_characterName){//角色的信息if (node.player){ui_characterName.text = player.name;ui_characterAvator.sprite = player.Avator;}else{ui_characterName.text = characterName;ui_characterAvator.sprite = characterAvatar;}}private IEnumerator PlayNode(DialogNode node){dialogMainUI.Show();CeckCharacterInfo(node, dialogMainUI.ui_characterAvator, dialogMainUI.ui_characterName);//开始事件OnEvent(node.onStartEventList);yield return OnBlockEvent(node.onStartEventList);//打字机yield return Typing(node.content, dialogMainUI.ui_contentText);//等待交互while (!Input.GetMouseButtonDown(0)) { yield return null; }//结束事件OnEvent(node.onEndEventList);yield return OnBlockEvent(node.onEndEventList);if (nodeNotOver){nodeIndex++;StartCoroutine(PlayNode(curDialogConfig.nodeList[nodeIndex]));}else{CloseDialog();}}private void OnEvent(List<IDialogEvent> dialogEvents){foreach (IDialogEvent sEvent in dialogEvents){sEvent.Execute();}}private IEnumerator OnBlockEvent(List<IDialogEvent> dialogEvents){foreach (IDialogEvent sBEvnt in dialogEvents){IEnumerator enumerator = sBEvnt.ExecuteBlock();if (enumerator == null) continue;yield return enumerator;}}public void CloseDialog(){StopAllCoroutines();curDialogConfig = null;nodeIndex = 0;dialogMainUI.Hide();}#endregion#region 打字机相关 public float delayBetweenContent = 0.1f;private Dictionary<string, string> keywordDic = new Dictionary<string, string>();public void SetKeyword(string key, string value){keywordDic[key] = value;}public void RemoveKeyword(string key){keywordDic.Remove(key);}private IEnumerator Typing(string content, Text ui_contentText){StringBuilder builder = new StringBuilder();foreach (var item in keywordDic){content = content.Replace(item.Key, item.Value);}foreach (var s in content){builder.Append(s);ui_contentText.text = builder.ToString();yield return new WaitForSeconds(delayBetweenContent);}}#endregion#region 资源管理public T GetDialogConfig<T>(string path) where T : ScriptableObject, new(){return Resources.Load<T>(path);}#endregion}

对话事件

这里我就展示其中一种事件,检查某一样子东西玩家是否已经应有 从而跳过对话

using System.Collections;
[DialogEvent("CheckKeyWordEvent")]
public class CheckKeyWordEvent : IDialogEvent
{public void ConverString(string excelString){}public void Execute(){//检查是否有选中物品 TODO:条件可以替换if (Player.Instance.selectItem != null){DialogManager.Instance.CloseDialog();}}public IEnumerator ExecuteBlock(){return null;}
}

对话配置脚本

using Sirenix.OdinInspector;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Tools", fileName = "创建新角色")]
public class DialogConfig : SerializedScriptableObject
{//角色名称public string characterName;//角色头像public Sprite characterAvatar;//显示索引,开启翻页[ListDrawerSettings(ShowIndexLabels = true, ShowPaging = true)]public List<DialogNode> nodeList = new List<DialogNode>();
}

对话节点脚本 

using System.Collections.Generic;
/// <summary>
/// 对话节点配置
/// </summary>
public class DialogNode
{//是否是玩家public bool player;//说的内容public string content;//对话事件public List<IDialogEvent> onStartEventList = new List<IDialogEvent>();public List<IDialogEvent> onEndEventList = new List<IDialogEvent>();}

3.使用指北

路径配置

        这个文件填写你的Excel表和so文件想在的位置

        但是我推荐将so文件放在Res下面 方便管理器读取

         如果有报错就把你的DialogImprotSetting的路径放在这里面

关于特性

        这个特性内填写你的事件名称即可 可以不和脚本一样 只需要和Excel表之中一样便可以读取

关于接口 

        事件需要继承这个接口

阻塞执行 里面直接return nul即可 因为外部会判断

当然你直接yield rerun null也可以,但是会造成延迟一帧后才执行其他语句

        UI接口的话可以选择性继承,因为里面也没什么方法,可以自己写

关于UI 

        在DialogManager里有两个UI的对象,其实所有在外部这个注释下的字段都可以自行做替换

    只要让Manager得到了你UI身上下面这些信息即可(方式自行选择比如事件中心或者订阅回调的方式)

        剩下的UI样式之类的自行配置即可

其余内容请自行查看源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73212.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux常用指令(6)

今天我们继续学习一些linux常用指令,丰富我们linux基础知识,那么话不多说,来看. 1.cp指令 功能描述&#xff1a;拷贝文件到指定目录 基本语法&#xff1a;cp [选项] source dest 常用选项&#xff1a;-r&#xff1a;递归复制整个文件夹 拷贝文件&#xff1a; 拷贝文件夹&am…

Vue 3 中的路由传参详解※※※※

前言 在Vue应用中&#xff0c;路由传参是非常常见的需求&#xff0c;它允许我们在不同的组件之间传递数据。Vue Router提供了两种主要的方式来传递参数&#xff1a;query参数和params参数。下面我们将详细探讨这两种传参方式的使用方法和注意事项。 一、query参数 Query参数…

如何创建一个socket服务器?

1. 导入必要的库 首先&#xff0c;需要导入Python的socket库&#xff0c;它提供了创建和管理socket连接的功能。 python import socket 2. 创建服务器端socket 使用socket.socket()函数创建一个服务器端的socket对象&#xff0c;指定协议族&#xff08;如socket.AF_INET表示…

lua垃圾回收

lua垃圾回收 lua 垃圾回收 lua 垃圾回收 collectgarbage(“count”)获取当前lua脚本占用内存字节数(单位为KB)。 collectgarbage(“collect”)执行一次垃圾回收。 xxxnil 将变量置为空&#xff0c;会释放内存。 lua中的机制和c#中回收机制很类似 解除羁绊(置为空)。 --垃圾回…

友思特应用 | 行业首创:基于深度学习视觉平台的AI驱动轮胎检测自动化

导读 全球领先的轮胎制造商 NEXEN TIRE 在其轮胎生产检测过程中使用了基于友思特伙伴Neurocle开发的AI深度学习视觉平台&#xff0c;实现缺陷检测率高达99.96%&#xff0c;是该行业首个使用AI平台技术推动缺陷检测自动化流程的企业。 将AI应用从轮胎开发扩展到制造过程 2024年…

前后端+数据库的项目实战:hbu迎新网-较复杂(下)javaweb

目录 十一、实现对内容的富文本编辑&#xff08;换行、图片颜色等等样式&#xff09; &#xff08;1&#xff09;下载富文本编辑器&#xff0c;引入资源 &#xff08;2&#xff09;将原项目的内容部分替换为富文本编辑器 1、替换添加页面 2、替换修改页面&#xff08;和添…

脚本语言 Lua

概念 Lua由标准C编写而成&#xff0c;几乎在所有操作系统和平台上都可以编译、运行。Lua脚本可以很容易地被C/C 代码调用&#xff0c;也可以反过来调用C/C的函数&#xff0c;这使得Lua在应用程序中可以被广泛应用。Lua并没有提供强大的库&#xff0c;它是不适合作为开发独立应…

【数据分享】2000—2024年我国乡镇的逐月归一化植被指数(NDVI)数据(Shp/Excel格式)

之前我们分享过2000—2024年我国省市县三级逐月归一化植被指数&#xff08;NDVI&#xff09;数据&#xff0c;该数据是基于NASA定期发布的MOD13A3数据集中的月度NDVI栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;计算得出。很多小伙伴拿到数据后反馈是否可以处理出…

【负载均衡系列】HAProxy

HAProxy(High Availability Proxy)是一款高性能的 ​TCP/HTTP 负载均衡器,专注于提供高可用性、灵活性和可靠性。以下是关于HAProxy的详细解析,涵盖其工作原理、工作机制、工作模式等核心方面: 一、HAProxy 工作原理 HAProxy的核心职责是将客户端请求高效、可靠地分发到后…

轻松迁移 Elasticsearch 数据:如何将自建索引导出并导入到另一个实例

概述 在日常的 Elasticsearch 运维和数据管理中&#xff0c;数据迁移是一个常见的需求。无论是为了备份、升级&#xff0c;还是将数据从一个集群迁移到另一个集群&#xff0c;导出和导入索引数据都是至关重要的操作。本文将详细介绍如何将自建 Elasticsearch 实例中的索引数据…

JVM 类加载器之间的层次关系,以及类加载的委托机制

JVM 类加载器之间存在一种层次关系&#xff0c;通常被称为双亲委派模型 (Parent Delegation Model)。这种层次关系和委托机制是 Java 类加载机制的核心&#xff0c;对于保证 Java 程序的安全性和避免类冲突至关重要。 1. 类加载器的层次关系: JVM 中的类加载器&#xff08;Cl…

基于 Vue 3 的PDF和Excel导出

以下是基于 Vue 3 Composition API 的完整实现&#xff0c;包括 PDF 和 Excel 导出。 一、PDF 导出 (Vue 3) 安装依赖 在项目中安装相关库&#xff1a; npm install html2canvas jspdf Vue 3 代码实现 <template><div><div ref"pdfContent" cla…

【Jupyter】notebook无法显示tqdm进度条

错误描述 from tqdm.notebook import tqdm 用的时候报错&#xff1a; Error displaying widget解决方式 # 先装nodejs conda install -c conda-forge nodejs20# 重装ipywidgets pip uninstall ipywidgets pip install ipywidgets jupyter labextension install jupyter-wid…

ubuntu20如何升级nginx到最新版本(其它版本大概率也可以)

前言&#xff1a; Nginx非常常用&#xff0c;所以在网络安全方面备受“关注”。其漏洞非常多&#xff0c;要经常保持软件更新版本才能更好的保证安全。但是Ubuntu官网适配nginx非常慢&#xff0c;所以nginx官方也会推出针对主流Linux操作系统的包管理工具安装方式。 步骤&…

word插入Mathtype公式居中和自动更新

word插入公式自动更新 前提&#xff1a;安装Mathtype 1.word中查看页的宽度 出现如下 2.设置样式 出现这个窗口 给样式随便起个名字 3.修改样式 3.1 设置两个制表位 第二个 3.2 修改公式字体 如下所示 4. 修改公式格式 4.1在word中打开 Mathtype 4.2 修改公式的格式 变成…

如何从后端实现页面跳转?

例&#xff1a;请求转发 例&#xff1a;重定向 例&#xff1a;区别&#xff1a;携带参数的后端跳转 例&#xff1a;是否可以访问外部资源 请求转发&#xff1a;客户端发起一个请求到服务端&#xff0c;服务端把这个请求转发至其他地方 重定向&#xff1a;客户端发起一个请求…

APIJSON快速入门

作者 版本 时间 内容 备注 Allen V1.0.0 2021/08/19 初稿完成 AllenV1.0.1 2021/08/22 添加常见问题 1.流程说明 一个接口的开发,比如Java用SpringBoot,Mybatis来开发一般来说就像下面这个流程 部署上这个项目后,流程变成了这样 如果使用 apijson-framework,还可进一步简化…

STM32八股【3】------RAM和片上FLASH

1、RAM和FLASH构成 1.RAM ┌──────────────────────────┐ │ 栈区 (Stack) │ ← 从RAM顶端向下扩展&#xff08;存储局部变量、函数调用信息&#xff09; │--------------------------│ │ 堆区 (Heap) │ ← …

基于springboot的星之语明星周边产品销售网站(050)

摘要 随着信息互联网信息的飞速发展&#xff0c;无纸化作业变成了一种趋势&#xff0c;针对这个问题开发一个专门适应洗衣店业务新的交流形式的网站。本文介绍了星之语明星周边产品销售网站的开发全过程。通过分析企业对于星之语明星周边产品销售网站的需求&#xff0c;创建了一…

Android Launcher3 HotSeat文件夹创建禁止方案全解析

一、技术背景与实现原理 在Android 13 Launcher3定制开发中&#xff0c;需屏蔽HotSeat区域的文件夹创建功能。该功能涉及的核心事件处理流程如下&#xff1a; 复制 [拖拽事件] -> [Workspace.onDrop()] -> [CellLayout.performReorder()]└─> [createUserFolderIf…