【Unity】运行时创建曲线(贝塞尔的运用)

2023-12-19

[Unity]运行时创建线(贝塞尔的运用)

1. 实现的目标

在运行状态下创建一条可以使用贝塞尔方法实时编辑的网格曲线。

2. 原理介绍

2.1 曲线的创建

unity建立网格曲线可以参考 Unity程序化网格体 的实现方法。主要分为顶点,三角面,UV和法线。笔者有类似的文章 unity 线绳管道纯代码创建方法_ ,详细的讲解了网格线的创建方法,这次的不同点在于法线的确立方法上。

2.2贝塞尔曲线点的确立

笔者有文章 Unity 贝塞尔曲线的创建_ 描述了贝塞尔的创建方法。

3. 实现过程

3.1曲线的创建方法

线的组成原理
曲线由横截面圆和中心轴线组成。横截面的法线方向为前后两点向量差,如下图绿色线为中心轴线,A点为横截面所在点,横截面的法线方法为向量 A D ⃗ \vec{AD} A D 既向量 C B ⃗ \vec{CB} CB 的单位向量;终点和起点的法线方向为自身和前点或者后一点的向量。

代码源码

3.1.1 横截圆的创建
           #region 横切圆创建
           /// <summary>
           /// 得到管线横切圆
           /// </summary>
           /// <param name="Count">段数</param>
           /// <param name="R">半径</param>
           /// <returns></returns>
           Vector3[] CircularSection(int Count, float R)
           {
               Vector3[] vector3s = new Vector3[Count];
               float angle = 360 / Count;
               Vector3 vector3 = new Vector3(R, 0, 0);
               for (int i = 0; i < Count; i++)
               {
                   //根据角度得到圆的分布点
                   vector3s[i] = vector3.ToAngle(angle * i, Vector3.zero, Vector3.forward);
               }
               return vector3s;
           }
           #endregion
          

​ vector3旋转扩展方法

        /// <summary>
        /// 角度旋转
        /// </summary>
        /// <param name="vector3"></param>
        /// <param name="angle">旋转角度</param>
        /// <param name="center">旋转中心点</param>
        /// <param name="direction">旋转轴</param>
        /// <returns></returns>
        public static Vector3 ToAngle(this Vector3 vector3, float angle, Vector3 center, Vector3 direction)
        {
            Vector3 pos = center;
            Quaternion quaternion = Quaternion.AngleAxis(angle, direction);
            Matrix4x4 matrix = new Matrix4x4();
            matrix.SetTRS(pos, quaternion, Vector3.one);
            vector3 = matrix.MultiplyPoint3x4(vector3);
            return vector3;
        }
3.1.2 中心线的确立
           class LinePoint
             {
                 Vector3 location;
                 Vector3 direction;
     
                 public Vector3 Location { get => location; set => location = value; }
                 public Vector3 Direction { get => direction; set => direction = value; }
             }
             /// <summary>
             /// 中心线的确立
             /// </summary>
             /// <param name="createPoint">曲线点</param>
             /// <returns></returns>
             List<LinePoint> SetLinePoint(Vector3[] createPoint)
             {
                 List<LinePoint> pipePoints = new List<LinePoint>();
                 int length = createPoint.Length;
                 for (int i = 0; i < length; i++)
                 {
                     if (i == 0)
                     {
                         Vector3 tangent = (createPoint[i + 1] - createPoint[i]).normalized;//法线
                         AddPipePoints(createPoint[i], tangent, ref pipePoints);
                     }
                     else if (i == length - 1)
                     {
                         Vector3 tangent = (createPoint[i] - createPoint[i - 1]).normalized;//法线
                         AddPipePoints(createPoint[i], tangent, ref pipePoints);
                     }
                     else
                     {
                         Vector3 tangent = (createPoint[i+1] - createPoint[i - 1]).normalized;//法线
                         AddPipePoints(createPoint[i], tangent, ref pipePoints);
                     }
                 }
                 return pipePoints;
             }
     /// <summary>
             /// 增加中心轴线点
             /// </summary>
             /// <param name="location">位置</param>
             /// <param name="direction">法线</param>
             void AddPipePoints(Vector3 location, Vector3 direction,  ref List<LinePoint> pipePoints)
             {
                 LinePoint pipePoint = new LinePoint();
                 pipePoint.Location = location;
                 pipePoint.Direction = direction;
                 pipePoints.Add(pipePoint);
             }
