经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 软件/图像 » unity » 查看文章
Unity使用LineRender实现签名效果
来源:jb51  时间:2021/10/11 9:22:36  对本文有异议

本文为大家分享了Unity制作签名功能的具体代码,供大家参考,具体内容如下

前言:项目中需要做一个签名的功能,同时需要两个两个屏幕进行显示,但是都是在UI上,从网上查了大量资料。

找到两种方法:

1、修改图片像素点  但是是马赛克效果,不满足需求
2、使用LineRenderer 的3D签名制作出2D效果

改像素点:

先上代码

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class Test : ObjBase
  6. {
  7. public GameObject m_obj;
  8. private Texture2D m_tex;
  9. public Color m_color;
  10. public int size = 3;
  11. private Color[] m_textureColorsStart;
  12. public RawImage showImg;
  13. void Start()
  14. {
  15. if (Display.displays.Length > 1)
  16. Display.displays[1].Activate();
  17. if (Display.displays.Length > 2)
  18. Display.displays[2].Activate();
  19. m_tex = m_obj.GetComponent<MeshRenderer>().material.mainTexture as Texture2D;
  20. //从纹理中获取像素颜色
  21. m_textureColorsStart = m_tex.GetPixels();
  22. Debug.Log(m_tex.name);
  23. }
  24. void Update()
  25. {
  26. //Vector3 oldPos=Vector3.zero;
  27. //oldPos = Input.mousePosition;
  28. //Ray ray = uiCam.ScreenPointToRay(Input.mousePosition);
  29. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  30. RaycastHit hit;
  31. if (Input.GetMouseButton(0))
  32. {
  33. // float m_magnitude = (Input.mousePosition - oldPos).magnitude;
  34. // Vector3 dir = Input.mousePosition - oldPos;
  35. if (Physics.Raycast(ray, out hit))
  36. {
  37. //在碰撞位置处的UV纹理坐标。
  38. Vector2 pixelUV = hit.textureCoord;
  39. //以像素为单位的纹理宽度
  40. pixelUV.x *= m_tex.width;
  41. pixelUV.y *= m_tex.height;
  42. //贴图UV坐标以右上角为原点
  43. for (float i = pixelUV.x - 1; i < pixelUV.x + size; i++)
  44. {
  45. for (float j = pixelUV.y - 1; j < pixelUV.y + size; j++)
  46. {
  47. m_tex.SetPixel((int)i, (int)j, m_color);
  48. }
  49. }
  50. Debug.Log(pixelUV);
  51. m_tex.Apply();
  52. showImg.texture = m_tex;
  53. }
  54. }
  55. if (Input.GetKeyDown(KeyCode.Space))
  56. {
  57. //还原
  58. m_tex.SetPixels(m_textureColorsStart);
  59. m_tex.Apply();
  60. }
  61. //在处理鼠标按下的记录下位置,抬起的时候记录下位置,取2个位置中间的位置发射射线
  62. //if (Input.GetMouseButtonDown(0))
  63. //{
  64. //}
  65. //if (Input.GetMouseButtonUp(0))
  66. //{
  67. //}
  68. }
  69. public void OnClick()
  70. {
  71. showImg.texture = m_tex;
  72. }
  73. }
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class ObjBase : MonoBehaviour
  5. {
  6. public bool IsShow
  7. {
  8. get { return gameObject.activeSelf; }
  9. }
  10. // Use this for initialization
  11. void Start()
  12. {
  13. }
  14. /// <summary>
  15. /// 显示
  16. /// </summary>
  17. /// <param name="parameter"></param>
  18. public virtual void Show(object parameter = null)
  19. {
  20. gameObject.SetActive(true);
  21. }
  22. /// <summary>
  23. /// 隐藏
  24. /// </summary>
  25. /// <param name="parameter"></param>
  26. public virtual void Hide(object parameter = null)
  27. {
  28. gameObject.SetActive(false);
  29. }
  30. }

Test脚本是用来修改像素点的,ObjBase只是一个根父类,控制显示和隐藏。

测试场景用的Quad,通过读取他的mainTexture对应的像素,进行修改,UI中的话就是将一张图片转成Texture2D形式,通过读取像素点,进行修改即可,同时还可以实现同步效果。

