Unity強化篇(三) —— 適用于Unity的HTC Vive教程(二)

版本記錄

版本號 時間
V1.0 2019.02.14 星期四

前言

Unity是由Unity Technologies開發(fā)的一個讓玩家輕松創(chuàng)建諸如三維視頻游戲奠伪、建筑可視化拷淘、實時三維動畫等類型互動內(nèi)容的多平臺的綜合型游戲開發(fā)工具止邮,是一個全面整合的專業(yè)游戲引擎滓窍。Unity類似于Director,Blender game engine, Virtools 或 Torque Game Builder等利用交互的圖型化開發(fā)環(huán)境為首要方式的軟件骄崩。其編輯器運行在Windows 和Mac OS X下绍载,可發(fā)布游戲至Windows校哎、Mac两波、Wii瞳步、iPhoneWebGL(需要HTML5)腰奋、Windows phone 8和Android平臺单起。也可以利用Unity web player插件發(fā)布網(wǎng)頁游戲,支持Mac和Windows的網(wǎng)頁瀏覽劣坊。它的網(wǎng)頁播放器也被Mac 所支持馏臭。網(wǎng)頁游戲 坦克英雄和手機游戲王者榮耀都是基于它的開發(fā)。感興趣的看下面幾篇文章讼稚。
1. Unity強化篇(一) —— 如何使用Vuforia制作AR游戲(一)
2. Unity強化篇(二) —— 適用于Unity的HTC Vive教程(一)

Using The Controllers With Physics Objects

VR為用戶提供了許多在現(xiàn)實世界中無法實現(xiàn)的機會括儒,包括拾取物體,檢查它們并將其丟棄而不必在之后進行清理锐想。

您可以通過使用一些觸發(fā)器碰撞器并執(zhí)行一些腳本來創(chuàng)建這種無憂無慮的虛擬體驗帮寻。

在層次結(jié)構(gòu)中選擇both controllers,并為它們添加Rigidbody組件赠摇。 (Add Component > Physics > Rigidbody)

選中Is Kinematic復選框并取消選中Use Gravity

現(xiàn)在固逗,向兩個控制器添加Box ColliderAdd Component > Physics > Box Collider)并選中Is Trigger

默認collider很大藕帜,因此您需要調(diào)整大小并重新定位它烫罩。 將Center設置為(X:0,Y:-0.04洽故,Z:0.02)并將Size設置為(X:0.14贝攒,Y:0.07,Z:0.05)时甚。 在這種情況下隘弊,您需要這些類型的精確值,因為即使百分之一單位也會影響collider的最終位置荒适。

再次運行游戲梨熙。 在層次結(jié)構(gòu)中選擇一個控制器并選擇真實控制器。 查看Scene視圖并將焦點放在您所持有的控制器上(按F鍵)刀诬。 collider正好位于控制器的頂部咽扇,這是您用來拾取物體的部分。

沒有腳本陕壹,這個collider只不過是一個無用的立方體质欲。 在RW \ Scripts中創(chuàng)建一個新的C#腳本,將其命名為ControllerGrabObject并將其打開帐要。

將其添加到腳本的頂部:

using Valve.VR;

與前一個腳本一樣把敞,這使您可以輕松訪問SteamVR類。

現(xiàn)在榨惠,刪除Start()方法并在其位置添加這個熟悉的代碼:

public SteamVR_Input_Sources handType;
public SteamVR_Behaviour_Pose controllerPose;
public SteamVR_Action_Boolean grabAction;

這與您用于輸入測試腳本的代碼相同奋早。 它存儲對手部類型和動作的引用盛霎。

將這些變量添加到您剛剛添加的變量之下:

private GameObject collidingObject; // 1
private GameObject objectInHand; // 2

每個變量都有一個目的:

  • 1) 存儲觸發(fā)器當前與之碰撞的GameObject,因此您可以抓取對象耽装。
  • 2) 用作玩家當前抓取的GameObject的參考愤炸。

接下來,在Update()上面添加此方法:

private void SetCollidingObject(Collider col)
{
    // 1
    if (collidingObject || !col.GetComponent<Rigidbody>())
    {
        return;
    }
    // 2
    collidingObject = col.gameObject;
}

此方法接受collider作為參數(shù)掉奄,并使用其GameObject作為抓取和釋放的collidingObject规个。 而且,它:

  • 1) 如果玩家已經(jīng)持有某物或物體沒有rigidbody姓建,則不會使GameObject成為潛在的抓取目標诞仓。
  • 2) 將對象指定為潛在的抓取目標。

