1290 字
6 分钟
【Unity】自定义2D对话系统

最终效果:

准备工作#

  • 一个八核十六线程的脑子

  • 一个Unity(最佳版本2022.X) ,DOTween插件(Unity商店免费)

对话数据存储#

首先创建一个存储对话数据的可序列化C#类DiaLog,ta用来存储每一个对话

每个对话拥有以下属性:

  • 该对话是谁说的(string Character)

  • 该对话要显示的文本(string comment)

  • 该对话文本的颜色(Color color)

    public class DiaLog
    {
    public string Character, Comment; //Character:人物的名称,Comment:显示的文本
    public Color color; //Color:对话文本颜色
    }

现在我们要创建一个 ScriptableObject类 存储这些对话变成一个完整的故事(Story类)

Story类仅包含一个DiaLog类的集合,用来存储故事中的对话数据

[CreateAssetMenu(fileName = "Story", menuName = "DiaLogSystem/Story", order = 0)] //在Assets下右键的Create菜单下添加一个新的菜单项Story
public class Story : ScriptableObject {
public List<DiaLog> diaLogs = new List<DiaLog>();
}

现在你可以在Assets下右键->Create->DiaLogSystem->Story以创建一个Story类对象,然后在里面编写剧情,如下图:

显示对话#

现在我们做一个对话UI,然后用一个StoryManager处理这对话数据

创建一个Canvas,在Canvas底下创建三个Text:

  • PressKeyContinue 提醒用户按XX键继续对话

  • StoryComment 显示对话的文本

  • StoryName 显示对话中的人物名称

如果你想做个对话框也行 用一个Image铺底

现在我们来思考以下对话系统怎么实现:

  • 1.用一个方法接受Story对象并开启对话

  • 2.遍历Story中的每个DiaLog,打字机效果输出它们的文本并修改文本的颜色

  • 3.每个DiaLog遍历时等待用户按下继续对话键,如果当前对话还没有显示完,则直接显示全部文本,否则遍历下一个DiaLog

用一个字符串变量定义继续对话键,我这里选择键盘上的Z键

接下来,创建一个StoryManager脚本。

首先定义一堆变量:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;//DOTween命名空间
public class StoryManager : MonoBehaviour
{
public Text StoryName;//对话UI部分的StoryName
public Text StoryComment;//对话UI部分的StoryComment
public Text PressKeyContinue;//对话UI部分的PressKeyContinue
public Story story;//当前Story对象
public float TypingTime = 0.05f;//打字机速度
public string ContinueCode = "Z";//继续对话键,便于用户修改热键,我这里使用字符串存储
KeyCode ContinueKey;//继续对话键的KeyCode

在Start方法中给ContinueCode做一个转化:

void Start(){
ContinueKey = (KeyCode)Enum.Parse(typeof(KeyCode), ContinueCode);//枚举方法:用枚举名找到枚举值
}

用一个SetStory方法接受Story对象并开启对话:

public void SetStory(Story story)
{
this.story = story;
StartCoroutine(StartStory());
}

哦我去我们终于要开始写主要部分了

一个协程StartStory,用来显示一个故事(这部分有点难,每句话都写了注释~~(我去累死我了)~~)

IEnumerator StartStory()
{
foreach (DiaLog d in story.diaLogs)
{
StoryComment.text = "";//初始化文本
StoryName.text = d.Character;//对话数据赋值
StoryComment.color = d.color;//对话数据赋值
//StoryComment.transform.DOPunchPosition(Vector2.one * 10, 0.5f, 10); 如果你想让你的对话看起来非常用动感,可以加上,我认为效果还行
Tween t = StoryComment.DOText(d.Comment, TypingTime * d.Comment.Length);//DOTween打字机
t.SetEase(Ease.Linear);//把dotween自带的缓动取消变成匀速打字,谁家打字还带缓动的()
while (!Input.GetKeyDown(ContinueKey))//等待用户按下继续键
{
yield return null;//累了歇会
}
//以下是用户按下继续键之后发生的事
if (StoryComment.text == d.Comment)//如果用户按下继续键的时候 打字机已经打完字了
{
yield return null;//缓一帧 不然可能出现【BIG BUG】 具体原因未知
PressKeyContinue.gameObject.SetActive(false);//把提示文本关掉
continue;//continue
}
else//如果用户按下继续键的时候 打字机还没打完
{
t.Kill();//把打字机关掉
StoryComment.text = d.Comment;//直接显示所有文字
PressKeyContinue.gameObject.SetActive(true);//把提示文字开启
yield return null;
while (!Input.GetKeyDown(ContinueKey))//等待用户按下继续键
{
yield return null;//再歇会
}
yield return null;//缓一帧 不然可能出现【BIG BUG】
PressKeyContinue.gameObject.SetActive(false);//把提示文本关掉
continue;//continue
}
}
StoryComment.text = "";//重置
StoryName.text = "";//重置
}

在unity中创建空对象StoryManager,挂上这个脚本,把三个Text组件赋值

现在你可以随便整个脚本,比如游戏一开始的时候给StoryManager一个Story试试效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FastSetStory : MonoBehaviour
{
public Story story;
// Start is called before the first frame update
void Start()
{
StoryManager.Instance.SetStory(story);
Destroy(gameObject);
}
}

或者用OnTriggerStay2D,在用户走进触发器区域并且按下对话键的时候触发对话

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OnTriggerSetStoMonoBehaviourlBase
{
public bool PressZ, pressz = false, into = false, DisAppear = false;//PressZ:true:玩家走进触发器后要按Z才能触发对话 false:与之相反,走进触发器立即触发对话
//pressz:用户是否按下了Z into:用户是否在触发器内 DisAppear:触发对话后是否销毁自身
public Story story;
void Update()
{
if (Input.GetKeyDown(KeyCode.Z) && PressZ && into)
{
pressz = true;
}
}
void OnTriggerStay2D(Collider2D collision)
{
into = true;
if (PressZ == false)
{
StoryManager.Instance.SetStory(story, true);
if (DisAppear) Destroy(gameObject);
}
else if (pressz)
{
StoryManager.Instance.SetStory(story, true);
pressz = false;
            if (DisAppear) Destroy(gameObject);
}
}
void OnTriggerExit2D(Collider2D collision)
{
pressz = false;
into = false;
}
}

然后你就能编排你的剧情啦!

【Unity】自定义2D对话系统
https://fuwari.vercel.app/posts/cv0002/
作者
丘比女王
发布于
2025-07-06
许可协议
CC BY-NC-SA 4.0