Unity3d 制作一个简单的NPC对话系统

2023-11-14

制作一个简单的NPC对话系统

前言

​ 最近在自己写一个比较小的项目,虽然自己是一个策划,但是程序方面我觉得也是很有必要学一学的。

​ 经过了接近一年的学习,也终于是可以独自写一些小的系统了。

​ 这次自己写了一个比较简单的NPC对话系统,供大家参考。

效果展示

进入对话区域

进入区域显示图标

开始对话

在这里插入图片描述

Inspector面板可调选项

在这里插入图片描述

准备工作

​ 为了完成对话系统,首先需要一个NPC以及一个UI界面。

​ 这里为了节省篇幅,就直接上图了。

NPC


在这里插入图片描述

UI

在这里插入图片描述在这里插入图片描述

其中Panel用来控制整体显示

NPCWord为文本

右侧还有放头像以及NPC名字的位置

代码

完整代码

这里就先上完整代码

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

public class NPC_Talk : MonoBehaviour
{
    //公共参数
    [Header("NPC姓名")]
    public string npcName;
    [Header("是否是可对话NPC")]
    public bool allowTalk;
    [Header("是否循环对话")]
    public bool isLoop;
    [Header("对话文本")]
    public TextAsset[] talkTxt;
    [Header("对话提示")]
    public GameObject talkSign;

    //内部参数
    [HideInInspector] public bool canTalk;
    private int txtOrder; //文本指针
    private GameObject player;
    private GameObject text;
    private int textRow;
    private bool isTalking;

    void Start()
    {
        canTalk = false;
        textRow = 0;

        player = GameObject.Find("Player");
    }


    void Update()
    {
        ShowSign();
        showText();
        CleanData();
    }

    private void ShowSign() //生成头顶标识
    {
        if (canTalk)
        {
            this.talkSign.SetActive(true);
        }
        else
        {
            this.talkSign.SetActive(false);
        }
    }

    private void OnMouseDown() //点击NPC显示对话UI 并重置Txt文本读取位置
    {
        if (canTalk)
        {
            isTalking = true;

            GameObject canvas = GameObject.Find("Canvas");
            Transform panel = canvas.transform.Find("NPCTalk_Panel");
            panel.gameObject.SetActive(true);
            textRow = 0;
        }
    }

    private void showText() //链接txt文本与UI界面Text 并且逐行读取显示 读取完毕隐藏UI
    {
        GameObject canvas = GameObject.Find("Canvas");
        Transform panel = canvas.transform.Find("NPCTalk_Panel");
        Text text = canvas.transform.Find("NPCTalk_Panel/NPCWord").gameObject.GetComponent<Text>();

        string[] str = talkTxt[txtOrder].text.Split('\n');       

        if (Input.GetMouseButtonDown(0) && isTalking)
        {
            canvas.transform.Find("NPCTalk_Panel/NPCName").gameObject.GetComponent<Text>().text = npcName;
            canvas.transform.Find("NPCTalk_Panel/Sprite").gameObject.GetComponent<Image>().sprite = this.GetComponent<SpriteRenderer>().sprite;
            text.text = str[textRow];
            textRow = textRow + 1;
        }

        if (textRow == str.Length)
        {
            panel.gameObject.SetActive(false);

            textRow = 0;
            txtOrder = txtOrder + 1; //第一个文本播完后 加载第二个文本
            if(txtOrder == talkTxt.Length)
            {
                txtOrder = 0; //全部文本播完后 重置文本指针
                if(!isLoop) //如果为不循环播放 则变为不可Talk的NPC
                {
                    allowTalk = false;
                    canTalk = false;
                }
            }
            isTalking = false;
        }
    }

    private void CleanData()    //走出对话区域重置当前文本
    {
        if (!canTalk && isTalking)
        {
            GameObject canvas = GameObject.Find("Canvas");
            Transform panel = canvas.transform.Find("NPCTalk_Panel");

            textRow = 0;
            isTalking = false;
            panel.gameObject.SetActive(false);
        }
    }
}

详细逻辑

开启对话
private void OnMouseDown() //点击NPC显示对话UI 并重置Txt文本读取位置
    {
        if (canTalk)
        {
            isTalking = true;

            GameObject canvas = GameObject.Find("Canvas");
            Transform panel = canvas.transform.Find("NPCTalk_Panel");
            panel.gameObject.SetActive(true);
            textRow = 0;
        }
    }

