这里的对话系统是根据麦扣老师的视频的笔记,这篇文章是根据记忆写的,肯定会有不全的地方,大家可以直接去麦扣老师B站看视频。
目录
制作UI对话界面
制作出来下面的ui效果
大家掌握基本UI就能制作出来的,所以就简单描述一下结构,大家可以根据自己的理解和需要对应设置UI页面。
整个UI结构分成,三部分:
第一部分就是对话框底板,建立一个UI的Panel,然后把对话框的背景图片拖进去Panel里image组件的source image中
第二部分就是对话框的头像,第三部分是对话框的文本
头像和文本就是上面panel的子物体,头像是UI的image,文本是ui的text
头像主要是将头像拖到上面图中的位置,并且调整位置和大小
文本设置如上图,并且调整大小和位置
做好上面的事情之后,就能做出来基本的对话框界面了。
注意:这里页面设置跟视频中不一样,视频中有设置对话框的设置为世界坐标(如下图),而我没有这样做,因为我觉得要在代码上设置对话框出现的位置,个人认为太麻烦了,所以还是维持默认设置(屏幕坐标),这样ui界面是固定的,不需要设置位置。
同时,在UI页面中,为了让ui页面大小随着游戏屏幕的大小变化而跟着变化,具体设置如下,下面的X Y中的大小是游戏窗口大小的长宽。
unity读取text文本
先准备一个txt文本,里面写清楚对话人物和内容,A、B是代表人物,A/B的下一行就是对话内容,示例如下
A
老师,请问哪里可以学习unity入门视频呢?
B
哪里都可以
A
unity难吗?
B
难的人想回归Hello world
把文件放进去unity中合适的位置,然后开始编写脚本控制对话,这个脚本可以命名为DialogueSystem,挂载在前面构建对话ui界面的物体中,如下:
下面开始编写脚本:
1、先明确我们需要什么变量,可能一开始并不完全,我们可以先写我们想到的。
我们需要一个TextAsset textFile变量存储刚刚写好的txt文件,int index记录读取到了文本的第几行,List<string> textList记录文本按行分隔的内容。
其次,我们需要俩个对话ui界面的两个组件,和两个头像的精灵图。
具体如下:
[Header("UI组件")]
public Text textLabel; //对话文本框
public Image faceImage; //对话人物头像
[Header("文本文件")]
public TextAsset textFile;
public int index = 0; //编号
[Header("头像")]
public Sprite face01; //头像1
public Sprite face02; //头像2
private List<string> textList = new List<string>(); //文本分行存储
2、获取文本内容
先写一个名为GetTextList的函数,功能是将text文本分行存储进去textList,注意要先清空textList,防止第二次再次对话时将内容混淆。
然后再Start函数调用GetTextList函数,并将index设置为0.
最后再Update函数写按下T键文本逐行显示的功能。
private void Start()
{
index = 0;
GetTextList(textFile);
}
private void GetTextList(TextAsset file)
{
textList.Clear();//先清除原来存储的内容
var tmp = file.text.Split('\n');
foreach(var line in tmp)
{
textList.Add(line);
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.T))
{
textLabel.text = textList[index];
index++;
}
}
在运行之前一定要注意将物体拖到脚本中对应变量里。
经过测试,这里会有bug,第一,index会超出textList的范围,第二,刚开始的时候文本框显示内容不正确
修改bug之后,代码如下:
private void Awake()
{
//Awake在OnEnable函数前调用,保证后面使用textList时能有数据
GetTextList(textFile);
}
private void OnEnable()
{
//解决对话框出现时第一句话显示不正常的问题
index = 0;
//先让文本框显示第一句话, 别忘记index++
textLabel.text = textList[index++];
}
private void GetTextList(TextAsset file)
{
textList.Clear();//先清除原来存储的内容
var tmp = file.text.Split('\n');
foreach(var line in tmp)
{
textList.Add(line);
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.R) && index == textList.Count)
{
index = 0;
gameObject.SetActive(false);
return;
//如果超过范围就让对话框隐藏
}
if (Input.GetKeyDown(KeyCode.R))
{
textLabel.text = textList[index];
index++;
}
}
这样就能实现对话框文本正常出现。
头像和文本处理
主要思路就是,根据文本的格式以及内容进行识别
先写处理文本和头像的相对应出现,然后在需要的地方进行调用,代码里面已经注释清楚
代码如下:
private void Awake()
{
//Awake在OnEnable函数前调用,保证后面使用textList时能有数据
GetTextList(textFile);
}
private void OnEnable()
{
//解决对话框出现时第一句话显示不正常的问题
index = 0;
//先让文本框显示第一句话, 别忘记index++
//textLabel.text = textList[index++]; //修改成下面这句话
SetDialogUI();
}
private void GetTextList(TextAsset file)
{
textList.Clear();//先清除原来存储的内容
var tmp = file.text.Split('\n');
foreach(var line in tmp)
{
textList.Add(line);
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.R) && index == textList.Count)
{
index = 0;
gameObject.SetActive(false);
return;
//如果超过范围就让对话框隐藏
}
if (Input.GetKeyDown(KeyCode.R))
{
//textLabel.text = textList[index++]; //修改成下面的
SetDialogUI();
}
}
private void SetDialogUI()
{
//实现对话框头像和文本相对应出现
//头像
switch (textList[index])
{
case "A":
faceImage.sprite = face01;
index++;
break;
case "B":
faceImage.sprite = face02;
index++;
break;
}
//文本
textLabel.text = textList[index++];
}
补充:在switch语句中,如果 case "A": 和 case "B": 不起作用的话,就需要修改成下面的样子。具体什么原因我没有搞懂,有大佬懂的话可以补充~~~
private void SetDialogUI()
{
//实现对话框头像和文本相对应出现
//头像
switch (textList[index])
{
case "A\r":
faceImage.sprite = face01;
index++;
break;
case "B\r":
faceImage.sprite = face02;
index++;
break;
}
//文本
textLabel.text = textList[index++];
}
总结
经过上面就可以实现一个简单的对话系统。但是还没有实现逐字出现的功能,在下一篇补充。
上面思路如果能正常运行就需要自行将对话框出现消失的脚本写出来,这里直接给大家贴出来,大致体会精神即可。
这个TalkButton脚本是挂载在与玩家对话的NPC,其中Button是NPC头上的提示符,talkUI是上面对话框UI界面中的Panel。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TalkButton : MonoBehaviour
{
public GameObject Button;
public GameObject talkUI;
private void OnCollisionEnter2D(Collision2D collision)
{
Button.SetActive(true);
}
private void OnCollisionExit2D(Collision2D collision)
{
Button.SetActive(false);
talkUI.SetActive(false);
}
private void Update()
{
if (Button.activeSelf && Input.GetKeyDown(KeyCode.R))
{
talkUI.SetActive(true);
}
}
}