【Unity】RPG2D龙城纷争(六)关卡编辑器之角色编辑

news2024/7/6 17:34:28

更新日期:2024年6月26日。
项目源码:第五章发布(正式开始游戏逻辑的章节)

索引

  • 简介
    • 一、角色编辑模式
      • 1.将字段限制为只读
      • 2.创建角色(刷角色)
      • 3.预览所有角色
      • 4.编辑选中角色属性
      • 5.移动角色位置
      • 6.移除角色

简介

上一篇完成的关卡编辑器已支持创建关卡环境(主要由地块单元组成),本篇,在关卡环境的基础上,需要完成角色编辑、要诀编辑等功能(角色编辑模式)。

一、角色编辑模式

1.将字段限制为只读

在开始角色编辑模式之前,我们对角色(Role)地块(Block)类定义的字段进行一些改进,为一些字段添加ReadOnly特性标记:

    public class Role : HTBehaviour
    {
        /// <summary>
        /// 角色头像
        /// </summary>
        [Label("角色头像"), ReadOnly] public Sprite HeadImage;

		//其他省略......
	}

    public class Block : HTBehaviour
    {
        /// <summary>
        /// 类型
        /// </summary>
        [Label("类型"), ReadOnly] public BlockType Type;

		//其他省略......
	}

ReadOnly使得该字段为只读的,在检视器面板上不可编辑。

在这里插入图片描述

这样做的目的是防止这些属性被不小心篡改,因为他们都将交由关卡编辑器来权衡设置。

当然你也可以不这样做,只需去掉ReadOnly标记即可。

2.创建角色(刷角色)

试想一下角色编辑的功能该如何展现,第一步必然是能够创建角色,在这里我们想像刷地块一样,鼠标停留到一个位置,直接就能在该位置刷出一个角色。

那么,着手开干:

        /// <summary>
        /// 是否激活角色刷子
        /// </summary>
        private bool _isActiveRoleBrush = false;
        /// <summary>
        /// 角色刷子类型名称
        /// </summary>
        private string[] _roleBrushTypeName = new string[] { "玩家", "敌人" };
        /// <summary>
        /// 角色刷子类型(刷出来的角色属于此阵营)
        /// </summary>
        private RoleCamp _roleBrushType = RoleCamp.Player;
        /// <summary>
        /// 角色刷子数据集(刷出来的角色使用此数据集)
        /// </summary>
        private RoleDataSet _roleDataSet;

        /// <summary>
        /// 创建一个角色
        /// </summary>
        /// <param name="block">角色所在地块</param>
        /// <param name="dataSet">角色数据集</param>
        private void CreateRole(Block block, RoleDataSet dataSet)
        {
        	//通过角色模板 _roleTmp 创建一个新角色
            GameObject obj = PrefabUtility.InstantiatePrefab(_roleTmp) as GameObject;
            obj.name = "Role";
            obj.transform.SetParent(_level.RolesRoot);
            obj.transform.localPosition = new Vector3(block.transform.position.x, block.transform.position.y, -1);
            obj.transform.localRotation = Quaternion.identity;
            obj.transform.localScale = Vector3.one;
            obj.SetActive(true);

			//为角色生成一个随机ID,并应用刷子类型(角色阵营),这里类似刷地块的逻辑
            Role role = obj.GetComponent<Role>();
            role.ID = Guid.NewGuid().ToString();
            role.Name = dataSet.name;
            role.Camp = _roleBrushType;
            //设置角色数据集
            role.SetDataSet(dataSet);
            _roles.Add(role);

			//与地块建立关联
            role.StayBlock = block;
            block.StayRole = role;

            EditorUtility.SetDirty(role);
            EditorUtility.SetDirty(block);

            Selection.activeGameObject = obj;
        }

如上,完成了创建角色的方法,再通过刷子相关的控制变量,实现UI控件面板后:

在这里插入图片描述

要实现按1键开刷的功能,依然是在OnSceneGui方法中补充代码:

        private void OnSceneGui(SceneView sceneView)
        {
            if (Event.current == null)
                return;

            if (_editMode == EditMode.Map && _isActiveMapBrush)
            {
               //地块编辑模式
            }
            else if (_editMode == EditMode.Role && _isActiveRoleBrush && _roleDataSet != null)
            {
                if (Event.current.isKey && Event.current.keyCode == KeyCode.Alpha1 && Event.current.type == EventType.KeyDown)
                {
                	//将Scene视图坐标转换为世界坐标
                    Vector2 pos = ScreenToWorldPointInScene(sceneView.camera, Event.current.mousePosition);
                    //获取坐标位置的地块
                    Block block = GetBlockByPoint(pos);
                    if (block)
                    {
                    	//必须该地块不存在角色
                        if (block.StayRole == null)
                        {
                        	//才在该地块创建一个角色
                            CreateRole(block, _roleDataSet);
                        }
                    }
                }

                EditorGUIUtility.AddCursorRect(sceneView.position, MouseCursor.SlideArrow);
            }
        }

