Unity 实用方法 合集

news2024/11/18 5:41:49

Unity 实用方法 合集

  • Unity 打字机效果
  • 2D 坐标旋转计算
  • 球面坐标求值
  • 平滑移动
  • 鼠标位置获取2D
  • 屏幕坐标转世界坐标
  • 物体朝向目标
  • 多物体中心点生成
  • 本地图片加载
  • 画面线框显示
    • 画面线框显示 搭载效果
  • 贝塞尔曲线绘制
    • 贝塞尔曲线绘制 搭载效果
  • 网格弯曲
    • 网格弯曲 搭载效果
  • Delaunay 模型生成

代码很简单没有难度,都有注解,可以收藏一下方便后期使用。

Unity 打字机效果

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

/// <summary>
/// 打字机效果
/// </summary>
public class TextWriter_ZH : MonoBehaviour
{
    [Header("打字速度")]
    public float _Delay = 0.1f;
    [Header("完整的文字")]
    public string _FullText;

    // 当前显示的文字
    private string _CurrentText = "";
    // 计时器
    private float _Timer;
    // 当前字符索引
    private int _CurrentIndex = 0; 

    void Start()
    {
        StartCoroutine(ShowText());
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            // 获取当前时间,精确到秒级
            float _CurrentTime = Time.time;
            // 将秒级时间转换为毫秒级时间
            int _Milliseconds = (int)((_CurrentTime - Mathf.Floor(_CurrentTime)) * 1000); 

            Debug.Log("Current Time: " + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss." + _Milliseconds.ToString("000")));

            _FullText = "当前时间是: " + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss." + _Milliseconds.ToString("000"));

            //初始化
            GetComponent<Text>().text = "";
            _CurrentIndex = 0;
        }

    }


    /// <summary>
    /// 文字显示
    /// </summary>
    /// <returns></returns>
    IEnumerator ShowText()
    {
        //字符长度判断
        while (_CurrentIndex < _FullText.Length)
        {
            //时间累加
            _Timer += Time.deltaTime;

            if (_Timer >= _Delay)
            {
                //文字输出
                _CurrentText += _FullText[_CurrentIndex];
                //显示
                GetComponent<Text>().text = _CurrentText;
                //步进
                _CurrentIndex++;
                _Timer = 0;
            }

            yield return null;
        }
    }
}

2D 坐标旋转计算

原理:以自身为原点,计算增加半径 R 并且旋转 指定角度后 圆上坐标
应用:应用方向有很多吧 比如旋转动量计算,等边三角形遍历等
  /// <summary>
    /// 2D 坐标旋转计算
    /// </summary>
    public void AngleCalculation2D()
    {
        // 假设原坐标的 x 值为 3
        double _X = 3;
        // 假设原坐标的 y 值为 4
        double _Y = 4;
        // 假设半径为 5
        double _Radius = 5;
        // 假设旋转角度为 30 度
        double _Angle = 30;

        // 将角度转换为弧度
        double _Radian = _Angle * Math.PI / 180;

        // 计算旋转后的 X Y 坐标
        double _NewX = _X * Math.Cos(_Radian) - _Y * Math.Sin(_Radian);
        double _NewY = _X * Math.Sin(_Radian) + _Y * Math.Cos(_Radian);

        // 计算旋转后点的距离原点的半径
        double _NewRadius = Math.Sqrt(_NewX * _NewX + _NewY * _NewY);

        // 考虑半径对坐标的缩放
        double _FinalX = _NewX * _Radius / _NewRadius;
        double _FinalY = _NewY * _Radius / _NewRadius;

        Vector2 _Vector2 = new Vector2((float)_FinalX, (float)_FinalY);

        Console.WriteLine("旋转后的坐标:({0})", _Vector2);
    }

球面坐标求值

原理:根据给定的圆心坐标和目标坐标,得到圆心到目标位置的向量。根据要求的延长半径和旋转角度
	  再把夹角转换为弧度之后得到目标点的球面坐标值 并返回
