对话系统代码解析(对话系统也可以作为交互系统)

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

public class Dialog : MonoBehaviour
{
    private List<DialogInfo[]> dialogInfoList; // 修正拼写错误
    private int contentIndex; // 对话内容的索引
    


    private void Start()
    {
        dialogInfoList = new List<DialogInfo[]>
        {
            new DialogInfo[]
            {
                new DialogInfo { name = "Luna", content = "hello,我是LuNa" }
            },
            new DialogInfo[]
            {
                new DialogInfo { name = "Nala", content = "好久不见了,luna" },
                new DialogInfo { name = "Luna", content = "好久不见了,Nana" }
            }
        };

        GameManager.instance.dialogInfoIndex = 0;
        contentIndex = 1;
    }

    public struct DialogInfo
    {
        public string name;
        public string content;
    }

    public void DisplayDialog()
    {
        if(GameManager.instance.dialogInfoIndex>7)
        {
            return;
        }
        if (contentIndex >= dialogInfoList[GameManager.instance.dialogInfoIndex].Length)
        {
            contentIndex = 0;
            UImanager.instance.showDialog();
            GameManager.instance.canControlLuna = true;
        }
        else
        {
            DialogInfo dialogInfo = dialogInfoList[GameManager.instance.dialogInfoIndex][contentIndex];
            UImanager.instance.showDialog(dialogInfo.content,dialogInfo.name);
            contentIndex++;
        }
    }
}

一、对话块列表的声明

private List<DialogInfo[]> dialogInfoList;
private int contentIndex;

  •  List<T> 列表概念:List<T> 是 .NET 中一个非常常用的数据结构,属于泛型集合类,位于 `System.Collections.Generic` 命名空间中。

    • dialogInfoList:一个存储对话块的列表,每个对话块是一个DialogInfo数组。每个对话块可能包含多个对话内容。

  • contentIndex:表示对话内容的索引,用于跟踪当前对话块中显示到哪一内容。

结构体的列表化 

public struct DialogInfo
{
    public string name;
    public string content;
}

  • DialogInfo是一个结构体(struct),包含两个字段:

    • name:表示说话者的名称。

    • content:表示对话内容。

  • 结构体是值类型,在C#中通常用于表示轻量级的数据类型。

private List<DialogInfo[]> dialogInfoList; 

  • dialogInfoList是一个List集合,它存储的类型是DialogInfo[]

  • 每个元素是一个DialogInfo的数组,相当于将多个对话内容分组存储。

  • 例如,你可以把它看作一个“对话块列表”,每个对话块是一个由DialogInfo组成的数组。

二、构造函数和初始化 

private void Start()
{
    dialogInfoList = new List<DialogInfo[]>
    {
        new DialogInfo[]
        {
            new DialogInfo { name = "Luna", content = "hello,我是LuNa" }
        },
        new DialogInfo[]
        {
            new DialogInfo { name = "Nala", content = "好久不见了,luna" },
            new DialogInfo { name = "Luna", content = "好久不见了,Nana" }
        }
    };

    GameManager.instance.dialogInfoIndex = 0;
    contentIndex = 1;
}
  • Start方法中,初始化了dialogInfoList

    • 第一个对话块是一个包含单个DialogInfo对象的数组,内容是露娜(Luna)的自我介绍。

    • 第二个对话块包含两个DialogInfo对象,分别是娜拉(Nala)和露娜(Luna)之间的对话。

  • 两种类型索引的不同之处

    • GameManager.instance.dialogInfoIndex = 0:这个是对话段落的索引,初始化对话块的索引为0,表示从第一个对话块开始。

    • contentIndex = 1;这个对话句的索引,初始开始序号也应该为0.

    • GameManager.instance.dialogInfoIndex = 0;contentIndex = 1这表示第一个对话块的第二句话。

三、显示对话的方法 