添加以下觸發(fā)器方法:

// 1
public void OnTriggerEnter(Collider other)
{
    SetCollidingObject(other);
}

// 2
public void OnTriggerStay(Collider other)
{
    SetCollidingObject(other);
}

// 3
public void OnTriggerExit(Collider other)
{
    if (!collidingObject)
    {
        return;
    }

    collidingObject = null;
}

這些方法處理trigger collider進入和退出另一個collider時應該發(fā)生的情況速兔。

  • 1) 當trigger collider進入另一個時墅拭,這會將另一個collider設置為潛在的抓取目標。
  • 2) 類似于第一部分(// 1)涣狗,但不同谍婉,因為它確保當玩家將控制器保持在對象上一段時間時設置目標。 如果沒有這個镀钓,碰撞可能會失敗或變成buggy穗熬。
  • 3) 當collider退出對象時,放棄未抓取的目標丁溅,此代碼通過將其設置為null來移除其目標唤蔗。

接下來,您將添加一些代碼來獲取OnTriggerExit()下面的對象:

private void GrabObject()
{
    // 1
    objectInHand = collidingObject;
    collidingObject = null;
    // 2
    var joint = AddFixedJoint();
    joint.connectedBody = objectInHand.GetComponent<Rigidbody>();
}

// 3
private FixedJoint AddFixedJoint()
{
    FixedJoint fx = gameObject.AddComponent<FixedJoint>();
    fx.breakForce = 20000;
    fx.breakTorque = 20000;
    return fx;
}

在這里唧瘾,您:

  • 1) 將碰撞的GameObject移動到玩家手中并將其從collidingObject變量中移除措译。
  • 2) 使用下面的AddFixedJoint()方法添加一個將控制器連接到對象的新關節(jié)。
  • 3) 制作一個新的固定接頭饰序,將其添加到控制器,然后進行設置规哪,使其不會輕易斷裂求豫。 最后,你返回它诉稍。

必須釋放什么可以被抓住蝠嘉。 下一個塊處理釋放對象:

private void ReleaseObject()
{
    // 1
    if (GetComponent<FixedJoint>())
    {
        // 2
        GetComponent<FixedJoint>().connectedBody = null;
        Destroy(GetComponent<FixedJoint>());
        // 3
        objectInHand.GetComponent<Rigidbody>().velocity = controllerPose.GetVelocity();
        objectInHand.GetComponent<Rigidbody>().angularVelocity = controllerPose.GetAngularVelocity();

    }
    // 4
    objectInHand = null;
}

此代碼移除抓取對象的固定關節(jié),并在玩家將其拋棄時控制其速度和旋轉(zhuǎn)杯巨。 控制器的速度在這里至關重要蚤告。 不使用它,無論你的投擲多么完美服爷,丟棄的物體都會直接下降杜恰。 相信我获诈,這樣感覺不是很好。

逐節(jié)細分:

  • 1) 確毙暮郑控制器上有固定接頭舔涎。
  • 2) 移除與關節(jié)保持的對象的連接并破壞關節(jié)。
  • 3) 當玩家釋放對象時添加控制器的速度和旋轉(zhuǎn)逗爹,因此結(jié)果是真實的弧亡嫌。
  • 4) 刪除對以前附加對象的引用。

最后掘而,在Update()中添加它來處理控制器輸入:

// 1
if (grabAction.GetLastStateDown(handType))
{
    if (collidingObject)
    {
        GrabObject();
    }
}

// 2
if (grabAction.GetLastStateUp(handType))
{
    if (objectInHand)
    {
        ReleaseObject();
    }
}
  • 1) 當玩家觸發(fā)抓取動作時挟冠,抓住該對象。
  • 2) 如果玩家釋放鏈接到Grab動作的輸入并且有一個對象附加到控制器袍睡,則會釋放它圃郊。

我打賭你迫不及待地試試這個! 保存腳本并返回編輯器女蜈。

在層次結(jié)構(gòu)中選擇兩個控制器持舆,并為每個控制器添加一個Controller Grab Object組件。

在兩個控制器仍處于選中狀態(tài)的情況下伪窖,將Grab Action設置為\ actions \ default \ in \ Grab逸寓。 接下來,僅選擇Controller (left)覆山,將其Hand Type設置為Left Hand并將Steam VR_Behaviour_Pose組件拖到Controller Pose插槽上竹伸。

