赞
踩
更新日期:2024年6月26日。
项目源码:第五章发布(正式开始游戏逻辑的章节)
上一篇完成的关卡编辑器
已支持创建关卡环境(主要由地块
单元组成),本篇,在关卡环境的基础上,需要完成角色编辑、要诀编辑等功能(角色编辑模式
)。
在开始角色编辑模式之前,我们对角色(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
标记即可。
试想一下角色编辑的功能该如何展现,第一步必然是能够创建角色,在这里我们想像刷地块
一样,鼠标停留到一个位置,直接就能在该位置刷出一个角色。
那么,着手开干:
/// <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
了,这里也修改不了啊(检视器面板只能看),所以,迫切需要在关卡编辑器
中实现对这一个个灰色属性的编辑功能。
首先,为了能全局预览场景中的所有角色,我们先将所有角色按阵营进行分类展示:
/// <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控件代码就不贴了,看看源码就一目了然
):
我们规定同时只能选中一个角色,进而进入编辑此角色状态。
那么,当选中角色时(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栏位
会自动根据角色的等级进行激活,当然,也可以在后期使用要诀研习
系统,为指定的角色学习任意要诀。
如果我们想修改一个角色的位置,直接删了重新刷是一种笨办法,但因为角色的位置
与其所处的地块
存在直接关联,我们手动调节角色的位置自然是不可行的,所以需要实现控制角色移动的功能,比如:
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); } }
需要上、下、左、右
移动功能,才能让角色可以变换到地图中的任意位置:
同样的,自然需要移除
角色的功能,我们在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); }
至此,角色编辑的功能便初步完成了,我们能够在关卡上任意布局角色
、编辑角色属性
、编辑角色要诀
等,为后续驱动角色,进而驱动整个游戏逻辑打下了坚实的基础。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。