当前位置:   article > 正文

Unity涂鸦【1】——基于Vectrosity的屏幕涂鸦和白板涂鸦的简单实现_unity 2d涂鸦原理与实现详解

unity 2d涂鸦原理与实现详解

〇、涂鸦与画线

  • Vectrosity的介绍
    The ultimate professional line-drawing utility, continuously supported since 2010! Find out why Unity users call Vectrosity “great,” “wonderful,” and why buying it “was the smartest move I’ve made for a long time.”

以上文字用人话来讲:
Vectrosity自2010年横空出世以来——牢记使命不忘初心,迄今已成为最为高端、大气、上档次的画线工具。

特点: Vectrosity是一个非开源的商业包。

本文讨论的内容:基于Vectrosity包进行画线涂鸦

一、简单涂鸦的演示

  • 屏幕涂鸦演示
    请添加图片描述
  • 黑板涂鸦演示
    请添加图片描述

二、用到的包

asset store中的Vectrosity包。

三、涂鸦的实现

1、屏幕涂鸦

  • 屏幕涂鸦的过程
1】按下鼠标左键开始单笔涂鸦,松开鼠标左键则停止本笔涂鸦。
 【2】再次按下则开始画另一笔涂鸦。
 【3】每一笔涂鸦信息用Vectrosity中的一个line对象来保存
 【4】涂鸦的时候,取点的位置是屏幕鼠标位置【2D取xy,3D自动取xy同时自动加一个固定的z】
 【5】用Vectrosity来显示所有的line对象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 屏幕涂鸦的代码实现
    代码来自官网DrawLinesMouse.cs,略有改动
// The DrawLinesTouch script adapted to work with mouse input, with the option for 3D or 2D lines
using UnityEngine;
using Vectrosity;
using System.Collections.Generic;

/// <summary>
/// 屏幕涂鸦:
/// 【1】按下鼠标左键开始单笔涂鸦,松开鼠标左键则停止本笔涂鸦。
/// 【2】再次按下则开始画另一笔涂鸦。
/// 【3】每一笔涂鸦信息用Vectrosity中的一个line对象来保存
/// 【4】涂鸦的时候,取点的位置是屏幕鼠标位置【2D取xy,3D自动取xy同时自动加一个固定的z】
/// 【5】用Vectrosity来显示所有的line对象
/// </summary>
public class DrawLinesMouseV2 : MonoBehaviour
{
    public Texture2D lineTex;
    public int maxPoints = 5000;
    public float lineWidth = 4.0f;
    public int minPixelMove = 5; // Must move at least this many pixels per sample for a new segment to be recorded
    public bool useEndCap = false;
    public Texture2D capLineTex;
    public Texture2D capTex;

    public float capLineWidth = 20.0f;

    // If line3D is true, the line is drawn in the scene rather than as an overlay. Note that in this demo, the line will look the same
    // in the game view either way, but you can see the difference in the scene view.
    public bool line3D = false;
    public float distanceFromCamera = 1.0f;

    /// <summary>
    /// line的对象
    /// </summary>
    private VectorLine line;

    /// <summary>
    /// 所有的线:有很多很多根线
    /// </summary>
    private List<VectorLine> lines = new List<VectorLine>();

    private Vector3 previousPosition;
    private int sqrMinPixelMove;
    private bool canDraw = false;

    /// <summary>
    /// line被创建了?,没被创建的话,按下鼠标左键则会创建一根线。
    /// </summary>
    private bool hasCreated;
    
    private float useLineWidth;
    private Texture2D tex;

    void Start()
    {
        if (useEndCap)
        {
            VectorLine.SetEndCap("RoundCap", EndCap.Mirror, capLineTex, capTex);
            tex = capLineTex;
            useLineWidth = capLineWidth;
        }
        else
        {
            tex = lineTex;
            useLineWidth = lineWidth;
        }

        // Used for .sqrMagnitude, which is faster than .magnitude
        sqrMinPixelMove = minPixelMove * minPixelMove;
    }