現(xiàn)在對右側(cè)的控制器執(zhí)行相同操作,但請使用右手簇宽。

是時候玩得開心了勋篓! 準備好控制器,開始游戲并戴上headset魏割。 使用hair triggergrip button拾起并折騰一些立方體和球譬嚣。 你甚至可以通過一些練習來兼顧。

你必須把它交給自己钞它,你現(xiàn)在非常棒拜银。 但是,我認為你可以讓你的VR體驗更酷遭垛。


Making A Laser Pointer

出于各種原因尼桶,激光指示器在VR世界中很方便。 您可以使用它們彈出虛擬氣球锯仪,更好地瞄準槍支泵督。

制作一個很簡單:你需要的只是一個立方體和另一個腳本。 首先在層次結(jié)構(gòu)的根目錄中創(chuàng)建一個新的CubeCreate > 3D Object > Cube)庶喜。

將其命名為Laser小腊,將其位置設置為(X:0救鲤,Y:5,Z:0)溢豆,將比例更改為(X:0.005蜒简,Y:0.005,Z:1)并移除Box Collider組件漩仙。 專注于它搓茬,你會看到它漂浮在其他level之上:

激光不應投射陰影,它們總是相同的顏色队他,因此您可以使用未照明的材料獲得所需的效果卷仑。

RW \ Materials中創(chuàng)建一個新材質(zhì),并將其命名為Laser麸折。 然后锡凝,將其著色器更改為Unlit / Color并將其Main Color設置為純紅色:

通過在層次結(jié)構(gòu)或Scene視圖中將其拖動到Laser來指定新材質(zhì):

最后,將Laser從層次結(jié)構(gòu)拖動到RW \ Prefabs并從層次結(jié)構(gòu)中刪除原始激光垢啼。

現(xiàn)在在RW \ Scripts中創(chuàng)建一個名為LaserPointer的新C#腳本并打開它窜锯。

使用聲明將此添加到文件的頂部:

using Valve.VR;

接下來,在Start()上方添加這些熟悉的變量:

public SteamVR_Input_Sources handType;
public SteamVR_Behaviour_Pose controllerPose;
public SteamVR_Action_Boolean teleportAction;

這些引用了控制器和Teleport動作芭析。

teleportAction下面添加這些變量:

public GameObject laserPrefab; // 1
private GameObject laser; // 2
private Transform laserTransform; // 3
private Vector3 hitPoint; // 4
  • 1) 這是對激光預制件的引用锚扎。
  • 2) laser存儲對激光器實例的引用。
  • 3) 存儲變換組件以便于使用馁启。
  • 4) 這是激光擊中的位置驾孔。

Update()下面添加此方法以顯示激光:

private void ShowLaser(RaycastHit hit)
{
    // 1
    laser.SetActive(true);
    // 2
    laserTransform.position = Vector3.Lerp(controllerPose.transform.position, hitPoint, .5f);
    // 3
    laserTransform.LookAt(hitPoint);
    // 4
    laserTransform.localScale = new Vector3(laserTransform.localScale.x,
                                            laserTransform.localScale.y,
                                            hit.distance);
}

此方法將RaycastHit作為參數(shù),因為它包含命中的位置和它行進的距離惯疙。

下面細分:

  • 1) 顯示激光翠勉。
  • 2) 將激光定位在控制器和光線投射點之間。 你使用Lerp是因為你可以給它兩個位置和它應該移動的百分比霉颠。 如果你傳遞0.5f对碌,即50%,它將返回精確的中間點掉分。
  • 3) 將激光指向光線投射的位置俭缓。
  • 4) 縮放激光,使其完全適合兩個位置酥郭。

Update()中添加以下內(nèi)容以使用玩家的輸入:

// 1
if (teleportAction.GetState(handType))
{
    RaycastHit hit;

    // 2
    if (Physics.Raycast(controllerPose.transform.position, transform.forward, out hit, 100))
    {
        hitPoint = hit.point;
        ShowLaser(hit);
    }
}
else // 3
{
    laser.SetActive(false);
}
  • 1) 如果激活Teleport操作:
  • 2) 從控制器射出光線。 如果碰到某些東西愿吹,請將其存放在它所觸及的位置并顯示激光不从。
  • 3) Teleport動作取消激活時隱藏激光。

