unity 使用Image的OnPopulateMesh绘制雷达图

在c#中使用一个类继承Image用来代替Image组件用。在该类中使用OnPopulateMesh()函数重新绘制网格,从而形成需要的图形。

其中需要先使用vertexHelper.AddVert(顶点位置,颜色,uv坐标)来添加顶点。颜色使用面板中设置的那个颜色(这里填个color即可)。由于不需要纹理贴图,uv坐标随便填个Vector2.zero即可。添加的顺序就会是这个顶点的编号,从0开始。

如何得到顶点位置:第一个顶点位置用中心位置,其它的通过计算得到。通过三角函数得到最内点和最外点的坐标(当值为0时雷达图相应那个点就会在最内点这个位置。这里最内点没有使用中心点),坐标为(距中心距离*cos(θ),距中心距离*sin(θ),0),θ为2*π/边数*顶点顺序编号。顶点编号范围0到边数-1。得到最内点和最外点坐标之后通过差值求得这两点间的那个顶点位置。当该项能力为0,则在最内点,能力增大时向外移动,能力为1时到达最外点。依次如此计算得到圆心外的各个顶点。

之后再通过vertexHelper.AddTriangle(顶点1编号,顶点2编号,顶点3编号),以3个顶点组成一个三角面片。一般需要顺时针选择顶点,虽然对UI来说顺序反了一般也能显示。这里的第顶点1都用圆心顶点,其它2个依次更换,产生需要的全部三角面片。

 

 如果需要在运行中修改顶点位置,可在修改后用SetVerticesDirty()函数刷新顶点。

下图为一组顶点和三角面片的示例。三角面片组成了要显示的雷达图。

 脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RadarImage : Image
{
    [SerializeField]//让私有变量在面板显示
    private int sideCount=5;//雷达图边数。这项应当在启动前设置而不要在运行中修改。
    public float minDistance=5;//顶点与中心的最小距离
    public float maxDistance = 50;//顶点与中心的最大距离
    public float[] eachPercent;//每一项数值的大小(0~1表示的百分比)
    private Vector3[] innerPositions;//雷达图最内圈的点
    private Vector3[] exteriorPositions;//雷达图最外圈的点
    public float initialRadian = 0;//第一个顶点的起始弧度。默认从正右方开始。

    protected override void Awake()
    {
        eachPercent = new float[sideCount];
    }

    private void Update()
    {
        //SetVerticesDirty();//刷新顶点,可用于实时修改
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear();//清除原信息
        
        initPositions();
        AddVerts(vh);
        AddTriangles(vh);
    }

    /// <summary>
    /// 初始化雷达图最内圈和最外圈的点
    /// </summary>
    private void initPositions()
    {
        innerPositions = new Vector3[sideCount];
        exteriorPositions = new Vector3[sideCount];
        float tempRadian = initialRadian;
        float radiamDelta = 2 * Mathf.PI / sideCount;//每两个相邻顶点相差的弧度

        for (int i = 0; i < sideCount; i++)
        {
            innerPositions[i] = new Vector3(minDistance * Mathf.Cos(tempRadian), minDistance * Mathf.Sin(tempRadian), 0);
            exteriorPositions[i] = new Vector3(maxDistance * Mathf.Cos(tempRadian), maxDistance * Mathf.Sin(tempRadian), 0);
            tempRadian += radiamDelta;
        }
    }

    /// <summary>
    /// 添加形成三角面片用的顶点
    /// </summary>
    /// <param name="vh"></param>
    private void AddVerts(VertexHelper vh)
    {
        vh.AddVert(Vector3.zero, color, Vector2.zero);//添加轴心点位置为第一个顶点
        for (int i = 0; i < sideCount; i++)
        {
            //通过在最内点和最外点间差值得到雷达图顶点实际位置,并添加到为vh的顶点。由于并没有图案,最后一项的uv坐标就随便填了。
            vh.AddVert(Vector3.Lerp(innerPositions[i], exteriorPositions[i], eachPercent[i]), color, Vector2.zero);
        }
    }
    /// <summary>
    /// 添加三角面片
    /// </summary>
    /// <param name="vh"></param>
    private void AddTriangles(VertexHelper vh)
    {
        for (int i = 0; i < sideCount-1; i++)
        {
            vh.AddTriangle(0, i + 1, i + 2);
        }
        vh.AddTriangle(0, sideCount, 1);
    }
}