此时,我们便可以创建一个角色数据集,然后开刷了:

在这里插入图片描述

不过,刚刷出来的角色是没有头像的,这里显示为红色是因为他所属敌方阵营

而且,角色头像前面已经被我们搞成ReadOnly了,这里也修改不了啊(检视器面板只能看),所以,迫切需要在关卡编辑器中实现对这一个个灰色属性的编辑功能。

3.预览所有角色

首先,为了能全局预览场景中的所有角色,我们先将所有角色按阵营进行分类展示:

        /// <summary>
        /// 玩家角色数量
        /// </summary>
        private int _playerNum;
        /// <summary>
        /// 敌人角色数量
        /// </summary>
        private int _enemyNum;
        /// <summary>
        /// 当前选中的角色物体
        /// </summary>
        private GameObject _currentSelectRoleObj;
        /// <summary>
        /// 当前选中的角色
        /// </summary>
        private Role _currentSelectRole;
        /// <summary>
        /// 是否显示所有玩家角色
        /// </summary>
        private bool _isShowPlayer = false;
        /// <summary>
        /// 是否显示所有敌人角色
        /// </summary>
        private bool _isShowEnemy = false;

通过加入上面的控制代码,然后再结合UI控件代码,实现在2个区域(玩家、敌人阵营)分别预览所有角色(UI控件代码就不贴了,看看源码就一目了然):

在这里插入图片描述

4.编辑选中角色属性

我们规定同时只能选中一个角色,进而进入编辑此角色状态。

那么,当选中角色时(Scene视图中选中角色物体),角色会被赋予到_currentSelectRoleObj_currentSelectRole

Tip:为了避免重复GetComponent<Role>(),使用_currentSelectRole来缓存当前角色物体身上的Role组件。

        private void EditRoleGUI()
        {
        		//如果Scene视图中选择的目标物体改变
                if (_currentSelectRoleObj != Selection.activeGameObject)
                {
                	//则尝试获取其上的Role组件
                    _currentSelectRoleObj = Selection.activeGameObject;
                    _currentSelectRole = _currentSelectRoleObj != null ? _currentSelectRoleObj.GetComponent<Role>() : null;
                }
                if (_currentSelectRole != null)
                {
                	//此时便选中了角色,在这里展示角色的相关属性,同时支持编辑
                }
        }

通过敲完繁琐的UI控件代码后,现在的编辑器界面便是这样:

在这里插入图片描述

在这里,我们可以重新赋予角色的数据集,以更换其内核。

赋予头像灰色头像(仅当角色禁用时展示),头像改变后,会立即体现在角色头像渲染器上:

在这里插入图片描述

角色的ID属性极其重要,在关卡间角色的属性继承存档读档剧情对话等一系列需要定位指定角色的功能,都是通过ID来确定的,所以ID不能重复,当然这里默认生成的Guid.NewGuid()是绝对不重复的。

只不过,为了方便后续关卡进行对应,主角的ID建议单独设置,比如某个主角ID为001,那么在所有关卡中,他的ID都必须为001

当我们把角色状态切换为Not Yet On Stage时,此角色将延时登场:

在这里插入图片描述

然后,下面列出了角色的8个要诀栏位对应的数据集,我们可以创建一系列要诀数据集,然后给每个角色都进行配置,4-8栏位会自动根据角色的等级进行激活,当然,也可以在后期使用要诀研习系统,为指定的角色学习任意要诀。

在这里插入图片描述

5.移动角色位置

如果我们想修改一个角色的位置,直接删了重新刷是一种笨办法,但因为角色的位置与其所处的地块存在直接关联,我们手动调节角色的位置自然是不可行的,所以需要实现控制角色移动的功能,比如:

        if (GUILayout.Button("上移", "ButtonLeft"))
        {
        	//保留角色所站的旧地块
            Block oldBlock = _currentSelectRole.StayBlock;
            //获取角色位置上方+1格的地块,为新地块
            Block newBlock = GetBlockByIndex(oldBlock.Pos + new Vector2Int(0, 1));
            if (newBlock != null && newBlock.StayRole == null)
            {
                _currentSelectRole.transform.localPosition = new Vector3(newBlock.transform.position.x, newBlock.transform.position.y, -1);

				//交换新、旧地块的关联属性
                oldBlock.StayRole = null;
                _currentSelectRole.StayBlock = newBlock;
                newBlock.StayRole = _currentSelectRole;

                EditorUtility.SetDirty(oldBlock);
                EditorUtility.SetDirty(newBlock);
                EditorUtility.SetDirty(_currentSelectRole);
            }
        }