项目中的Hierarchy窗口设置:

项目需求:使用了两个画布,MainCamera照射Quad,两个UI相机分别照射两个画布,画布的Render Mode设置为Screen Space -Camera格式。GameObject挂载脚本,Quad用来修改其上的图片的像素点。

效果图:

使用LineRenderer   3D划线方法实现2D签名效果:

先上代码:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. using System.Text;
  6. using System.IO;
  7. using UnityEngine.EventSystems;
  8. public class Test5 : MonoBehaviour {
  9. public GameObject drawObj;
  10. private bool beginDraw;
  11. private GameObject obj;
  12. public Transform parent;
  13. public RawImage rawImg;
  14. public Camera UICam;
  15. public Camera main;//主相机和UI相机共同照射到的地方进行截图
  16. Color[] colors;
  17. Texture2D myTexture2D;
  18. public RawImage photo;
  19. public RawImage showImg;
  20. [SerializeField] private string _name;
  21. public RectTransform canvas1;
  22. public void SaveFile()
  23. {
  24. Camera mainCam;
  25. GameObject cam = Camera.main.gameObject;
  26. if (cam)
  27. {
  28. mainCam = cam.GetComponent<Camera>();
  29. }
  30. else
  31. {
  32. return;
  33. }
  34. RenderTexture renderTex;
  35. renderTex = new RenderTexture(Screen.width, Screen.height, 24);
  36. mainCam.targetTexture = renderTex;
  37. mainCam.Render();
  38. myTexture2D = new Texture2D(renderTex.width, renderTex.height);
  39. RenderTexture.active = renderTex;
  40. myTexture2D.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
  41. myTexture2D.Apply();
  42. byte[] bytes = myTexture2D.EncodeToJPG();
  43. myTexture2D.Compress(true);
  44. myTexture2D.Apply();
  45. RenderTexture.active = null;
  46. File.WriteAllBytes(Application.dataPath + "/StreamingAssets/TextureTemp.png", bytes);
  47. mainCam.targetTexture = null;
  48. GameObject.Destroy(renderTex);
  49. }
  50. public void OnClick()
  51. {
  52. main.rect = new Rect(0, 0, 1, 1);
  53. CaptureCamera( main,new Rect(Screen.width * 0f, Screen.height * 0f, Screen.width * 1f, Screen.height * 1f));
  54. }
  55. /// <summary>
  56. /// 对相机截图。
  57. /// </summary>
  58. /// <returns>The screenshot2.</returns>
  59. /// <param name="camera">Camera.要被截屏的相机</param>
  60. /// <param name="rect">Rect.截屏的区域</param>
  61. Texture2D CaptureCamera(Camera camera,Rect rect)
  62. {
  63. // 创建一个RenderTexture对象
  64. RenderTexture rt = new RenderTexture((int)rect.width, (int)rect.height, 0);
  65. // 临时设置相关相机的targetTexture为rt, 并手动渲染相关相机
  66. camera.targetTexture = rt;
  67. camera.Render();
  68. //ps: --- 如果这样加上第二个相机,可以实现只截图某几个指定的相机一起看到的图像。
  69. //camera2.targetTexture = rt;
  70. // camera2.Render();
  71. //ps: -------------------------------------------------------------------
  72. // 激活这个rt, 并从中中读取像素。
  73. RenderTexture.active = rt;
  74. Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24, false);
  75. screenShot.ReadPixels(rect, 0, 0);// 注:这个时候,它是从RenderTexture.active中读取像素
  76. screenShot.Apply();
  77. // 重置相关参数,以使用camera继续在屏幕上显示
  78. camera.targetTexture = null;
  79. // camera2.targetTexture = null;
  80. //ps: camera2.targetTexture = null;
  81. RenderTexture.active = null; // JC: added to avoid errors
  82. GameObject.Destroy(rt);
  83. // 最后将这些纹理数据,成一个png图片文件
  84. byte[] bytes = screenShot.EncodeToPNG();
  85. string filename = Application.dataPath + string.Format("/Screenshot_{0}.png", _name);
  86. System.IO.File.WriteAllBytes(filename, bytes);
  87. Debug.Log(string.Format("截屏了一张照片: {0}", filename));
  88. showImg.texture = screenShot;
  89. main.rect = new Rect(0.25f, 0.35f, 0.5f, 0.5f);
  90. return screenShot;
  91. }
  92. void Start () {
  93. if (Display.displays.Length > 1)
  94. Display.displays[1].Activate();
  95. if (Display.displays.Length > 2)
  96. Display.displays[2].Activate();
  97. }
  98. // Update is called once per frame
  99. void Update () {
  100. if (Input.GetMouseButtonDown(0))
  101. {
  102. beginDraw = true;
  103. obj = Instantiate(drawObj) as GameObject;
  104. obj.transform.parent = parent;
  105. }
  106. if (Input.GetMouseButtonUp(0))
  107. {
  108. beginDraw = false;
  109. }
  110. if (beginDraw)
  111. {
  112. Vector3 position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10f);
  113. position = Camera.main.ScreenToWorldPoint(position);
  114. //Vector3 localPoint;
  115. //if(RectTransformUtility.ScreenPointToWorldPointInRectangle(canvas1, position, null, out localPoint))
  116. //{
  117. // position = localPoint;
  118. //}
  119. DrawText dt = obj.GetComponent<DrawText>();
  120. dt.points.Add(position);
  121. dt.Draw();
  122. dt.line.startColor = Color.yellow;
  123. dt.line.endColor = Color.yellow;
  124. dt.line.startWidth = 0.03f;
  125. dt.line.endWidth = 0.03f;
  126. }
  127. }
  128. }