​ 开启对话我使用的是点击NPC的碰撞体的方式。

​ 并且通过canTalk判断是否能够对话

​ canTalk是通过玩家是否在对话的区域内(TalkArea)进行判断

在这里插入图片描述

此处在TalkArea上挂载了一个TalkCheck脚本

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

public class TalkCheck : MonoBehaviour
{
    public GameObject npc;

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (npc.GetComponent<NPC_Talk>().allowTalk)
        {
            npc.GetComponent<NPC_Talk>().canTalk = true;
        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        npc.GetComponent<NPC_Talk>().canTalk = false;
    }
}

我一开始在TalkCheck.cs脚本中使用的是事件的方式,进出碰撞体都会发送一个事件给NPC_Talk.cs脚本,但是后期实践的时候如果存在两个以上的NPC,那使用事件就会造成错误。所以还是用了比较繁琐的办法,将canTalk放到TalkCheck.cs中进行判断。

这里我还加了一个allowTalk来给玩家自行控制是否开启NPC的对话功能,如果玩家关闭allowTalk,那canTalk会一直处于关闭状态。

显示对话

接着上文所述,玩家点击NPC后开启对话,为了显示文本,所以下面写了ShowText()函数

private void showText() //链接txt文本与UI界面Text 并且逐行读取显示 读取完毕隐藏UI
    {
        GameObject canvas = GameObject.Find("Canvas");
        Transform panel = canvas.transform.Find("NPCTalk_Panel");
        Text text = canvas.transform.Find("NPCTalk_Panel/NPCWord").gameObject.GetComponent<Text>();

        string[] str = talkTxt[txtOrder].text.Split('\n');       

        if (Input.GetMouseButtonDown(0) && isTalking)
        {
            canvas.transform.Find("NPCTalk_Panel/NPCName").gameObject.GetComponent<Text>().text = npcName;
            canvas.transform.Find("NPCTalk_Panel/Sprite").gameObject.GetComponent<Image>().sprite = this.GetComponent<SpriteRenderer>().sprite;
            text.text = str[textRow];
            textRow = textRow + 1;
        }

        if (textRow == str.Length)
        {
            panel.gameObject.SetActive(false);

            textRow = 0;
            txtOrder = txtOrder + 1; //第一个文本播完后 加载第二个文本
            if(txtOrder == talkTxt.Length)
            {
                txtOrder = 0; //全部文本播完后 重置文本指针
                if(!isLoop) //如果为不循环播放 则变为不可Talk的NPC
                {
                    allowTalk = false;
                    canTalk = false;
                }
            }
            isTalking = false;
        }
    }

首先将获取的第一个文本按行拆分,并且存入str[]中备用。

 string[] str = talkTxt[txtOrder].text.Split('\n');   

当开始对话后,点击左键即可按行显示文本内容,其中isTalking为之前定义的一个bool变量,其状态代表玩家是否在对话中。

if (Input.GetMouseButtonDown(0) && isTalking)
        {
            canvas.transform.Find("NPCTalk_Panel/NPCName").gameObject.GetComponent<Text>().text = npcName;
            canvas.transform.Find("NPCTalk_Panel/Sprite").gameObject.GetComponent<Image>().sprite = this.GetComponent<SpriteRenderer>().sprite;
            text.text = str[textRow];
            textRow = textRow + 1;
        }

当一个文本读完之后,txtOrder会加一,也就是读取的txt文件从talkTxt[i]变成了talkTxt[i+1]。也就是开始读取下一个文件

当txtOrder和txt文件数相等时,也就是读完了所有的文件,将会重置至第一个文件。

其中isLoop函数代表该NPC是否可以循环播放文本,玩家可以手动设置。

如果不能循环播放,则在播放完后控制allowTalk,使NPC无法对话。

if (textRow == str.Length)
        {
            panel.gameObject.SetActive(false);

            textRow = 0;
            txtOrder = txtOrder + 1; //第一个文本播完后 加载第二个文本
            if(txtOrder == talkTxt.Length)
            {
                txtOrder = 0; //全部文本播完后 重置文本指针
                if(!isLoop) //如果为不循环播放 则变为不可Talk的NPC
                {
                    allowTalk = false;
                    canTalk = false;
                }
            }
            isTalking = false;
        }
头顶标识

头顶标识为一个表现向的元素,当玩家进入TalkArea时即开启。

