unity实现对话控制

目的:创建一个能控制对话框出现以及对话内容的脚本,在角色与npc下相距较近时,可以通过按下e键,进行下一步对话。

附加内容:如果加入了任务系统,在接任务时玩家多次点击e跳过了对话,而没有明白任务要求,因此再次接近npc按e时,应该能重复说明任务要求,即重复文本的最后一句话,另外,一个npc可能有多套文本内容,要能够在各内容间切换

演示视频

方案:

1.储存对话内容

采取xml文件储存,原因:方便,一个角色对应的文本内容属于其子元素,引用c#提供的库即可对内容进行读取。另外,已有采取xml的教程,便于实现,在此感谢知乎用户5hT89p

的教程用Unity创建一个对话系统 - 知乎 (zhihu.com)

<?xml version="1.0" encoding="UTF-8"?>
<Root>
<girl>
<suit>
<line>hello</line>
<line>come here</line>
<line>go to fight a monster</line>
</suit>
<suit>
<line>you win!</line>
<line>go to open the box</line>
<line>just continue to adventure,bye</line>
</suit>
</girl>
</Root>

上述文本中有两套对话内容,一套是接取任务时:

hello

comehere

go to fight a monster

另一套是完成任务后:

you win!

go to open the box

just continue to adventure,bye

感谢菜鸟教程,让我了解xml并能够写出上述代码

XML 元素 | 菜鸟教程 (runoob.com)

2.对话内容的控制

首先,要从xml读出来文本,并存入二维数组content,二维的原因是一个npc有多套对话内容,一套对话占一行,多套就要多行了。

private List<List<string>> content=new List<List<string>>();
void Start()
    {
        XmlDocument xml = new XmlDocument();
        xml.Load(Application.dataPath + "/dialog/content.xml");        
        XmlNode xmlNode = xml.SelectSingleNode("Root" + "/" + transform.name);       
        foreach (XmlNode x1 in xmlNode.ChildNodes)
        {
            List<string> my = new List<string>();
            foreach (XmlNode x2 in x1.ChildNodes)
            {
                my.Add(x2.InnerText);
            }
            content.Add(my);                
        }        
    }

其次,切换文本内容,并显示到对话框

需要考虑两点,一是只有当角色靠近时,才会显示对话框,因此需要在代码中加入距离判断。二是有的npc可能一直可以对话,但有的npc只能对话一次,就不再能交互,因此需要设置一个public的flag,便于区分这两类npc。

IEnumerator nextWord()
    {
        if (Input.GetKeyDown(KeyCode.F))
        {
            nextOrNot = true;
        }
        if (Input.GetButtonDown("Submit") && Mathf.Abs(player.transform.position.x - transform.position.x)<effectDist)
        {         
            if(nextOrNot)
            {                
                whichSuit++;
                whichString = 0;
                nextOrNot = false;
            }
            if (whichSuit < content.Count)
            {
                if (whichString < content[whichSuit].Count)
                {
                    panel.SetActive(true);
                    words.enabled = true;
                    words.text = content[whichSuit][whichString];
                    whichString++;
                }
                else
                {
                    panel.SetActive(false);
                    words.enabled = false;
                    if (alwaysHasWord)
                    {                        
                        whichString--;
                    }
                    else
                    {
                        this.enabled = false;                      
                    }                    
                }
            }
            else
            {
                panel.SetActive(false);
                words.enabled = false;
            }
            yield return new WaitForSeconds(.2f);
        }
        else if(Mathf.Abs(player.transform.position.x - transform.position.x) > effectDist)
        {
            panel.SetActive(false);
            words.enabled = false;
        }
        yield return null;
    }

为了能找到具体要说哪句话,设置int型whichSuit,和whichString作为二维数组的索引,定位到要说的话,alwaysHasWord就是决定角色是否重复最后一句话的标志位,为了能重复最后一句话,因此判断,如果alwaysHasWord为真,那么将索引减去1,仍在在二位数组有效范围内(实际是该行的最后一列对应的字符串,即最后一句话)。     

 为什么使用协程?

因为在写脚本中,总能发现因为getButtonDown执行了不止一个帧,这样就会导致文本的连续跳过,因此使用协程,在按下e后,总要等待0.2s,才能再次执行nextWord(),跳转到下一句话                                                                 

猜你喜欢

转载自blog.csdn.net/qq_16198739/article/details/126650482
今日推荐