3.1.3 网格创建
        /// <summary>
        /// 立体网格创建
        /// </summary>
        /// <param name="createPoint">创建的点数据</param>
        /// <param name="circularCount">圆的段数</param>
        /// <param name="circularR">圆的半径</param>
        /// <returns></returns>
        public Mesh CreateLine3D(Vector3[] createPoint, int circularCount, float circularR)
        {
            //截面圆
            Vector3[] circul = CircularSection(circularCount, circularR);
            //中心线
            List<LinePoint> centreLine = SetLinePoint(createPoint);
            //网格点数据
            Vector3[] meshPoint = CreateMeshPoint(centreLine, circul);
            float uvX = Vector3.Distance(circul[0], circul[1]);
            //返回网格
            return CreatMesh(centreLine, meshPoint, circul.Length, uvX);
        }
/// <summary>
        /// 创建网格点数据
        /// </summary>
        /// <param name="linePoint"></param>
        /// <param name="circular"></param>
        /// <returns></returns>
        Vector3[] CreateMeshPoint(List<LinePoint> linePoint, Vector3[] circular)
        {
            int length = linePoint.Count;
            int circularCount = circular.Length;
            Vector3[] meshPoint = new Vector3[length * circularCount];
            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < circularCount; j++)
                {
                    meshPoint[(i * circularCount) + j] = circular[j].FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                }
            }
            return meshPoint;
        }
        /// <summary>
        /// 网格创建
        /// </summary>
        /// <param name="linePoints">线的轴心线组</param>
        /// <param name="meshPoint">网格点</param>
        /// <param name="count">段数</param>
        /// <param name="uvX">uv宽度</param>
        /// <returns></returns>
        Mesh CreatMesh(List<LinePoint> linePoints, Vector3[] meshPoint, int count, float uvX)
        {
            Mesh mesh = new Mesh();
            mesh.vertices = meshPoint;
            mesh.triangles = GetTriangles(linePoints.Count, count);
            mesh.uv = GetUV(linePoints, count, uvX);
            mesh.RecalculateNormals();
            mesh.RecalculateBounds();
            return mesh;
        }
   /// <param name="length">线段段数</param>
        /// <param name="count">横截面段数(也就是圆的段数)</param>
        /// <returns></returns>
        int[] GetTriangles(int length, int count)
        {
            int[] triangles = new int[(count * (length - 1)) * 6];
            int k = 0;
            if (count == 1)
            {
                for (int i = 0; i < length-1; i++)
                {
                    int a = i * 2;
                    triangles[k] = a;
                    triangles[k + 1] = a + 1;
                    triangles[k + 2] = a + 3;
                    triangles[k + 3] = a;
                    triangles[k + 4] = a + 3;
                    triangles[k + 5] = a + 2;
                    k += 6;
                }
            }
            else
            {
                for (int i = 0; i < length - 1; i++)
                {
                    
                    for (int j = 0; j < count; j++)
                    {
                        if (j == count - 1)
                        {
                           // Debug.Log("k=" + k);
                            triangles[k] = (i * count) + j;
                            triangles[k + 1] = (i * count) + 0;
                            triangles[k + 2] = ((i + 1) * count) + 0;
                            triangles[k + 3] = (i * count) + j;
                            triangles[k + 4] = ((i + 1) * count) + 0;
                            triangles[k + 5] = ((i + 1) * count) + j;
                        }
                        else
                        {
                            triangles[k] = (i * count) + j;
                            triangles[k + 1] = (i * count) + j + 1;
                            triangles[k + 2] = ((i + 1) * count) + j + 1;
                            triangles[k + 3] = (i * count) + j;
                            triangles[k + 4] = ((i + 1) * count) + j + 1;
                            triangles[k + 5] = ((i + 1) * count) + j;
                        }
                        k += 6;
                    }
                }
            }
            return triangles;
        }
  /// <summary>
        /// 创建uv
        /// </summary>
        /// <param name="linePoints"></param>
        /// <param name="count"></param>
        /// <param name="uvX"></param>
        /// <returns></returns>
        Vector2[] GetUV(List<LinePoint> linePoints,int count, float uvX)
        {
            int length = linePoints.Count;
            if (count == 1) { count = 2; }
            Vector2[] uvs = new Vector2[(count * length)];
            float lineDis = 0;
            int k = 0;
            for (int i = 0; i < length; i ++)
            {
                
                if (i != 0)
                {
                    lineDis += Vector3.Distance(linePoints[i].Location, linePoints[i - 1].Location);
                }
                for (int j = 0; j < count; j++)
                {
                    Vector2 vector2;
                    if (j % 2 != 0)
                    {
                        vector2 = new Vector2(uvX, lineDis);
                    }
                    else
                    {
                        vector2 = new Vector2(0, lineDis);
                    }
                    uvs[k] = vector2;
                    k += 1;
                }
            }
            return uvs;
        }