供大家参考。

在这里插入图片描述

private void ShowSign() //生成头顶标识
    {
        if (canTalk)
        {
            this.talkSign.SetActive(true);
        }
        else
        {
            this.talkSign.SetActive(false);
        }
    }
头顶标识

头顶标识为一个表现向的元素,当玩家进入TalkArea时即开启。

供大家参考。

[外链图片转存中…(img-6kUZhC1w-1637120093347)]

private void ShowSign() //生成头顶标识
    {
        if (canTalk)
        {
            this.talkSign.SetActive(true);
        }
        else
        {
            this.talkSign.SetActive(false);
        }
    }

后话

由于本人不是程序员,本职工作是一个小策划,所以希望大家不要介意我混乱的代码逻辑。
其中还有很多优化的地方,比如现在的txt文件需要在最后一行再加一个回车。如果大家有什么好的改进方案,欢迎交流。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity3d 制作一个简单的NPC对话系统 的相关文章

  • 【UGUI】2D头顶血条制作

    前言 近期因为需要制作玩家和敌人头顶的2D血条 查找了很多博客 发现很多都拘束于Canvas的渲染模式必须要设定为ScreenSpace Overlay 还有应该是版本原因 我的是unity2019 1 11f1 用RecttTransfo
  • Unity中loading页加载的实现

    首先创建一个Global cs 使用单例用于存储场景的名字 便于后续脚本的调用 此脚本不必挂载在游戏物体上 using UnityEngine using System Collections public class Global Mon
  • Unity-AR 简介

    Unity AR 简介 现有Unity AR Sdk ARKit 苹果推出的AR开发平台 ARCore Google 推出的增强现实 SDK ARFoundation ARFoundation是ARKit XR插件和ARCore XR插件
  • Unity3d 插件 系列——DoTweenPro介绍(图文详细+案例)

    Unity3d 插件 系列 DoTweenPro介绍 图文详细 案例 前言 一 DoTweenPro简介 二 DoTweenPro安装 三 DoTweenPro主要组件 1 DoTweenAnimation 2 DoTweenPath 3
  • Unity中UI框架的使用1-添加面板、显示Loading页面

    其中BasePanel和Canvas都是挂在面板的预制物上的 1 导入我们的UI框架 本篇文章中有用的是两个UIPanelType NUIManager和NBasePanel 会放在文章最后供大家使用 2 先将我们做好的Panel设置成预制
  • unity中创建询问弹出窗口

    在开发过程中进程会遇到需要弹出一个窗口询问用户是否进行的操作 今天就来制作一个这样弹出窗口 然后根据弹出窗口的选择内容不同进行不同的操作 本例中主要是为了删除一个数据 而在删除数据操作前需要得到用户的一个确认操作 这里面主要用到了Notif
  • 【IMGUI】 各种辅助类 EditorGUIUtility、EditorUtility、GUIUtility、GUILayoutUtility

    EditorGUIUtility class in Editor 继承自 GUIUtility EditorGUI 的各种辅助程序 EditorGUIUtility currentViewWidth 我尝试打印了下这个值和position
  • Unity 键盘控制人物移动——之输入方式代码的编写

    键盘输入 控制人物移动 在我们制作游戏中最常见的需求之一就是使用键盘移动游戏角色 那么我们首先需要获取键盘输入 以下提供两种方法获取键盘 这里尽量通过截图解释让大家理解代码的含义 GetInput void FixedUpdate Move
  • Unity打开工程时卡住的问题

    自从Unity升级了一个版本后 Unity打开工程卡住的问题越来越严重了 具体表现为 选择工程后 Unity窗口消失 但进程还在 有时候等个几分钟能出来 有时候等10分钟都不见得能出来 直观感受上看 似乎是Unity加载工程的时候某一步卡了
  • unity 性能查看工具Profiler

    文章目录 前言 profiler工具介绍 菜单栏 帧视图 模块视图 模块详细信息 通过profiler分析优化游戏性能 最后 前言 每次进行游戏优化的时候都用这个工具查看内存泄漏啊 代码优化啊之类的东西 真的好用 但是之前也就是自己摸索一下
  • unity3d 自定义的图片无法放入source image中

    须将图片的texture type改为 sprite
  • unity小球跟随音乐节奏放大缩小和改变颜色

    放在小球身上 设置对应组件即可 using System Collections using System Collections Generic using Unity VisualScripting using UnityEngine
  • 【Unity】如何让Unity程序一打开就运行命令行命令

    背景 Unity程序有时依赖于某些服务去实现一些功能 此时可能需要类似打开程序就自动运行Windows命令行命令的功能 方法 using UnityEngine using System Diagnostics using System T
  • 【Unity】运行时创建曲线(贝塞尔的运用)

    Unity 运行时创建线 贝塞尔的运用 1 实现的目标 在运行状态下创建一条可以使用贝塞尔方法实时编辑的网格曲线 2 原理介绍 2 1 曲线的创建 unity建立网格曲线可以参考 Unity程序化网格体 的实现方法 主要分为顶点 三角面 U
  • 【Unity】运行时创建曲线(贝塞尔的运用)

    Unity 运行时创建线 贝塞尔的运用 1 实现的目标 在运行状态下创建一条可以使用贝塞尔方法实时编辑的网格曲线 2 原理介绍 2 1 曲线的创建 unity建立网格曲线可以参考 Unity程序化网格体 的实现方法 主要分为顶点 三角面 U
  • Unity学习笔记

    一 旋转欧拉角 四元数 Vector3 rotate new Vector3 0 30 0 Quaternion quaternion Quaternion identity quaternion Quaternion Euler rota
  • U3D游戏开发中摇杆的制作(UGUI版)

    在PC端模拟摇杆 实现玩家通过控制摇杆让玩家移动 以下是完整代码 using System Collections using System Collections Generic using UnityEngine using Unity
  • U3D游戏开发中摇杆的制作(NGUI版)

    在PC端模拟摇杆 实现控制摇杆让玩家或者物体移动 以下是完整代码 using System Collections using System Collections Generic using UnityEngine public clas
  • 游戏开发创建操作之玩家信息系统的建立

    游戏一般都需要玩家信息系统 那么我们应该如何搭建玩家信息系统 接下来我将展示一种简单的方法 完整代码如下 using System Collections using System Collections Generic using Uni
  • 游戏开发常见操作梳理之小地图的制作

    游戏中一般存在小地图系统 实际上就是设置一个新的摄像机放置在玩家的正上方 然后在小地图上显示新摄像机看见的东西就可以了 在小地图上一般存在放大地图和缩小地图的按钮可以方便放大和缩小地图 这些操作是如何实现的呢 接下来直接上核心代码 usin