应用:球面计算和均匀分配
 /// <summary>
    /// 3D 坐标旋转计算
    /// </summary>
    /// <param 圆心坐标="_Center"></param>
    /// <param 半径="_Radius"></param>
    /// <param 目标位置="_PointA"></param>
    /// <param 旋转角度="_Angle"></param>
    public void AngleCalculation3D(Vector3 _Center, float _Radius, Vector3 _PointA, float _Angle)
    {
         假设圆心坐标为 (1, 2, 3)
        //Vector3 _Center = new Vector3(1, 2, 3);
         假设半径为 5
        //float _Radius = 5;
         假设三维坐标点 a 为 (4, 5, 6)
        //Vector3 _PointA = new Vector3(4, 5, 6);
         假设旋转角度为 30 度
        //float _Angle = 30;

        //转换后球面坐标值
        Vector3 _RotatedPoint = RotatePoint(_Center, _PointA, _Radius, _Angle);
        Console.WriteLine("旋转后的球面坐标:");
        //magnitude 返回该向量的长度
        Console.WriteLine("半径:{0}", _RotatedPoint.magnitude);
        //Rad2Deg 弧度到度数的转换常数
        Console.WriteLine("纬度:{0}", Mathf.Rad2Deg * Mathf.Asin(_RotatedPoint.normalized.y));
        Console.WriteLine("经度:{0}", Mathf.Rad2Deg * Mathf.Atan2(_RotatedPoint.normalized.z, _RotatedPoint.normalized.x));
    }
    /// <summary>
    /// 球面坐标求值
    /// </summary>
    /// <param 圆心坐标="_Center"></param>
    /// <param 目标位置="_Point"></param>
    /// <param 半径="_Radius"></param>
    /// <param 旋转角度="_Angle"></param>
    /// <returns></returns>
    public static Vector3 RotatePoint(Vector3 _Center, Vector3 _Point, float _Radius, float _Angle)
    {
        // 统一坐标系
        Vector3 _ShiftedPoint = _Point - _Center;

        // 计算旋转后的坐标
        float _Radian = _Angle * Mathf.Deg2Rad;
        float _RotatedX = _ShiftedPoint.x * Mathf.Cos(_Radian) - _ShiftedPoint.z * Mathf.Sin(_Radian);
        float _RotatedY = _ShiftedPoint.y;
        float _RotatedZ = _ShiftedPoint.x * Mathf.Sin(_Radian) + _ShiftedPoint.z * Mathf.Cos(_Radian);

        // 计算旋转后的球面坐标
        Vector3 _RotatedPoint = new Vector3(_RotatedX, _RotatedY, _RotatedZ).normalized * _Radius;

        // 返回旋转后的点
        return _RotatedPoint;
    }

平滑移动

原理:Vector3.Lerp 插值计算  然后返回趋近值
应用:字面意思  平滑 可以应用到双曲线螺旋
  /// <summary>
    /// 平滑移动
    /// </summary>
    /// <param name="_ObjectToMove"></param>
    /// <param name="_TargetPosition"></param>
    /// <param name="_Duration"></param>
    /// <returns></returns>
    public IEnumerator SmoothMoveObject(Transform _ObjectToMove, Vector3 _TargetPosition, float _Duration)
    {
        float _ElapsedTime = 0f;
        Vector3 _StartingPosition = _ObjectToMove.position;

        while (_ElapsedTime < _Duration)
        {
            _ObjectToMove.position = Vector3.Lerp(_StartingPosition, _TargetPosition, _ElapsedTime / _Duration);
            _ElapsedTime += Time.deltaTime;
            yield return null;
        }

        _ObjectToMove.position = _TargetPosition;
    }

鼠标位置获取2D

原理:Camera.main.ScreenToWorldPoint
应用:获取鼠标在2D空间中的位置,通常用于2D游戏的鼠标交互
   /// <summary>
    /// 鼠标位置获取
    /// </summary>
    /// <returns></returns>

    public Vector2 GetMousePosition2D()
    {
        Vector3 _MousePosition = Input.mousePosition;
        _MousePosition.z = -Camera.main.transform.position.z;
        return Camera.main.ScreenToWorldPoint(_MousePosition);
    }

屏幕坐标转世界坐标

原理:Camera.main.ScreenToWorldPoint
应用:将屏幕坐标转换为世界坐标,通常用于将鼠标点击位置转换为游戏世界中的坐标
    /// <summary>
    /// 屏幕坐标转世界坐标
    /// </summary>
    /// <param name="screenPosition"></param>
    /// <returns></returns>
    public Vector3 ScreenToWorldPoint(Vector3 _ScreenPosition)
    {
        return Camera.main.ScreenToWorldPoint(_ScreenPosition);
    }

物体朝向目标

原理: Quaternion.LookRotation()
应用:使物体的正面朝向目标位置
   /// <summary>
    /// 物体朝向目标
    /// </summary>
    /// <param 当前物体 ="_ObjectToRotate"></param>
    /// <param 朝向目标 ="_TargetPosition"></param>
    public void LookAtTarget(Transform _ObjectToRotate, Vector3 _TargetPosition)
    {
        Vector3 _Direction = _TargetPosition - _ObjectToRotate.position;
        Quaternion _Rotation = Quaternion.LookRotation(_Direction);
        _ObjectToRotate.rotation = _Rotation;
    }

多物体中心点生成

原理:所有位置向量累加并除以数量得到平均值
应用:鱼群算法中心计算、多物体连线等
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 多物体中心点判定
/// </summary>
public class ObjectsCenterPoint_ZH : MonoBehaviour
{
    public List<GameObject> _GameObject; // 存储多个物体的数组

    private void Start()
    {
        // 计算中心点
        Vector3 _CenterPoint = CalculateCenterPoint();

        // 在场景中创建一个表示中心点的标记物体
        CreateCenterPointMarker(_CenterPoint);
    }

