〇、涂鸦与画线
- 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对象
- 屏幕涂鸦的代码实现
代码来自官网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;
}
}
}
2、白板涂鸦的实现
- 白板涂鸦的实现原理
/***********************************************************
*实现原理:
* 【0】使用的包asset store 中的Vectrosity
* 【1】从摄像机发射一条射线,方向指向屏幕的鼠标位置
* 【2】白板Plane上设置一个BoxCollider,把BoxCollider扩大一点,
* 特别是靠近相机的一边,要覆盖白板,让字显示在白板前面,而不是白板上【不然模型重叠会闪烁】
* 【3】每帧刷新射线,捕捉在白板上的碰撞点
* 【4】鼠标按下时开始涂鸦,弹起时完成一笔涂鸦,调用Vectrosity把该笔涂鸦的点显示出来
* 【5】调用Vectrosity显示每一笔涂鸦的点
***********************************************************/
- 白板涂鸦的代码脚本
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;
}
}
}
五、其它
实际上,要能够愉快的涂鸦,还得增加许多功能,比如全部清屏,部分擦除,颜色设置等等。
如果单纯只是涂鸦的话,用texture的setpixels可能更“勤俭节约”。
备注:本案例只在Win10 + Unity Editor 环境中测试过