随机推荐

  • Git(5)已有项目连接远端git仓库

    文章目录 初始化git 连接远程仓库 拉下仓库代码 添加代码到本地仓库 删除idea配置的git本地缓存 提交代码 推上去 初始化git git init 连接远程仓库 git remote add origin 你的仓库地址 拉下仓库代码
  • qqkey获取原理_【逆向】QQkey盗号木马原理分析

    一 简介 QQkey是一段字符串 通过这段字符串在没有QQ登录密码的前提下你依然能够在浏览器中对别人QQ空间 邮箱等应用进行随意访问和操作 现在市面上已经有很多使用易语言编写的盗号木马 专门盗取别人的QQkey 通过QQkey改绑关联了该邮
  • Robot Framework 自动化测试详解

    一 Robot Framework 简介 1 界面自动化测试工具 界面自动化测试 即UI自动化测试 比较常见的工具有 QTP AutoIt Selenium等 像QTP经历了很多版本 最新的版本好像叫UFT了 对初学者来说 录制回放是相当容
  • 搭建免费IP代理池

    声明 本文章中所有内容仅供学习交流 不可用于任何商业用途和非法用途 否则后果自负 如有侵权 请联系作者立即删除 由于本人水平有限 如有理解或者描述不准确的地方 还望各位大佬指教 搭建代理池思路 思路来源 崔庆才大佬的爬虫书 代理从何而来 用
  • 网络安全面试必问

    项目经历 因为大家写的都是渗透相关 所以编故事也要编的圆润些 题材可以去freebuf看 https search freebuf com search search E6 8C 96 E6 B4 9E article 这里主要记录如何挖洞
  • 三类保留地址(私有地址)【个人笔记,仅供参考】

    A类 10 X X X是私有地址 私有地址就是在互联网上不使用 而被用在局域网络中的地址 127 X X X是保留地址 用做循环测试用的 B类 172 16 0 0 172 31 255 255是私有地址 169 254 X X是保留地址
  • 在Anaconda下安装并使用Pytorch,pillow,numpy等库及Python版本的匹配

    我在Anaconda下创建的新环境为 python 3 7 0 pytorch 1 8 0 pillow 9 5 0 numpy 1 21 5 能够正常运行 如果我这个版本够用的话可以按照这个版本进行安装 具体步骤如下 1 在Anacond
  • PROFINET从站设备描述文件

    目录 一 GSDML文件名格式 二 设备标识信息 三 设备支持的通讯周期时间 四 设备支持的槽位和数据模块 相信熟悉工业现场的工程师们 对于PROFIBUS和PROFINET这两个工业协议都不陌生 过去在使用PROFIBUS现场总线时 从站
  • matlab 专家pid,专家PID

    3 专家控制器 专家控制器的模型是整个仿真模型中的重点 其实质就是把专家规则用Matlab现有计算元件实现出来 当前一般的做法就是利用 IF THEN 语句来表述一条一条的专家规则 28 把单回路控制中的部分规则做成的专家控制器如下所示 图
  • 什么是Elastic Stack

    什么是Elastic Stack Elastic Stack是由ELK演化而来 ELK是三种软件的简称 分别是Elasticsearch logstash kibana组成 在发展的过程中 又有新成员Beats的加入 形成了Elastic
  • Hadoop运行模式 之 本地运行模式

    Hadoop的运行模式包括 本地模式 伪分布式模式以及完全分布式模式 Hadoop官网地址 https hadoop apache org 本次使用的Hadoop的版本是2 7 2 官网文档 https hadoop apache org
  • ssh-keygen 常用命令与参数

    ssh keygen 常用命令与参数 生成密钥 默认生成 2048 位 RSA 密钥 ssh keygen 生成 4096 位 RSA 密钥 ssh keygen t rsa b 4096 生成 521 位 ECDSA 密钥 ssh key
  • sql注入基础原理(超详细)

    一 Sql注入简介 Sql 注入攻击是通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中 再在后台 Sql 服务器上解析执行进行的攻击 它目前黑客对数据库进行攻击的最常用手段之一 二 Web 程序三层架构 三层架构 3 tier a
  • qt信号槽之创建和连接自定义的槽

    在第一版的Qt设计器中 你可以创建你自定义的槽的信号并使他们连接起来 但是你不能直接实现你的槽 你不得不子集于该窗体 并在这个子集中对你自定义的槽编码 子集的方法依然有用 在某些情况下仍起作用 Make sense 但是现在你可以在Qt设计
  • R语言入门

    安装 R语言开源 安装很简单 此处带过 界面 R语言的使用简介 赋值命令 赋值符号为 lt 或 但是建议使用 lt x lt 10 赋值10给变量x R语言的数据类型 R是一种基于对象 object 的语言 在R中看到的一切事物都是对象 对
  • JAVA遇见HTML—JSP篇(六.JSP指令与动作元素)

    include指令 语法 代码示例 新建date jsp文件
  • TensorFlow2.0.0开发环境安装

    TensorFlow 框架支持多种常见的操作系统 如 Windows 10 Ubuntu 18 04 Mac OS 等等 同时也支持运行在 NVIDIA 显卡上的 GPU 版本和仅适用 CPU完成计算的 CPU 版本 我们以最为常见的 Wi
  • c语言练习49:有多少⼩于当前数字的数字

    有多少 于当前数字的数字 给你 个数组 nums 对于其中每个元素 nums i 请你统计数组中 它 的所有数字的数 换 之 对于每个 nums i 你必须计算出有效的 j 的数量 其中 j 满 j i 且nums j lt nums i
  • 【网络层】网络基础 -- IP协议

    引入 IP 协议头格式 网段划分 特殊的IP地址 IP地址的数量限制 私有IP地址和公网IP地址 分片与组装 如何分片与组装 引入 我们前面学习了传输层的相关知识 难道真的就是直接传送吗 当然不是 那TCP究竟做了什么 IP又扮演什么角色
  • Unity3d 制作一个简单的NPC对话系统

    制作一个简单的NPC对话系统 文章目录 制作一个简单的NPC对话系统 前言 效果展示 进入对话区域 开始对话 Inspector面板可调选项 准备工作 NPC UI 代码 完整代码 详细逻辑 开启对话 显示对话 头顶标识 头顶标识 后话 前