在空的Start()方法中添加以下內(nèi)容:

// 1
laser = Instantiate(laserPrefab);
// 2
laserTransform = laser.transform;
  • 1) 產(chǎn)生一種新的激光并在laser中保存它的引用犁跪。
  • 2) 存儲激光的變換組件椿息。

保存此腳本并返回編輯器歹袁。 在層次結(jié)構(gòu)中選擇兩個控制器,并為每個控制器添加一個Laser Pointer寝优。

在兩個控制器仍處于選中狀態(tài)的情況下条舔,將Hand Type設置為Left Hand并將Steam VR_Behaviour_Pose組件拖動到Controller Pose插槽。 接下來乏矾,將Teleport Action設置為Teleport孟抗。

最后,將Laser預制件從Prefabs拖到Inspector中的Laser插槽上:

現(xiàn)在選擇正確的控制器并將Laser Pointer組件上的Hand Type設置為Right Hand钻心。

保存您的項目并讓游戲再次運行凄硼。 拿起一個控制器,戴上headset捷沸,然后拿著觸摸板摊沉。 你現(xiàn)在會看到激光:

在繼續(xù)之前,通過右鍵單擊控制器并選擇Remove Component痒给,從控制器中刪除Actions Test組件说墨。

您正在刪除它們,因為它們?yōu)槊總€幀編寫字符串并將它們記錄到控制臺苍柏。這對于性能來說并不是一件好事尼斧,每一毫秒都在VR中。它們對于測試輸入很方便序仙,但是你不應該將它們用于實際的游戲玩法突颊。

下一步是使用這種激光傳送到房間周圍!


Moving Around

在VR中移動并不像推動玩家那么簡單潘悼;這樣做是一種引起惡心的可靠方法律秃。一種更可行的出行方式是遠程傳送雹熬。

玩家的感知感比漸進的感覺更容易接受突然的位置變化闯捎。 VR設置中的微妙變化可能會讓您感覺平衡和速度更快滔以,而不是突然發(fā)現(xiàn)自己處于一個新的位置贫橙。

要準確顯示最終的位置破婆,您將使用Prefabs中提供的標記或標線勺像。

十字線是一個簡單的债沮,沒有照明的圓盤:

要使用reticle擂错,您將附加LaserPointer腳本缕陕,因此在代碼編輯器中打開它并將這些變量添加到現(xiàn)有的變量下面:

// 1
public Transform cameraRigTransform; 
// 2
public GameObject teleportReticlePrefab;
// 3
private GameObject reticle;
// 4
private Transform teleportReticleTransform; 
// 5
public Transform headTransform; 
// 6
public Vector3 teleportReticleOffset; 
// 7
public LayerMask teleportMask; 
// 8
private bool shouldTeleport; 

每個變量都起著重要作用:

  • 1) [CameraRig]的變換粱锐。
  • 2) 存儲傳送十字線預制件的引用。
  • 3) 對標線(reticle)實例的引用扛邑。
  • 4) 存儲對傳送十字線變換的參考以便于使用怜浅。
  • 5) 存儲對玩家頭部(相機)的引用。
  • 6) 標線偏離平面,因此與其他表面沒有“Z-fighting”恶座。
  • 7) 一個圖層蒙版搀暑,用于過濾允許傳送的區(qū)域。
  • 8) 找到有效的傳送位置時設置為true跨琳。

Update()中自点,替換此行:

if (Physics.Raycast(controllerPose.transform.position, transform.forward, out hit, 100))

使用這個將圖層蒙版考慮在內(nèi)的:

if (Physics.Raycast(controllerPose.transform.position, transform.forward, out hit, 100, teleportMask))

這可以確保激光只能擊中你可以傳送到的游戲?qū)ο蟆?/p>

同樣在Update()中,在ShowLaser(hit)調(diào)用下添加此代碼:

// 1
reticle.SetActive(true);
// 2
teleportReticleTransform.position = hitPoint + teleportReticleOffset;
// 3
shouldTeleport = true;

這是這樣做的:

  • 1) 顯示傳送的十字線(reticle)脉让。
  • 2) 將(reticle)移動到光線投射所在的位置并添加偏移以避免Z-fighting桂敛。
  • 3) 將shouldTeleport設置為true表示腳本找到了傳送的有效位置。

仍然在Update()中侠鳄,找到laser.SetActive(false);并在其下添加以下行:

reticle.SetActive(false);

