Unity實(shí)現(xiàn)VR中在黑板上寫字效果
本文實(shí)例為大家分享了Unity實(shí)現(xiàn)VR中在黑板上寫字的具體代碼,供大家參考,具體內(nèi)容如下
一、工具
1.開發(fā)用的是Unity 5.6.2版本
2.VR中的物理交互用的是VRTK插件,這個(gè)插件集成了比較好的物理交互功能;
3.HTC Vive
二、概述
實(shí)現(xiàn)的功能: 在一個(gè)白板上,用不同顏色的筆,在白板畫出任何想要的圖形;
因?yàn)橹皇且粋€(gè)初級(jí)篇所以只是用兩個(gè)腳本簡(jiǎn)單的實(shí)現(xiàn),而且并沒有黑板擦等功能 ,也不能兩個(gè)筆同時(shí)畫畫,這些功能將會(huì)在未來的升級(jí)篇中寫出;
三、知識(shí)點(diǎn)
其實(shí)這個(gè)功能很簡(jiǎn)單,只是簡(jiǎn)單的運(yùn)用Unity Texure2D類中的兩個(gè)函數(shù):
public void SetPixels32(int x, int y, int blockWidth, int blockHeight, Color32[] colors, int miplevel = 0);
前面4個(gè)參數(shù)相當(dāng)于一個(gè)矩形,x和y就是矩形的左下角的那個(gè)點(diǎn),blockWidth和blockHeight分別是矩形的寬和高,這個(gè)矩形所代表的范圍就是blockWidth*blockHeight個(gè)像素所在的位置,不妨稱這個(gè)矩形范圍為一個(gè)色塊;
colors這個(gè)參數(shù)的大小必須等于blockWidth*blockHeight,因?yàn)檫@個(gè)方法就是給坐標(biāo)(x,y)開始,從左到右,從下到上,一行一行的對(duì)矩形范圍內(nèi)的每個(gè)像素賦值;
也就是把colors[0]~colors[blockWidth - 1]分別賦值到坐標(biāo)為(x,y)~(x + blockWidth,y)的像素,以此類推;
最后一個(gè)參數(shù),因?yàn)槲覀冇玫膱D片把Generate Min Maps這個(gè)選項(xiàng)關(guān)閉了,所以用默認(rèn)的可選參數(shù)0;
public void Apply(bool updateMipmaps = true, bool makeNoLongerReadable = false);
當(dāng)對(duì)圖片改動(dòng)完成以后,需要調(diào)用這個(gè)方法,才能讓改動(dòng)真正的應(yīng)用在圖片上;
四、場(chǎng)景搭建
1.畫板
在場(chǎng)景中建一個(gè)Quad,把它的x和y方向的Scale分別設(shè)置為1.92和1.08(或者其它尺寸);注意這個(gè)Quad一定要用Mesh Collider作為碰撞體,不然到時(shí)候射線獲取的紋理坐標(biāo)有誤,并為它設(shè)置一個(gè)Tag為Board;
2.筆
建一個(gè)尺寸合適的筆,創(chuàng)建一個(gè)空的子物體,命名為SnapPoint,并設(shè)置SnapPoint的Z方向指向筆尖方向,這個(gè)子物體就是,手柄拿筆的位置就是,并且保證筆的姿態(tài)是相當(dāng)于人正常拿筆的樣子;
3.其它
創(chuàng)建一個(gè)放筆的物體,讓筆處于比較好拿的位置;
我的場(chǎng)景中代表畫板的是WhiteBoard下的Board物體;
五、代碼實(shí)現(xiàn)功能
這個(gè)腳本是掛在代表畫板的物體上的:
using System.Linq; using UnityEngine; /// <summary> /// 畫板 /// </summary> public class Board : MonoBehaviour { //當(dāng)畫筆移動(dòng)速度很快時(shí),為了不出現(xiàn)斷斷續(xù)續(xù)的點(diǎn),所以需要對(duì)兩個(gè)點(diǎn)之間進(jìn)行插值,lerp就是插值系數(shù) [Range(0, 1)] public float lerp = 0.05f; //初始化背景的圖片 public Texture2D initailizeTexture; //當(dāng)前背景的圖片 private Texture2D currentTexture; //畫筆所在位置映射到畫板圖片的UV坐標(biāo) private Vector2 paintPos; private bool isDrawing = false;//當(dāng)前畫筆是不是正在畫板上 //離開時(shí)畫筆所在的位置 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() { //計(jì)算當(dāng)前畫筆,所代表的色塊的一個(gè)起始點(diǎn) 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); //如果快速移動(dòng)畫筆的話,會(huì)出現(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> /// 用于比較兩個(gè)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; } }
下面這個(gè)腳本是掛在畫筆上的:
using UnityEngine; public class Painter : MonoBehaviour { /// <summary> /// 畫筆的顏色 /// </summary> public Color32 penColor; public Transform rayOrigin; private RaycastHit hitInfo; //這個(gè)畫筆是不是正在被手柄抓著 private bool IsGrabbing; private static Board board;//設(shè)置成類型的成員,而不是類型實(shí)例的成員,因?yàn)樗挟嫻P都是用的同一個(gè)board private void Start() { //將畫筆部件設(shè)置為畫筆的顏色,用于識(shí)別這個(gè)畫筆的顏色 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è)置畫筆所在位置對(duì)應(yīng)畫板圖片的UV坐標(biāo) 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.畫筆所能畫的最小點(diǎn)是有大小的,也就是SetPixels參數(shù)中的blockWidth*blockHeight的大小,當(dāng)這個(gè)畫筆在畫板的邊緣的時(shí)候,那么這個(gè)畫筆所能畫的色塊的矩形范圍就到圖片之外去了,這會(huì)引起未處理異常;
2.同時(shí)只有一個(gè)筆能在畫板上畫畫;
3.沒有黑板擦功能;
4.沒有顏色混合功能;
5.畫筆是純粹的顏色,其實(shí)可以用一個(gè)圖片設(shè)置畫筆的形狀;
6.筆可以穿透畫板
這些問題都將在升級(jí)篇中完善;
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:Unity實(shí)現(xiàn)多平臺(tái)二維碼掃描
欄 目:C#教程
下一篇:C# winform程序讀取文本中的值實(shí)例講解
本文標(biāo)題:Unity實(shí)現(xiàn)VR中在黑板上寫字效果
本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/4724.html
您可能感興趣的文章
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并打開的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新聞效果的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已安裝軟件變化的方法
- 01-10C#實(shí)現(xiàn)多線程下載文件的方法
- 01-10C#實(shí)現(xiàn)Winform中打開網(wǎng)頁頁面的方法
- 01-10C#實(shí)現(xiàn)遠(yuǎn)程關(guān)閉計(jì)算機(jī)或重啟計(jì)算機(jī)的方法
- 01-10C#自定義簽名章實(shí)現(xiàn)方法
- 01-10C#文件斷點(diǎn)續(xù)傳實(shí)現(xiàn)方法


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10C#中split用法實(shí)例總結(jié)
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置