Unity如何从0实现技能CD遮罩

需求描述


我们需要实现一个类似英雄联盟中的技能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;

  }

效果展示

posted @ 2024-02-22 22:46  dewxin  阅读(42)  评论(0编辑  收藏  举报