欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity实现3D书写功能

程序员文章站 2024-03-25 23:55:46
...

利用LineRender实现书写功能

利用LineRender的点位功能实现画线功能

画板的预制排版

Unity实现3D书写功能

画板的脚本放置与Board上并添加BoxCollider
UICanvasTemp为临时画板层
UICanvasSave为最终画板层
UICanvasTempUICanvasSave中的Comtent采用RawImage
UICameraUICanvasTemp中的Comtent使用RenderTexture进行关联,使UICanvasTemp中的Comtent的图片始终为UICamera的图片
UICanvasSave中的Comtent的图片为LineRender的保存图片

画板的缺陷

  • 需要保证UICanvasTempUICanvasSave的大小全部占满UICamera的区间,否则会出现BUG
  • UICanvasTempLayer层需要为Default
  • UICanvasTempLayer层需要为DrawUI
  • UICamera为画板的专用相机,只看画板层DrawUI
  • 场景中的主相机不可场景DrawUI
  • 画布CanvasRenderMode的类型必须为WorldSpace否则主相机无法查看

画板的优化

  • UICanvasSave的作用为避免LineRender中的点位过多导致程序卡死,所有每一笔画完都需要对LineRender的内容进行保存.或当LineRender的点位达到一定的数量进行临时保存.或当相邻点位的距离较小时忽略点位的写入
  • 画布CanvasRenderMode的类型可以用ScreenSpace-Camera来确保画布的大小占满UICamera的区间

画板功能代码


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.UI;

public class DrawBoard : MonoBehaviour
{
    public Transform mRoot;
    public Camera mUICamera;
    public Canvas mUICanvasSave;
    public Canvas mUICanvasTemp;
    public RawImage mSaveDrawCanvas;
    public RawImage mTempDrawCanvas;

    Dictionary<uint, LineRenderer> mAllLine = new Dictionary<uint, LineRenderer>();
    List<LineRenderer> mLastLine = new List<LineRenderer>();
	/// <summary>
    /// 开始绘画参数准备
    /// </summary>
    /// <param name="varPenID">笔的ID,方便与多笔同时进行作画</param>
    /// <param name="size">笔的粗细</param>
    /// <param name="color">笔的颜色</param>
    public void StartDraw(uint varPenID, float size, Color color, Vector3 pos)
    {
        if (mAllLine.ContainsKey(varPenID))
        {
            return;
        }
        LineRenderer Line;
        if (mLastLine.Count != 0)
        {
            Line = mLastLine[0];
            mLastLine.RemoveAt(0);
        }
        else
        {
            Line = NewGameobject<LineRenderer>("Line", mRoot);
            Line.gameObject.layer = LayerMask.NameToLayer("DrawUI");
            Line.material = new Material(Shader.Find("Unlit/Color"));
            Line.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
        }
        Line.material.SetColor("_Color", color);
        Line.widthMultiplier = size;
        Line.positionCount = 2;
        Line.SetPosition(0, pos);
        Line.SetPosition(1, pos);
        Line.gameObject.SetActive(true);
        mAllLine.Add(varPenID, Line);
    }
    /// <summary>
    /// 进行绘画
    /// </summary>
    /// <param name="varPen">笔的ID,方便与多笔同时进行作画</param>
    /// <param name="pos">绘画的点位</param>
    public void Drawing(uint varPenID, Vector3 pos)
    {
        if (!mAllLine.ContainsKey(varPenID))
        {
            return;
        }
        LineRenderer Line = mAllLine[varPenID];
        Line.positionCount += 1;
        Line.SetPosition(Line.positionCount - 1, pos);
    }
    /// <summary>
    /// 结束绘画
    /// </summary>
    /// <param name="varPen">笔的ID,方便与多笔同时进行作画</param>
    public void EndDraw(uint varPenID)
    {
        if (!mAllLine.ContainsKey(varPenID))
        {
            return;
        }
        LineRenderer varLine = mAllLine[varPenID];
        mAllLine.Remove(varPenID);
        //进行图画保存
        SaveDraw();
        varLine.gameObject.SetActive(false);
        mLastLine.Add(varLine);
    }
    /// <summary>
    /// 对相机渲染的图片进行存储进指定的图片中
    /// </summary>
    private void SaveDraw()
    {
        if (mSaveDrawCanvas == null || mTempDrawCanvas == null)
            return;
        foreach (var item in mAllLine)
        {
            item.Value.gameObject.SetActive(false);
        }

        Texture2D screenShot = CaptureScreen(mUICamera);

        mSaveDrawCanvas.texture = screenShot;
        mSaveDrawCanvas.color = Color.white;


        foreach (var item in mAllLine)
        {
            item.Value.gameObject.SetActive(true);
        }
    }
    /// <summary>
    /// 将相机看到的画面转换成Texture2D
    /// </summary>
    /// <param name="came"></param>
    /// <param name="r"></param>
    /// <returns></returns>
    public Texture2D CaptureScreen(Camera camera)
    {
        camera.Render();
        RenderTexture.active = camera.targetTexture;
        Rect r = new Rect(Vector2.zero, new Vector2(camera.activeTexture.width, camera.targetTexture.height));
#if UNITY_2017
        Texture2D screenShot = new Texture2D((int)r.width, (int)r.height, TextureFormat.RGBA32, false);
#elif UNITY_2019
        Texture2D screenShot = new Texture2D((int)r.width, (int)r.height, GraphicsFormat.R32G32B32A32_SFloat, TextureCreationFlags.None);
#endif
        screenShot.ReadPixels(r, 0, 0);
        screenShot.Apply();
        RenderTexture.active = null;
        return screenShot;
    }