public void DisplayDialog()
{
    if(GameManager.instance.dialogInfoIndex >7)
    {
        return;
    }
    if (contentIndex >= dialogInfoList[GameManager.instance.dialogInfoIndex].Length)
    {
        contentIndex = 0;
        UImanager.instance.showDialog();
        GameManager.instance.canControlLuna = true;
    }
    else
    {
        DialogInfo dialogInfo = dialogInfoList[GameManager.instance.dialogInfoIndex][contentIndex];
        UImanager.instance.showDialog(dialogInfo.content, dialogInfo.name);
        contentIndex++;
    }
}

 判断条件的代码含义是

  • 检查当前对话块(dialogInfoList[GameManager.instance.dialogInfoIndex])中的对话内容索引 contentIndex 是否超出了当前对话块的长度。

  • DisplayDialog方法是展示对话的核心逻辑:

    • 首先检查GameManager.instance.dialogInfoIndex的值是否超过7,如果超过则退出方法。

    • 如果contentIndex的值超过了当前对话块的长度,说明当前对话块的内容已经全部显示完毕:

      • 重置contentIndex为0。

      • 调用UImanager.instance.showDialog()方法来更新UI状态(可能隐藏对话框或重置显示)。

      • 设置GameManager.instance.canControlLunatrue,表示可以控制Luna(在对话结束时)。

    • 否则,获取当前对话块中的DialogInfo对象,并调用UImanager.instance.showDialog(dialogInfo.content, dialogInfo.name)方法显示对话内容,同时递增contentIndex

四、对话panel的建立与人物角色的切换

 在UImanager中调用这个方法:

public GameObject TalkpanelGo;
    public Image characterImage; // 确保是 Image 类型
    public Sprite[] characterSprites;
    public TMP_Text contentText; // 使用 TMP_Text 而不是 Text
    public TMP_Text nameText;   // 使用 TMP_Text 而不是 Text

public void showDialog(string content =null,string name=null)
    {
        //如果内容不为空,不激活对话面板
        if (content == null)
        {
            TalkpanelGo.SetActive(false);
        }

        //否则激活对话面板
        else
        {
            TalkpanelGo.SetActive(true);
            if(name!= null)
            {

                //利用数组存储人物剪影图片,利用名字确定放上哪个图片
                if (name == "Luna")
                {
                    characterImage.sprite = characterSprites[0];
                }
                else
                {
                    characterImage.sprite = characterSprites[1];
                }
                characterImage.SetNativeSize();
            }

            //是将传入该方法的参数content,name赋值给文本,相当于是动态修改文本。
            contentText.text = content;
            nameText.text = name;
        }
    }

五、结束未完成任务的对话

1、将结束对话的方法放入dialog脚本中

public  void SetcontentIndex()
    {
        contentIndex = dialogInfoList[GameManager.instance.dialogInfoIndex].Length;
    }

 将当前对话块长度的值赋值给当前对话的索引号。

2、将dialog脚本作为类,设置在GameManager管理脚本中 

 public Dialog npc;

 public void SetContentIndex()
    {
         npc.SetcontentIndex();
    }

         如果有多个NPC可以交付任务系统时,使用这种流程可以完成与多个NPC的人物互动

六、其他注意事项 

1、设置了button组件给“talk”panel

        将挂载在nala身上的dialog脚本,设置给button,调用其DisplayDialog方法

