使用Unity實現VR中在黑板上寫字(初級篇)

1、工具ide

1.開發用的是Unity 5.6.2版本
函數

2.VR中的物理交互用的是VRTK插件,這個插件集成了比較好的物理交互功能;工具

3.HTC Vive動畫

2、概述this

實現的功能: 在一個白板上,用不一樣顏色的筆,在白板畫出任何想要的圖形;spa

由於只是一個初級篇因此只是用兩個腳本簡單的實現,並且並無黑板擦等功能 ,也不能兩個筆同時畫畫,這些功能將會在將來的升級篇中寫出;插件

3、知識點code

其實這個功能很簡單,只是簡單的運用Unity Texure2D類中的兩個函數:orm

public void SetPixels32(int x, int y, int blockWidth, int blockHeight, Color32[] colors, int miplevel = 0);blog

 

前面4個參數至關於一個矩形,x和y就是矩形的左下角的那個點,blockWidth和blockHeight分別是矩形的寬和高,這個矩形所表明的範圍就是blockWidth*blockHeight個像素所在的位置,不妨稱這個矩形範圍爲一個色塊;

colors這個參數的大小必須等於blockWidth*blockHeight,由於這個方法就是給座標(x,y)開始,從左到右,從下到上,一行一行的對矩形範圍內的每一個像素賦值;

也就是把colors[0]~colors[blockWidth - 1]分別賦值到座標爲(x,y)~(x + blockWidth,y)的像素,以此類推;

 

最後一個參數,由於咱們用的圖片把Generate Min Maps這個選項關閉了,因此用默認的可選參數0;

public void Apply(bool updateMipmaps = true, bool makeNoLongerReadable = false);

當對圖片改動完成之後,須要調用這個方法,才能讓改動真正的應用在圖片上;

4、場景搭建

1.畫板

在場景中建一個Quad,把它的x和y方向的Scale分別設置爲1.92和1.08(或者其它尺寸);注意這個Quad必定要用Mesh Collider做爲碰撞體,否則到時候射線獲取的紋理座標有誤,併爲它設置一個Tag爲Board;

2.筆

建一個尺寸合適的筆,建立一個空的子物體,命名爲SnapPoint,並設置SnapPoint的Z方向指向筆尖方向,這個子物體就是,手柄拿筆的位置就是,而且保證筆的姿態是至關於人正常拿筆的樣子;

3.其它

建立一個放筆的物體,讓筆處於比較好拿的位置;

個人場景中表明畫板的是WhiteBoard下的Board物體;

5、代碼實現功能

這個腳本是掛在表明畫板的物體上的:

using System.Linq;
using UnityEngine;

/// <summary>
/// 畫板
/// </summary>
public class Board : MonoBehaviour
{
    //當畫筆移動速度很快時,爲了避免出現斷斷續續的點,因此須要對兩個點之間進行插值,lerp就是插值係數
    [Range(0, 1)]
    public float lerp = 0.05f;
    //初始化背景的圖片
    public Texture2D initailizeTexture;
    //當前背景的圖片
    private Texture2D currentTexture;
    //畫筆所在位置映射到畫板圖片的UV座標
    private Vector2 paintPos;

    private bool isDrawing = false;//當前畫筆是否是正在畫板上
    //離開時畫筆所在的位置 
    private int lastPaintX;
    private int lastPaintY;
    //畫筆所表明的色塊的大小
    private int painterTipsWidth = 30;
    private int painterTipsHeight = 15;
    //當前畫板的背景圖片的尺寸
    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

        //設置當前圖片
        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()
    {
        //計算當前畫筆,所表明的色塊的一個起始點
        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);
            //若是快速移動畫筆的話,會出現斷續的現象,因此要插值
            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>
    /// 設置當前畫筆所在的UV位置
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    public void SetPainterPositon(float x, float y)
    {
        paintPos.Set(x, y);
    }

    /// <summary>
    /// 畫筆當前是否是在畫畫
    /// </summary>
    public bool IsDrawing
    {
        get
        {
            return isDrawing;
        }
        set
        {
            isDrawing = value;
        }
    }

    /// <summary>
    /// 使用當前正在畫板上的畫筆的顏色
    /// </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;//設置成類型的成員,而不是類型實例的成員,由於全部畫筆都是用的同一個board

    private void Start()
    {
        //將畫筆部件設置爲畫筆的顏色,用於識別這個畫筆的顏色
        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")
            {
                //設置畫筆所在位置對應畫板圖片的UV座標 
                board.SetPainterPositon(hitInfo.textureCoord.x, hitInfo.textureCoord.y);
                //當前筆的顏色
                board.SetPainterColor(penColor);
                board.IsDrawing = true;
                IsGrabbing = true;
            }
        }
        else if(IsGrabbing)
        {
            board.IsDrawing = false;
            IsGrabbing = false;
        }
    }

}

6、等待完善的地方 

1.畫筆所能畫的最小點是有大小的,也就是SetPixels參數中的blockWidth*blockHeight的大小,當這個畫筆在畫板的邊緣的時候,那麼這個畫筆所能畫的色塊的矩形範圍就到圖片以外去了,這會引發未處理異常;

2.同時只有一個筆能在畫板上畫畫;

3.沒有黑板擦功能;

4.沒有顏色混合功能;

5.畫筆是純粹的顏色,其實能夠用一個圖片設置畫筆的形狀;

6.筆能夠穿透畫板

這些問題都將在升級篇中完善;

 最後工程下載地址:連接:https://pan.baidu.com/s/1o9c1RNO 密碼:kg8j

相關文章
相關標籤/搜索