- 投稿日:2021-08-15T23:32:09+09:00
Oculus Quest2で実機テスト
背景 開発していて実機テストするとき、毎回ビルドしていては時間がかかる。 UnityのPlaymodeで即時実機テストしたい。 解決法 Build SettingsのPlatformを選択し、Target PlatformをWindowsにする。 Project Settingsを開き、XR Plug-in ManagementでWindowsタブのOculusにチェックを入れる。 XR Plug-in Managementの下のOculusを選び、Stereo Rendering ModeをMulti Passにする。
- 投稿日:2021-08-15T23:29:43+09:00
【Unity(C#)】ARFoundationのImageTrackingを使って三人称視点の実装
はじめに ARでの空間共有の手法をまとめた記事が意外となかったので書きます。 既にきれいにまとめている方がいたり、サンプルが出回っていたりしたわけじゃないので もっといいやり方はあるかもしれません。 それはそれで各自どこかにまとめてくれると助かります。 バージョン情報 諸々名前 バージョン Unity 2020.3.4f1 ARFoundation 4.0.12 ARCore XR Plugin 4.0.12 ARKit XR Plugin 4.0.12 XR Plugin Management 4.0.1 PUN 2 2.34.1 デモ 他の人がプレイ中のARが確認可能です。 位置合わせの手法 まず、ARでの位置合わせが一筋縄ではいかない理由を説明します。 2人のプレイヤーがARで空間共有を行う想定の図が下記です。 Unityの原点座標はARを起動した際に決定されます。 Aにとっての原点はBから見れば全く違う座標になってしまいます。Bからの目線も同様です。 この状態でImageTrackingを行ったものが下記の図です。 読み取る画像マーカーの位置が現実空間上で一致していても、 お互いのUnityの原点が異なる以上はAR空間上では同じ位置にあることになりません。 このように、起動した端末の位置に応じてAR空間が展開されるため、何かしらの解決策が必要となります。 ARFoundationにはちゃんと解決策が用意されていました。 説明の前段階として、まずは下記GIFの挙動が重要となります。 【引用元】:Scaling with ARFoundation マウスで動かしているオブジェクトが動いているのではなく、 周りのPlaneやAR Camera(AR Session Origin)が動くことで相対的に動いて見える仕組みです。 この仕組みを使ってUnityのワールド空間の原点と画像マーカーをぴったり重ね合わせます。 下記のようなイメージです。赤い点がUnityのワールド空間の原点です。 すなわち、端末の起動位置に関係無く画像マーカーの位置を原点として扱えるようになるということです。 コード まず、画像マーカーから原点を定める処理を担うScriptです。 適当なオブジェクトにアタッチ using System.Collections; using UnityEngine; using UnityEngine.XR.ARFoundation; /// <summary> /// 画像マーカーから原点を定める /// </summary> public class OriginDecideFromImageMaker : MonoBehaviour { /// <summary> /// ARTrackedImageManager /// </summary> [SerializeField] private ARTrackedImageManager _imageManager; /// <summary> /// ARSessionOrigin /// </summary> [SerializeField] private ARSessionOrigin _sessionOrigin; /// <summary> /// ワールドの原点として振る舞うオブジェクト /// </summary> private GameObject _worldOrigin; /// <summary> /// コルーチン /// </summary> private Coroutine _coroutine; private void OnEnable() { _worldOrigin = new GameObject("Origin"); _imageManager.trackedImagesChanged += OnTrackedImagesChanged; } private void OnDisable() { _imageManager.trackedImagesChanged -= OnTrackedImagesChanged; } /// <summary> /// 原点を定める /// 今回は画像マーカーの位置が原点となる /// </summary> /// <param name="trackedImage">認識した画像マーカー</param> /// <param name="trackInterval">認識のインターバル</param> /// <returns></returns> private IEnumerator OriginDecide(ARTrackedImage trackedImage,float trackInterval) { yield return new WaitForSeconds(trackInterval); var trackedImageTransform = trackedImage.transform; _worldOrigin.transform.SetPositionAndRotation(Vector3.zero,Quaternion.identity); _sessionOrigin.MakeContentAppearAt(_worldOrigin.transform, trackedImageTransform.position,trackedImageTransform.localRotation); _coroutine = null; } /// <summary> /// ワールド座標を任意の点から見たローカル座標に変換 /// </summary> /// <param name="world">ワールド座標</param> /// <returns></returns> public Vector3 WorldToOriginLocal(Vector3 world) { return _worldOrigin.transform.InverseTransformDirection(world); } /// <summary> /// TrackedImagesChanged時の処理 /// </summary> /// <param name="eventArgs">検出イベントに関する引数</param> private void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs) { foreach (var trackedImage in eventArgs.added) { StartCoroutine(OriginDecide(trackedImage,0)); } foreach (var trackedImage in eventArgs.updated) { if(_coroutine == null) _coroutine = StartCoroutine(OriginDecide(trackedImage, 5)); } } } MakeContentAppearAt MakeContentAppearAtが先述の"AR Session Originを動かして原点と画像マーカーの位置を合わせる処理" を実行してくれる関数です。 第二、第三引数で指定した位置・回転を、第一引数に渡したTransformに反映します。 ただし、第一引数のTransformに直接値を反映するわけではなく、AR Session Originの位置・回転を変更することで 相対的に指定位置へ移動したように見えるだけなので要注意です。 OnTrackedImagesChanged 過去に少しまとめてます。 【参考リンク】:ARTrackedImageManager.trackedImagesChanged 位置合わせを画像認識するたびに行うとカクカクするので、認識頻度にインターバルを設けています。 ここからはPhotonの実装です。 下記はPhotonの簡易版ルーム入室コードです。 using Photon.Pun; using Photon.Realtime; using UnityEngine; /// <summary> /// サーバーへ接続 /// </summary> public class ConnectPunServer : MonoBehaviourPunCallbacks { [SerializeField] private GameObject _playerPrefab; void Start() { PhotonNetwork.ConnectUsingSettings(); } public override void OnConnectedToMaster() { PhotonNetwork.JoinOrCreateRoom("TestRoom", new RoomOptions(), TypedLobby.Default); } public override void OnJoinedRoom() { PhotonNetwork.Instantiate(_playerPrefab.name, Vector3.zero, Quaternion.identity); } } 次にお絵描き機能です。 using Photon.Pun; using UnityEngine; public class Paint : MonoBehaviourPun { [SerializeField] private GameObject _inkPrefab; [SerializeField] private Transform _inkParent; /// <summary> /// 原点を定めるコンポーネント /// </summary> private OriginDecideFromImageMaker _originDecideFromImageMaker; private void Start() { _originDecideFromImageMaker = FindObjectOfType<OriginDecideFromImageMaker>(); } private void Update() { if (!photonView.IsMine) return; if (0 < Input.touchCount) { var touch = Input.GetTouch(0); var inputPosition = Input.GetTouch(0).position; var paintPosZ = 0.5f; var tmpTouchPos = new Vector3(inputPosition.x, inputPosition.y, paintPosZ); var touchWorldPos = _originDecideFromImageMaker.WorldToOriginLocal(Camera.main.ScreenToWorldPoint(tmpTouchPos)); if (touch.phase == TouchPhase.Began) { photonView.RPC(nameof(PaintStartRPC), RpcTarget.All, touchWorldPos); } else if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary) { photonView.RPC(nameof(PaintingRPC), RpcTarget.All, touchWorldPos); } } } /// <summary> /// RPCで生成 /// </summary> [PunRPC] private void PaintStartRPC(Vector3 inkPosition) { Instantiate(_inkPrefab, inkPosition, Quaternion.identity, _inkParent); } /// <summary> /// RPCで動かす /// </summary> [PunRPC] private void PaintingRPC(Vector3 inkPosition) { if (_inkParent.childCount > 0) { _inkParent.transform.GetChild(_inkParent.childCount - 1).transform.position = inkPosition; } } } 線の描画はTrailRendererを動かしているだけです。 【参考リンク】:【Unity(C#)】ハンドトラッキングで簡易版VRお絵かきアプリ おわりに 自己位置推定の精度を考えると、スマホで画像マーカーだけで位置合わせを支えるのは無理があるなーというのがやってみた感想です。 参考リンク ARSessionOrigin transform position and rotation to make created ARTrackedImage become Unity space zero Class ARSessionOrigin PUN2(Photon Unity Networking 2)で始めるオンラインゲーム開発入門
- 投稿日:2021-08-15T23:13:24+09:00
[備忘録]意外と知らない(かもしれない)小ネタ-Inspector上で編集可能な特定のClassを継承したScriptのフィールドを作る
C#で特定のclassを継承したスクリプトしか代入できないフィールドを作りたいと思った時、真っ先に思いつくのは恐らくジェネリックであろう。 しかしながらUnityでは、ジェネリックは厳しい制約の下で使用しないとInspectorからは扱えないようになっている。 となると、Inspector上で編集可能な特定のclassを継承したスクリプトしか代入できないフィールドを作ることなど不可能に思える(筆者はつい五分程前までそう思っていた)のだが 「特定のclassを継承したスクリプト」のフィールドなら出来ることが判明したので(他は未検証)、その方法を載せておく。 これが誰かのためになれば幸いだ。 方法 継承元のclass型のフィールドを宣言する。 それだけ。 例えばclass"hoge"を継承したスクリプト(厳密にはclass)しか代入できないフィールド"hogehoge"を作りたいなら public hoge hogehoge; と宣言するだけ。 余談=物凄いどうでもいい話 この方法は筆者が思いついたことを試した結果通ったという経緯で出来たもの。 ほぼ一日潰したが、思い付きは大事だと改めて気づかされた。 LGTM,質問,意見,その他諸々もらえたらうれしいです。
- 投稿日:2021-08-15T20:11:11+09:00
UI Toolkit(旧:UI Elements)でデフォルト画像の再生ボタンを使う
Unityの比較的新しいUIフレームワークでUI Toolkit(旧:UI Elements)というものがあります。 html & cssのノリで要素を定義したファイルを.uxmlファイルとして、スタイルを定義したファイルを.ussとして分けることができ、従来のEditor拡張に比べて一つのファイルに対するコードの量を短くすることができるというメリットがあるようです。 「Debuggerもあって便利そうだし試しに使ってみよう」と思ったのですが、公式ドキュメントを見ていて困ったところがあったのでメモしておきます。 やろうと思ったこと Timelineで使われてるような再生管理のためのシンプルなボタンを実装しようと思い、 せっかくなのでEditorに使われている画像を使えたらわざわざ画像用意しなくても良さそうなので 調べてみるとUnity Default Resourcesというところに配置されているようです。 Unity Default Resources デフォルトでは画像は見えないようなのでファイル名を確認するためには少し手順を踏む必要があります。(チートシート/一覧とか無いのかな。。) こちらの記事を参考にmacだとUnity.app/Contents/Resourcesフォルダにあるunity editor resourcesをコピーして.asset拡張子をつけてからUnityのAssets以下に配置するとUnity上で確認することができました。 確認方法としてはWindow>UI Toolkit>Debuggerでデバッガーを開いて上部ツールバーからPick Elementをクリックして実装中のEditorWindowをクリック、そして確認したい要素を選択しbackground-imageから確認することができます。 もしくは実装中のEditorWindow右上の3つの点ボタン>UI Toolkit Debuggerでデバッガーを開いて確認することもできます。 コード 少しスタイルを整える必要がありそうですがひとまずこんな感じになりました。 Test.uxml <?xml version="1.0" encoding="utf-8"?> <engine:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:engine="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements" > <engine:Box class="toolbarHeader"> <engine:Button name="firstKeyButton" class="toolbarItem firstKeyButton" /> <engine:Button name="prevKeyButton" class="toolbarItem prevKeyButton" /> <engine:Button name="playButton" class="toolbarItem playButton" /> <engine:Button name="nextKeyButton" class="toolbarItem nextKeyButton" /> <engine:Button name="lastKeyButton" class="toolbarItem lastKeyButton" /> </engine:Box> </engine:UXML> Test.uss .playButton { background-image: resource("d_Animation.Play") } .prevKeyButton { background-image: resource("d_Animation.PrevKey") } .nextKeyButton { background-image: resource("d_Animation.NextKey") } .firstKeyButton { background-image: resource("d_Animation.FirstKey") } .lastKeyButton { background-image: resource("d_Animation.LastKey") } TestWindow.cs using UnityEngine; using UnityEditor; using UnityEngine.UIElements; public class TestWindow : EditorWindow { [MenuItem("Window/TestWindow")] public static void ShowWindow() => GetWindow<TestWindow>("TestWindow"); void OnEnable() { var root = rootVisualElement; root.styleSheets.Add(Resources.Load<StyleSheet>("Test")); var docTree = Resources.Load<VisualTreeAsset>("Test"); docTree.CloneTree(root); root.Q<Button>("firstKeyButton").clickable.clicked += () => Debug.Log("firstKeyButton"); root.Q<Button>("prevKeyButton").clickable.clicked += () => Debug.Log("prevKeyButton"); root.Q<Button>("playButton").clickable.clicked += () => Debug.Log("playButton"); root.Q<Button>("nextKeyButton").clickable.clicked += () => Debug.Log("nextKeyButton"); root.Q<Button>("lastKeyButton").clickable.clicked += () => Debug.Log("lastKeyButton"); } } 困ったこと https://docs.unity3d.com/ja/2019.4/Manual/UIE-USS-PropertyTypes.html 公式リファレンスでは以下のように記載されています。 ファイルが Editor Default Resources下にある場合は、ファイル拡張子を入れる必要があります。例えば background-image: resource("Images/default-image.png")。 ですが拡張子を入れるとエラーになり、逆に外すと反映されるようになりました。
- 投稿日:2021-08-15T17:01:13+09:00
UnityのAndroid開発でエラーもなく突然激重になる現象とそれを対処した方法
こんにちは、ハニカムラボのおのです. 現象や状況 Unity2020系、Unity2019系で発生 Unity上のエディタでは発生しない 実機(Android6系と7系)で発生確認 3種類ほどのタブレット端末で発生 Android5系のスマートフォンでは発生せず 10分から40分画面スリープもなく放置していると発生 操作していても発生することも 見た目はほぼフリーズしている状態だが、画面遷移など時間が立つと起こるためたぶんすごいFPSが下がっているという状況 Android Logcatなどでもエラーが発見できない ただ、警告がずっと出続けていた(EGL_BAD_PARAMETER) 結果 Optimized Frame Pacing の項目のチェックをはずすと警告が消え、フリーズ現象がなくなった Optimized Frame Pacingとは 2019.2から追加されたっぽい > 低変動でのフレームの分散を可能にすることで、安定したフレームレートとよりスムーズなゲームプレイ体験を実現します だそうだ この記事とは別の現象だったが、不具合があるらしい 2020.3.8fではまだまだ不具合があるっぽい この機能に全く対応できない古すぎる端末だと動くのかなと予想 Android5系のスマートフォンで動いた理由 対処した方法など 今回単純なエラーが出たわけでもなく、そのエラーを見つけるのも大変だったため、色々試してみたのでその共有です。 Firebaseなど外部ライブラリとUnityのバージョンの相性確認 https://firebase.googleblog.com/2020/08/firebase-compatibility-with-unity-20201.html http://halcyonsystemblog.jp/blog-entry-732.html 警告を消す DOTweenの警告を消した https://qiita.com/neusstudio/items/f4429024ea0f54d727fc Android Logcatを利用 Unity内でも見れるのもある:https://baba-s.hatenablog.com/entry/2018/12/25/090000 Unity内にLogViewerを入れた https://assetstore.unity.com/packages/tools/integration/log-viewer-12047?locale=ja-JP Android StudioのProfilerでCPUやメモリ確認 https://developer.android.com/studio/profile/cpu-profiler?hl=ja シーン内のオブジェクト全部削除 新規プロジェクトの作成 端末で他のサードパーティ製アプリで同じく放置してみる Unityのダウングレード 色々な端末向けにビルド iPhone向けにしてみたり、PC向けにしてみたり 色々なビルド設定で虱潰しに
- 投稿日:2021-08-15T16:22:29+09:00
【シューティングゲーム】ゲームを仕上げていく!【Unity Editorから独立できるゲーム順序にする】
目的 ゲームを仕上げます。 今のままだと終了するたびに、Unity Editorで再生しなおす必要がありますが、本来ゲームはEditorに依存しません。 なので今回は、【開始】⇒【ゲームをする】⇒【終了】⇒【結果表示】⇒【データの保存】⇒【初めから】の繰り返しを再現します。 つまり、Unity Editorから独立できるところまで行う!! ステップの確認 今回は最終ステップでゲーム全体を仕上げていきます! ほとんどが今までの応用ですので、今回初めて出る機能だけ紹介します。 今までのステップについて確認していきましょう。 Step1 Step2, Step3, Step4 Step5 Step1, Step2, Step3 Step4 Step5 Step1, Step2 Step1, Step2 Step外編 Step3, Step4 Step5 1.Title画面を作成(新しいシーン) 新しくSceneを作成する。 以下のような画面を作成する Buttonにページ移動機能をつける 以下のようなスクリプトを作成して、ボタンの「On Click()」機能にアタッチする。 ButtonClickGameStart.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class ButtonClickGameStart : MonoBehaviour { public void BottonClikStart(){ SceneManager.LoadScene("MainGame"); } } ボタンにスクリプトをアタッチ ※ゲームシーンを「MainGame」という名前にしておく必要がある! 動作確認① これで「Title」からゲーム開始ができるようになった! 2.ランキングに「登録する」か「登録しないか」決めるボタンをつける このページで作成した「ゲーム終了」画面にボタンをつけて、ランキングに登録するかそのまま終わるかの選択ができるようにする。 下のような感じで作成をする。 構成は以下のよう Buttonに機能をつけていく ①「登録する」を押した場合⇒ランキング登録画面を表示する 「ButtonClickShowRegister.cs」スクリプトを作成↓ ButtonClickShowRegister.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class ButtonClickShowRegister : MonoBehaviour { public GameObject register; public void ButtonClickShow() { register.SetActive(true); } } スクリプトをボタンにアタッチして「On Click()」機能を設定する ②「しないで終了」を押した場合⇒「Title」画面に戻る 「ButtonClickFinishGame.cs」スクリプトを作成↓ ButtonClickFinishGame.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class ButtonClickFinishGame : MonoBehaviour { public void BottonClikFinish() { SceneManager.LoadScene("Title"); } } スクリプトをボタンにアタッチして「On Click()」機能を設定する ランキング表示時間を10秒にして、自動で「Title」に移動 ここまででほぼ完成しましたが、「登録する」を押した場合ランキングが表示されます。 ランキング画面には、ボタンをつけていないので「Title」画面に戻ることができない。 そこでランキングが表示されてから10秒で「Tilte」に自動移動するようにする。 スクリプトの改良 「ShowRank.cs」スクリプトを改良する 改良後 ShowRank.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class ShowRank : MonoBehaviour { public exportCsvScript ecs; string path = "savedata.csv"; List<string[]> csvresult; List<csv_sorted> cs = new List<csv_sorted>(); //Text public Text Rank1; public Text Rank2; public Text Rank3; public Text Rank4; public Text Rank5; public Text Rank6; int i=1; //【追加】① private bool finish_bool = false; float timecount = 0; // Start is called before the first frame update void OnEnable() { csvresult = ecs.ReadCSV(path); make_csv_sort(csvresult); cs.Sort((a, b) => b.num_enemy - a.num_enemy); //順位6番までを表示 foreach (var ds in cs) { switch (i) { case 1: Rank1.text = ds.name + " " + ds.num_enemy.ToString() + "体"; break; case 2: Rank2.text = ds.name + " " + ds.num_enemy.ToString() + "体"; break; case 3: Rank3.text = ds.name + " " + ds.num_enemy.ToString() + "体"; break; case 4: Rank4.text = ds.name + " " + ds.num_enemy.ToString() + "体"; break; case 5: Rank5.text = ds.name + " " + ds.num_enemy.ToString() + "体"; break; case 6: Rank6.text = ds.name + " " + ds.num_enemy.ToString() + "体"; break; default: break; } i++; } //【追加】② finish_bool = true; } void make_csv_sort(List<string[]> csv) { int i = 0; foreach (string[] c in csv) { if (i != 0) { cs.Add(new csv_sorted { name = c[0], num_enemy = int.Parse(c[1]) }); } i++; } } //【追加】③ private void Update() { if (finish_bool) { timecount += Time.deltaTime; if (timecount > 10) SceneManager.LoadScene("Title"); } } } public class csv_sorted { public string name { get; set; } public int num_enemy { get; set; } } 動作確認② 下のGIFは、編集で10秒を早送りしてます。(実際は10秒待ってからの画面移動) ゲーム終了後の「敵を倒した数」のカウントを回避 ゲームが終了した後も、敵を倒すと点数が加算されてしまうので、それを修正。 点数を加算するスクリプトは、「Collision_Bullet.cs」なので、そこに「DataScripts.cs」の「Finish_Game」で分岐をする。 Collision_Bullet.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class Collision_Bullet : MonoBehaviour { public GameObject Explosion; private void OnTriggerEnter(Collider other) { //【追加】Tagを使って衝突した時のオブジェクトを選別 if (other.gameObject.tag == "Bullet") { Debug.Log("破壊"); Destroy(this.gameObject); Destroy(other.gameObject); GameObject instance_explosion = Instantiate(Explosion, this.transform.position, Quaternion.identity); Destroy(instance_explosion.gameObject, 2.0f); //【追加】 if (!DataScripts.Finish_Game) { DataScripts.Enemy_killed++; } } } } ゲームの完成!! お疲れさまでした。これでshootingゲームが一通り完成しました。 あとは、音や敵オブジェクトのデザインなどを変えれば、いろんな可能性が広がると思います。 Asset Storeを使ってもよし!Blenderなどで自作してもよしです。
- 投稿日:2021-08-15T01:28:50+09:00
VSCodeにUSS(Unity style sheets)をシンタックスハイライトさせる
UnityのUIElementsを用いたエディタ拡張では*.ussファイルを記述することがあります。 しかし、VSCodeでは標準で*.ussにシンタックスハイライトがなく、もちろんフォーマッタも利用できません。 また、マーケットプレースにこれを解決するズバリな拡張機能を見つけることはできませんでした。(2021/08/15現在) VSCodeに*.ussと*.cssを関連付けさせることでシンタックスハイライトをさせる方法を紹介したいと思います。 settings.jsonを開く ctrl + shift + pで開いたコマンドパレットにsettings.jsonと入力してください。 ちなみに、基本設定:ワークススペースを開く(JSON)を選択すると現在のプロジェクトのみに設定が反映されるようになります。 settings.jsonを編集する settings.json "files.associations": { "*.uss": "css", } 上記の記述をsettings.jsonへ追加することで*.ussと*.cssに関連付けがなされます。 *.ussを編集する *.ussと*.cssに関連付けがなされたためシンタックスハイライトされるようになりました。 また、これにより*.css同様にカラーピッカーやフォーマッタが利用できるようになりました。 参照 [VSCode] Language Identifiers