    /// <summary>
    /// 中心点坐标
    /// </summary>
    /// <returns></returns>
    Vector3 CalculateCenterPoint()
    {
        Vector3 _CenterPoint = Vector3.zero;

        // 累加所有物体的位置
        foreach (GameObject obj in _GameObject)
        {
            _CenterPoint += obj.transform.position;
        }

        // 求取平均位置
        _CenterPoint /= _GameObject.Count;

        return _CenterPoint;
    }

    /// <summary>
    /// 中心点位置 物体生成
    /// </summary>
    /// <param 中心点位置="_CenterPoint"></param>
    void CreateCenterPointMarker(Vector3 _CenterPoint)
    {
        // 在场景中创建一个标记物体表示中心点
        GameObject _Marker = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        _Marker.transform.position = _CenterPoint;
        _Marker.transform.localScale = Vector3.one * 0.5f;
        _Marker.GetComponent<Renderer>().material.color = Color.red;
    }
}

本地图片加载

原理:IO流 读取
应用:场景加载界面或则动态变更图标
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 图片加载
/// </summary>
public class ImageLoader_ZH : MonoBehaviour
{
    public string _ImagePath;
    public RawImage _RawImage;

    private void Start()
    {
        // 加载本地图片
        Texture2D _Texture = LoadLocalImage(_ImagePath);

        if (_Texture != null)
        {
            // 将纹理设置给RawImage组件进行显示
            _RawImage.texture = _Texture;
        }
        else
        {
            Debug.LogError("没有显示载体。");
        }
    }

    private Texture2D LoadLocalImage(string _Path)
    {
        // 使用Unity的API加载本地图片
        // 创建一个空的Texture2D对象
        Texture2D _Texture = new Texture2D(2, 2);
        // 读取图片数据
        byte[] _ImageData = System.IO.File.ReadAllBytes(_Path);
        // 将图片数据加载到Texture2D对象中
        bool _Success = _Texture.LoadImage(_ImageData); 

        if (_Success)
        {
            return _Texture;
        }
        else
        {
            Destroy(_Texture); // 加载失败时销毁Texture2D对象
            return null;
        }
    }
}

画面线框显示

原理: 就是 GL 绘画应用变形
应用:画面标注、主题框选等
	/// <summary>
    /// 在渲染后执行
    /// 屏幕框选
    /// </summary>
    private void OnPostRender()
    {
        //画线这种操作推荐在 OnPostRender()里进行 而不是直接放在Update,所以需要标志来开启
        if (_IsSelecting)
        {
            //开始绘图位置
            var startMousePosition = MousePosition;
            //鼠标当前位置
            Vector3 _MouseEnd = Input.mousePosition;
            //保存摄像机变换矩阵
            GL.PushMatrix();

            //显示材质
            if (!_RectMat)
            {
                return;
            } 
            else
            {
                //生成画线的材质
                _RectMat = new Material(Shader.Find("UI/Default"));
                //GameObject(游戏对象)没有显示在层次结构中,没有保存到场景中,也没有被Resources.UnloadUnusedAssets卸载。
                _RectMat.hideFlags = HideFlags.HideAndDontSave;
                //Gameobject(游戏对象)没有显示在层次结构中,没有保存到场景中,也没有被Resources.UnloadUnusedAssets卸载
                _RectMat.shader.hideFlags = HideFlags.HideAndDontSave;
            }

            _RectMat.SetPass(0);
            //设置用屏幕坐标绘图
            GL.LoadPixelMatrix();
            GL.Begin(GL.QUADS);
            //设置颜色和透明度,方框内部透明
            GL.Color(new Color(_RectColor.r, _RectColor.g, _RectColor.b, 0.1f));
            GL.Vertex3(startMousePosition.x, startMousePosition.y, 0);
            GL.Vertex3(_MouseEnd.x, startMousePosition.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseEnd.y, 0);
            GL.Vertex3(startMousePosition.x, _MouseEnd.y, 0);
            GL.End();
            GL.Begin(GL.LINES);
            //设置方框的边框颜色 边框不透明
            GL.Color(_RectColor);
            GL.Vertex3(startMousePosition.x, startMousePosition.y, 0);
            GL.Vertex3(_MouseEnd.x, startMousePosition.y, 0);
            GL.Vertex3(_MouseEnd.x, startMousePosition.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseEnd.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseEnd.y, 0);
            GL.Vertex3(startMousePosition.x, _MouseEnd.y, 0);
            GL.Vertex3(startMousePosition.x, _MouseEnd.y, 0);
            GL.Vertex3(startMousePosition.x, startMousePosition.y, 0);
            GL.End();
            //恢复摄像机投影矩阵
            GL.PopMatrix();
        }

    }

画面线框显示 搭载效果

请添加图片描述

贝塞尔曲线绘制

原理:P(t) = (1 - t)^2 * P0 + 2 * (1 - t) * t * P1 + t^2 * P2
	  曲线上的点可以通过参数t(取值范围为01)来表示。参数t表示曲线上某一点的位置,其中t=0表示起点,t=1表示终点。对于给定的t值,可以使用以下公式计算曲线上的点P(t)的坐标:
	  其中,P0P1P2分别是起点、控制点和终点的坐标。
