Unity使用LineRender实现签名效果
作者:吸血鬼1124 发布时间:2023-08-02 20:40:00
本文为大家分享了Unity制作签名功能的具体代码,供大家参考,具体内容如下
前言:项目中需要做一个签名的功能,同时需要两个两个屏幕进行显示,但是都是在UI上,从网上查了大量资料。
找到两种方法:
1、修改图片像素点 但是是马赛克效果,不满足需求
2、使用LineRenderer 的3D签名制作出2D效果
改像素点:
先上代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Test : ObjBase
{
public GameObject m_obj;
private Texture2D m_tex;
public Color m_color;
public int size = 3;
private Color[] m_textureColorsStart;
public RawImage showImg;
void Start()
{
if (Display.displays.Length > 1)
Display.displays[1].Activate();
if (Display.displays.Length > 2)
Display.displays[2].Activate();
m_tex = m_obj.GetComponent<MeshRenderer>().material.mainTexture as Texture2D;
//从纹理中获取像素颜色
m_textureColorsStart = m_tex.GetPixels();
Debug.Log(m_tex.name);
}
void Update()
{
//Vector3 oldPos=Vector3.zero;
//oldPos = Input.mousePosition;
//Ray ray = uiCam.ScreenPointToRay(Input.mousePosition);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Input.GetMouseButton(0))
{
// float m_magnitude = (Input.mousePosition - oldPos).magnitude;
// Vector3 dir = Input.mousePosition - oldPos;
if (Physics.Raycast(ray, out hit))
{
//在碰撞位置处的UV纹理坐标。
Vector2 pixelUV = hit.textureCoord;
//以像素为单位的纹理宽度
pixelUV.x *= m_tex.width;
pixelUV.y *= m_tex.height;
//贴图UV坐标以右上角为原点
for (float i = pixelUV.x - 1; i < pixelUV.x + size; i++)
{
for (float j = pixelUV.y - 1; j < pixelUV.y + size; j++)
{
m_tex.SetPixel((int)i, (int)j, m_color);
}
}
Debug.Log(pixelUV);
m_tex.Apply();
showImg.texture = m_tex;
}
}
if (Input.GetKeyDown(KeyCode.Space))
{
//还原
m_tex.SetPixels(m_textureColorsStart);
m_tex.Apply();
}
//在处理鼠标按下的记录下位置,抬起的时候记录下位置,取2个位置中间的位置发射射线
//if (Input.GetMouseButtonDown(0))
//{
//}
//if (Input.GetMouseButtonUp(0))
//{
//}
}
public void OnClick()
{
showImg.texture = m_tex;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjBase : MonoBehaviour
{
public bool IsShow
{
get { return gameObject.activeSelf; }
}
// Use this for initialization
void Start()
{
}
/// <summary>
/// 显示
/// </summary>
/// <param name="parameter"></param>
public virtual void Show(object parameter = null)
{
gameObject.SetActive(true);
}
/// <summary>
/// 隐藏
/// </summary>
/// <param name="parameter"></param>
public virtual void Hide(object parameter = null)
{
gameObject.SetActive(false);
}
}
Test脚本是用来修改像素点的,ObjBase只是一个根父类,控制显示和隐藏。
测试场景用的Quad,通过读取他的mainTexture对应的像素,进行修改,UI中的话就是将一张图片转成Texture2D形式,通过读取像素点,进行修改即可,同时还可以实现同步效果。
项目中的Hierarchy窗口设置:
项目需求:使用了两个画布,MainCamera照射Quad,两个UI相机分别照射两个画布,画布的Render Mode设置为Screen Space -Camera格式。GameObject挂载脚本,Quad用来修改其上的图片的像素点。
效果图:
使用LineRenderer 3D划线方法实现2D签名效果:
先上代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Text;
using System.IO;
using UnityEngine.EventSystems;
public class Test5 : MonoBehaviour {
public GameObject drawObj;
private bool beginDraw;
private GameObject obj;
public Transform parent;
public RawImage rawImg;
public Camera UICam;
public Camera main;//主相机和UI相机共同照射到的地方进行截图
Color[] colors;
Texture2D myTexture2D;
public RawImage photo;
public RawImage showImg;
[SerializeField] private string _name;
public RectTransform canvas1;
public void SaveFile()
{
Camera mainCam;
GameObject cam = Camera.main.gameObject;
if (cam)
{
mainCam = cam.GetComponent<Camera>();
}
else
{
return;
}
RenderTexture renderTex;
renderTex = new RenderTexture(Screen.width, Screen.height, 24);
mainCam.targetTexture = renderTex;
mainCam.Render();
myTexture2D = new Texture2D(renderTex.width, renderTex.height);
RenderTexture.active = renderTex;
myTexture2D.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
myTexture2D.Apply();
byte[] bytes = myTexture2D.EncodeToJPG();
myTexture2D.Compress(true);
myTexture2D.Apply();
RenderTexture.active = null;
File.WriteAllBytes(Application.dataPath + "/StreamingAssets/TextureTemp.png", bytes);
mainCam.targetTexture = null;
GameObject.Destroy(renderTex);
}
public void OnClick()
{
main.rect = new Rect(0, 0, 1, 1);
CaptureCamera( main,new Rect(Screen.width * 0f, Screen.height * 0f, Screen.width * 1f, Screen.height * 1f));
}
/// <summary>
/// 对相机截图。
/// </summary>
/// <returns>The screenshot2.</returns>
/// <param name="camera">Camera.要被截屏的相机</param>
/// <param name="rect">Rect.截屏的区域</param>
Texture2D CaptureCamera(Camera camera,Rect rect)
{
// 创建一个RenderTexture对象
RenderTexture rt = new RenderTexture((int)rect.width, (int)rect.height, 0);
// 临时设置相关相机的targetTexture为rt, 并手动渲染相关相机
camera.targetTexture = rt;
camera.Render();
//ps: --- 如果这样加上第二个相机,可以实现只截图某几个指定的相机一起看到的图像。
//camera2.targetTexture = rt;
// camera2.Render();
//ps: -------------------------------------------------------------------
// 激活这个rt, 并从中中读取像素。
RenderTexture.active = rt;
Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24, false);
screenShot.ReadPixels(rect, 0, 0);// 注:这个时候,它是从RenderTexture.active中读取像素
screenShot.Apply();
// 重置相关参数,以使用camera继续在屏幕上显示
camera.targetTexture = null;
// camera2.targetTexture = null;
//ps: camera2.targetTexture = null;
RenderTexture.active = null; // JC: added to avoid errors
GameObject.Destroy(rt);
// 最后将这些纹理数据,成一个png图片文件
byte[] bytes = screenShot.EncodeToPNG();
string filename = Application.dataPath + string.Format("/Screenshot_{0}.png", _name);
System.IO.File.WriteAllBytes(filename, bytes);
Debug.Log(string.Format("截屏了一张照片: {0}", filename));
showImg.texture = screenShot;
main.rect = new Rect(0.25f, 0.35f, 0.5f, 0.5f);
return screenShot;
}
void Start () {
if (Display.displays.Length > 1)
Display.displays[1].Activate();
if (Display.displays.Length > 2)
Display.displays[2].Activate();
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0))
{
beginDraw = true;
obj = Instantiate(drawObj) as GameObject;
obj.transform.parent = parent;
}
if (Input.GetMouseButtonUp(0))
{
beginDraw = false;
}
if (beginDraw)
{
Vector3 position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10f);
position = Camera.main.ScreenToWorldPoint(position);
//Vector3 localPoint;
//if(RectTransformUtility.ScreenPointToWorldPointInRectangle(canvas1, position, null, out localPoint))
//{
// position = localPoint;
//}
DrawText dt = obj.GetComponent<DrawText>();
dt.points.Add(position);
dt.Draw();
dt.line.startColor = Color.yellow;
dt.line.endColor = Color.yellow;
dt.line.startWidth = 0.03f;
dt.line.endWidth = 0.03f;
}
}
}
Test5是划线和截取签名的操作,绑定在空物体上,OnClick函数绑定在按钮上
Line:制作签名预制体
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DrawText : MonoBehaviour {
public List<Vector3> points = new List<Vector3>();
public LineRenderer line;
private void Awake()
{
line = GetComponent<LineRenderer>();
}
public void Draw()
{
line.positionCount = points.Count;
for (int i = 0; i < points.Count; i++)
{
line.SetPosition(i, points[i]);
line.startWidth =2f;
line.endWidth =2f;
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
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制作的时候需要签名的部分制作成透明的即可。
效果图:
来源:https://blog.csdn.net/weixin_37608784/article/details/82835865


猜你喜欢
- 前言项目中有使用到水印效果,如下图所示。在实现过程中,最终选用ItemDecoration来实现,其中有两大步骤:自定义Drawable来完
- 一、封装一个工具类1、简易版package net.aexit.construct.acceptance.websky.utils;impo
- 前言本文主要给大家介绍了关于C/C++混合编程extern “C”使用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介
- 前言前一阵项目中的上传图片改为上传到阿里上,记录一下实现的过程,方便以后查看。参考资料:官方文档配置Android studio添加依赖de
- 问题描述:服务器接收后台返回的报文时,提示java.lang.NegativeArraySizeException分析:这种异常返回的原因,
- 一、首先下载 ZXing.Net地址是:http://zxingnet.codeplex.com/releases/view/117068然
- 目录什么是Insets?Insets相关类InsetsStateInsetsStateControllerInsetsSourceInset
- spring Boot的学习持续进行中。前面两篇博客我们介绍了如何使用Spring Boot容器搭建Web项目以及怎样为我们的Project
- 简介String是我们最常用的一个类,和普通java类一样其对象会存在java堆中。但是String类有其特殊之处,可以通过new方法生成,
- c#中Math类的方法Math.Abs 已重载。 返回指定数字的绝对值。Math.Acos 返回余弦值为指定数字的角度。Math.Asin
- 本文实例讲述了C#导出数据到csv文件的通用类。分享给大家供大家参考。具体如下:通过这个类可以很简单的定义数据格式,并导出到csv文件//这
- 一、简介构造函数,基本用法是在类对象声明的时候完成初始化工作。二、实例构造函数1、构造函数的名字与类名相同。2、使用 new 表达式创建类的
- 有想过hash["A1"] = DateTime.Now;这句是怎么实现的吗?我们来重温下学校时代就学过的哈希算法吧。我
- 在项目中,经常会遇到页面分割,最常见的系统或网站的主界面。主页面分为,上面系统简介、下面作者简介、左边系统功能菜单、右边则是菜单真正展示的界
- 本文实例为大家分享了C#用NPOI导出导入Excel帮助类的具体代码,供大家参考,具体内容如下1、准备工作在管理NuGet程序包中搜索 NP
- 以下这段C#代码实现的功能是在屏幕上画图的效果!具体代码如下://DllImport所在的名字空间 using System.Ru
- C# 8.0中的模式匹配相对C# 7.0来说有了进一步的增强,对于如下类:class Point{ public
- VB.NET中的除法运算符有两个:/(浮点除法)、\(整数除法)C#中的除法运算符只有一个:/(除法)VB.NET中的除法运算符与C#中的除
- 这篇文章主要介绍了Java集合使用 Iterator 删除元素,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,
- 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些