Unity 简单RPG对话系统——龙之谷2的NPC对话系统
龙之谷2手游正式上线后不久,试玩了十几分钟(包括捏脸的5分钟),之后就再也没有打开过了。
本文章将对龙之谷2的NPC对话系统进行高仿,同时考虑到 策划可能不断修改对话内容 工具的重复利用,将部分通过编辑器模式来进行制作。
写在前边:
1.参考文章
知乎: Yumir——用128行代码实现一个文字冒险游戏
站内: 虚拟喵——Unity 编辑器扩展总结 五:数组或list集合的显示方式
2.素材使用
unity AssetStore——Unity-Chan! Model
3.根据如下游戏内截图,拆分实现步骤
正式开始
一、新建Unity工程,并设置UIPanel
- 这里为了
偷懒方便演示,使用Unity娘到场景中,并复制出两个作为NPC,每个人物模型的Perfab作为空物体**Handle的子物体
- NPC1Handle和NPC2Handle分别创建子物体SphereCollider,调整至合适大小,勾选Is Trigger,并重命名为DialogTrigger,Tag选择为NPCDialog,作为主角经过时触发对话界面的触发器
- 新建Canvas–>Panel,重命名为DialogPanel,根据个人感觉调整DialogPanel的显示区域
- Canvas目录下新建一个空物体,添加GridLayoutGroup组件,用作问答类对话加载答案选项,这里重命名为AnswerGrid
- 新建一个空物体,用来挂载接下来的对话系统控制组件,这里重命名为DialogSystem
整体的层级图如下:
二、调整项目内Player的控制
根据自己的项目情况,让场景内放置好的人物能够实现简单的移动,这里以我下载好的Unity娘为例
- MainCamera代码:ThirdPersonCamera.cs,找到FixedUpdate ()部分,取消鼠标控制摄像机(因为后边希望通过鼠标左键控制对话进度,当然有需要的同学可以分开写鼠标操作)
- Player组件中,去除用不到的组件,并新建一个DialogTriggerEvent.cs,用于控制Player和NPC相遇弹出对话框,具体代码内容稍后再写
三、逻辑代码拆分
四、对话系统控制组件和他的自定义Inspector显示
1.为场景中的对话系统控制器挂载代码(DialogSystemController.cs),控制NPC图片和姓名的显示
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DialogSystemController : MonoBehaviour
{
[SerializeField]
public List<NPCItem> npcItemArray = new List<NPCItem>();
public static DialogSystemController Instance;
void Awake()
{
Instance = this;
}
}
[System.Serializable]
public class NPCItem
{
[SerializeField]
public string ID;
[SerializeField]
public Sprite icon;
[SerializeField]
public string name;
}
2.Assets目录下,新建Editor文件夹,新建DialogSystemEditor,依赖于DialogSystemController.cs,自定义Inspector面板显示内容
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(DialogSystemController))]
public class DialogSystemEditor : Editor
{
private ReorderableList _npcItemArray;
private void OnEnable()
{
_npcItemArray = new ReorderableList(serializedObject, serializedObject.FindProperty("npcItemArray")
, true, true, true, true);
//自定义列表名称
_npcItemArray.drawHeaderCallback = (Rect rect) =>
{
GUI.Label(rect, "NPC Array");
};
//定义元素的高度
_npcItemArray.elementHeight = 88;
//自定义绘制列表元素
_npcItemArray.drawElementCallback = (Rect rect, int index, bool selected, bool focused) =>
{
//根据index获取对应元素
SerializedProperty item = _npcItemArray.serializedProperty.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
EditorGUI.PropertyField(rect, item, new GUIContent("Index " + index));
};
//当删除元素时候的回调函数,实现删除元素时,有提示框跳出
_npcItemArray.onRemoveCallback = (ReorderableList list) =>
{
if (EditorUtility.DisplayDialog("Warnning", "Do you want to remove this element?", "Remove", "Cancel"))
{
ReorderableList.defaultBehaviours.DoRemoveButton(list);
}
};
}
public override void OnInspectorGUI()
{
serializedObject.Update(