    private T NewGameobject<T>(string varContent,Transform varParent) where T:Component
    {
        GameObject TempGame = new GameObject(varContent);
        TempGame.transform.SetParent(varParent);
        TempGame.transform.localPosition = Vector3.zero;
        TempGame.transform.localEulerAngles = Vector3.zero;
        TempGame.transform.localScale = Vector3.one;
        T TempT = TempGame.AddComponent<T>();
        return TempT;
    }
}

画笔的功能实现


using UnityEngine;

public class DrawPen : MonoBehaviour
{
    /// <summary>
    /// 笔的ID
    /// </summary>
    public uint mID;
    /// <summary>
    /// 笔的输入大小
    /// </summary>
    public float mSize = 0.01f;
    /// <summary>
    /// 笔的输入颜色
    /// </summary>
    public Color mPenColor = Color.white;
    /// <summary>
    /// 笔的输入类型
    /// </summary>
    public PenType mPenType = PenType.Collider;


    private void Awake()
    {
        ColliderDrawInit();
    }

    #region ColliderDraw//使用碰撞实现画笔的输入功能

    private void ColliderDrawInit()
    {
        if (mPenType != PenType.Collider)
            return;
        BoxCollider box = this.GetComponent<BoxCollider>();
        if (box == null)
        {
            box = this.gameObject.AddComponent<BoxCollider>();
        }
        box.isTrigger = true;

        Rigidbody Rig = this.GetComponent<Rigidbody>();
        if (Rig == null)
        {
            Rig = this.gameObject.AddComponent<Rigidbody>();
        }
        Rig.isKinematic = true;
        Rig.useGravity = false;
    }
    private void OnTriggerEnter(Collider other)
    {
        if (mPenType != PenType.Collider)
            return;
        DrawBoard board = other.GetComponent<DrawBoard>();
        if (board == null)
            return;
        Vector3 pos = other.ClosestPoint(this.transform.position);
        board.StartDraw(mID, mSize, mPenColor, pos);
    }
    private void OnTriggerStay(Collider other)
    {
        if (mPenType != PenType.Collider)
            return;
        DrawBoard board = other.GetComponent<DrawBoard>();
        if (board == null)
            return;
        Vector3 pos = other.ClosestPoint(this.transform.position);
        board.Drawing(mID, pos);
    }
    private void OnTriggerExit(Collider other)
    {
        if (mPenType != PenType.Collider)
            return;
        DrawBoard board = other.GetComponent<DrawBoard>();
        if (board == null)
            return;
        board.EndDraw(mID);
    }
    
    #endregion
    
    #region RayDraw//使用射线实现画笔的输入功能



    #endregion

    public enum PenType
    {
        Collider,
        Ray,
    }
}