Unity 实用方法 合集

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 算法进行 外接圆判断
应用:模型生成、动态模型补面、最大面积计算等
有一点不完善,过两天抽时间单独研究研究,再出一篇看看。
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.mfbz.cn/a/306809.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年全国教资笔试报名流程(建议电脑报名),看看有啥新要求?

一.报名、考试时间节点 1.笔试报名时间: 2024年1月12日-15日 2.笔试考试时间:2024年3月9日 3.笔试成绩查询时间:2024年4月15日 4.面试报名时间:2024年4月15日 5.面试考试时间:2024年5月18日 6.面试成绩查询时间:2024年6月14日 二.笔试报名流程: 登陆→考生注册 →填报个…

获取深层次字段报错TypeError: Cannot read properties of undefined (reading ‘title‘)

动态生成菜单时报错,不能多层获取路由meta下面的title字段 <template><p>{{ meneList }}</p><template v-for"item in meneList" :key"item.path"><el-menu-item v-if"!item.children"><template #title>{…

6 个适用于 Android 手机的有效照片恢复工具

我们大多数人都经历过至少一次从智能手机中意外删除照片或视频的经历。是否可以恢复这些文件&#xff1f;幸运的是&#xff0c;答案是肯定的。如果您正在寻找高级 图片恢复应用程序 来从 Android 中检索已删除的内容&#xff0c;那么这正是这篇文章将要展示的内容。 6 个照片恢…

控制论和科学方法论

《控制论与科学方法论》&#xff0c;真心不错。 书籍原文电子版PDF&#xff1a;https://pan.quark.cn/s/aa40d59295df&#xff08;分类在学习目录下&#xff09; 备用链接&#xff1a;https://pan.xunlei.com/s/VNgj2vjW-Hf_543R2K8kbaifA1?pwd2sap# 控制论是一种让系统按照我…

《PCI Express体系结构导读》随记 —— 第I篇 第2章 PCI总线的桥与配置(15)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第2章 PCI总线的桥与配置&#xff08;14&#xff09; 2.3.1 PCI桥 在PCI Agent设备的配置空间中包含了许多寄存器&#xff0c;这些寄存器决定了该设备在PCI总线中的使用方法&#xff0c;本节不会全部介绍…

SCT2A27STER:5.5V-100V Vin,4A峰值限流,高效异步降压DCDC转换器,集成200mA LDO

特性&#xff1a; • 5.5V-100V 输入电压范围 • 最大输出电压&#xff1a;30V • 2A 连续输出电流 • 4A峰值电流限制 • 1.2V 1% 反馈电压 • 集成500mΩ 高侧功率 MOSFETs • 可选5V或者3.3V,输出一路200mA LDO • 25uA静态电流&#xff0c;VBIAS连接到高于6V的辅助电源 •…

从起高楼到楼塌了的中台战略 —— 业务中台、数据中台、技术中台

目录 一. 前言 二. 中台能力总体框架 三. 业务中台 四. 数据中台 五. 技术中台 5.1. API 网关 5.2. 开发框架 5.3. 微服务治理 5.4. 分布式数据库 5.5. 数据处理组件 六. 阿里拆中台的原因和意义 七. 总结 一. 前言 中台是近年来互联网行业的一个热门话题。它最早是…

Linux系统与windows系统设置定时任务的具体操作方法,如数据库自动备份等

设置定时备份 要设置数据库定时备份&#xff0c;你可以使用操作系统的定时任务功能来自动执行 backup.sh 脚本(此脚本可关注文末公众号回复04获取)。不同的操作系统有不同的方法来设置定时任务&#xff0c;但一般来说&#xff0c;你可以按照以下步骤进行操作&#xff1a; 打开…

书生.浦语大模型实战一

从专用模型到通用大模型 数据 书生.万卷1.0 文本图像-文本视频数据 OpenDataLab开放平台 图像&#xff1a;ImageNettokens语料&#xff1a;WikiQA音频视频&#xff1a;MovieNet3D模型 预训练 微调 增量续训 使用场景&#xff1a;让基座模型学习到一些新知识&#xff0…

鸿蒙原生应用/元服务开发-长时任务

