成果発表
前回
焚火ができた。
今回やること
焚火でマシュマロを焼く。
無心でマシュマロを焼いているときが幸せ。
ライブラリ探し
さすがにね、VRで物をつかんだり、放したりが簡単にできるライブラリなんて専門的なものは、ないよね。よね…
VRTK - Virtual Reality Toolkit - [ VR Toolkit ] - Asset Store
あるよね。すごいよね。
テレポートとかUI作成とかまでできるのね。
しかも無料なのね。
自分でやってみよう
完璧なライブラリはあるが、Unityの勉強もしたいので物をつかむぐらいは自分で書いてみようと思う。
コントローラーの色々はここ
HTC Vive向けにアプリケーションを開発する〜コントローラでインタラクション編〜 - VOYAGE GROUP VR室ブログ
衝突判定についてはここを参考にしました。
衝突判定のあれこれ - Qiita
毎度ながら日本語情報の多さには助けられてます。
コードと動作
ほぼコピペです。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RightArmController : MonoBehaviour { GameObject grababbleObject; FixedJoint joint; private void Start() { joint = gameObject.GetComponent<FixedJoint>(); } void Update () { var trackedObject = GetComponent<SteamVR_TrackedObject>(); var device = SteamVR_Controller.Input((int)trackedObject.index); if (device.GetPress(SteamVR_Controller.ButtonMask.Trigger)) { grab(); } } void grab() { if (grababbleObject == null || joint.connectedBody != null) { return; } joint.connectedBody = grababbleObject.GetComponent<Rigidbody>(); } void OnTriggerEnter(Collider other) { grababbleObject = other.gameObject; } }
すごいつかんだものが、荒ぶってますが出来ました。
相変わらず十数行で立派なものができてしまう。
衝突判定とFixed Joint
今回初めて使ったのが衝突判定とFixed Joint、自分なりにまとめてみる。
衝突判定
Collision系とTrigger系の二つの衝突判定の仕方がある。
Collisionは、衝突の結果、物理挙動を起こしたいときに使う。
(物理挙動を起こすので、Rigidbodyを設定する必要がある)
Triggerは、衝突したことを検知したいときに使う。
なので、今回は手が物体に触れているかを判定したいだけなので、Triggerを使う。
Fixed Joint
一時的にオブジェクトをほかのオブジェクトとつなげる。
今回はCubeを右手につなげた。
で、接合部はFixedなのでくねくね動かない。
(すごい荒ぶってらっしゃいましたが)
よし、マシュマロ焼こう
なんとなくわかったような気がしているうちに、本題のものを作っていきます。
マシュマロモデリング編
まずは、マシュマロを作りましょう。
モデリングなんてしたことないので、一からお勉強です。
ソフトはBlenderを使いました。
マシュマロ作成には過ぎたツールかもしれませんが、これもお勉強。
というわけで入門書を一冊。
これ最高にわかりやすくて、いちから丁寧に丁寧すぎるぐらいに解説がされている。
Blenderの一歩目としては最適ではなかろうか。
Blender 3DCG モデリング・マスター
久しぶりにペンダブを引っ張り出して、仕事から帰った後のお楽しみタイムにコツコツやって3日ほどで終わりました。
最終的にわんことかスクーターを作れるところまで行けますぞ。
今の自分なら、マシュマロ作るなんて余裕よ。まかせとけって。
そして、完成。
あ、うん。テクスチャマッピングはこの入門書の範囲外だからさ…
多少の粗さは目をつぶっていこうね。
blenderファイルとをUnityに読み込んで、大きさを調整してCylinderをズブリと刺します。
(そのままUnityに読み込めるのが驚き)
VRTK編
VRTKを使って物を持つ挙動を実装する。
更新が結構頻繁らしくて、ブログ記事とかを参考にしてるとスクリプトの名前が変わってたりで躓くので、公式を見に行く。
すごいドキュメントの充実ぶり。
とりあえず、Getting Startedで入門してみよう。
VRTKのセットアップ
結構苦労したので手順を書いておきます。
Getting Started · VRTK - Virtual Reality Toolkit
※このあたりの記事の設定(SteamVRでモデルを動かす)が完了している前提です。
takeda-san.hatenablog.com
- VRTKをアセットストアからダウンロードする
- 空のオブジェクト(名前は何でも、今回は
[VRTK_SDKManager]
)を作って、VRTK_SDKManager
をアタッチする - その子にさらに空のオブジェクト(名前は何でも、今回は
[SDKSetup]
)を作って、VRTK_SDKSetup
をアタッチする - SteamVR三種の神器、[CameraRig]、[Status]、[SteamVR]をまるごと2.のオブジェクトに追加する
- VRTK_SDKSetupをアタッチしたオブジェクトのInspectorからVRTK_SDKSetupの
SDK Selection
の値をSteam VR
に変更する Object References
内のActual Objects
の 各値を設定する(画像のような設定にする)
- 空のオブジェクト(名前は何でも、今回は
[VRTK_Scripts]
)を作る - その子にさらに2つオブジェクトをつくる(Viveコントローラ用。名前は何でも、今回は
LeftController
、RightController
にする) - VRTK_SDKManagerをアタッチしたオブジェクトのInspectorからSetups内の
Auto Populate
ボタンを押してSetupsにVRTK_SDK(VRTK_SDKSetup)
が追加されたのを確認する - さらに、Script AliasesのLeft ControllerとRight Controllerに8で作ったオブジェクトをそれぞれ設定する
- 頭のカメラの位置がちょっとずれてるので調整
参考までに私の設定後の画像。
物をつかめるようにする
あとはやりたいことをドキュメントの中から見つけて、Examplesの中のシーンを眺めて使い方を真似するとよさそう。
今回は物をつかんだり放したりする、005_Controller_BasicObjectGrabbingを真似っこする。
VRTK_SDKManagerをアタッチしたオブジェクトの子のコントローラーオブジェクトに次の3つをアタッチ。
- VRTK_ControllerEvents
- VRTK_InteractTouch
- VRTK_InteractGrab
VRTK_InteractGrabのController Attach Point
に[CameraRig]配下の方の左手のコントローラを設定します。
その後、VRTK_SDKManagerをアタッチした方のオブジェクトにRigidbodyを設定します。
これがないと物がつかめない。
こんな感じで、つかめます。
手の位置とviveコントローラの位置がどうしても一致しなかったので、作成中はこんな感じでコントローラを表示ながらやってました。
焚火との当たり判定編
マシュマロを焚火で炙っていきましょう。
ここまで、長かった…
焚火にBox Collider
を新規に作成して当たり判定の範囲を設定します。
マシュマロには、005_Controller_BasicObjectGrabbingのつかめるオブジェクトと同様に以下の設定を追加します。
- Rigidbody
- Box Collider
- VRTK_SwapControllerGrabAction
- VRTK_FixedJointGrabAttach
- VRTK_InteractableObject(Is Grabbableにチェック)
で、焚火に当たっている時間に応じてテクスチャを切り替えるようにスクリプトを書きましょう。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MashuOnHit : MonoBehaviour { // テクスチャ const string TEXTURE_FILE_BAKE = "bake"; const string TEXTURE_FILE_BLACK = "black"; Texture2D bake; Texture2D black; // 焚火に触れている間のカウント private int count = 0; void Start() { bake = Resources.Load<Texture2D>(TEXTURE_FILE_BAKE); black = Resources.Load<Texture2D>(TEXTURE_FILE_BLACK); } void OnTriggerStay(Collider other) { if (other.gameObject.tag == "Fire") { count++; // 焼けたかな if (count == 1000) { this.GetComponent<Renderer>().material.mainTexture = bake; } else if (count == 2000) { this.GetComponent<Renderer>().material.mainTexture = black; } } } }
テクスチャを読み込んで、当たり判定に入っているカウントによって、焼き目が付いたり、焦げが付いたりという脳筋スクリプト。
Resourcesフォルダというフォルダにテクスチャを入れて、Loadで読みだすとスクリプトの中で各種リソースが使えるらしい。
調整編
マシュマロをちゃんと手に持つ
現状、マシュマロを持つとviveコントローラの位置にマシュマロがくるので、フォースの使い手のようになってしまう。
これをちゃんと手の位置に固定したい。
ものをつかむときの挙動がVRTKでは、数種類準備されている。
そのなかでもVRTK_ChildOfControllerGrabAttach
というつかんだものを子要素に設定するというスクリプトを使って手に持てるようにする。
VRTK_ChildOfControllerGrabAttach · VRTK - Virtual Reality Toolkit
VRTK_ChildOfControllerGrabAttachクラスを継承しStartGrab
メソッドで自分のモデルの手首の位置にマシュマロが来るようにする。
今回私が使っているNaokoさんは、手首がWRist_LとRなのでそれをパラメータとして受け取って親子関係を紐づける。
using System.Collections; using System.Collections.Generic; using UnityEngine; using VRTK.GrabAttachMechanics; public class GrabCharactor : VRTK_ChildOfControllerGrabAttach { // Naokoさんの手首を設定する [SerializeField] GameObject rist; public override bool StartGrab(GameObject grabbingObject, GameObject givenGrabbedObject, Rigidbody givenControllerAttachPoint) { if (base.StartGrab(grabbingObject, givenGrabbedObject, givenControllerAttachPoint)) { // つかんだものの親に手首を設定する givenGrabbedObject.gameObject.transform.parent = rist.gameObject.transform; // 手首の位置につかんだものを移動する Vector3 ristPos = rist.gameObject.transform.position; givenGrabbedObject.gameObject.transform.position = new Vector3(ristPos.x, ristPos.y + 0.25f, ristPos.z + -0.05f); grabbedObjectScript.isKinematic = true; return true; } return false; } }
フォースの力は失われ、それっぽくなりました。
いま焼けてんのか、焼けてないのかわからない
マシュマロを焚火にしばらく近づけていると、焼き目が付くんですが、焼いている最中に今、焼けているのか焼けてないのが見た目で判別つかないんですね。
なので、焼けてる判定のときはパーティクルで湯気を出すようにしたい。
マシュマロの子として煙パーティクルを追加。
焚火の当たり判定にパーティクルのON/OFF処理を追加。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MashuOnHit : MonoBehaviour { // テクスチャ const string TEXTURE_FILE_BAKE = "bake"; const string TEXTURE_FILE_BLACK = "black"; [SerializeField] GameObject smoke; Texture2D bake; Texture2D black; // 焚火に触れている間のカウント private int count = 0; void Start() { bake = Resources.Load<Texture2D>(TEXTURE_FILE_BAKE); black = Resources.Load<Texture2D>(TEXTURE_FILE_BLACK); // パーティクルオフ smoke.GetComponent<ParticleSystem>().Stop(); } void OnTriggerEnter(Collider other) { // パーティクルオン if (other.gameObject.tag == "Fire") { smoke.GetComponent<ParticleSystem>().Play(); } } void OnTriggerStay(Collider other) { if (other.gameObject.tag == "Fire") { count++; // 焼けたかな if (count == 1000) { this.GetComponent<Renderer>().material.mainTexture = bake; } else if (count == 2000) { this.GetComponent<Renderer>().material.mainTexture = black; } } } void OnTriggerExit(Collider other) { if (other.gameObject.tag == "Fire") { // パーティクルオフ smoke.GetComponent<ParticleSystem>().Stop(); } } }
ParticleSystem.Start()
とParticleSystem.Stop()
でパーティクルのON/OFFができました。
うんうん、いい感じ。
その後、ちょっと照明を調整したものが動画の内容です。
次回予定
モデリング楽しかったので、人型モデルをひとつ作ります。
そして、挫折予定。
Youtubeに読書感想動画上げてます。
チャンネル登録してもらえると、すごく喜びます。