首页 > 其他分享 >可视化对话树编辑笔记

可视化对话树编辑笔记

时间:2024-09-28 10:34:47浏览次数:9  
标签:node DialogueNode Dialogue 笔记 public 对话 newNode 可视化 节点

在制作 RPG 甚至 AVG 的时候,我们要涉及大量的文本编辑,这个时候不可视化的界面会大大提升项目的维护成本(非常好脚本,使我 AVG 项目崩溃),所以我们需要自己创建一个文本编辑界面。
而在涉及对话的时候,这个叫做对话树的结构具有不错的性质,对话树是一个简单的多叉树结构,但节点被分为 CP 对话和 PL 对话,这样我们就能比较方便的依靠节点来推出 UI,下面我们就来细说一下如何实现可视化编辑对话树。

首先对于一个对话树,我们采用两个脚本来实现,一个是节点脚本,存放了每个对话节点的信息,比如:
· 文本:此时对话中显示的文本
· 对话者:这个节点中对话的发起者
· 出度:这个节点连接的下一个节点

而为了实现可视化编辑,我们还另外储存这些信息:
· 节点的 Rect :记录节点在编辑界面中的位置和长宽
· 节点的 Texture2D :记录节点的背景样式,这样对眼睛比较友好,而且易于维护节点中的对话者
· isChange :节点信息是否被改变,这样我们只在节点大小改变的时候重新绘制一次 Texture2D

那么节点的代码如下

namespace Dia.Dialogue
{
    public enum Speaker 
    {
        Player,
        Npc
    }

    public class DialogueNode : ScriptableObject
    {
        [TextArea(5,50)]
        public string text;
        public Texture2D texture;
        public Speaker speaker = Speaker.Npc;
        [HideInInspector] public bool isChange = false;
        [HideInInspector] public Rect pos = new Rect(0, 0, 180, 90);
        [HideInInspector] public List<string> nxt = new List<string>();

        private void OnValidate() => isChange = true;
    }
}

另一个则是文本库,用来返回任一文本节点的信息,以及维护文本节点的创建与删除,在文本库中我们需要实现以下功能:

· 查询:利用文本节点 .name 来获取文本节点
· 获得根节点:便于后续对话的运作
· 获得所有节点:获取文本库中的所有节点
· 获取所有后代:获取一个节点的所有后代,方便对话选项的建立和 CP 随机对话
· 创建与删除节点:在可视化编辑界面中创建和删除节点,在创建或删除之后要重载查询字典和处理节点的后代

那么代码如下

namespace Dia.Dialogue
{
    [CreateAssetMenu(fileName = "New Dialogue", menuName = "New Dialogue", order = 0)]
    public class Dialogue : ScriptableObject
    {
        [SerializeField] private List<DialogueNode> nodes = new List<DialogueNode>();
        private Dictionary<string, DialogueNode> NodeLookUp =new Dictionary<string, DialogueNode>();

#if UNITY_EDITOR
        private void Awake()
        {
            Initialize();
        }
#endif

        public void Initialize()
        {
            if (nodes.Count != 0) return;
            CreateNode(null);
        }

        private void OnValidate()
        {
            NodeLookUp.Clear();

            foreach(DialogueNode node in GetNodes())
                NodeLookUp[node.name] = node;
            
        }

        public IEnumerable<DialogueNode> GetNodes()
        {
            return nodes;
        }

        public DialogueNode GetRootNode()
        {
            return nodes[0];
        }

        public IEnumerable<DialogueNode> GetAllChildren(DialogueNode parentNode)
        {
            foreach(string childID in parentNode.nxt)
            {
                if (!NodeLookUp.ContainsKey(childID)) continue;
                yield return NodeLookUp[childID];
            }
        }

        public IEnumerable<DialogueNode> GetPlayerChildren(DialogueNode currentNode)
        {
            foreach(DialogueNode node in GetAllChildren(currentNode))
            {
                if (node.speaker == Speaker.Player) yield return node;
            }
        }

        public IEnumerable<DialogueNode> GetAIChildren(DialogueNode currentNode)
        {
            foreach (DialogueNode node in GetAllChildren(currentNode))
            {
                if (node.speaker == Speaker.Npc) yield return node;
            }
        }

        public void CreateNode(DialogueNode parent) 
        {
            DialogueNode newNode = CreateInstance<DialogueNode>();
            if (parent != null) newNode.name = System.Guid.NewGuid().ToString();
            else newNode.name = "Root";

            //Undo.RecordObject(this, "A");
            //Undo.RegisterCreatedObjectUndo(newNode, "Creating Dialogue Node");

            if (parent != null)
            {
                parent.nxt.Add(newNode.name);
                newNode.pos.x = parent.pos.x + 10f;
                newNode.pos.y = parent.pos.y + 10f;
                if (parent.speaker == Speaker.Npc) newNode.speaker = Speaker.Player;
                if (parent.speaker == Speaker.Player) newNode.speaker = Speaker.Npc;
            }
            nodes.Add(newNode);
            AssetDatabase.AddObjectToAsset(newNode, this);
            OnValidate();
        }

        public void DeleteNode(DialogueNode nodeToRemove)
        {
            Undo.RecordObject(this, "Delete Dialogue Node");
            nodes.Remove(nodeToRemove);
            OnValidate();
            CleanChildren(nodeToRemove);
            Undo.DestroyObjectImmediate(nodeToRemove);
        }

        private void CleanChildren(DialogueNode nodeToRemove)
        {
            foreach (DialogueNode node in GetNodes())
                node.nxt.Remove(nodeToRemove.name);
        }
    }
}