    void Update()
    {
        //获取鼠标位置
        var newPoint = GetMousePos();

        // 按下鼠标左键,则开始一笔新的涂鸦(对应为一根新的线)。Mouse button clicked, so start a new line
        if (Input.GetMouseButtonDown(0))
        {
            if (!hasCreated)
            {
                if (line3D)
                {
                    line = new VectorLine("DrawnLine3D", new List<Vector3>(), tex, useLineWidth, LineType.Continuous,
                        Joins.Weld);
                }
                else
                {
                    line = new VectorLine("DrawnLine", new List<Vector2>(), tex, useLineWidth, LineType.Continuous, Joins.Weld);
                }

                line.endPointsUpdate =
                    2; // Optimization for updating only the last couple points of the line, and the rest is not re-computed
                if (useEndCap)
                {
                    line.endCap = "RoundCap";
                }

                hasCreated = true;
            }

            if (line3D)
            {
                line.points3.Clear();
                line.Draw3D();
            }
            else
            {
                line.points2.Clear();
                //line.Draw();

                //显示多笔涂鸦信息
                if(! lines.Contains(line)) lines.Add(line);
                lines.ForEach(line=>line.Draw());
            }

            previousPosition = Input.mousePosition;
            if (line3D)
            {
                line.points3.Add(newPoint);
            }
            else
            {
                line.points2.Add(newPoint);
            }

            canDraw = true;
        }
        // Mouse button held down and mouse has moved far enough to make a new point
        //鼠标左键一直按着,并且鼠标在移动,新点与上一个点距离达到一定程度时,该点才能被取样
        else if (Input.GetMouseButton(0) && (Input.mousePosition - previousPosition).sqrMagnitude > sqrMinPixelMove &&
                 canDraw)
        {
            previousPosition = Input.mousePosition;
            int pointCount;
            if (line3D)
            {
                line.points3.Add(newPoint);
                pointCount = line.points3.Count;
                line.Draw3D();
            }
            else
            {
                line.points2.Add(newPoint);
                pointCount = line.points2.Count;
                line.Draw();
                if (pointCount >= maxPoints)
                {
                    canDraw = false;
                }
            }
        }

        //松开鼠标左键,本次画线结束,hasCreated置空,后面再次按下的时候,可以创建新的线
        if (Input.GetMouseButtonUp(0))
        {
            hasCreated = false;
        }

        Vector3 GetMousePos()
        {
            var p = Input.mousePosition;
            if (line3D)
            {
                p.z = distanceFromCamera;
                return Camera.main.ScreenToWorldPoint(p);
            }

            return p;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171

2、白板涂鸦的实现

  • 白板涂鸦的实现原理
 /***********************************************************
  *实现原理:
  * 【0】使用的包asset store 中的Vectrosity
  * 【1】从摄像机发射一条射线,方向指向屏幕的鼠标位置
  * 【2】白板Plane上设置一个BoxCollider,把BoxCollider扩大一点,
  * 特别是靠近相机的一边,要覆盖白板,让字显示在白板前面,而不是白板上【不然模型重叠会闪烁】
  * 【3】每帧刷新射线,捕捉在白板上的碰撞点
  * 【4】鼠标按下时开始涂鸦,弹起时完成一笔涂鸦,调用Vectrosity把该笔涂鸦的点显示出来
  * 【5】调用Vectrosity显示每一笔涂鸦的点
  ***********************************************************/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 白板涂鸦的代码脚本
using UnityEngine;
using Vectrosity; //asset store search [Vectrosity]
using System.Collections.Generic;

/// <summary>
/// 白板涂鸦
/// 白板——就是一个3D Plane,增加一个BOX碰撞体包围框
/// </summary>
public class DrawOnWhiteBoard : MonoBehaviour
{
    /***********************************************************
     *实现原理:
     * 【0】使用的包asset store 中的Vectrosity
     * 【1】从摄像机发射一条射线,方向指向屏幕的鼠标位置
     * 【2】白板Plane上设置一个BoxCollider,把BoxCollider扩大一点,
     * 特别是靠近相机的一边,要覆盖白板,让字显示在白板前面,而不是白板上【不然模型重叠会闪烁】
     * 【3】每帧刷新射线,捕捉在白板上的碰撞点
     * 【4】鼠标按下时开始涂鸦,弹起时完成一笔涂鸦,调用Vectrosity把该笔涂鸦的点显示出来
     * 【5】调用Vectrosity显示每一笔涂鸦的点
     ***********************************************************/

    /// <summary>
    /// 主相机
    /// </summary>
    public Camera camera;

    /// <summary>
    /// 白板的名字
    /// </summary>
    public string whiteBoardName;

    public Texture2D lineTex;
    public int maxPoints = 5000;
    public float lineWidth = 4.0f;
    public int minPixelMove = 5; // Must move at least this many pixels per sample for a new segment to be recorded
    public bool useEndCap = false;
    public Texture2D capLineTex;
    public Texture2D capTex;

    public float capLineWidth = 20.0f;

    // If line3D is true, the line is drawn in the scene rather than as an overlay. Note that in this demo, the line will look the same
    // in the game view either way, but you can see the difference in the scene view.
    public bool line3D = false;
    public float distanceFromCamera = 1.0f;

    /// <summary>
    /// line的对象:单笔涂鸦的信息
    /// </summary>
    private VectorLine line;

    /// <summary>
    /// 所有的线:很多根线,多笔涂鸦信息
    /// </summary>
    private List<VectorLine> lines = new List<VectorLine>();

    private Vector3 previousPosition;
    private int sqrMinPixelMove;
    private bool canDraw = false;

    /// <summary>
    /// line被创建了?,没被创建的话,按下鼠标左键则会创建一根线。
    /// </summary>
    private bool hasCreated;

    private float useLineWidth;
    private Texture2D tex;

    // Start is called before the first frame update
    void Start()
    {
        if (useEndCap)
        {
            VectorLine.SetEndCap("RoundCap", EndCap.Mirror, capLineTex, capTex);
            tex = capLineTex;
            useLineWidth = capLineWidth;
        }
        else
        {
            tex = lineTex;
            useLineWidth = lineWidth;
        }

        // Used for .sqrMagnitude, which is faster than .magnitude
        sqrMinPixelMove = minPixelMove * minPixelMove;
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 newPoint = new Vector3();
        bool canWrite = false;  //鼠标按下在白板上涂鸦时,为true

        //相机激发射线穿过屏幕鼠标
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit, Mathf.Infinity))
        {
            //鼠标【碰到】白板
            if (hit.transform.name == whiteBoardName)
            {
                newPoint = hit.point;
                canWrite = true;
            }
        }

        //
        if (canWrite)
        {
            // 按下鼠标左键,则开始一根新的线。
            if (Input.GetMouseButtonDown(0))
            {
                if (!hasCreated) //本笔涂鸦的线存在,则不创建,不然要创建新的line
                {
                    if (line3D)
                    {
                        line = new VectorLine("DrawnLine3D", new List<Vector3>(), tex, useLineWidth, LineType.Continuous,
                            Joins.Weld);
                    }
                    else
                    {
                        line = new VectorLine("DrawnLine", new List<Vector2>(), tex, useLineWidth, LineType.Continuous, Joins.Weld);
                    }

                    line.endPointsUpdate =
                        2; // Optimization for updating only the last couple points of the line, and the rest is not re-computed
                    if (useEndCap)
                    {
                        line.endCap = "RoundCap";
                    }

                    hasCreated = true;
                }

                if (line3D)
                {
                    line.points3.Clear();

                    if (!lines.Contains(line)) lines.Add(line);
                    lines.ForEach(line => line.Draw3D());
                }
                else
                {
                    line.points2.Clear();

                    if (!lines.Contains(line)) lines.Add(line);
                    lines.ForEach(line => line.Draw());
                }

                previousPosition = Input.mousePosition;
                if (line3D)
                {
                    line.points3.Add(newPoint);
                }
                else
                {
                    line.points2.Add(newPoint);
                }

                canDraw = true;
            }
            // Mouse button held down and mouse has moved far enough to make a new point
            //鼠标左键一直按着,并且鼠标在移动,新点与上一个点距离达到一定程度时,该点才能被取样
            else if (Input.GetMouseButton(0) && (Input.mousePosition - previousPosition).sqrMagnitude > sqrMinPixelMove &&
                     canDraw)
            {
                previousPosition = Input.mousePosition;
                int pointCount;
                if (line3D)
                {
                    line.points3.Add(newPoint);
                    pointCount = line.points3.Count;
                    line.Draw3D();
                }
                else
                {
                    line.points2.Add(newPoint);
                    pointCount = line.points2.Count;
                    line.Draw();
                    if (pointCount >= maxPoints)
                    {
                        canDraw = false;
                    }
                }
            }

            //松开鼠标左键,本笔涂鸦的画线结束,hasCreated置空,后面再次按下的时候,可以创建新一笔的涂鸦
            if (Input.GetMouseButtonUp(0))
            {
                hasCreated = false;
            }
        }
        Vector3 GetMousePos()
        {
            var p = Input.mousePosition;
            if (line3D)
            {
                p.z = distanceFromCamera;
                return Camera.main.ScreenToWorldPoint(p);
            }

            return p;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205

五、其它

实际上,要能够愉快的涂鸦,还得增加许多功能,比如全部清屏,部分擦除,颜色设置等等。

如果单纯只是涂鸦的话,用texture的setpixels可能更“勤俭节约”。

备注:本案例只在Win10 + Unity Editor 环境中测试过

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/117066?site
推荐阅读
相关标签
  

闽ICP备14008679号