
最终效果:
准备工作
-
一个八核十六线程的脑子
-
一个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菜单下添加一个新的菜单项Storypublic 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; }}
然后你就能编排你的剧情啦!