接下来就是可视化界面的创建, GetWindow(typeof(this.name),false,"editor name") 函数可以创建一个自定义编辑器窗口,这个函数必须在一个拥有 [MenuItem()] 属性的函数中执行,代码如下。

[MenuItem("Window/Dialogue Editor")] //Create a option in tools
public static void ShowEditorWindow()
{
    GetWindow(typeof(DialogueEditor), false, "Dialogue System");
}

而我们需要实现选中一个文本库时,自动弹出编辑窗口,这个时候我们就要使用 Unity 提供的一个回调属性 OnOpenAsset(x) ,其中 x 是函数被调用的次序,加入了这个属性的静态函数将会在一个资产被选中的时候被调用,这个静态函数必须拥有以下两种特征之一。(这是官网对该属性的介绍
· static bool OnOpenAsset(int instanceID, int line)
· static bool OnOpenAsset(int instanceID, int line, int column)
其中, instanceID 是选中资产在 Unity 中的编号,利用 EditorUtility.InstanceIDToObject 函数就能获取这个资产,接着我们使用投射 as 来将这个资产转换为 dialogue ,如果转换成功的话,我们就选中这个 dialogue 作为当前编辑的文本库,并且打开编辑器窗口。以下是代码。

[OnOpenAsset(1)] //Open the window
public static bool OpenDialogue(int instanceID,int line)
{
    Dialogue tmp = EditorUtility.InstanceIDToObject(instanceID) as Dialogue;
    if (tmp == null) return false;

    ShowEditorWindow();
    return true;
}

标签:node,DialogueNode,Dialogue,笔记,public,对话,newNode,可视化,节点
From: https://www.cnblogs.com/caijiLYC/p/18437057

相关文章

  • 2款.NET开源且免费的Git可视化管理工具
    Git是什么?Git是一种分布式版本控制系统,它可以记录文件的修改历史和版本变化,并可以支持多人协同开发。Git最初是由Linux开发者LinusTorvalds创建的,它具有高效、灵活、稳定等优点,如今已成为软件开发领域中最流行的版本控制系统之一。Git使用一种名为“仓库”的数据结构来保存代码......
  • python爬虫广州城市租房需求数据分析系统 可视化大屏分析系统xumld.
    目录项目介绍技术栈具体实现截图Scrapy爬虫框架关键技术和使用的工具环境等的说明解决的思路开发流程爬虫核心代码展示系统设计论文书写大纲详细视频演示源码获取项目介绍租房者模块账户管理:注册、登录、个人信息编辑、密码更改、账户注销。房源浏览:查看不同类型......
  • SpringBoot笔记
    类的封装类的封装//使用注解自动生成Setter、Getter、toString、无参构造函数、全参构造函数@Data@NoArgsConstructor@AllArgsConstructorpublicclassEmp{privateIntegerid;//IDprivateStringusername;//用户名privateStringpassword;//密码......
  • 笔记篇 SSH密钥泄露&&提权&&溢出&&服务发现
    首先启动我们的靶机和KALI(需要靶机环境请在本篇留言或私信作者)首先在KALI输入ifconfig查看网段此处为80网段接着输入netdiscover -r 192.168.80.1/24 <IP/子网掩码>除去192.168.80.1网关和192.168.80.2我们对剩下的进行ping检测192.168.80.254丢包192.168.80.......
  • 山海鲸可视化 VS PowerBI,中外免费报表软件对比
    在数据分析与可视化的时代,选择合适的报表工具显得尤为重要。山海鲸可视化和PowerBI是市场上颇受欢迎的两款免费报表软件,各有特色。接下来,我们将从功能、优缺点等方面进行对比,帮助你找到最适合的工具。山海鲸可视化山海鲸可视化是一款国内自主研发的报表工具,专注于用户体验和简易......
  • prometheus学习笔记之node-export
    1.安装node-exportk8s各node节点使⽤⼆进制或者daemonset⽅式安装node_exporter,⽤于收集各k8snode节点宿主机的监控指标数据,默认监听端⼝为9100wgethttps://github.com/prometheus/node_exporter/releases/download/v1.3.1/node_exporter-1.3.1.linux-amd64.tar.gztarxfno......
  • JavaSE笔记_dai01
    标识符(一)标识符的命名规则1.由字母、数字、下划线、$组成,不能由数字开头2.严格区分大小写3.不能使用系统保留字4.参考驼峰命名法(二)标识符的命名规范1.见名知意2.参考驼峰命名法3.类、接口:都首字母大写,其他单词小写4.常量:全部大写,用下划线分割5.方法、变量:首字母小写,后......
  • 智慧园区:打造安全调度、集中监控与可视化园区体验
    随着科技的快速发展,智慧园区逐渐成为城市发展的新焦点。作为一种融合了信息技术、通信技术和物联网技术的智慧化管理模式,智慧园区不仅可以提高园区管理的效率和智能化水平,更能够为居民和企业提供便利与安全。安全调度是智慧园区建设中不可或缺的环节。园区内部的复杂人流、车流、物......
  • Flask学习笔记(二)
    目录1.遇到的问题2.知识点2.1ORM模型2.1.1 ORM模型映射到增删改查2.1.2数据库外键2.1.3创建一个数据库表2.1.4通过外键关系查找2.1.5更新数据库结构2.2请求上下文和应用上下文2.2.1请求上下文2.2.2应用上下文3.开启Debug模式1.遇到的问题1.sqlalchemy.......
  • 前端功能页面展示笔记(一)
    同域:协议域名和端口号相同解决跨域问题 CORS(app)   fromflask_corsimportCORSapp=Flask(__name__)CORS(app)@app.route('/',methods=['GET'])defgenerate_images():配置路由后面要紧跟调用的函数通过this.images调用的数据一定要存在于data()中声明后端......