自我介紹
廣東雙非一本的大三小白,計科專業,想在製作畢設前夯實基礎,畢設做出一款屬於自己的遊戲!
使用頂點描繪圓形圖片
這是製作一個以後都能泛用的圓形image(可以做CD技能相關)
主要是兩個腳本
- CircleImage.cs
- CircleImageEditor.cs
CircleImage.cs 主要是用 segements 來控制邊的數量,畢竟圖像由多個三角形構成,只要三角形(直邊)夠多,看上去就像是圓形
該腳本還完成的功能有:
- 製作類似技能CD的功能
- 精確點擊(可以掛載一個Button組件測試)
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Sprites;
using UnityEngine.UI;
public class CircleImage : Image
{
/// <summary>
/// 圓形由多少塊三角形拼成
/// </summary>
[SerializeField]
private int segements = 100;
//顯示部分佔圓形的百分比.
[SerializeField]
private float showPercent = 1;
[SerializeField]
private Color32 hideColor = new Color32(60, 60, 60, 255);
private List<Vector3> _vertexList;
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
_vertexList = new List<Vector3>();
AddVertex(vh, segements);
AddTriangle(vh, segements);
}
private void AddVertex(VertexHelper vh, int segements)
{
float width = rectTransform.rect.width;
float heigth = rectTransform.rect.height;
int realSegments = (int)(segements * showPercent);
Vector4 uv = overrideSprite != null ? DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
float uvWidth = uv.z - uv.x;
float uvHeight = uv.w - uv.y;
Vector2 uvCenter = new Vector2(uvWidth * 0.5f, uvHeight * 0.5f);
Vector2 convertRatio = new Vector2(uvWidth / width, uvHeight / heigth);
float radian = (2 * Mathf.PI) / segements;
float radius = width * 0.5f;
Vector2 originPos = new Vector2((0.5f - rectTransform.pivot.x) * width, (0.5f - rectTransform.pivot.y) * heigth);
Vector2 vertPos = Vector2.zero;
Color32 colorTemp = GetOriginColor();
UIVertex origin = GetUIVertex(colorTemp, originPos, vertPos, uvCenter, convertRatio);
vh.AddVert(origin);
int vertexCount = realSegments == 0 ? 0 : realSegments + 1;
float curRadian = 0;
Vector2 posTermp = Vector2.zero;
for (int i = 0; i <= segements; i++)
{
float x = Mathf.Cos(curRadian) * radius;
float y = Mathf.Sin(curRadian) * radius;
curRadian += radian;
if (i < vertexCount)
{
colorTemp = color;
}
else
{
colorTemp = hideColor;
}
posTermp = new Vector2(x, y);
UIVertex vertexTemp = GetUIVertex(colorTemp, posTermp + originPos, posTermp, uvCenter, convertRatio);
vh.AddVert(vertexTemp);
_vertexList.Add(posTermp + originPos);
}
}
private Color32 GetOriginColor()
{
Color32 colorTemp = (Color.white - hideColor) * showPercent;
return new Color32(
(byte)(hideColor.r + colorTemp.r),
(byte)(hideColor.g + colorTemp.g),
(byte)(hideColor.b + colorTemp.b),
255);
}
private void AddTriangle(VertexHelper vh, int realSegements)
{
int id = 1;
for (int i = 0; i < realSegements; i++)
{
vh.AddTriangle(id, 0, id + 1);
id++;
}
}
private UIVertex GetUIVertex(Color32 col, Vector3 pos, Vector2 uvPos, Vector2 uvCenter, Vector2 uvScale)
{
UIVertex vertexTemp = new UIVertex();
vertexTemp.color = col;
vertexTemp.position = pos;
vertexTemp.uv0 = new Vector2(uvPos.x * uvScale.x + uvCenter.x, uvPos.y * uvScale.y + uvCenter.y);
return vertexTemp;
}
public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
{
Vector2 localPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out localPoint);
return IsValid(localPoint);
}
private bool IsValid(Vector2 localPoint)
{
return GetCrossPointNum(localPoint, _vertexList) % 2 == 1;
}
private int GetCrossPointNum(Vector2 localPoint, List<Vector3> vertexList)
{
int count = 0;
Vector3 vert1 = Vector3.zero;
Vector3 vert2 = Vector3.zero;
int vertCount = vertexList.Count;
for (int i = 0; i < vertCount; i++)
{
vert1 = vertexList[i];
vert2 = vertexList[(i + 1) % vertCount];
if (IsYInRang(localPoint, vert1, vert2))
{
if (localPoint.x < GetX(vert1, vert2, localPoint.y))
{
count++;
}
}
}
return count;
}
private bool IsYInRang(Vector2 localPoint, Vector3 vert1, Vector3 vert2)
{
if (vert1.y > vert2.y)
{
return localPoint.y < vert1.y && localPoint.y > vert2.y;
}
else
{
return localPoint.y < vert2.y && localPoint.y > vert1.y;
}
}
private float GetX(Vector3 vert1, Vector3 vert2, float y)
{
float k = (vert1.y - vert2.y) / (vert1.x - vert2.x);
return vert1.x + (y - vert1.y) / k;
}
}
只有這一個腳本是不能展示我們自定義的一些字段的,比如 segements 所以我們需要一個editor文件
CircleImageEditor.cs
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(CircleImage), true)]
[CanEditMultipleObjects]
public class CircleImageEditor : UnityEditor.UI.ImageEditor
{
SerializedProperty _fillPercent;
SerializedProperty _segements;
SerializedProperty _hideColor;
protected override void OnEnable()
{
base.OnEnable();
_fillPercent = serializedObject.FindProperty("showPercent");
_segements = serializedObject.FindProperty("segements");
_hideColor = serializedObject.FindProperty("hideColor");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
EditorGUILayout.Slider(_fillPercent, 0, 1, new GUIContent("showPercent"));
EditorGUILayout.PropertyField(_segements);
EditorGUILayout.PropertyField(_hideColor);
serializedObject.ApplyModifiedProperties();
if (GUI.changed)
{
EditorUtility.SetDirty(target);
}
}
}
測試:
CD功能
精確點擊
不規則圖形點擊
unity自帶的精確點擊策略
新添一個 IrregularShapeClick.cs 原理是識別圖片透明度,小於0.1的就不能被點擊
using UnityEngine;
using UnityEngine.UI;
public class IrregularShapeClick : MonoBehaviour
{
void Start()
{
GetComponent<Image>().alphaHitTestMinimumThreshold = 0.1f;
}
}
除了要掛載上述腳本,還需要在圖片的高級設置中設置,一定要勾選 Read/Write Enabled 開啓讀寫模式
如果不開啓,unity是不允許你訪問image裏的alphaHitTestMinimumThreshold屬性的
測試成功
但是這種方法不推薦,開啓讀寫模式之後,會增大圖片內存,會有性能負擔
我們需要使用 Polygon Collider 2D 組件配合我們自定義的腳本完成精確點擊
爲此我們需要兩個腳本:
- CustomImage.cs
- CustomImageEditor.cs
CustomImage.cs
using UnityEngine;
using UnityEngine.UI;
public class CustomImage : Image
{
private PolygonCollider2D _polygon;
private PolygonCollider2D Polygon
{
get
{
if (_polygon == null) _polygon = GetComponent<PolygonCollider2D>();
return _polygon;
}
}
public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
{
Vector3 point;
RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, screenPoint, eventCamera, out point);
return Polygon.OverlapPoint(point);
}
}
CustomImageEditor.cs
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
public class CustomImageEditor : Editor
{
private const string UI_LAYER = "UI";
[MenuItem("GameObject/UI/CustomImage", priority = 0)]
private static void AddImage()
{
Transform canvasTrans = GetCanvasTrans();
Transform image = AddCustomImage();
if (Selection.activeGameObject!= null && Selection.activeGameObject.layer == LayerMask.NameToLayer(UI_LAYER))
image.SetParent(Selection.activeGameObject.transform);
else
image.SetParent(canvasTrans);
image.localPosition = Vector3.zero;
}
private static Transform GetCanvasTrans()
{
Canvas canvas = GameObject.FindObjectOfType<Canvas>();
if (canvas == null)
{
GameObject canvasObj = new GameObject("Canvas");
canvasObj.layer = LayerMask.NameToLayer(UI_LAYER);
canvasObj.AddComponent<RectTransform>();
canvasObj.AddComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
canvasObj.AddComponent<CanvasScaler>();
canvasObj.AddComponent<GraphicRaycaster>();
return canvasObj.transform;
}
else
{
return canvas.transform;
}
}
private static Transform AddCustomImage()
{
GameObject image = new GameObject("Image");
image.layer = LayerMask.NameToLayer(UI_LAYER);
image.AddComponent<RectTransform>();
image.AddComponent<PolygonCollider2D>();
image.AddComponent<CustomImage>();
return image.transform;
}
}
在編輯器中還設置了在hierarchy中添加 CustomImage 上面的 CircleImage 也可以在Editor腳本中進行如此操作
測試成功