Unity實現(xiàn)VR中在黑板上寫字效果
本文實例為大家分享了Unity實現(xiàn)VR中在黑板上寫字的具體代碼,供大家參考,具體內(nèi)容如下

一、工具
1.開發(fā)用的是Unity 5.6.2版本
2.VR中的物理交互用的是VRTK插件,這個插件集成了比較好的物理交互功能;
3.HTC Vive
二、概述
實現(xiàn)的功能: 在一個白板上,用不同顏色的筆,在白板畫出任何想要的圖形;
因為只是一個初級篇所以只是用兩個腳本簡單的實現(xiàn),而且并沒有黑板擦等功能 ,也不能兩個筆同時畫畫,這些功能將會在未來的升級篇中寫出;
三、知識點
其實這個功能很簡單,只是簡單的運用Unity Texure2D類中的兩個函數(shù):
public void SetPixels32(int x, int y, int blockWidth, int blockHeight, Color32[] colors, int miplevel = 0);
前面4個參數(shù)相當(dāng)于一個矩形,x和y就是矩形的左下角的那個點,blockWidth和blockHeight分別是矩形的寬和高,這個矩形所代表的范圍就是blockWidth*blockHeight個像素所在的位置,不妨稱這個矩形范圍為一個色塊;
colors這個參數(shù)的大小必須等于blockWidth*blockHeight,因為這個方法就是給坐標(x,y)開始,從左到右,從下到上,一行一行的對矩形范圍內(nèi)的每個像素賦值;
也就是把colors[0]~colors[blockWidth - 1]分別賦值到坐標為(x,y)~(x + blockWidth,y)的像素,以此類推;

最后一個參數(shù),因為我們用的圖片把Generate Min Maps這個選項關(guān)閉了,所以用默認的可選參數(shù)0;
public void Apply(bool updateMipmaps = true, bool makeNoLongerReadable = false);
當(dāng)對圖片改動完成以后,需要調(diào)用這個方法,才能讓改動真正的應(yīng)用在圖片上;
四、場景搭建
1.畫板
在場景中建一個Quad,把它的x和y方向的Scale分別設(shè)置為1.92和1.08(或者其它尺寸);注意這個Quad一定要用Mesh Collider作為碰撞體,不然到時候射線獲取的紋理坐標有誤,并為它設(shè)置一個Tag為Board;
2.筆
建一個尺寸合適的筆,創(chuàng)建一個空的子物體,命名為SnapPoint,并設(shè)置SnapPoint的Z方向指向筆尖方向,這個子物體就是,手柄拿筆的位置就是,并且保證筆的姿態(tài)是相當(dāng)于人正常拿筆的樣子;
3.其它
創(chuàng)建一個放筆的物體,讓筆處于比較好拿的位置;

