更舒适的阅读体验,请关注公众号
1 简介
游戏中的新手引导可以帮助玩家快速了解游戏规则和操作方式,对于玩家体验来说非常重要
实现强制新手引导有很多种方式,这里基于 OnPopulateMesh 方法,相比 Shader、Mask 等方式,该方法通过减少 DrawCall 来提高 UI 渲染性能
2 效果预览
点击镂空区域按的钮触发对应事件
3 实现方案
3.1 构成分析
强制新手引导一般由两部分构成:
-
遮罩区域:全屏半透黑色遮罩,屏蔽玩家操作
-
镂空区域:透传点击操作,触发目标按钮事件
在此基础上,还可以通过增加目标按钮的边框效果及手指点击等动画增强游戏视觉体验
3.2 实现方法
遮罩及镂空区域
「方法介绍」
protected virtual void OnPopulateMesh(VertexHelper vh);
UI 元素需要生成顶点时的回调函数,通常用于自定义 UI 元素的渲染,可以通过重写该方法来实现自定义的 UI 元素渲染效果
vh 参数是一个 VertexHelper 类型的对象,用于生成网格数据。在该方法中,可以通过调用 VertexHelper 的方法来添加顶点、三角形和颜色等信息,从而生成网格数据
在重写该方法时,需要注意以下几点:
-
在方法中添加顶点、三角形和颜色等信息时,需要按照一定的顺序添加,以确保生成的网格数据正确无误
-
在方法中添加顶点、三角形和颜色等信息时,需要注意坐标系的转换,以确保生成的网格数据与 UI 元素的位置和大小一致
-
在方法中添加顶点、三角形和颜色等信息时,需要注意性能问题,尽量避免生成过多的网格数据,以提高渲染效率
「具体实现」
根据遮罩区域和镂空区域交叉后的位置,计算出 8 个顶点,绘制遮罩区域的三角形,保留镂空区域
-
计算镂空区域相对于遮罩区域的包围盒坐标,即交叉的四个顶点坐标
Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(this.rectTransform, this._target);
this._targetBoundsMin = bounds.min;
this._targetBoundsMax = bounds.max;
-
根据交叉的顶点坐标,填充顶点数据,绘制镂空区域外的三角形
protected override void OnPopulateMesh(VertexHelper vh)
{
if (this._targetBoundsMin == Vector3.zero && this._targetBoundsMax == Vector3.zero)
{
base.OnPopulateMesh(vh);
return;
}
vh.Clear();
Vector2 pivot = this.rectTransform.pivot;
Rect rect = this.rectTransform.rect;
float outerLeftBottomX = -pivot.x * rect.width;
float outerLeftBottomY = -pivot.y * rect.height;
float outerRightTopX = (1 - pivot.x) * rect.width;
float outerRightTopY = (1 - pivot.y) * rect.height;
// 准备顶点数据
UIVertex vert = UIVertex.simpleVert;
// 填充顶点颜色
vert.color = this.color;
// 计算遮罩区域顶点位置
// 0 outer LeftTop
vert.position = new Vector3(outerLeftBottomX, outerRightTopY);
vh.AddVert(vert);
// 1 outer RightTop
vert.position = new Vector3(outerRightTopX, outerRightTopY);
vh.AddVert(vert);
// 2 outer RightBottom
vert.position = new Vector3(outerRightTopX, outerLeftBottomY);
vh.AddVert(vert);
// 3 outer LeftBottom
vert.position = new Vector3(outerLeftBottomX, outerLeftBottomY);
vh.AddVert(vert);
// 计算镂空区域顶点位置
// 4 outer LeftTop
vert.position = new Vector3(_targetBoundsMin.x, _targetBoundsMax.y);
vh.AddVert(vert);
// 5 inner RightTop
vert.position = new Vector3(_targetBoundsMax.x, _targetBoundsMax.y);
vh.AddVert(vert);
// 6 inner RightBottom
vert.position = new Vector3(_targetBoundsMax.x, _targetBoundsMin.y);
vh.AddVert(vert);
// 7 inner LeftBottom
vert.position = new Vector3(_targetBoundsMin.x, _targetBoundsMin.y);
vh.AddVert(vert);
// 向缓冲区中添加三角形
vh.AddTriangle(4, 0, 1);
vh.AddTriangle(4, 1, 5);
vh.AddTriangle(5, 1, 2);
vh.AddTriangle(5, 2, 6);
vh.AddTriangle(6, 2, 3);
vh.AddTriangle(6, 3, 7);
vh.AddTriangle(7, 3, 0);
vh.AddTriangle(7, 0, 4);
}
镂空区域内事件透传
「方法介绍」
bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
给定一个点和一个摄像机,判断射线投射是否有效,通常用于自定义 UI 元素的交互,通过继承 ICanvasRaycastFilter 接口并实现该方法来自定义 UI 元素射线检测效果
screenPoint 参数是一个屏幕坐标系下的二维向量,表示射线检测的位置,eventCamera 参数是一个 Camera 类型的对象,表示射线检测所使用的相机。该方法返回一个 bool 类型的值,表示 UI 元素是否可以被射线检测到
「具体实现」
在该方法中判断事件坐标是否在镂空区域内,如果在该区域内则透传事件
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
// 透传镂空区域事件
return !RectTransformUtility.RectangleContainsScreenPoint(this._target, sp, eventCamera);
}
游戏开发手记,,,
源码商店
小程序