需要上、下、左、右移动功能,才能让角色可以变换到地图中的任意位置:

在这里插入图片描述

6.移除角色

同样的,自然需要移除角色的功能,我们在UI面板上已经画好了控件,只需实现对应的移除角色功能即可:

        /// <summary>
        /// 移除一个角色
        /// </summary>
        /// <param name="role">角色</param>
        private void RemoveRole(Role role)
        {
            _roles.Remove(role);

            Block block = role.StayBlock;
            role.StayBlock = null;
            block.StayRole = null;

			//立即销毁角色物体
            DestroyImmediate(role.gameObject);
            EditorUtility.SetDirty(block);
        }

至此,角色编辑的功能便初步完成了,我们能够在关卡上任意布局角色编辑角色属性编辑角色要诀等,为后续驱动角色,进而驱动整个游戏逻辑打下了坚实的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1866900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

postgres数据库的流复制

1. 流复制和逻辑复制的差异 逻辑复制和流复制最直观的不同是&#xff0c;逻辑复制支持表级别复制区分点事原理不同 逻辑日志是在wal日志产生的数据库上&#xff0c;由逻辑解析模块对wal日志进行初步的解析&#xff0c;解析结果是ReorderBufferChange&#xff08;理解为HeapTup…

EOS Black延迟高如何处理 EOS Black延迟高这样降低

EOS Black中&#xff0c;玩家可以根据自己的喜好和策略&#xff0c;随心所欲地雕琢人物属性&#xff0c;选择装备属性&#xff0c;搭配千变万化的技能&#xff0c;甚至还可以自定义职业&#xff0c;打造出最适合自己的角色。这种自由度极高的玩法&#xff0c;让玩家能够真正体验…

3D Web轻量引擎HOOPS Web Platform赋能AEC行业数字化,高效渲染与多格式支持!

在建筑、工程和施工&#xff08;AEC&#xff09;行业&#xff0c;数字化转型和高效协作正变得越来越重要。为应对日益复杂的项目需求和不断提升的质量标准&#xff0c;AEC企业需要一种强大的工具来实现高效的3D可视化和数据管理。HOOPS Web Platform作为一款综合性3D开发平台&a…

数字图像分析(第二部分)

文章目录 第8章 图像分割图像分割定义阈值分割依赖像素的阈值选取Otsus方法依赖区域的阈值选取依赖坐标的阈值选取变化阈值法区域生长法分裂合并方法分水岭算法聚类分割算法K-meansAP算法Graph cut第9章 图像特征表达基于全局特征的图像表达直方图GIST基于局部特征的图像表达简…

【护眼科普】台灯怎么选对眼睛好?五大适合学生写作业的台灯推荐

作为一位家长&#xff0c;我深切地领悟到保护孩子眼部健康的至关重要性。随着科技的日新月异&#xff0c;孩子们愈发频繁地接触和使用各类电子设备&#xff0c;如平板电脑、手机和电视&#xff0c;屏幕时间几乎占据了他们日常生活的相当一部分。然而&#xff0c;不容忽视的是&a…

openlayers性能优化——开启图层预加载、减少空白等待时间

使用切片图层时、地图拖拽会有空白图片&#xff0c;为了减少空白等待时间&#xff0c;我们可以开始图层预加载。 const map_top new Map({layers: [new TileLayer({preload:Infinity, //预加载source: new StadiaMaps({layer: "outdoors",}),}),],target: "ma…

【5】apollo编写python节点步骤及实例

在workspace/modules下新建包buildtool create --template component modules/test_one 编译包 buildtool build -p modules/test_two/ 增加自己的proto消息 在刚才自动生成的proto文件里面添加自己定义的消息,记得重新编译. syntax "proto2";package apollo;…

网络编程基础知识拾遗:用大白话解释什么是交换机、路由器、光猫、IP地址和子网掩码、公网和内网IP、端口和域名

二层交换机 在没有二层交换机的环境中&#xff0c;两台电脑或多个电脑之间的通信主要依赖于直连方式或共享介质。假如你和你的舍友都有一台电脑&#xff0c;当你们之间想要进行通信的时候&#xff0c;在没有二层交换机的情况下&#xff0c;可以使用网线&#xff08;为了方便理…

SpringBoot中使用多线程调用异步方法,异步方法有无返回值例子。