应用:物体弯曲,道路生成,画面扭曲等

扩展:
	三次贝塞尔曲线:三次贝塞尔曲线由四个锚点(起点、终点和两个控制点)组成。
	P(t) = (1 - t)^3 * P0 + 3 * (1 - t)^2 * t * P1 + 3 * (1 - t) * t^2 * P2 + t^3 * P3
	P0P1P2P3分别是起点、两个控制点和终点的坐标。
	通过调整控制点的位置,可以改变贝塞尔曲线的形状。控制点的位置决定了曲线的弯曲程度和方向。
using UnityEngine;
/// <summary>
/// 贝塞尔曲线绘制
/// </summary>
public class BezierCurve_ZH : MonoBehaviour
{
    [Header("开始位置")]
    public Transform _StartPoint;
    [Header("控制带你")]
    public Transform _ControlPoint;
    [Header("结束位置")]
    public Transform _EndPoint;
    [Header("精度")]
    //越高越平滑  性能消耗越大
    public int _Resolution = 10;

    private void OnDrawGizmos()
    {
        DrawBezierCurve();
    }

    /// <summary>
    /// 贝塞尔曲线绘制
    /// </summary>
    private void DrawBezierCurve()
    {
        //曲线数组
        Vector3[] _Points = new Vector3[_Resolution + 1];

        //数组变更
        for (int i = 0; i <= _Resolution; i++)
        {
            float t = i / (float)_Resolution;
            _Points[i] = CalculatePointOnCurve(t);
        }

        //绘制颜色
        Gizmos.color = Color.cyan;

        //曲线绘制
        for (int i = 0; i < _Resolution; i++)
        {
            Gizmos.DrawLine(_Points[i], _Points[i + 1]);
        }
    }

    /// <summary>
    /// 计算曲线上的点
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    private Vector3 CalculatePointOnCurve(float t)
    {
        float u = 1 - t;
        float tt = t * t;
        float uu = u * u;

        Vector3 point = uu * _StartPoint.position;
        point += 2 * u * t * _ControlPoint.position;
        point += tt * _EndPoint.position;

        return point;
    }
}

贝塞尔曲线绘制 搭载效果

请添加图片描述

网格弯曲

原理:使用 三次贝塞尔曲线 得到变形后的顶点,然后根据变形后的顶点进行网格重组	 
应用:物体弯曲,道路生成,画面扭曲等
using UnityEngine;

/// <summary>
/// 网格弯曲
/// </summary>
public class ObjectBender_ZH : MonoBehaviour
{
    //起始点
    public Transform _StartPoint;
    //控制点 1
    public Transform _ControlPoint1;
    //控制点 2
    public Transform _ControlPoint2;
    //结束点
    public Transform _EndPoint;
    //精细程度
    public int _Eesolution = 10;

    //变形网格
    public MeshFilter _MeshFilter;
    //出十万个
    private Mesh _OriginalMesh;
    //变形网格
    private Mesh _BentMesh;

    private void Start()
    {
        //初始化
        //_MeshFilter = GetComponent<MeshFilter>();
        _OriginalMesh = _MeshFilter.mesh;
        _BentMesh = new Mesh();
        _MeshFilter.mesh = _BentMesh;

        BendObject();
    }

    private void Update()
    {
        BendObject();
    }

    /// <summary>
    /// 网格变形方法
    /// </summary>
    private void BendObject()
    {
        //顶点数组
        Vector3[] _Vertices = _OriginalMesh.vertices;
        //变形顶点数组
        Vector3[] _BentVertices = new Vector3[_Vertices.Length];

        //变形顶点 遍历赋值
        for (int i = 0; i < _Vertices.Length; i++)
        {
            Vector3 vertex = _Vertices[i];
            Vector3 bentPosition = BendVertex(vertex);
            _BentVertices[i] = bentPosition;
        }

        //顶点  三角面  法线 设置
        _BentMesh.vertices = _BentVertices;
        _BentMesh.triangles = _OriginalMesh.triangles;
        _BentMesh.RecalculateNormals();
    }

    /// <summary>
    /// 顶点变形 重组
    /// </summary>
    /// <param name="_Vertex"></param>
    /// <returns></returns>
    private Vector3 BendVertex(Vector3 _Vertex)
    {
        float t = _Vertex.x / (float)(_OriginalMesh.bounds.size.x);

        Vector3 _StartPointPosition = _StartPoint.position;
        Vector3 _ControlPoint1Position = _ControlPoint1.position;
        Vector3 _ControlPoint2Position = _ControlPoint2.position;
        Vector3 _EndPointPosition = _EndPoint.position;

        Vector3 _BentPosition = CalculatePointOnCurve(t, _StartPointPosition, _ControlPoint1Position, _ControlPoint2Position, _EndPointPosition);

        return _BentPosition;
    }