3.2贝塞尔曲线的建立方法

源码

 /// <summary>
    /// 获取绘制点
    /// </summary>
    /// <param name="controlPoints"></param>
    /// <param name="segmentsPerCurve"></param>
    /// <returns></returns>
    public List<Vector3> GetDrawingPoints(List<Vector3> controlPoints, int segmentsPerCurve)
    {
        List<Vector3> points = new List<Vector3>();
        // 下一段的起始点和上段终点是一个,所以是 i+=3
        for (int i = 0; i <= controlPoints.Count - 4; i += 3)
        {

            var p0 = controlPoints[i];
            var p1 = controlPoints[i + 1];
            var p2 = controlPoints[i + 2];
            var p3 = controlPoints[i + 3];
            float dis = Vector3.Distance(p0, p3);
            int count = Mathf.CeilToInt(segmentsPerCurve * dis);
            if (count < segmentsPerCurve)
            {
                count = segmentsPerCurve;
            }

            for (int j = 0; j <= count; j++)
            {
                var t = j / (float)count;
                points.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
            }
        }
        return points;
    }
    // 三阶公式
    Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
    {
        Vector3 result;

        Vector3 p0p1 = (1 - t) * p0 + t * p1;
        Vector3 p1p2 = (1 - t) * p1 + t * p2;
        Vector3 p2p3 = (1 - t) * p2 + t * p3;

        Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
        Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3;

        result = (1 - t) * p0p1p2 + t * p1p2p3;
        return result;
    }

3.3贝塞尔曲线应用

基于上述方法实现了贝塞尔创建,保存,读取,在编辑功能。
案例下载地址

创建曲线

保存曲线

保存方法有两种分别是,长期保存和暂时保存。长期保存是将保存数据写入本地文件,项目重启也可以读取;暂时保存是在项目运行期间保存数据,重启后丢失。demo使用暂时保存的方法

读取再编辑

读取曲线后可以继续编辑当前曲线

