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.canControlLuna
为true
,表示可以控制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;