Test5是划线和截取签名的操作,绑定在空物体上,OnClick函数绑定在按钮上

Line:制作签名预制体

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class DrawText : MonoBehaviour {
  5. public List<Vector3> points = new List<Vector3>();
  6. public LineRenderer line;
  7. private void Awake()
  8. {
  9. line = GetComponent<LineRenderer>();
  10. }
  11. public void Draw()
  12. {
  13. line.positionCount = points.Count;
  14. for (int i = 0; i < points.Count; i++)
  15. {
  16. line.SetPosition(i, points[i]);
  17. line.startWidth =2f;
  18. line.endWidth =2f;
  19. }
  20. }
  21. // Use this for initialization
  22. void Start () {
  23. }
  24. // Update is called once per frame
  25. void Update () {
  26. }
  27. }

Draw Text脚本挂在预制体Line上,Line 添加LineRenderer组件,同时Material中加入自己创建的材质球

项目需求:Hierarchy窗口设置

和上面一种方法一样,也是两个画布,两个UI相机,同时需要一个MainCamera

parent为空物体,用来作为根节点,将签名时实时生成的预制体放在其下面,作为子节点,方便后面进行销毁,重新签名。

重点:

第二种方法使用的是特定相机照射画面进行截图,Test5中的CaptureCamera方法就是截取主相机照射到的画面。由于签名不能进行全屏进行截图,只能部分截图,类似相面的画面

下面会有一些常规的功能按钮,重新签名,保存签名等等操作,这些操作就是在UI上进行签名。

所以,通过修改MainCamera的Viewport Rect窗口来进行截图,同时能够实现正常的签名操作。

MainCamera的Viewport Rect设置:

运行刚开始:

通过设置这个属性,可以使签名界面呈现上一个图的效果,前面是UI层,后面是3D层。

然而在截屏图的时候如果始终保持Viewport Rect是上面的设置,则截图的时候仍把周围的黑色部分也截取出来,刚开始以为特定相机照射截图只截取Viewport Rect中的图像,后来测试是周围的所有黑色部分也截取了,这样就不满足要求。

所以,在代码中签字 的时候保持上面的设置,截图之前main.rect = new Rect(0, 0, 1, 1);设置成全屏,截好之后重新回复成原来的设置  main.rect = new Rect(0.25f, 0.35f, 0.5f, 0.5f);,截图完成之后将签名图片赋值给第二个屏幕画布中的RawImage进行展示。

达到效果。结合UI实际签名过程中

中间的白色部分,通过设置MainCamera中的Camera组件中的Background(设置为白色)以及天空盒(Windows->Lighting->Settings->Scene->Skybox Material设置为空),设置为需要的颜色。UI制作的时候需要签名的部分制作成透明的即可。

效果图:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号