【Unity实战】实现强大通用易扩展的对话系统(附项目源码)

文章目录

  • 先看看实现的最终效果
  • 前言
  • 素材
  • 前期准备工作
    • 1. 简单绘制地形
    • 2. 绘制对话框
    • 3. 配置人物动画
    • 4. 实现简单的控制人物移动
  • 控制对话框的显示隐藏
  • 定义对话内容
  • 实现简单的对话功能
  • 逐字打印效果
  • 按下按键快速显示文本
  • 实现多个NPC配置不同对话
  • 扩展
    • TextAsset 读取文档文件
    • 实际应用
  • 补充
  • 源码
  • 参考
  • 完结

先看看实现的最终效果

在这里插入图片描述

前言

之前的对话系统因为存在一些错误和原作者不允许我分享,所以被我下架了,而且之前对话系统确实少了一些功能,比如最基本的逐字打印功能,原本来是打算后面补充的。

对话系统在游戏中实现太常见了,所以我又重新去找了一些对话系统的课程进行学习,把实现过程和笔记分享出来,后面肯定会用到。

本文是参考b站麦扣老师比较老的课程了,我已经看完了,后面发现缺失了挺多功能的:

  • 比如扩展性不好,多NPC很难将对话分开
  • 快速显示的实现过于麻烦了
  • 对话框显示在世界坐标,UI无法适配屏幕的变化
  • 文本只支持显示内容,不支持显示角色名称和人物的不同表情变化

所以我改动的地方可能比较多,因为我想实现的是一个通用的对话脚本,可以很方便的对多个NPC绑定不一样的对话内容,当然,麦扣老师的视频链接我会放在文章底部,感兴趣的也可以去看看原版,对照着学习!

素材

素材下载地址:
https://bakudas.itch.io/generic-rpg-pack
在这里插入图片描述

前期准备工作

1. 简单绘制地形

关于TileMap的使用,这里就不再过多介绍了,感兴趣的可以查看我之前的文章:
【Unity小技巧】Unity2D TileMap的探究(最简单,最全面的TileMap使用介绍)
在这里插入图片描述

2. 绘制对话框

在这里插入图片描述

3. 配置人物动画

在这里插入图片描述

4. 实现简单的控制人物移动

新建脚本,简单的控制人物的移动和动画切换