這在沒有有效目標的情況下隱藏了reticle埠啃。

ShowLaser()下面添加以下方法來處理傳送行為:

private void Teleport()
{
    // 1
    shouldTeleport = false;
    // 2
    reticle.SetActive(false);
    // 3
    Vector3 difference = cameraRigTransform.position - headTransform.position;
    // 4
    difference.y = 0;
    // 5
    cameraRigTransform.position = hitPoint + difference;
}

誰知道傳送就像五行一樣簡單? 是時候逐步完成代碼了:

  • 1) 在傳送正在進行時將shouldTeleport設置為false伟恶。
  • 2) 隱藏十字線(reticle)碴开。
  • 3) 計算攝像機裝備中心位置與玩家頭部之間的差異。
  • 4) 將上述差異的y位置重置為0博秫,因為計算不考慮玩家頭部的垂直位置潦牛。
  • 5) 將攝像機裝置移動到命中點的位置并添加計算的差異。 沒有差別挡育,玩家將傳送到不正確的位置巴碗。 請參閱以下示例:

正如您所看到的,差異在精確定位攝像機裝置并將玩家準確放置到他們希望降落的位置方面起著至關重要的作用即寒。

Update()的末尾添加它橡淆,就在teleport動作狀態(tài)if-else語句之外:

if (teleportAction.GetStateUp(handType) && shouldTeleport)
{
    Teleport();
}

如果觸摸板被釋放并且有一個有效的傳送位置,則傳送玩家母赵。

最后逸爵,將此代碼添加到Start()

// 1
reticle = Instantiate(teleportReticlePrefab);
// 2
teleportReticleTransform = reticle.transform;
  • 1) 產(chǎn)生一個新的reticle并在reticle中保存對它的引用。
  • 2)存儲reticle的變換組件凹嘲。

就這些师倔! 保存您的腳本并返回Unity

在層次結(jié)構(gòu)中選擇兩個控制器并記下新字段:

[CameraRig]拖動到Camera Rig Transform插槽周蹭,將TeleportReticlePrefabs拖動到Teleport Reticle Transform插槽趋艘,然后將Camera拖動到Head Transform插槽。

現(xiàn)在將Teleport Reticle Offset設置為(X:0凶朗,Y:0.05瓷胧,Z:0)并將Teleport Mask設置為CanTeleportCanTeleport不是默認圖層 - 它是為本教程創(chuàng)建的棚愤。 FloorTable對象是該層中唯一的對象抖单。

玩游戲并在地板上使用激光傳送到房間周圍。

在這個針對UnityHTC Vive教程中遇八,您學習了如何:

  • 下載并配置SteamVR矛绘。
  • 處理HTC Vive控制器輸入。
  • 在VR中與物理對象交互刃永。
  • 制作激光筆货矮。
  • 傳送一個區(qū)域。

這個項目只是一個開始 - 讓它成為你自己的斯够。 我很想知道你想出了什么囚玫。

后記

本篇主要講述了適用于Unity的HTC Vive教程,感興趣的給個贊或者關注~~~

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末读规,一起剝皮案震驚了整個濱河市抓督,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌束亏,老刑警劉巖铃在,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碍遍,居然都是意外死亡定铜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門怕敬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揣炕,“玉大人,你說我怎么就攤上這事东跪』福” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵虽填,是天一觀的道長丁恭。 經(jīng)常有香客問我,道長卤唉,這世上最難降的妖魔是什么涩惑? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮桑驱,結(jié)果婚禮上竭恬,老公的妹妹穿的比我還像新娘。我一直安慰自己熬的,他們只是感情好痊硕,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著押框,像睡著了一般岔绸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天盒揉,我揣著相機與錄音晋被,去河邊找鬼。 笑死刚盈,一個胖子當著我的面吹牛羡洛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播藕漱,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼欲侮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肋联?” 一聲冷哼從身側(cè)響起威蕉,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎橄仍,沒想到半個月后韧涨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡沙兰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年氓奈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鼎天。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡舀奶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斋射,到底是詐尸還是另有隱情育勺,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布罗岖,位于F島的核電站涧至,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏桑包。R本人自食惡果不足惜南蓬,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哑了。 院中可真熱鬧赘方,春花似錦、人聲如沸弱左。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拆火。三九已至跳夭,卻和暖如春涂圆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背币叹。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工润歉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人套硼。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓卡辰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親邪意。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容