    /// <summary>
    /// 曲线计算
    /// 三次贝塞尔曲线
    /// </summary>
    /// <param 变形 t ="t"></param>
    /// <param 开始位置="_StartPoint"></param>
    /// <param 控制点01="_ControlPoint1"></param>
    /// <param 控制点02 ="_ControlPoint2"></param>
    /// <param 结束位置="_EndPoint"></param>
    /// <returns></returns>
    private Vector3 CalculatePointOnCurve(float t, Vector3 _StartPoint, Vector3 _ControlPoint1, Vector3 _ControlPoint2, Vector3 _EndPoint)
    {
        float t2 = t * t;
        float t3 = t2 * t;

        Vector3 _Point =
            0.5f * ((2.0f * _ControlPoint1) +
            (-_StartPoint + _EndPoint) * t +
            (2.0f * _StartPoint - 5.0f * _ControlPoint1 + 4.0f * _ControlPoint2 - _EndPoint) * t2 +
            (-_StartPoint + 3.0f * _ControlPoint1 - 3.0f * _ControlPoint2 + _EndPoint) * t3);

        return _Point;
    }
}

网格弯曲 搭载效果

注意需要勾选导入模型的读写权限

请添加图片描述

变形前

请添加图片描述

变形后

请添加图片描述

Delaunay 模型生成

原理:就是使用 Delaunay 算法进行 外接圆判断
应用:模型生成、动态模型补面、最大面积计算等
有一点不完善,过两天抽时间单独研究研究,再出一篇看看。

填坑了哈,大家如果有兴趣可以去看看。
Delaunay 算法 的 解析 : Unity Delaunay三角剖分算法

using UnityEngine;
using System.Collections.Generic;

/// <summary>
///  Delaunay 模型生成
/// </summary>
public class DelaunayAlgorithm : MonoBehaviour
{
    [Header("存储要生成 Delaunay 物体")]
    public List<Transform> _TraList = new List<Transform>();

    [Header("存储要生成 Delaunay 三角网格的点")]
    private List<Vector2> _Points = new List<Vector2>();

    // 存储生成的三角形
    private List<Triangle> _Triangles = new List<Triangle>(); 

    private void Start()
    {
        for (int i = 0; i < _TraList.Count; i++)
        {
            _Points.Add(new Vector2(_TraList[i].position.x, _TraList[i].position.z));
        }
        GenerateDelaunay();
    }

    /// <summary>
    /// Delaunay 三角形生成
    /// </summary>
    void GenerateDelaunay()
    {
        // 在点集中加入一个超级三角形
        float _MinX = float.MaxValue;
        float _MinY = float.MaxValue;
        float _MaxX = float.MinValue;
        float _MaxY = float.MinValue;

        //最大值最小值判定
        foreach (Vector2 _Point in _Points)
        {
            if (_Point.x < _MinX) _MinX = _Point.x;
            if (_Point.y < _MinY) _MinY = _Point.y;
            if (_Point.x > _MaxX) _MaxX = _Point.x;
            if (_Point.y > _MaxY) _MaxY = _Point.y;
        }

        float _DeltaX = _MaxX - _MinX;
        float _DeltaY = _MaxY - _MinY;
        float _DeltaMax = Mathf.Max(_DeltaX, _DeltaY);
        float _MidX = (_MinX + _MaxX) / 2f;
        float _MidY = (_MinY + _MaxY) / 2f;

        Vector2 p1 = new Vector2(_MidX - 20 * _DeltaMax, _MidY - _DeltaMax);
        Vector2 p2 = new Vector2(_MidX, _MidY + 20 * _DeltaMax);
        Vector2 p3 = new Vector2(_MidX + 20 * _DeltaMax, _MidY - _DeltaMax);

        //三角形片元 添加
        _Triangles.Add(new Triangle(p1, p2, p3));

        // 逐个加入点并更新三角形
        foreach (Vector2 _Point in _Points)
        {
            List<Edge> _Polygon = new List<Edge>();

            for (int i = _Triangles.Count - 1; i >= 0; i--)
            {
                //如果是外接圆就证明 当前三角形是 Delaunay 三角形
                //如果有任何一个点在其他三角形的外接圆内,则该三角形不是Delaunay三角形。
                if (_Triangles[i].CircumcircleContains(_Point))
                {
                    _Polygon.Add(_Triangles[i].e1);
                    _Polygon.Add(_Triangles[i].e2);
                    _Polygon.Add(_Triangles[i].e3);
                    _Triangles.RemoveAt(i);
                }
            }

            //顶点移除
            for (int i = _Polygon.Count - 2; i >= 0; i--)
            {
                for (int j = _Polygon.Count - 1; j >= i + 1; j--)
                {
                    if (_Polygon[i] == _Polygon[j])
                    {
                        _Polygon.RemoveAt(j);
                        _Polygon.RemoveAt(i);
                        j--;
                    }
                }
            }

            //加入片元 数组
            foreach (Edge edge in _Polygon)
            {
                _Triangles.Add(new Triangle(edge.p1, edge.p2, _Point));

            }
        }

        // 剔除超级三角形相关的三角形
        List<Triangle> _TrianglesToRemove = new List<Triangle>();

        foreach (Triangle _Triangle in _Triangles)
        {
            if (_Triangle.ContainsAnyVertex(p1, p2, p3))
            {
                _TrianglesToRemove.Add(_Triangle);
            }
        }

        foreach (Triangle triangle in _TrianglesToRemove)
        {
            _Triangles.Remove(triangle);
        }



        // 在 Unity 中绘制生成的三角形  Delaunay
        foreach (Triangle _Triangle in _Triangles)
        {
            //if (_Triangle.e1.IsDelaunayEdge(_Triangles))
            //{
            //    Debug.DrawLine(_Triangle.p1, _Triangle.p2, Color.green, 15f);
            //}
            //if (_Triangle.e2.IsDelaunayEdge(_Triangles))
            //{
            //    Debug.DrawLine(_Triangle.p2, _Triangle.p3, Color.green, 15f);
            //}
            //if (_Triangle.e3.IsDelaunayEdge(_Triangles))
            //{
            //    Debug.DrawLine(_Triangle.p3, _Triangle.p1, Color.green, 15f);
            //}

            Debug.DrawLine(_Triangle.p1, _Triangle.p2, Color.green, 15f);
            Debug.DrawLine(_Triangle.p2, _Triangle.p3, Color.green, 15f);
            Debug.DrawLine(_Triangle.p3, _Triangle.p1, Color.green, 15f);
        }
    }
}