public class Player : MonoBehaviour
{[Header("移动速度")]public float speed;Animator animator;Vector3 movement;void Start(){animator = GetComponent<Animator>();}void Update(){//移动movement = new Vector3(Input.GetAxisRaw("Horizontal") * Time.deltaTime * speed, Input.GetAxisRaw("Vertical") * Time.deltaTime * speed, transform.position.z);transform.Translate(movement);//动画if (movement != Vector3.zero){animator.SetBool("run", true);}else{animator.SetBool("run", false);}//翻面if(movement.x>0){transform.localScale = new Vector3(1, 1, 1);}if(movement.x<0){transform.localScale = new Vector3(-1, 1, 1);}}
}

效果
在这里插入图片描述

控制对话框的显示隐藏

新增脚本TalkButton,控制NPC对话提示和对话框的显示和隐藏

public class TalkButton : MonoBehaviour
{private GameObject tipsButton;//对话提示按钮[Header("对话框")]public GameObject dialogBox;private void OnTriggerEnter2D(Collider2D other){tipsButton = other.transform.Find("对话提示").gameObject;tipsButton.SetActive(true);}private void OnTriggerExit2D(Collider2D other){tipsButton.SetActive(false);dialogBox.SetActive(false);}private void Update(){if (tipsButton != null && tipsButton.activeSelf && Input.GetKeyDown(KeyCode.R)){dialogBox.SetActive(true);}}
}

效果
在这里插入图片描述

定义对话内容

新建DialogNode,定义每段对话的各种属性

// 代表了一个对话节点。
[Serializable]
public class DialogNode
{[Header("角色的名字")]public string name;[Header("角色的头像")]public Sprite sprite;[TextArea, Header("对话的内容")]public string content;
}

新建Dialogue脚本,继承ScriptableObject,这样我们就可以在界面方便的新建各种对话了

// 表示一段对话
[CreateAssetMenu(menuName="创建对话" ,fileName = "对话")]
public class Dialogue : ScriptableObject 
{// 对话节点public DialogNode[] dialogNodes;
}

回到界面,创建各种对话,并配置对话内容
在这里插入图片描述

实现简单的对话功能

定义NPC脚本

public class NPC : MonoBehaviour {[Header("对话内容")]public Dialogue dialogue;
}

给不同NPC挂载不同的对话
在这里插入图片描述
修改TalkButton,获取对应的NPC对话内容,并修改为单例,方便其他地方调用dialogue对话内容

[NonSerialized]
public Dialogue dialogue;//对话内容//单例
public static TalkButton instance;
private void Awake()
{if(instance == null){instance = this;}else{if(instance != this){Destroy(gameObject);}}DontDestroyOnLoad(gameObject);
}private void OnTriggerEnter2D(Collider2D other)
{dialogue = other.GetComponent<NPC>().dialogue;//。。。
}

新增DialogSystem脚本,挂载在对话框上

public class DialogSystem : MonoBehaviour
{private Dialogue dialogue;//对话内容//索引private int index;//对话内容框TextMeshProUGUI dialogueContent;//名称框TextMeshProUGUI dialogueName;//头像框Image dialogueImage;private void Awake() {gameObject.SetActive(false);    }private void OnEnable(){dialogue = TalkButton.instance.dialogue;dialogueContent = transform.Find("内容").GetComponent<TextMeshProUGUI>();dialogueName = transform.Find("名字").GetComponent<TextMeshProUGUI>();dialogueImage = transform.Find("头像").GetComponent<Image>();//设置人物头像保持宽高比,防止压缩变形dialogueImage.preserveAspect = true;index = 0;Play();}private void Update(){if (Input.GetKeyDown(KeyCode.R) && dialogue != null){//对话播放完,关闭对话if (index == dialogue.dialogNodes.Length){gameObject.SetActive(false);index = 0;}else{//开始对话Play();}}}// Play 函数用于开始播放对话。private void Play(){// 获取当前对话节点,并更新索引值。DialogNode node = dialogue.dialogNodes[index++];// 设置对话内容、角色名称和头像dialogueContent.text = node.content;dialogueName.text = node.name;dialogueImage.sprite = node.sprite;}
}

效果
在这里插入图片描述

逐字打印效果

修改DialogSystem,创建携程实现逐字打印效果,为了防止字体发生错乱我们要加判断,每一行执行完成后才可以继续进入下一段对话

[SerializeField, Header("目前逐字打印速度")]
private float textSpeed;bool isDialogue;//是否正在对话private void OnEnable()
{isDialogue = false;// 。。。
}private void Update()
{if (Input.GetKeyDown(KeyCode.R) && dialogue != null){if (!isDialogue){//对话播放完,关闭对话if (index == dialogue.dialogNodes.Length){gameObject.SetActive(false);index = 0;}else{//开始对话Play();}}}
}// Play 函数用于开始播放对话。
private void Play()
{// 获取当前对话节点,并更新索引值。DialogNode node = dialogue.dialogNodes[index++];// 设置对话内容、角色名称和头像// dialogueContent.text = node.content;StartCoroutine(SetTextUI(node));dialogueName.text = node.name;dialogueImage.sprite = node.sprite;
}//逐字打印
IEnumerator SetTextUI(DialogNode node)
{isDialogue = true;dialogueContent.text = "";for (int i = 0; i < node.content.Length; i++){dialogueContent.text += node.content[i];yield return new WaitForSeconds(textSpeed);}isDialogue = false;
}

效果,记得在面板配置textSpeed值,我这里定为0.1
在这里插入图片描述

按下按键快速显示文本

修改DialogSystem,我们通过控制文本播放速度实现

private float startTextSpeed;//开始逐字打印速度
private void OnEnable()
{//...startTextSpeed = textSpeed;
}private void Update()
{if (Input.GetKeyDown(KeyCode.R) && dialogue != null){//如果正在对话,再次按下R,快速显示所有对话if (isDialogue){textSpeed = 0;}else{//回复文本速度textSpeed = startTextSpeed;//对话播放完,关闭对话if (index == dialogue.dialogNodes.Length){gameObject.SetActive(false);index = 0;}else{//开始对话Play();}}}
}

效果
在这里插入图片描述

实现多个NPC配置不同对话

配置多个NPC,给每个NPC配置不同的对话
在这里插入图片描述
最终效果
在这里插入图片描述

扩展

麦扣的课程用的是TextAsset读取txt文本,这种方式因为不方便配置显示角色名称和头像表情变化,所有我放弃了,但是还是补充一下TextAsset的用法,因为他可能在其他地方可以应用

TextAsset 读取文档文件

TextAsset 是把一种某种格式的文件输入到我们的游戏项目当中,然后它可以帮助我们转换这里边的这个文本
它可以支持的类型有:
在这里插入图片描述
它里边也有一个自带的一个参数的方法,就是.text,它会把整个文件转换成一个单独的字符型的数据

实际应用

比如这样的文本
在这里插入图片描述
代码读取文本

public class DialogSystem : MonoBehaviour
{[Header("文本文件")]public TextAsset textFile; // 用于存储对话文本的文本文件public int index; // 对话索引,用于跟踪当前对话位置List<string> textList = new List<string>(); // 存储从文本文件中读取的对话内容的列表void Start(){GetTextFromFile(textFile);}void GetTextFromFile(TextAsset file){var lineData = file.text.Split('\n'); // 将文本文件按行分割foreach (var line in lineData){textList.Add(line); // 将每行对话文本添加到对话内容列表中}}
}

补充

逐字打印的时候,还可以加入一些打字音效,这里我就不加了,留给大家自己补充

源码

https://gitcode.net/unity1/dialoguesystem
在这里插入图片描述

参考

【视频】https://www.bilibili.com/video/BV1WJ411Y71J/

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

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

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

相关文章

No183.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

Mac电脑配置Flutter开发环境

1.进入官网下载页&#xff1a; Flutter SDK releases | Flutter 可以看到有 Windows、macOS、Linux三种系统的下载包 选择macOS&#xff0c;然后点击下载 Stable channel&#xff08;稳定版&#xff09;中的最新版本&#xff0c;下载完成后可以移动到资源库Library中。 2.下载…

TCP触发海康扫码相机S52CN-IC-JQR-NNN25

PC环境设置 为保证客户端正常运行以及数据传输的稳定性&#xff0c;在使用客户端软件前&#xff0c;需要对 PC 环境 进行设置 关闭防火墙 操作步骤如下&#xff1a; 1. 打开系统防火墙。 2. 在自定义设置界面中&#xff0c;选择关闭防火墙的对应选项&#xff0c;并单击…

74hc595模块参考

74hc595模块参考 8位串行并行输出&#xff08;SIPO&#xff09;移位寄存器 使用74HC595移位寄存器扩展微控制器上的输出引脚数量。如果你需要扩充输入引脚的数量那么你需要74HC165移位寄存器。 SER&#xff08;串行输入&#xff09;引脚用于一次一位地将数据发送到移位寄存器…

快速教程|如何在 AWS EC2上使用 Walrus 部署 GitLab

Walrus 是一款基于平台工程理念的开源应用管理平台&#xff0c;致力于解决应用交付领域的深切痛点。借助 Walrus 将云原生的能力和最佳实践扩展到非容器化环境&#xff0c;并支持任意应用形态统一编排部署&#xff0c;降低使用基础设施的复杂度&#xff0c;为研发和运维团队提供…

459. 重复的子字符串

459. 重复的子字符串 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;__459重复的子字符串_枚举__459重复的子字符串_字符串匹配__459重复的子字符串_KMP算法__459重复的子字符串_优化的KMP算法 错误经验吸取 原题链接&#xff1a; 459. …

酷开科技持续推动智能投影行业创新发展

近年来&#xff0c;投影仪逐渐成为年轻人追捧的家居时尚单品。据国际数据公司&#xff08;IDC&#xff09;报告显示&#xff0c;2022年中国投影机市场总出货量505万台&#xff0c;超80%为家用投影仪。相比于电视&#xff0c;投影仪外观小巧、屏幕大小可调节&#xff0c;无论是卧…

OpenCV:图像旋转与缩放

人工智能的学习之路非常漫长&#xff0c;不少人因为学习路线不对或者学习内容不够专业而举步难行。不过别担心&#xff0c;我为大家整理了一份600多G的学习资源&#xff0c;基本上涵盖了人工智能学习的所有内容。点击下方链接,0元进群领取学习资源,让你的学习之路更加顺畅!记得…

从白日梦到现实:推出 Elastic 的管道查询语言 ES|QL

作者&#xff1a;George Kobar, Bahubali Shetti, Mark Settle 今天&#xff0c;我们很高兴地宣布 Elastic 的新管道查询语言 ES|QL&#xff08;Elasticsearch 查询语言&#xff09;的技术预览版&#xff0c;它可以转换、丰富和简化数据调查。 ES|QL 由新的查询引擎提供支持&am…

vmware16.2内部win7联网

1、主机配置 前置条件&#xff1a;DHCP和NAT服务已启动 设置无线IP与虚拟机IP为自动获取 二者都是&#xff1a;右键-属性 选择IPv4 自动获取 2、虚拟机配置 设置虚拟机上网方式为NAT 菜单栏-虚拟机-设置 NMnet8改为NAT模式 菜单栏-编辑-虚拟网络编辑器 win7系统内部网…

13. 高精度延时

13. 高精度延时 GPT 定时器简介GPT 定时器结构GPT 定时器工作模式 GPT 定时器相关寄存器GPTx_CRGPTx_PRGPTx_SRGPTx_CNTGPTx_OCR GPT 配置步骤程序编写bsp_delay.hbsp_delay.cmain GPT 定时器简介 GPT 定时器是一个 32 位向上定时器&#xff0c;也就是从0x00000000 开始向上递…

【算法与数据结构】93、LeetCode复原 IP 地址

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;参照【算法与数据结构】131、LeetCode分割回文串的思路&#xff0c;需要将IP字符串进行分割&#xff0…

No181.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

k8s-实验部署 1

1、k8s集群部署 更改所有主机名称和解析 开启四台实验主机&#xff0c;k8s1 仓库&#xff1b;k8s2 集群控制节点&#xff1b; k8s3 和k8s4集群工作节点&#xff1b; 集群环境初始化 使用k8s1作为仓库&#xff0c;将所有的镜像都保存在本地&#xff0c;不要将集群从外部走 仓库…

班级新闻管理系统asp.net+sqlserver

班级新闻管理系统 附加功能 新闻图片&#xff0c;点击次数访问自增&#xff0c;每个人都只能增删改查自己发布的新闻&#xff0c;并可以看到所有人发布的新闻 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于asp.net架构和sql serve…

剑指JUC原理-16.读写锁

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

利用百度地图api获取经纬度和uniapp定位计算距离

获取百度地图api&#xff0c;成为开发者&#xff0c;去控制台创建应用得到一个ak就可以了 百度地图开放平台网址&#xff1a;百度地图开放平台 | 百度地图API SDK | 地图开发 后端是node // 根据百度api地址获取经纬度 https://lbsyun.baidu.com/async getLocation(val) {try …

【修车案例】一波形一案例(9)

故障车型&#xff1a;捷豹X-Type 故障现象&#xff1a;发动机故障指示灯点亮&#xff0c;加速时动力不足&#xff0c;扫描工具显示EGR阀和涡轮增压器增压控制位置传感器电路故障 示波器诊断&#xff1a;检测增压控制位置传感器电路的完整性 A通道 - 增压控制执行电机电源电压B通…

【SoC基础】DMA的工作原理

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

Technology Strategy Patterns 学习笔记8- Communicating the Strategy-Decks(ppt模板)

1 Ghost Deck/Blank Deck 1.1 It’s a special way of making an initial deck that has a certain purpose 1.2 you’re making sure you have figured out what all the important shots are before incurring the major expense of shooting them 1.3 需要从技术、战略、产…