曲线浏览

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Unity】运行时创建曲线(贝塞尔的运用) 的相关文章

  • Unity中级客户端开发工程师的进阶之路

    上期UWA技能成长系统之 Unity高级客户端开发工程师的进阶之路 得到了很多Unity开发者的肯定 通过系统的学习 可以掌握游戏性能瓶颈定位的方法和常见的CPU GPU 内存相关的性能优化方法 UWA技能成长系统是UWA根据学员的职业发展
  • Unity 粒子特效、材质发光 HDR ShaderGraph图文教程[完成lit发光设置]

    效果如图 准备工作 在hdr模式下 关闭Directional Light 相机设置 移动球挂一个点光源作为子节点 设置自行调节 0 创建移动球的材质及shader shader gt 在Project Create Shader Grap
  • Unity-AR 简介

    Unity AR 简介 现有Unity AR Sdk ARKit 苹果推出的AR开发平台 ARCore Google 推出的增强现实 SDK ARFoundation ARFoundation是ARKit XR插件和ARCore XR插件
  • FBX导入Unity中模型没有材质的处理

    一 3dMax导出FBX时的注意事项 导出时 确保maps文件存在 里面放着fbx用到的image 二 在Unity中的设置 1 文件拖入Unity的Assets文件夹中 2 查看模型的材质是否存在 如下所示 材质为None 此时拖入sce
  • Unity Shader入门精要第七章 基础纹理之遮罩纹理

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一 实践 参考 前言 遮罩纹理 mask texture 是本章要介绍的最后一种纹理 它非常有用 在很多商业游戏中 都可以见到它的身影 那么什么是遮罩呢 简单来讲 遮罩允许我们
  • UnityVR--组件3--Line Renderer--线性渲染

    目录 线性渲染组件简介 绘制线条Line Renderer组件介绍 绘制拖尾Trail Renderer组件介绍 应用1 使用Line Renderer绘制线段 应用1实现 使用系统工具或自定义工具绘制线段 应用2 Trail Render
  • Unity中UI框架的使用1-添加面板、显示Loading页面

    其中BasePanel和Canvas都是挂在面板的预制物上的 1 导入我们的UI框架 本篇文章中有用的是两个UIPanelType NUIManager和NBasePanel 会放在文章最后供大家使用 2 先将我们做好的Panel设置成预制
  • Unity打开工程时卡住的问题

    自从Unity升级了一个版本后 Unity打开工程卡住的问题越来越严重了 具体表现为 选择工程后 Unity窗口消失 但进程还在 有时候等个几分钟能出来 有时候等10分钟都不见得能出来 直观感受上看 似乎是Unity加载工程的时候某一步卡了
  • unity: C#的Action Event Delegate的异同

    目录 一 Action 二 Event 三 Action和Event区别 四 Delegate 总结 Action Event Delegate的异同 前言 Action Event和Delegate都是C 语言中的重要概念 分别用于管理函
  • unity 性能查看工具Profiler

    文章目录 前言 profiler工具介绍 菜单栏 帧视图 模块视图 模块详细信息 通过profiler分析优化游戏性能 最后 前言 每次进行游戏优化的时候都用这个工具查看内存泄漏啊 代码优化啊之类的东西 真的好用 但是之前也就是自己摸索一下
  • unity3d切换场景Application.LoadLevel(1)含义

    Application LoadLevel 1 场景ID
  • Unity万向节死锁解决方案(2023/12/4)

    1 万向节死锁无法解决 这是因为它的特性就是如此 就像玻璃杯就是玻璃 这不可否认 别钻牛角尖昂 2 大多数情况下欧拉角足够用 例如 CF 摄像机不可能绕z轴旋转 x轴旋转也不会超过九十度 因为那样人物的腰子会被扭断 塔防游戏 保卫萝卜 吃鸡
  • unity3d image组件不显示

    需要将UI组件放到画布下面
  • Unity中URP下的指数雾

    文章目录 前言 一 指数雾 雾效因子 1 FOG EXP 2 FOG EXP2 二 MixFog 1 ComputeFogIntensity 雾效强度计算 2 lerp fogColor fragColor fogIntensity 雾效颜
  • VS2022 | 显示Unreal Engine日志

    VS2022 显示Unreal Engine日志 视图 gt 其他窗口 gt Unreal Engine日志 视图 gt 其他窗口 gt Unreal Engine日志
  • U3D游戏开发中摇杆的制作(NGUI版)

    在PC端模拟摇杆 实现控制摇杆让玩家或者物体移动 以下是完整代码 using System Collections using System Collections Generic using UnityEngine public clas
  • 游戏开发常见操作梳理之NPC任务系统

    多数游戏存在任务系统 接下来介绍通过NPC触发任务的游戏制作代码 using System Collections using System Collections Generic using UnityEngine
  • 游戏开发常见操作梳理系列之——玩家信息的显示系统

    在游戏中 有不少游戏在左上角会出现玩家的头像和等级以及血量 这就是玩家的信息显示系统 那么这些是如何制作的呢 接下来我将讲讲代码的操作 其它操作我会在其它笔记中一一说明 敬请期待 信息的显示相当简单就是控制一些UI 然后在其它系统里面填写相
  • 游戏开发常用实践操作之按动任意键触发

    接下来一些笔记会对于一些大大小小的实践操作进行记录 希望对你有所帮助 在游戏中 我们经常会遇到一些按动任意键触发的操作 接下来展示核心代码 以下是对于Unity中的操作 使用的UI是NGUI 对于核心操作没有影响 你可以自己置换 void
  • 游戏开发常见操作系列之敌人系统的开发一(U3D)

    在开发游戏的过程中 我们常常会出现一些敌人攻击我们玩家 并且实现掉血以及死亡的现象 敌人还会源源不断地生成 这是怎么制作的呢 接下来为大家提供方法 其中使用了NGUI 后续会更新其它方法 敬请期待 使用HUDText实现扣血时显示文本 直接

随机推荐