public void DisplayDialog()
    {
        if(GameManager.instance.dialogInfoIndex>7)
        {
            Debug.Log("1");
            return;
        }
        animator.SetTrigger("talk");
        if (contentIndex >= dialogInfoList[GameManager.instance.dialogInfoIndex].Length)
        {
            Debug.Log("2");
            contentIndex = 0;
            UImanager.instance.showDialog();
            GameManager.instance.canControlLuna = true;
        }
        else
        {
            Debug.Log("3");
            DialogInfo dialogInfo = dialogInfoList[GameManager.instance.dialogInfoIndex][contentIndex];
            UImanager.instance.showDialog(dialogInfo.content,dialogInfo.name);
            contentIndex++;
        }

        这个操作是点击对话面板结束初始对话,以及在NPC对话中点击对话面板加载后续对话。

2、设置与NPC对话的代码 

 在lunacontroller调用方法

 public void talk()
    {
        Collider2D collider = Physics2D.OverlapCircle(rigidbody2d.position, 0.5f, LayerMask.GetMask("NPC"));
        if(collider != null)
        {
            if(collider.name=="nala")
            GameManager.instance.canControlLuna = false;
            collider.GetComponent<Dialog>().DisplayDialog();
        }
    }

 1. 检测碰撞体

Collider2D collider = Physics2D.OverlapCircle(rigidbody2d.position, 0.5f, LayerMask.GetMask("NPC"));
  • Physics2D.OverlapCircle:这是一个Unity提供的方法,用于检测以指定位置为中心、指定半径的圆形区域内是否存在特定图层的碰撞体。

    • rigidbody2d.position:表示当前对象的刚体(Rigidbody2D)的位置,即检测的中心点。

    • 0.5f:表示检测的圆形范围的半径,单位是世界坐标单位。

    • LayerMask.GetMask("NPC"):表示只检测属于“NPC”图层的碰撞体。

2. 调用对话组件

collider.GetComponent<Dialog>().DisplayDialog();
  • collider.GetComponent<Dialog>():获取检测到的碰撞体上名为Dialog的组件。

  • DisplayDialog():调用Dialog组件中的DisplayDialog方法,可能是用来显示对话框或触发对话的逻辑。

3、为NPC设置NPC图层方便交互

4、对话系统可以作为任务交互系统使用

    public void DisplayDialog()
    {
        if(GameManager.instance.dialogInfoIndex>7)
        {
            return;
        }
        if (contentIndex >= dialogInfoList[GameManager.instance.dialogInfoIndex].Length)
        {
            if (GameManager.instance.dialogInfoIndex == 2 &&!GameManager.instance.hasPetTheDog)
            {

            }

            else if (GameManager.instance.dialogInfoIndex == 4 && GameManager.instance.candleNum < 2)
            {

            }

            else if (GameManager.instance.dialogInfoIndex == 6 && GameManager.instance.killNum < 2)
            {

            }
            else
            {
                GameManager.instance.dialogInfoIndex++;
            }

            if (GameManager.instance.dialogInfoIndex == 6)
            {
                GameManager.instance.ShowMonsters();
            }

            contentIndex = 0;
            UImanager.instance.showDialog();
            GameManager.instance.canControlLuna = true;
        }
                
            
        else
        {
            DialogInfo dialogInfo = dialogInfoList[GameManager.instance.dialogInfoIndex][contentIndex];
            UImanager.instance.showDialog(dialogInfo.content,dialogInfo.name);
            contentIndex++;
            animator.SetTrigger("talk");
        }
    }

1. if (GameManager.instance.dialogInfoIndex > 7)

  • 情况描述:如果 dialogInfoIndex 大于 7,则直接返回,不执行任何对话逻辑。

  • 目的:限制对话块的显示范围,避免超出预设的对话索引范围。

 2.if (contentIndex >=dialogInfoList[GameManager.instance.dialogInfoIndex].Length)

 情况描述:如果当前对话内容的索引 contentIndex 超过了当前对话组的长度,说明当前对话组的所有内容已经显示完毕,需要推进到下一组对话。

        特殊情况:如果对话块索引推进,但haspetthedog未完成,进入该判断条件则什么也不做,直接继续执行后续代码,及执行对话块索引值==2时的对话:“你有安抚好我的狗狗吗?”

        正常情况:如果完成了特殊情况,则会执行推进下一个对话块的代码:

else
            {
                GameManager.instance.dialogInfoIndex++;
            }

        容易误解的地方: 容易将并列条件判断和单独条件判断弄混——注意三个特殊条件的判定,如果不满足三个特殊条件的判定条件(即完成了特殊任务),则执行默认条件(即推进对话)

  if (GameManager.instance.dialogInfoIndex == 2 &&!GameManager.instance.hasPetTheDog)
            {

            }

            else if (GameManager.instance.dialogInfoIndex == 4 && GameManager.instance.candleNum < 2)
            {

            }

            else if (GameManager.instance.dialogInfoIndex == 6 && GameManager.instance.killNum < 2)
            {

            }
            else
            {
                GameManager.instance.dialogInfoIndex++;
            }

        而怪物出现的条件是单独出来的判断条件,只要对话块推进到第6个对话块就可以执行:

if (GameManager.instance.dialogInfoIndex == 6)
            {
                GameManager.instance.ShowMonsters();
            }

        必定执行的:以上特殊条件是否满足,必定会执行重置索引值和显示对话面板。

            contentIndex = 0;
            UImanager.instance.showDialog();
            GameManager.instance.canControlLuna = true;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值