概述 功能介绍 应用退至后台后&#xff0c;对于在后台需要长时间运行用户可感知的任务&#xff0c;例如播放音乐、导航等。为防止应用进程被挂起&#xff0c;导致对应功能异常&#xff0c;可以申请长时任务&#xff0c;使应用在后台长时间运行。申请长时任务后&#xff0c;系统…

MyBatis 源码分析(五):异常模块

1、前言 上一篇我们解了Mybatis解析器模块&#xff0c;本篇我们来了解反射模块。本文&#xff0c;我们来分享 MyBatis 的异常模块。 对应 exceptions 包&#xff0c;如下图所示&#xff1a; 在 MyBatis源码分析&#xff08;二&#xff09;&#xff1a;项目结构 中&#xff0c;简…

大创项目推荐 深度学习实现语义分割算法系统 - 机器视觉

文章目录 1 前言2 概念介绍2.1 什么是图像语义分割 3 条件随机场的深度学习模型3\. 1 多尺度特征融合 4 语义分割开发过程4.1 建立4.2 下载CamVid数据集4.3 加载CamVid图像4.4 加载CamVid像素标签图像 5 PyTorch 实现语义分割5.1 数据集准备5.2 训练基准模型5.3 损失函数5.4 归…

Django web开发(一) - 前端

文章目录 前端开发1.快速开发网站2.标签2.1 编码2.2 title2.3 标题2.4 div和span2.5 超链接2.6 图片小结标签的嵌套2.7 列表2.8 表格2.9 input系列2.10 下拉框2.11 多行文本用户注册案例: 用户注册GET 方式POST 方式表单数据提交优化 3.CSS样式3.1 快速上手3.2 CSS应用方式1. 在…

【原生部署】SpringBoot+Vue前后端分离项目

本次主要讲解SpringBootVue前后端完全分离项目在CentOS云服务器上的环境搭建与部署过程&#xff0c;我们主要讲解原生部署。 一.原生部署概念 原生部署是指将应用程序&#xff08;一般是指软件、应用或服务&#xff09;在底层的操作系统环境中直接运行和部署&#xff0c;而不…

AI大语言模型会带来了新一波人工智能浪潮?

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

基于 InternLM 和 LangChain 搭建你的知识库

基于 InternLM 和 LangChain 搭建你的知识库 大模型开发范式LLM的局限性&#xff1a;RAG 检索增强生成 LangChain简介构建向量数据库搭建知识库助手Web Demo部署环境配置下载 NLTK 相关资源下载本项目代码 大模型开发范式 LLM的局限性&#xff1a; 知识实效性受限&#xff1a…

实用Unity3D Log打印工具XDebug

特点 显示时间&#xff0c;精确到毫秒显示当前帧数&#xff08;在主线程中的打印才有意义&#xff0c;非主线程显示为-1&#xff09;有三种条件编译符(如下图) 注&#xff1a;要能显示线程中的当前帧数&#xff0c;要在app启动时&#xff0c;初始化mainThreadID字段条件编译符…

8.1、5G网络切片认识篇

首先&#xff0c;3G上网时代来临&#xff0c;流量高速增长&#xff0c;但是网络资源有限&#xff0c;不可能保证所有业务都能全速进行&#xff0c;总得捡重要的首先保障&#xff0c;因此就对业务进行分类&#xff0c;给予不同优先级的业务不同的资源&#xff0c;不同的服务质量…

基于filter的内存马

主要是通过过滤器来拦截severlet请求中的参数&#xff0c;作为过滤器中的参数&#xff0c;来调用自定义过滤器中的恶意函数 在这里我们分析一下filter的实现原理&#xff0c;循序渐进 Demo1&#xff1a; 直接使用filter模拟内存马效果&#xff1a; 1.配置一个简单的severlet的…

[uniapp] uni-ui+vue3.2小程序评论列表组件 回复评论 点赞和删除

先看效果 下载地址 uni-app官方插件市场: cc-comment组件 环境 基于vue3.2和uni-ui开发; 依赖版本参考如下: "dependencies": {"dcloudio/uni-mp-weixin": "3.0.0-3090820231124001","dcloudio/uni-ui": "^1.4.28","…
最新文章