テンケイ的ブログ

unityでVRコンテンツを作りたい人のブログです

LayoutGroupの子要素のpositionを正しく参照できない

自作ゲームを作っているときにUIのpositionが思うように参照できず解決に時間がかかったので備忘録として残す

問題のシーン

現在選択されているUIを下のように黄色い枠で囲もうとしたところ 予想.PNG

実行時はこのようにずれてしまう 実際.PNG

コードは以下のようになっている。選択したいUIと同じpositionに黄色い枠を移動しているだけ

private void Start()
    {
        select.rectTransform.position = currentGauge.rectTransform.position;
    }

構成はこんな感じ CyrinderにはLayoutGroupがついている。SelectをCyrinderの子要素であるRedやGreenの位置に動かしたい

オブジェクト構成.PNG

Cyrinder構成.PNG

参考になった記事

unityのフォーラムで同じような症状についてやりとりしていた

https://forum.unity.com/threads/positions-of-ui-elements-under-vertical-layout-group.266775/

どうやらLayoutGroupなどのUI関係のコンポーネントの位置計算はフレームの最後に働くためにstart時は正しいpositionを参照できないとのこと       そこでUpdate中に黄色い枠を動かしてみると 正常.PNG

正しく動いた

解決法

しかし最初のUpdate時のみ動かすみたいなことはしたくない どうしたものかと調べたところ普通に公式リファレンスに書いてあった https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/class-RectTransform.html

You can work around this by creating a Start() callback and adding Canvas.ForceUpdateCanvases() method to it. This will force Canvas to be updated not at the end of the frame, but when that method is called.

つまりCanvas.ForceUpdateCanvas()を呼ぶことでCanvasのUpdateをフレームの最後ではなく即時にすることができるらしい

private void Start()
    {
    Canvas.ForceUpdateCanvases();//追加
        select.rectTransform.position = currentGauge.rectTransform.position;
    }

これでstart時でも正しくpositionを参照できるようになった

Unity素人が「大きな筆で習字ができるVRゲーム」を作ってみた

f:id:milkyway8008:20200213163956j:plain

 

初めてVRコンテンツを作成したので感想やつらかったことなどを挙げていきたいと思います.

 

作るに至るまでの経緯

      VRやりたさにデスクトップPC&Oculus riftを購入する

                ↓

           VRコンテンツたーのしー!

                ↓

      どうやらそんなVRコンテンツがunityで作れるらしい・・・

                ↓

              作ってみた

自分のスペック

学校の授業で簡単なゲームを作る機会があり,それがきっかけでunityを勉強し始めました.まだ数か月程度です.

それ以前は授業でC++Javaの基本を学び,人口無能に興味がでたきっかけでrubyを少しかじった程度でした.

 

作ったゲーム

大きな筆で習字ができるゲームです.部屋を汚すことなく大きな和紙に目一杯文字を書きなぐることができます(なお僕の住居は1R5畳)

制作期間は2週間くらいです.

 

 

大変だったこと

正直挙げていったらきりがないのですが,セーブ回りが一番大変でした.

 

今回文字を書く処理にはTrailRendererを用いました.具体的には筆先を和紙につけるとそこが始点となり,筆先を和紙から離したところを終点としてそこまでの軌跡が描かれるというものです.

 

当初は「まあいい感じにセーブしてくれる機能があるでしょ~」なんて楽観的に思っていました.いざセーブ機能を実装しようとしたらunityの標準でセーブできるのは数値や文字列で,TrailRendererの記録をそのまま保存してくれるなんて考えは甘々の甘ということに気づきました.

 

そこで作品を別カメラでスクショして保存することにしました.スクショした画像はtexture2D形式からpngファイルに変換してローカルに保存,次回のゲーム開始時に保存したpngファイルを再びtexture2Dに変換して表示します.この一連の流れに相当な時間を使ったのですが長くなるので端折ります.別の記事で書くかもしれないです.

 

頑張ったこと