/// <summary>
/// 三角形数据类
/// </summary>
public class Triangle
{
    // 三角形的顶点
    public Vector2 p1, p2, p3;
    // 三角形的边
    public Edge e1, e2, e3;

    /// <summary>
    /// 三角形  片元
    /// </summary>
    /// <param 顶点="p1"></param>
    /// <param 顶点="p2"></param>
    /// <param 顶点="p3"></param>
    /// <returns></returns>
    public Triangle(Vector2 p1, Vector2 p2, Vector2 p3)
    {
        this.p1 = p1;
        this.p2 = p2;
        this.p3 = p3;

        e1 = new Edge(p1, p2);
        e2 = new Edge(p2, p3);
        e3 = new Edge(p3, p1);
    }

    /// <summary>
    /// 检查三角形的外接圆是否包含指定的点
    /// 给定的点在三角形的外接圆内,返回true;否则,说明给定的点不在外接圆内,返回false
    /// </summary>
    /// <param 顶点="point"></param>
    /// <returns></returns>
    public bool CircumcircleContains(Vector2 point)
    {
        //计算三角形的外接圆的圆心和半径
        float d1 = (p1.x - point.x) * (p1.x - point.x) + (p1.y - point.y) * (p1.y - point.y);
        float d2 = (p2.x - point.x) * (p2.x - point.x) + (p2.y - point.y) * (p2.y - point.y);
        float d3 = (p3.x - point.x) * (p3.x - point.x) + (p3.y - point.y) * (p3.y - point.y);


        float a = (p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y)) * 2f;

        if (Mathf.Abs(a) < 0.00001f)
        {
            return false;
        }

        //检查指定点与圆心的距离是否小于或等于半径的平方
        float centerX = ((p1.x * p1.x + p1.y * p1.y) * (p2.y - p3.y) + (p2.x * p2.x + p2.y * p2.y) * (p3.y - p1.y) + (p3.x * p3.x + p3.y * p3.y) * (p1.y - p2.y)) / a;
        float centerY = ((p1.x * p1.x + p1.y * p1.y) * (p3.x - p2.x) + (p2.x * p2.x + p2.y * p2.y) * (p1.x - p3.x) + (p3.x * p3.x + p3.y * p3.y) * (p2.x - p1.x)) / a;

        float radius = Mathf.Sqrt((centerX - p1.x) * (centerX - p1.x) + (centerY - p1.y) * (centerY - p1.y));

        float dist = (point.x - centerX) * (point.x - centerX) + (point.y - centerY) * (point.y - centerY);

        return dist <= radius * radius;
    }

    /// <summary>
    /// 检查三角形是否包含指定的顶点
    /// 最大三角形判定
    /// </summary>
    /// <param name="v1"></param>
    /// <param name="v2"></param>
    /// <param name="v3"></param>
    /// <returns></returns>
    public bool ContainsAnyVertex(Vector2 v1, Vector2 v2, Vector2 v3)
    {
        return p1 == v1 || p1 == v2 || p1 == v3 ||
               p2 == v1 || p2 == v2 || p2 == v3 ||
               p3 == v1 || p3 == v2 || p3 == v3;
    }

    / <summary>
    / Delaunay边  判定
    / 确定是否绘制当前三角形的边
    / </summary>
    / <returns></returns>
    //public bool IsDelaunay()
    //{
    //    // 检查三角形的每条边是否是Delaunay边
    //    //return e1.IsDelaunayEdge() && e2.IsDelaunayEdge() && e3.IsDelaunayEdge();
    //}

  
}