我的場景中代表畫板的是WhiteBoard下的Board物體;
五、代碼實現(xiàn)功能
這個腳本是掛在代表畫板的物體上的:
using System.Linq;
using UnityEngine;
/// <summary>
/// 畫板
/// </summary>
public class Board : MonoBehaviour
{
//當(dāng)畫筆移動速度很快時,為了不出現(xiàn)斷斷續(xù)續(xù)的點,所以需要對兩個點之間進行插值,lerp就是插值系數(shù)
[Range(0, 1)]
public float lerp = 0.05f;
//初始化背景的圖片
public Texture2D initailizeTexture;
//當(dāng)前背景的圖片
private Texture2D currentTexture;
//畫筆所在位置映射到畫板圖片的UV坐標
private Vector2 paintPos;
private bool isDrawing = false;//當(dāng)前畫筆是不是正在畫板上
//離開時畫筆所在的位置
private int lastPaintX;
private int lastPaintY;
//畫筆所代表的色塊的大小
private int painterTipsWidth = 30;
private int painterTipsHeight = 15;
//當(dāng)前畫板的背景圖片的尺寸
private int textureWidth;
private int textureHeight;
//畫筆的顏色
private Color32[] painterColor;
private Color32[] currentColor;
private Color32[] originColor;
private void Start()
{
//獲取原始圖片的大小
Texture2D originTexture = GetComponent<MeshRenderer>().material.mainTexture as Texture2D;
textureWidth = originTexture.width;//1920
textureHeight = originTexture.height;//1080
//設(shè)置當(dāng)前圖片
currentTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false, true);
currentTexture.SetPixels32(originTexture.GetPixels32());
currentTexture.Apply();
//賦值給黑板
GetComponent<MeshRenderer>().material.mainTexture = currentTexture;
//初始化畫筆的顏色
painterColor = Enumerable.Repeat<Color32>(new Color32(255, 0, 0, 255), painterTipsWidth * painterTipsHeight).ToArray<Color32>();
}
private void LateUpdate()
{
//計算當(dāng)前畫筆,所代表的色塊的一個起始點
int texPosX = (int)(paintPos.x * (float)textureWidth - (float)(painterTipsWidth / 2));
int texPosY = (int)(paintPos.y * (float)textureHeight - (float)(painterTipsHeight / 2));
if (isDrawing)
{
//改變畫筆所在的塊的像素值
currentTexture.SetPixels32(texPosX, texPosY, painterTipsWidth, painterTipsHeight, painterColor);
//如果快速移動畫筆的話,會出現(xiàn)斷續(xù)的現(xiàn)象,所以要插值
if (lastPaintX != 0 && lastPaintY != 0)
{
int lerpCount = (int)(1 / lerp);
for (int i = 0; i <= lerpCount; i++)
{
int x = (int)Mathf.Lerp((float)lastPaintX, (float)texPosX, lerp);
int y = (int)Mathf.Lerp((float)lastPaintY, (float)texPosY, lerp);
currentTexture.SetPixels32(x, y, painterTipsWidth, painterTipsHeight, painterColor);
}
}
currentTexture.Apply();
lastPaintX = texPosX;
lastPaintY = texPosY;
}
else
{
lastPaintX = lastPaintY = 0;
}
}
/// <summary>
/// 設(shè)置當(dāng)前畫筆所在的UV位置
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public void SetPainterPositon(float x, float y)
{
paintPos.Set(x, y);
}
/// <summary>
/// 畫筆當(dāng)前是不是在畫畫
/// </summary>
public bool IsDrawing
{
get
{
return isDrawing;
}
set
{
isDrawing = value;
}
}
/// <summary>
/// 使用當(dāng)前正在畫板上的畫筆的顏色
/// </summary>
/// <param name="color"></param>
public void SetPainterColor(Color32 color)
{
if (!painterColor[0].IsEqual(color))
{
for (int i = 0; i < painterColor.Length; i++)
{
painterColor[i] = color;
}
}
}
}
public static class MethodExtention
{
/// <summary>
/// 用于比較兩個Color32類型是不是同種顏色
/// </summary>
/// <param name="origin"></param>
/// <param name="compare"></param>
/// <returns></returns>
public static bool IsEqual(this Color32 origin, Color32 compare)
{
if (origin.g == compare.g && origin.r == compare.r)
{
if (origin.a == compare.a && origin.b == compare.b)
{
return true;
}
}
return false;
}
}
下面這個腳本是掛在畫筆上的:
using UnityEngine;
public class Painter : MonoBehaviour
{
/// <summary>
/// 畫筆的顏色
/// </summary>
public Color32 penColor;
public Transform rayOrigin;
private RaycastHit hitInfo;
//這個畫筆是不是正在被手柄抓著
private bool IsGrabbing;
private static Board board;//設(shè)置成類型的成員,而不是類型實例的成員,因為所有畫筆都是用的同一個board
private void Start()
{
//將畫筆部件設(shè)置為畫筆的顏色,用于識別這個畫筆的顏色
foreach (var renderer in GetComponentsInChildren<MeshRenderer>())
{
if (renderer.transform == transform)
{
continue;
}
renderer.material.color = penColor;
}
if (!board)
{
board = FindObjectOfType<Board>();
}
}
private void Update()
{
Ray r = new Ray(rayOrigin.position, rayOrigin.forward);
if (Physics.Raycast(r, out hitInfo, 0.1f))
{
if (hitInfo.collider.tag == "Board")
{
//設(shè)置畫筆所在位置對應(yīng)畫板圖片的UV坐標
board.SetPainterPositon(hitInfo.textureCoord.x, hitInfo.textureCoord.y);
//當(dāng)前筆的顏色
board.SetPainterColor(penColor);
board.IsDrawing = true;
IsGrabbing = true;
}
}
else if(IsGrabbing)
{
board.IsDrawing = false;
IsGrabbing = false;
}
}
}
六、等待完善的地方
1.畫筆所能畫的最小點是有大小的,也就是SetPixels參數(shù)中的blockWidth*blockHeight的大小,當(dāng)這個畫筆在畫板的邊緣的時候,那么這個畫筆所能畫的色塊的矩形范圍就到圖片之外去了,這會引起未處理異常;
2.同時只有一個筆能在畫板上畫畫;
3.沒有黑板擦功能;
4.沒有顏色混合功能;
5.畫筆是純粹的顏色,其實可以用一個圖片設(shè)置畫筆的形狀;
6.筆可以穿透畫板
這些問題都將在升級篇中完善;
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#實現(xiàn)WPS文件轉(zhuǎn)PDF格式的方法示例
這篇文章主要介紹了C#實現(xiàn)WPS文件轉(zhuǎn)PDF格式的方法,涉及C#針對office組件的相關(guān)引用與操作技巧,需要的朋友可以參考下2017-11-11
12306奇葩驗證碼引發(fā)思考之C#實現(xiàn)驗證碼程序
春運最高峰來了!明天通過網(wǎng)絡(luò)將能買到小年夜的車票,本周四就將開售除夕日車票,但不少人被首次在春運期間使用的圖片驗證碼搞得很火大,小編也正在對驗證碼進行研究,編寫了由C#實現(xiàn)驗證碼程序,分享給大家2015-12-12
C#在Windows窗體控件實現(xiàn)內(nèi)容拖放(DragDrop)功能
這篇文章介紹了C#在Windows窗體控件實現(xiàn)內(nèi)容拖放(DragDrop)的功能,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05