正直挙げていったらきりが(ry  チュートリアルの実装が個人的に頑張ったところかなと思います.

 

ゲームを他人に遊んでもらおう!ということで友達数人に体験してもらったのですが,横で動かし方を教えないと遊べない状態でした.そこでチュートリアルを実装するに至りました.まだまだ改善の余地はありますがひとまずは僕のアシストなしでも遊んでもらえるようにはなりました.

 

まとめ

初めてVRゲーム制作だったのでコードもUI部分も未熟なものですがかなりの知識を得られたのではないかと思います.

これからもVRの開発,発信をしていきたいと思います.

[Unity] VR空間を移動する方法3選 [Oculus]

f:id:milkyway8008:20200131015758p:plain

初投稿です,よろしくお願いします.

備忘録もかねてVR空間上での移動方法をいくつか書き留めておきたいと思います.

 

 

 

開発環境

windows10

unity2019 1.4f1

Oculus Integration ver1.44

Oculus rift s

 

 

準備

 1.アセットストアからOculus Integrationをインポート後,OVRCameraRigをヒエラルキーにドラッグ&ドロップしてください.

f:id:milkyway8008:20200129031200p:plain

 

2.OVRCameraRig内にあるLeftHandAnchorとRightHandAnchorにOVRControllerPrefabを入れてください.

f:id:milkyway8008:20200129081110p:plain

 

3.左右OVRControllerPrefabのインスペクターからControllerをそれぞれL Touch,R Touchに変更してください.

f:id:milkyway8008:20200129081720p:plain

 

移動方法

これから紹介する移動方法のスクリプトは全てOVRCameraRigにアタッチして使用してください.

 

ジョイスティック移動

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class JoyStickMove : MonoBehaviour
{
    void Update()
    {
        Move();
    }

    void Move()
    {
        //右ジョイスティックの情報取得
        Vector2 stickR = OVRInput.Get(OVRInput.RawAxis2D.RThumbstick);
        Vector3 changePosition = new Vector3((stickR.x), 0, (stickR.y));
        //HMDのY軸の角度取得
        Vector3 changeRotation = new Vector3(0, InputTracking.GetLocalRotation(XRNode.Head).eulerAngles.y, 0);
        //OVRCameraRigの位置変更
        this.transform.position += this.transform.rotation * (Quaternion.Euler(changeRotation) * changePosition);
    }
}


ジョイスティック移動

現在向いている方を前として右ジョイスティックを傾けた方向に進みます.

InputTracking.GetLocalRotation(XRNode.Head)でHMDの向いている方向を取得することができます.

 

こちらは移動方向を固定したものです.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class JoyStickMove : MonoBehaviour
{
    float angle=30;
    
    void Update()
    {
        ChangeDirection();
        Move();
    }

    //OVRCameraRigの角度変更
    void ChangeDirection()
    {
        if (OVRInput.GetDown(OVRInput.RawButton.LThumbstickLeft))
        {
            this.transform.Rotate(0,-angle, 0);
        }
        else if (OVRInput.GetDown(OVRInput.RawButton.LThumbstickRight))
        {
            this.transform.Rotate(0, angle, 0);
        }
    }

    void Move()
    {
        //右ジョイスティックの情報取得
        Vector2 stickR = OVRInput.Get(OVRInput.RawAxis2D.RThumbstick);
        //OVRCameraRigの位置変更
        this.transform.position+=this.transform.rotation*(new Vector3((stickR.x),0, (stickR.y)));
    }
}

 


ジョイスティック移動2

ジョイスティックの左右で30度ずつ向きを変えることができます.

 

掴み移動

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GrabMove : MonoBehaviour
{
    [SerializeField]
    GameObject hand;
    //移動量調整用
    float adjustDistance = 2.0f;
    bool grabJudge = false;
    Vector3 movePosition;
    Vector3 startGrabPosition;
    Vector3 startViewPosition;
    
    void Update()
    {
        CheckGrab();
        Move();
    }

    //人差し指トリガーを押しているかどうか
    void CheckGrab()
    {
        if (OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger))
        {
            grabJudge = true;
            //トリガーを押したときのHandAnchorの位置
            startGrabPosition = hand.transform.localPosition;
            //トリガーを押したときのOVRCameraRigの位置
            startViewPosition = this.transform.position;
        }
        if (OVRInput.GetUp(OVRInput.RawButton.RIndexTrigger))
        {
            grabJudge = false;
        }
    }

    void Move()
    {
        if (grabJudge == true )
        {
            //トリガーを押したときのHandAnchorと現在のHandAnchorの差分
            movePosition = startGrabPosition - hand.transform.localPosition;
            //HandAnchorの差分をOVRCameraRigに適用
            this.transform.position = startViewPosition + this.transform.rotation*(movePosition*adjustDistance);
        }
    }
}

 

hand にはRightHandAnchorをアタッチしてください.

 

f:id:milkyway8008:20200129082923p:plain