/// <summary>
/// 三角边
///  表示边的类
/// </summary>
public class Edge
{
    // 边的两个顶点
    public Vector2 p1, p2;

    public Edge(Vector2 p1, Vector2 p2)
    {
        this.p1 = p1;
        this.p2 = p2;
    }

    /// <summary>
    ///  重写 Equals 方法,用于比较边的相等性
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }

        Edge other = (Edge)obj;
        return (p1 == other.p1 && p2 == other.p2) || (p1 == other.p2 && p2 == other.p1);
    }

    /// <summary>
    ///  重写 GetHashCode 方法,用于哈希表存储
    /// </summary>
    /// <returns></returns>
    public override int GetHashCode()
    {
        return p1.GetHashCode() ^ p2.GetHashCode();
    }

    /// <summary>
    ///  Delaunay 三角边判断
    /// </summary>
    /// <param 三角片元数组="_Triangles"></param>
    /// <returns></returns>
    public bool IsDelaunayEdge(List<Triangle> _Triangles)
    {
        // 检查当前边是否与其他三角形的外接圆相交
        foreach (Triangle _Triangle in _Triangles)
        {
            if (_Triangle.CircumcircleContains(new Vector2(p1.x, p1.y)) || _Triangle.CircumcircleContains(new Vector2(p2.x, p2.y)))
            {
                return false;
            }
        }
        return true;
    }
}
最大三角形

请添加图片描述

生成物体分布

请添加图片描述

生成网格 有点问题 需要后期修改

请添加图片描述

暂时先这样吧,如果有时间的话就会更新,实在看不明白就留言,看到我会回复的。
路漫漫其修远兮,与君共勉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1371772.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【FPGA/verilog -入门学习17】vivado 实现串口自发自收程序

1&#xff0c;需求 PC使用串口助手给FPGA板发送9600 波特率的数据&#xff0c;FPGA板接收到数据后&#xff0c;回复同样的数据给PC 2&#xff0c;需求分析 按模块可以划分为&#xff1a; rx接收模块&#xff0c;将输入的8位并行rx 数据转换成[7:0]rx_data 信号&#xff0c;当…

性能优化--实战利用arthas排查java服务cpu占用过高的问题

使用jps -l查看目前的java应用进程 启动arthas&#xff0c;选择需要监控的进程 dashboar查看该应用整体情况 使用thread命令&#xff0c;查看占用cpu过高的几个线程ID 然后使用thread 线程ID查看具体线程在执行哪些内容&#xff0c;可以看到对应的类和方法 正在上传… 重…

K8S实践:非常实用kubectl的别名工具推荐,助你高效工作

转载至我的博客 &#xff0c;公众号&#xff1a;架构成长指南 大家好&#xff0c;我是蜗牛哥&#xff0c;今天介绍一款k8s的别名工具&#xff0c;可以让你高效工作&#xff0c;下面是相关介绍 介绍 我们在管理 Kubernetes集群和执行指定任务是比较复杂和费时的。但是如果使用…

益生菌抗癌?补充这种益生菌,抑制肝癌,还改善肠道健康

撰文 | 宋文法 肠道菌群&#xff0c;是人体不可分割的组成部分&#xff0c;生活在我们肠道内的数万亿细菌对健康起着重要作用&#xff0c;它们影响着人的新陈代谢、消化能力、抵御感染、控制人体对药物的反应&#xff0c;甚至还能预防某些癌症。 非酒精性脂肪肝病&#xff0c;是…

虚幻UE 材质-纹理 1

本篇笔记主要讲两个纹理内的内容&#xff1a;渲染目标和媒体纹理 媒体纹理可以参考之前的笔记&#xff1a;虚幻UE 媒体播放器-视频转成材质-播放视频 所以本篇主要讲两个组件&#xff1a;场景捕获2D、场景捕获立方体 两个纹理&#xff1a;渲染目标、立方体渲染目标 三个功能&am…

构建高效学习平台:企业培训系统源码深度解析

企业培训系统是组织中培养和提升员工技能的核心工具。本文将深入探讨企业培训系统的源码&#xff0c;通过关键技术代码解析&#xff0c;揭示其中的设计原理和功能实现&#xff0c;以构建更高效的学习平台。 1. 环境配置与依赖项安装 首先&#xff0c;让我们关注源码的环境配…

Vue3:Axios配置及使用

Axios官方 一、安装&#xff1a; //使用 npm: $ npm install axios//使用 bower: $ bower install axios//使用 yarn: $ yarn add axios 在package-lock.json文件可以查看axios版本 二、配置&#xff1a; milliaAxios.js 配置axios import axios from axios // 创建一个 ax…

AI老照片修复-Bringing-Old-Photos-Back-to-Life