快速了解Async注解的用法&#xff0c;包括异步方法无返回值、有返回值&#xff0c;最后总结Async注解失效的几个坑。 在我们的 SpringBoot 应用中&#xff0c;经常会遇到在一个接口中&#xff0c;同时做事情1&#xff0c;事情2&#xff0c;事情3&#xff0c;如果同步执行的话&a…

发电机保护屏产品介绍,组成

发电机保护屏产品介绍&#xff0c;组成 发电机保护屏是用于保护发电机组的电气装置。它根据发电机的类型和实际运行要求&#xff0c;将多种保护装置组合在一起&#xff0c;形成一个保护屏柜。发电机保护测控屏是指把发电机类保护装置集中在安装在一个控制柜里&#xff0c;主要用…

【软件下载】Folx详细安装教程视频-Folx软件最新版下载

根据大数据调查表明Acceleration PRO下载&#xff1a;抽出多达10个流的故障能够显着提高下载速度。根据行业数据显示与iTunes PRO集成&#xff1a;通过将Folx集成到iTunes来下载歌曲和视频&#xff0c;能够在下载后立即自动添加到iTunes库。实际上我们可以这样讲通过代理下载&a…

LabVIEW遇到无法控制国外设备时怎么办

当使用LabVIEW遇到无法控制国外产品的问题时&#xff0c;解决此类问题需要系统化的分析和处理方法。以下是详细的解决思路和具体办法&#xff0c;以及不同方法的分析和比较&#xff0c;包括寻求代理、国外技术支持、国内用过的人请教等内容。 1. 了解产品的通信接口和协议 思路…

小型海外仓布局策略:高效利用有限空间,标准化3F流程

合理高效的仓库空间设计&#xff0c;不只是对大型海外仓很关键。对空间有限的小型海外仓来说或许价值更大。 本身仓储空间就有限&#xff0c;如果还没有科学规划&#xff0c;造成空间浪费&#xff0c;那将直接影响到核心业务的运转。 今天我们就给大家整理了对小型海外仓布局…

制作一个智能体:抖音热点话题文案制作助手

文章目录 第一步&#xff0c;添加助手第二步&#xff0c;选择语聚GPT第三步&#xff0c;填写相关信息第四步&#xff0c;工具中选择抖音(普通号)第五步&#xff0c;选择“查询热门视频数据”第六步&#xff0c;测试总结 这篇文章&#xff0c;我们手把手的演示开发一个智能体&am…

Mysql回表和覆盖索引

一、简述 回表&#xff0c;顾名思义就是回到表中&#xff0c;也就是先通过普通索引扫描出数据所在的行&#xff0c;再通过行主键ID 取出索引中未包含的数据。所以回表的产生也是需要一定条件的&#xff0c;如果一次索引查询就能获得所有的select 记录就不需要回表&#xff0c;…

LLM文本数据集775TB:覆盖32个领域,444个数据集

大语言模型在各领域展现出巨大潜力&#xff0c;其性能在很大程度上依赖于训练和测试所用的数据集。然而&#xff0c;目前在如何构建和优化这些数据集方面&#xff0c;尚缺乏统一的认识和方法论。下面从五个方面整合和分类了LLM数据集的基本内容&#xff1a;预训练语料库、指令微…

在低版本Excel中创建次级下拉列表

在低版本中indirect函数不支持选区&#xff0c;创建次级下拉列表得依靠“名称管理”给选区命名。 (笔记模板由python脚本于2024年06月26日 06:24:22创建&#xff0c;本篇笔记适合常用Excel处理数据的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www…

数据库系统概论(第5版教材)

第一章 绪论 1、数据(Data)是描述事物的符号记录&#xff1b; 2、数据库系统的构成&#xff1a;数据库 、数据库管理系统&#xff08;及其开发工具&#xff09; 、应用程序和数据库管理员&#xff1b; 3、数据库是长期存储在计算机内、有组织、可共享的大量数据的集合&…

linux应用开发基础知识(七)——管道和消息队列进程通信

管道通信 匿名管道 #include <unistd.h> int pipe(int pfd[2]);pfd[0]用于读管道&#xff0c;而pdf[1]用于写管道。 注意&#xff1a;匿名管道只能用于亲缘关系的进程之间通信。管道通道是单向的&#xff0c;一边读&#xff0c;另一边写。管道可以用于大于两个进程共…

基线核查--渗透

基线检查 基线核查概念 it中定义&#xff1a; 基线为初始的标准&#xff0c;以后更改就要经过授权&#xff0c;形成下一基线。 软件配置管理的基线&#xff1a;1功能基线&#xff0c;分配基线&#xff0c;产品基线 安全配置基线--基线核查 安全基线可以说是木桶理论&…