掴み移動


トリガーを押している間コントローラーを動かした方向と逆方向に自分が動きます.

ちょっとした位置調整用に便利なのではないでしょうか.

 

ワープ移動

 ワープ移動を実装するにあたってこちらのサイトをかなり参考にしました.

レーザーを作成するときに用いるベジェ曲線のしくみが詳しく記載されています.

https://qiita.com/kousaku-maron/items/106619d0c065be155bbb#comments

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;


public class WarpMove : MonoBehaviour
{
    [SerializeField]
    GameObject hand;
    GameObject line;
    int positionCount = 10;
    float angle = 30;
    float groundPositionY = 0;
    float lazerStartDistance = 0.15f;
    float lazerStartPosition;
    float distance=10;
    float minDistance=2;
    float maxDistance = 100;
    float dropHeight=5;
    float width = 0.1f;
    bool activeDraw = false;
    Vector3 hitPoint;
    LineRenderer linerenderer;

    void Start()
    {
        Line();
    }
    
    void Update()
    {
        ChangeDirection();
        Controller();
    }

    //トリガーを押しているかどうか
    void Controller()
    {
        if (OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger))
        {
            activeDraw = true;
            line.SetActive(true);
        }

        if (OVRInput.GetUp(OVRInput.RawButton.RIndexTrigger))
        {
            activeDraw = false;
            line.SetActive(false);
            distance = 10;
            this.transform.position = new Vector3(hitPoint.x,this.transform.position.y,hitPoint.z);
        }

        if (activeDraw)
        {
            Ray();
        }
    }

    //OVRCameraRigの角度変更
    void ChangeDirection()
    {
        if (OVRInput.GetDown(OVRInput.RawButton.LThumbstickLeft))
        {
            this.transform.Rotate(0, -angle, 0);
        }
        else if (OVRInput.GetDown(OVRInput.RawButton.LThumbstickRight))
        {
            this.transform.Rotate(0, angle, 0);
        }
    }

    //LineRendererコンポーネントがアタッチされたゲームオブジェクトを作成
    void Line()
    {
        line = new GameObject("Line");
        line.transform.parent = hand.transform;
        linerenderer = line.AddComponent<LineRenderer>();
        linerenderer.receiveShadows = false;
        linerenderer.shadowCastingMode = ShadowCastingMode.Off;
        linerenderer.loop = false;
        linerenderer.positionCount = positionCount;
        linerenderer.startWidth = width;
        linerenderer.endWidth = width;
    }

    public void Ray()
    {
        ChangeDistance();
        Vector3 rayStartPosition = hand.transform.position+hand.transform.forward * lazerStartDistance;
        Vector3 rayMiddlePosition = rayStartPosition + hand.transform.forward * (distance / 2);
        Vector3 rayFinishPosition = rayStartPosition + hand.transform.forward * distance;
        rayFinishPosition.y = groundPositionY;
        Vector3 _tmp3 = rayStartPosition;

        for (int i = 0; i < positionCount; i++)
        {
            float plotPosition = (i / (float)(positionCount - 1));
            Vector3 tmp1 = Vector3.Lerp(rayStartPosition, rayMiddlePosition, plotPosition);
            Vector3 tmp2 = Vector3.Lerp(rayMiddlePosition, rayFinishPosition, plotPosition);
            Vector3 tmp3 = Vector3.Lerp(tmp1, tmp2, plotPosition);

            RaycastHit hit;
            if (Physics.Linecast(_tmp3, tmp3, out hit))
            {
                Debug.Log("hit!!");
                hitPoint = hit.point;
                for (int j = i; j < positionCount; j++)
                {
                    linerenderer.SetPosition(j, hitPoint);
                }
                break;
            }
            else
            {
                linerenderer.SetPosition(i, tmp3);
                _tmp3 = tmp3;
            }
        }
    }

    //レーザーの長さ変更
    void ChangeDistance()
    {
        Vector2 stickR = OVRInput.Get(OVRInput.RawAxis2D.RThumbstick);
        distance = Mathf.Clamp(distance += stickR.y, minDistance, maxDistance);
        
    }
}

 

handには右手のOVRControllerPrefabをアタッチしてください

 

f:id:milkyway8008:20200129091043p:plain

 


ワープ移動

 

トリガーを押すとレーザーが描画され,右コントローラーのジョイスティックを上下することによってレーザーの長さを変えることができます.

トリガーを離すとレーザーの終点に自身が移動します.