Unity 电子签名 贝塞尔曲线 保存图片

提示:源码附在文后~大家互相学习


前言

平常去银行办理业务,总会遇到电子签名,通过线上电子签名文件,证明自己身份。近期制作的系统中也需要电子签名功能用于人员线上审核文件签字!所以整理出组件,方便每次使用!大家共同学习!


一、组件结构

  1. 预制体结构
    在这里插入图片描述

  2. 项目结构
    在这里插入图片描述

二、功能实现

1.贝塞尔曲线签名

  • 增加签字区域面板signImage,获取鼠标放置在面板上,再实时刷新显示签字内容。
  	/// <summary>
    /// 获取鼠标当前所在UI
    /// </summary>
    /// <param name="canvas"></param>
    /// <returns></returns>
    public GameObject GetOverUI(GameObject canvas)
    {
        PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
        pointerEventData.position = Input.mousePosition;
        GraphicRaycaster gr = canvas.GetComponent<GraphicRaycaster>();
        List<RaycastResult> results = new List<RaycastResult>();
        gr.Raycast(pointerEventData, results);
        if (results.Count != 0)
        {
            return results[0].gameObject;
        }
        return null;
    }
    
	void Update()
    {
        if (Input.GetMouseButton(0) && GetOverUI(GameObject.Find("Canvas")) == signImage.gameObject)
        {

                IsUpdataRawImage = true;
                OnMouseMove(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
                queue.Add(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
                BiHua.Add(1);
        }
        if (Input.GetMouseButtonUp(0) && GetOverUI(GameObject.Find("Canvas")) == signImage.gameObject)
        {
           
                OnMouseUp();
                queue.Add(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
                BiHua.Add(0);
                //添加比划
                BiHuanIndex++;
                BiHuaList.Add(getTexture2d(texRender));
                print("BiHuanIndex" + BiHuanIndex + "BiHuaList" + BiHuaList.Count);
               
        //一直刷新显示
        if (IsUpdataRawImage == true)
        {
            DrawImage();
        }
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }
        if (IsRePlay == true && numA > 0)
        {
            numA--;
            print("现在的Index" + ((queue.Count - 1) - numA).ToString());
            if (BiHuaIndex > queue.Count - 2)
            {
                return;
            }
            else
            {
                AutoPlay((Vector3)queue[(queue.Count - 1) - numA], (queue.Count - 1) - numA);
            }
        }
    }
  • 核心绘制方法,通过获取鼠标移动、抬起,处理绘制曲线。
   /// <summary>
    /// 当鼠标移动时
    /// </summary>
    /// <param name="pos"></param>
    void OnMouseMove(Vector3 pos)
    {
        if (startPosition == Vector3.zero)
        {
            startPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
        }
        endPosition = pos;
        float distance = Vector3.Distance(startPosition, endPosition);
        brushScale = SetScale(distance);
        ThreeOrderBézierCurse(pos, distance, 4.5f);
        startPosition = endPosition;
        lastDistance = distance;
    }
 void OnMouseUp()
    {
        startPosition = Vector3.zero;
        //brushScale = 0.5f;
        a = 0;
        b = 0;
        s = 0;
    }

	// <summary>
    /// 三阶贝塞尔曲线,获取连续4个点坐标,通过调整中间2点坐标,画出部分(我使用了num/1.5实现画出部分曲线)来使曲线平滑;通过速度控制曲线宽度。
    /// </summary>
    /// <param name="pos"></param>
    /// <param name="distance"></param>
    /// <param name="targetPosOffset"></param>
    private void ThreeOrderBézierCurse(Vector3 pos, float distance, float targetPosOffset)
    {
        //记录坐标
        PositionArray1[b] = pos;
        b++;
        //记录速度
        speedArray[s] = distance;
        s++;
        if (b == 4)
        {
            Vector3 temp1 = PositionArray1[1];
            Vector3 temp2 = PositionArray1[2];

            //修改中间两点坐标
            Vector3 middle = (PositionArray1[0] + PositionArray1[2]) / 2;
            PositionArray1[1] = (PositionArray1[1] - middle) * 1.5f + middle;
            middle = (temp1 + PositionArray1[3]) / 2;
            PositionArray1[2] = (PositionArray1[2] - middle) * 2.1f + middle;

            for (int index1 = 0; index1 < num / 1.5f; index1++)
            {
                float t1 = (1.0f / num) * index1;
                Vector3 target = Mathf.Pow(1 - t1, 3) * PositionArray1[0] +
                                 3 * PositionArray1[1] * t1 * Mathf.Pow(1 - t1, 2) +
                                 3 * PositionArray1[2] * t1 * t1 * (1 - t1) + PositionArray1[3] * Mathf.Pow(t1, 3);
                //float deltaspeed = (float)(distance - lastDistance) / num;
                //获取速度差值(存在问题,参考)
                float deltaspeed = (float)(speedArray[3] - speedArray[0]) / num;
                //float randomOffset = Random.Range(-1/(speedArray[0] + (deltaspeed * index1)), 1 / (speedArray[0] + (deltaspeed * index1)));
                //模拟毛刺效果
                float randomOffset = Random.Range(-targetPosOffset, targetPosOffset);
                DrawBrush(texRender, (int)(target.x + randomOffset), (int)(target.y + randomOffset), brushTypeTexture, brushColor, SetScale(speedArray[0] + (deltaspeed * index1)));
            }

            PositionArray1[0] = temp1;
            PositionArray1[1] = temp2;
            PositionArray1[2] = PositionArray1[3];

            speedArray[0] = speedArray[1];
            speedArray[1] = speedArray[2];
            speedArray[2] = speedArray[3];
            b = 3;
            s = 3;
        }
        else
        {
            DrawBrush(texRender, (int)endPosition.x, (int)endPosition.y, brushTypeTexture,
                brushColor, brushScale);
        }

    }

2.清除

书写错误时,需要清除页面上已生成的签字,重新绘制。

   /// <summary>
    /// 重写
    /// </summary>
    public void OnClickClear()
    {
        text.gameObject.SetActive(false);
        Clear(texRender);
        BiHuaList.Clear();
        BiHuanIndex = 0;
        Resources.UnloadUnusedAssets();
        GC.Collect();
        BiHuaIndex = 0;
        queue.Clear();
        BiHua.Clear();
        IsUpdataRawImage = true;
        startPosition = Vector3.zero;
        //brushScale = 0.5f;
        a = 0;
        b = 0;
        s = 0;
        isClear = true;
    }

3.保存

以图片形式保存到指定目录中;或以二进制形式保存到数据库。

 /// <summary>
    /// 将书写好的效果保存成为Png
    /// </summary>
    public void Save()
    {
        string filepath = System.DateTime.Now.ToString("yyMMddHHmmss") + ".png";
        StartCoroutine(SaveRenderTextureToPNG(texRender, filePath, filepath));
    }

    /// <summary>
    /// 将RenderTexture保存成一张png图片
    /// </summary>
    /// <param name="rt"></param>
    /// <param name="contents"></param>
    /// <param name="pngName"></param>
    /// <returns></returns>
    IEnumerator SaveRenderTextureToPNG(RenderTexture rt, string contents, string pngName)
    {
        RenderTexture prev = RenderTexture.active;
        RenderTexture.active = rt;
        Texture2D png = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
        png.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        Color[] cc = png.GetPixels();
        for (int i = 0; i < cc.Length; i++)
            if (cc[i].r == 1)
                cc[i] = Color.clear;
        png.SetPixels(cc);
        byte[] bytes = png.EncodeToPNG();
        //-------------1.可舍弃方式2,在此处上传图片至服务器存储------------
        //signPicture.sprite = ByteToSprite(bytes, 15, 15);//反显数据库取回图片
        //-------------2.此处为保存至本地-----------
        if (!Directory.Exists(contents))
            Directory.CreateDirectory(contents);
        FileStream file = File.Open(contents + "/" + pngName, FileMode.Create);
        BinaryWriter writer = new BinaryWriter(file);
        writer.Write(bytes);
        file.Close();
        Texture2D.DestroyImmediate(png);
        RenderTexture.active = prev;
        text.text = "已保存成功";
        text.gameObject.SetActive(true);
        yield return writer;
    }

PS:目前代码中将图片以png格式存放至项目下 Application.streamingAssetsPath + "/QianMing/"路径下。

4.重放

实现按书写顺序,重播签名。

 /// <summary>
    /// 重放功能  
    /// </summary>
    public void RePlay()
    {
        text.gameObject.SetActive(false);
        BiHuaIndex = 0;
        Clear(texRender);
        Proxy = queue[0];
        IsRePlay = true;
        numA = queue.Count;
        BiHuanIndex = 0;
        BiHuaList.Clear();
        startPosition = Vector3.zero;
        a = 0;
        b = 0;
        s = 0;

    }


三、脚本说明

功能方法归类在SignPanel.cs中,将脚本绑定在Camera上,引入笔刷材质等相关内容。
在这里插入图片描述

总结

组件结构简单,层级明朗,便于理解。可更改ui和尺寸用于不同场合。个人总结归纳,便于使用。避免重复造轮子~~~

CSDN组件下载:https://download.csdn.net/download/u014641682/87577410

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Unity中制作贝塞尔曲线可以使用Unity自带的LineRenderer组件以及自定义脚本来实现。以下是一个简单的步骤: 1. 创建一个空物体作为贝塞尔曲线的父物体,并将LineRenderer组件添加到它上面。 2. 在脚本中定义贝塞尔曲线的起点、控制点和终点坐标,并根据这些点计算曲线上的点。 3. 将计算出来的曲线上的点赋值给LineRenderer组件的positions属性,使其显示出曲线。 这里是一个简单的例子代码,用于在Unity中制作二次贝塞尔曲线: ```csharp using UnityEngine; [RequireComponent(typeof(LineRenderer))] public class BezierCurve : MonoBehaviour { public Vector3 startPoint; public Vector3 controlPoint; public Vector3 endPoint; public int vertexCount = 20; private LineRenderer lineRenderer; private void Start() { lineRenderer = GetComponent<LineRenderer>(); lineRenderer.positionCount = vertexCount; } private void Update() { for (int i = 0; i < vertexCount; i++) { float t = i / (float)(vertexCount - 1); Vector3 vertex = CalculateBezierPoint(startPoint, controlPoint, endPoint, t); lineRenderer.SetPosition(i, vertex); } } private Vector3 CalculateBezierPoint(Vector3 p0, Vector3 p1, Vector3 p2, float t) { float u = 1 - t; float tt = t * t; float uu = u * u; Vector3 point = uu * p0; point += 2 * u * t * p1; point += tt * p2; return point; } } ``` 这个脚本可以添加到Unity场景中的一个空物体上,通过调整startPoint、controlPoint和endPoint的值来控制贝塞尔曲线的形状。vertexCount用于控制曲线上的点数,越大曲线越平滑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值