这里定义的变量并不能直接在面板显示,如果需要在面板显示,还需要加一个脚本并放在一个名为Editor的文件夹下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(RadarImage), true)]
[CanEditMultipleObjects]
public class RadarImageEditor :  UnityEditor.UI.ImageEditor
{
    SerializedProperty _sideCount;
    SerializedProperty _minDistance;
    SerializedProperty _maxDistance;
    SerializedProperty _eachPercent;
    SerializedProperty _initialRadian;

    protected override void OnEnable()
    {
        base.OnEnable();
        _sideCount = serializedObject.FindProperty("sideCount");
        _minDistance = serializedObject.FindProperty("minDistance");
        _maxDistance = serializedObject.FindProperty("maxDistance");
        _eachPercent = serializedObject.FindProperty("eachPercent");
        _initialRadian = serializedObject.FindProperty("initialRadian");
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        serializedObject.Update();

        EditorGUILayout.PropertyField(_sideCount);
        EditorGUILayout.PropertyField(_minDistance);
        EditorGUILayout.PropertyField(_maxDistance);
        EditorGUILayout.PropertyField(_eachPercent,true);
        EditorGUILayout.PropertyField(_initialRadian);

        RadarImage radar = target as RadarImage;


        serializedObject.ApplyModifiedProperties();
        if (GUI.changed)
        {
            EditorUtility.SetDirty(target);
        }

    }
}

在启动前设置好边数(Side Count),启动后可以设置每一项的能力(Each Percent)。通过Color控制颜色和透明度。

默认从正右方开始绘制。可以修改初始弧度(Intial Radian)改变开始绘制的位置,让图像进行旋转:

 覆盖在一张图片上,形成通常见到的雷达图:

### 如何在 Unity 中实现雷达扫描效果 #### 准备工作 为了创建雷达扫描效果,在Unity环境中需先准备好场景基础构建。对于初学者而言,虽然有建议放弃治疗的说法[^1],但实际上通过合理的学习路径可以逐步掌握所需技能。 #### 创建雷达界面布局 将项目设置为2D模式有助于简化开发流程。具体操作是在Game视中切换至2D视角。接着向Canvas下添加Image组件作为背景显示,并放置Text用于描述信息或数值输出,以此搭建起基本的UI框架[^4]。 #### 编写脚本控制逻辑 编写C#脚本来模拟雷达旋转及目标检测功能至关重要。下面是一个简单的示例代码片段来启动这一过程: ```csharp using UnityEngine; public class RadarScanner : MonoBehaviour { public Transform radarOrigin; // 定义雷达中心位置 private float angle = 0; void Update() { Scan(); } void Scan(){ angle += Time.deltaTime * 60f % 360; // 控制扫射速度与角度范围 Debug.DrawRay(radarOrigin.position, Quaternion.Euler(0,angle,0)*Vector3.forward*10, Color.green); RaycastHit hitInfo; if (Physics.Raycast(radarOrigin.position,Quaternion.Euler(0,angle,0)*Vector3.forward,out hitInfo, Mathf.Infinity)){ Debug.Log($"Detected object {hitInfo.collider.name} at distance {hitInfo.distance}"); } } } ``` 此段程序实现了基于时间增量更新的角度变化,利用`Debug.DrawRay()`函数绘制出一条代表当前扫描方向的绿色光线,并尝试发射射线进行碰撞测试以发现周围物体的存在情况。 #### 整合视觉特效 为了让雷达的效果更加逼真,可以在上述基础上加入更多形化元素,比如使用Sprite动画表现扇形区域内的光晕扩散现象,或是当探测到特定对象时触发特殊音效反馈等。 #### 获取外部数据源支持(可选) 如果希望进一步扩展应用,则可以通过集成像TUIO这样的交互协议获取来自真实物理设备的数据输入,从而增强用户体验的真实性和互动性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值