&#x1f3e1; 个人主页&#xff1a;IT贫道-CSDN博客 &#x1f6a9; 私聊博主&#xff1a;私聊博主加WX好友&#xff0c;获取更多资料哦~ &#x1f514; 博主个人B栈地址&#xff1a;豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录 1. AI老照片修复原理-…

AP5153 低压差 线性降压恒流IC 手电筒LED电源驱动

AP5153 是一种 PWM 调光的、低压 差的 LED 线性降压恒流驱动器。 AP5153 仅需要外接一个电阻和一个 NMOS 管就可以构成一个完整的 LED 恒 流驱动电路&#xff0c; 调节该外接电阻就可以调节 输出电流&#xff0c;输出电流可调范围为 20mA 到 3.0A。 AP5153 还可以通过在 DIM…

怎么修改照片尺寸?来分享3款实用的工具!

在当今的自媒体时代&#xff0c;照片是吸引读者眼球的重要元素之一。有时候&#xff0c;我们需要在不同的平台上传照片&#xff0c;但不同的平台对照片的尺寸要求却不尽相同。为了满足这些要求&#xff0c;我们经常需要修改照片的尺寸。那么&#xff0c;如何快速、准确地修改照…

Linux内核之红黑树详解(2)

该博客结合了维基百科、博客和一些其他的资料&#xff0c;按照理解整理的&#xff0c;如有错误&#xff0c;欢迎指正。 1、创建红黑树&#xff1a; 在实现插入、删除等操作之前&#xff0c;需要先创建一棵红黑树&#xff0c;返回的是红黑树的根结点&#xff1a; RBRoot* crea…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷⑤

2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷5 目录 需要竞赛软件包环境以及备赛资源可私信博主&#xff01;&#xff01;&#xff01; 2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷5 模块一 …

《MyBatis》--多数据源使用实现--超级详细!!!!

阿丹-需求/场景&#xff1a; 在项目中可能要使用到几个数据源&#xff0c;所以本文章解决一个项目中多个数据源的方式。使用简单的配置和注解来实现。 适用于在确定的数据源的场景下。 实现&#xff1a; 三个核心&#xff1a; 构造DataSource、SqlSessionFactory、SqlSessio…

TS 36.322 V12.0.0-过程

​本文的内容主要涉及TS 36.322&#xff0c;版本是C00&#xff0c;也就是V12.0.0。

机器之心 AI 技术--人工智能助力个性化视频实战经验分享(文末送书)

【清华社&机器之心】视频生成前沿研究与应用特别活动 在视频生成即将迎来技术和应用大爆发之际&#xff0c;为了帮助企业和广大从业者掌握技术前沿&#xff0c;把握时代机遇&#xff0c;机器之心AI论坛就将国内的视频生成技术力量齐聚一堂&#xff0c;共同分享国内顶尖力量…

100V耐压 LED恒流驱动芯片 SL2516D兼容替换LN2516车灯照明芯片

SL2516D LED恒流驱动芯片是一款专为LED照明设计的高效、高精度恒流驱动芯片。与LN2516车灯照明芯片兼容&#xff0c;可直接替换LN2516芯片&#xff0c;为LED车灯照明提供稳定、可靠的电源解决方案。 一、SL2516D LED恒流驱动芯片的特点 1. 高效率&#xff1a;SL2516D采用先进的…

【OpenCV学习笔记06】- 制作使用轨迹条控制的调色板

内容 学习将轨迹栏绑定到 OpenCV 窗口。你将学习这些函数&#xff1a;cv.getTrackbarPos(), cv.createTrackbar() 等等。 调色板代码 这里&#xff0c;我们将创建用以显示指定颜色的简单程序。 你有一个显示颜色的窗口和三个轨迹栏&#xff0c;用来指定 B&#xff0c;G&…

师傅带练|在线实习项目,提供实习证明

八大项目&#xff1a;某实习网站招聘信息采集与分析&#xff08;Python数据采集与分析&#xff09; 股票价格形态聚类与收益分析&#xff08;Python金融分析&#xff09; 某平台网络入侵用户自动识别&#xff08;Python机器学习&#xff09; 某平台广东省区采购数据分析&…

信号-进程间通信

信号 1. 信号概述 在 Linux 操作系统中&#xff0c;信号是一种进程间通信的机制&#xff0c;用于通知进程发生了某个事件。信号可以由内核、其他进程&#xff0c;或者进程自身发送。每个信号都对应一个特定的事件或异常&#xff0c;例如进程终止、CtrlC 中断等。 本质上是一…

“To-Do Master“ GPTs:重塑任务管理的趣味与效率

有 GPTs 访问权限的可以点击链接进行体验&#xff1a;https://chat.openai.com/g/g-IhGsoyIkP-to-do-master 部署私人的 To-Do Master 教程&#xff1a;https://github.com/Reborn14/To-Do-Master/tree/main 引言 在忙碌的日常生活中&#xff0c;有效地管理日常任务对于提高生…