需求描述
我们需要实现一个类似英雄联盟中的技能CD遮罩,施放技能后,技能遮罩占满技能图标,随着时间推移,技能遮罩顺时针减少遮挡面积,CD结束时,遮罩应完全消失。
需求分析
由于每个时刻,都会有一条线从中心点射向上面那条边的中点,我们自然可以想到将遮罩面片分为8个三角形。
并且我们只需要按照给定的角度angle,发出射线相交于长方形,获得起点,然后从起点开始顺时针遍历长方形边上的点,直到长方形上面那条边的中点。
代码
private UIMeshInfo GetMeshInfoFilled_Radial360()
{
var uvRect = NGUIMath.ConvertToTexCoords(SpriteData.Rect, atlasTexture.width, atlasTexture.height);
var rectCorners = RenderTargetLocalCorners;
var meshInfo = new UIMeshInfo();
var vertexBeforeCutList = new List<Vector3>
{
(rectCorners.TopLeft + rectCorners.TopRight) / 2, //↑
rectCorners.TopRight, // ↗
(rectCorners.TopRight + rectCorners.BottomRight) / 2, //→
rectCorners.BottomRight, //↘
(rectCorners.BottomLeft + rectCorners.BottomRight) / 2, //↓
rectCorners.BottomLeft, //↙
(rectCorners.BottomLeft + rectCorners.TopLeft) / 2, //←
rectCorners.TopLeft, //↖
};
var uvBeforeCutList = new List<Vector2>
{
new Vector2(uvRect.center.x, uvRect.yMax), //↑
new Vector2(uvRect.xMax, uvRect.yMax), // ↗
new Vector2(uvRect.xMax, uvRect.center.y), //→
new Vector2(uvRect.xMax, uvRect.yMin), //↘
new Vector2(uvRect.center.x, uvRect.yMin), //↓
new Vector2(uvRect.xMin, uvRect.yMin), //↙
new Vector2(uvRect.xMin, uvRect.center.y), //←
new Vector2(uvRect.xMin, uvRect.yMax), //↖
};
fillAmount = Mathf.Clamp01(fillAmount);
var angle = fillAmount * 360;
var indexFromEnd = Mathf.CeilToInt(angle/45);
var vertexAfterCutList = new List<Vector3>();
var uvAfterCutList = new List<Vector2>();
var colorList = new List<Color>();
vertexAfterCutList.Add(rectCorners.Center);
uvAfterCutList.Add(uvRect.center);
colorList.Add(color);
var indexList = new List<int>();
int currentIndex = 0;
for(int i = vertexBeforeCutList.Count-indexFromEnd; i < vertexBeforeCutList.Count; i++)
{
var vertex = vertexBeforeCutList[i];
var uv = uvBeforeCutList[i];
//TODO 还需要调整uv
if (i == vertexBeforeCutList.Count-indexFromEnd)
{
vertex = GetPointOnRect(rectCorners.Center, angle, rectCorners.Size / 2);
}
vertexAfterCutList.Add(vertex);
uvAfterCutList.Add(uv);
colorList.Add(color);
currentIndex++;
indexList.Add(0);
indexList.Add(currentIndex);
indexList.Add(currentIndex+1);
}
vertexAfterCutList.Add(vertexBeforeCutList[0]);
uvAfterCutList.Add(uvBeforeCutList[0]);
colorList.Add(color);
meshInfo.uvList = uvAfterCutList;
meshInfo.vertexList = vertexAfterCutList;
for(int i = 0; i < vertexAfterCutList.Count; i++)
{
vertexAfterCutList[i] = transform.TransformPoint(vertexAfterCutList[i]);
}
meshInfo.colorList = colorList;
meshInfo.indexList = indexList;
return meshInfo;
}
private Vector2 GetPointOnRect(Vector2 center,float angle, Vector2 halfSize)
{
var radian = angle * Mathf.Deg2Rad;
//https://python.tutorialink.com/finding-points-on-a-rectangle-at-a-given-angle/
//TODO 这里角度需要通过 长方形的 长和宽 arctan来算的
if (315 <=angle || angle <= 45)
{
return new Vector2(center.x - halfSize.y * Mathf.Tan(radian), center.y + halfSize.y);
}
else if(135<=angle && angle<=225)
{
return new Vector2(center.x + halfSize.y * Mathf.Tan(radian), center.y - halfSize.y);
}
else if(45<=angle && angle <= 135)
{
return new Vector2(center.x - halfSize.x, center.y + halfSize.x / Mathf.Tan(radian));
}
else if(225<=angle && angle <= 315)
{
return new Vector2(center.x + halfSize.x, center.y - halfSize.x / Mathf.Tan(radian));
}
return default;
}