20190226のUnityに関する記事は5件です。

Unity NGUIの座標と、3D空間の座標のやりとり

よく迷うのでメモ。

3Dの座標を、NGUIのポジションから取得

Vector3 worldPos = mainCameraObj.GetComponent<Camera>().WorldToScreenPoint(nguiHogeLabel.transform.position);
  • 3DキャラのHPの表示や、ダメージ等に使える

NGUIの座標を、3Dカメラの座標から取得

Vector3 coinUiPos = mainCameraObj.GetComponent<Camera>().ScreenToWorldPoint(nguiHogeLabel.gameObject.transform.position);
  • 3Dキャラがコイン等を落としたものを、NGUIの位置にホーミングさせる場合などに利用できる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityでテストを書くのが当然になる時代に今から備えよう


# 実は既にもうそんな時代なのかも...

レガシーコードとはテストのないコードのことである

スクリーンショット 2019-02-22 19.57.04.png
テスト駆動開発継続的インテグレーション/デリバリー といった言葉が注目される昨今ですが、その中心にあるのが言うまでもなく テスト です。

テストを蔑ろにしていた(画面をポチポチして動作確認していただけの)過去の自分を反省し、この潮流に乗るためにUnityでのテスト導入方法をまとめました。バージョンは 2018.3.2 です。説明はMacでの画面ベースになります。

Unityにおけるテストの導入

Test Runner

Test Runner と呼ばれる機能がUnityにはあります。私が使ってなかっただけで昔からあったんです。
しかし、起動は[Window]>[General]>[Test Runner]からと回りくどさも否めません。(ショートカットもありません)
スクリーンショット 2019-02-22 20.10.55.png

とりあえず作る

プロジェクトのAssets/で右クリックから[Create]>[Testing]を選びます。
スクリーンショット 2019-02-22 20.28.40.png
[C# Test Script]がグレーアウトされています。ここは一旦[Tests Assembly Folder]を選択し、Testsというディレクトリを作成します。一緒にTests.asmdefというファイルも作成されますが一旦無視します。

Assets/Tests/に移動し、もう一度右クリックから[Create]>[Testing]を選ぶと[C# Test Script]が選択できるようになっているので、NewTestScript.csを作ります。
するとTest Runnerの[PlayMode]タブに以下のようにデフォルトのテストが追加されます。
スクリーンショット 2019-02-22 20.42.11.png
注意: Unity2018-TestRunnerというのは今回作ったプロジェクト名です。機能とは無関係です。

[Run All]が押せるようになっているので押してみます。
すると普段通りUnityEditorから実行されたような挙動を見せ、全テストに ✅ がついて終了します。
デフォルトのままでテストに何も書いてないので、全てパスします。
テストの目標は全て✅になり、❌が0になることです。

EditMode

とりあえずで[PlayMode]を試しましたが、もう一方の[EditMode]とはなんでしょうか。
https://docs.unity3d.com/ja/2018.1/Manual/testing-editortestsrunner.html
はっきりと違いが書かれた記述は見当たりませんが、

  • EditMode: 起動が早くカジュアルに実行できる。Classの単体テストに使う。
  • PlayMode: シーンの実行とほぼ同等のため起動がやや遅い。シーンの動作確認やMonobehaviourを組み合わせた結合テストに使う。

ような認識でいます。

EditModeのテストコードはUnityの特殊ディレクトリであるEditor/配下に置く必要があるようです。
Tests/にEditorディレクトリを作り、そこで[C# Test Scrit]からNewTestScriptEditor.csを作ります。すると[EditMode]にNewTestScriptEditorが追加され........

ません!
[PlayMode]のテストの一部とみなされています。
スクリーンショット 2019-02-22 21.18.52.png

Editor/配下のテストコードが勝手に[EditMode]に振り分けられるのではないのです。

Assembly Definition Files

さっき無視したTests.asmdefが鍵を握っています。
Unity2017.3以降から Assembly Definition Files いう機能が追加され、これがテストコードを作成する際の手順に大きく関わっているようです。

最初に行った[Create]>[Tests Assembly Folder]とは作成したディレクトリ配下にテストコード用のAssemblyを定義することに他なりません。

Tests Assembly Folder配下にTests Assembly Folderをさらに作ることはできないので、テストコードのディレクトリはEditMode用とPlayMode用とで分ける必要があるようです。

こちらの記事 (Unityでちゃんとテストを書きたい人のためのまとめ) の ここ を参考に、プロジェクトを1から作り直します。

  1. Assets/にTestsディレクトリを 普通に 作ります。
  2. Assets/Tests/でPlayModeディレクトリを[Tests Assembly Folder]から作成します。
  3. Assets/Tests/PlayMode/で[C# Test Script]を作成し、名前をNewTestScriptPlay.csとします。
  4. Assets/Tests/にEditModeディレクトリを[Tests Assembly Folder]から作成します。
  5. Assets/Tests/EditMode/にあるEditMode.asmdefを選択し、右のinspectorから Platforms で AnyPlatform にチェックが入っているのを、Editorだけにチェックが入った状態にして[Apply]を押します。
  6. Assets/Tests/EditMode/にEditorディレクトリを作成します。
  7. Assets/Tests/EditMode/Editor/で[C# Test Script]を作成し、名前をNewTestScriptEdit.csとします。

これでやっとNewTestScriptEditがTest Runnerの[EditMode]タブに追加されました。
EditModeのテストは[Run All]してもサックと終わります。
スクリーンショット 2019-02-22 21.59.27.png

つまり、EditorのPlatformを対象としたTest用 .asmdefファイルを持つディレクトリ下のEditor/下にあるTest ScriptがEditModeで実行できるのです。

ちなみに、Test Runnerのウィンドウから[Create EditMode Test Assembly Folder]をすればPlatforms=Editorの.asmdefが作成されます。
スクリーンショット 2019-02-26 15.37.30.png

最終的な配置

Assets/
└ Tests/
  ┝ EditMode/
  │ ┝ Editor/
  │ │ └ NewTestScriptEdit.cs
  │ └ EditMode.asmdef
  └ PlayMode/
    ┝ NewTestScriptPlay.cs
    └ PlayMode.asmdef

いちいちAssemblyなんて気にするのは煩わしい気もしますが、テストコードがプロダクトコードに含まれてしまいアプリサイズが肥大化するのを防いでくれる面もあります。

テストの作成

ここまでで導入です。ここから実際にテストを書いていきます。

EditModeのテスト

テストの対象となるScriptを作成します。

Assets/Scripts/MyClass.cs
public class MyClass {
    private string name;

    public MyClass() {
        name = string.Empty;
    }
    public MyClass(string name) {
        this.name = name;
    }

    public void SetName(string name) { this.name = name; }
    public string GetName() { return name; }
}

nameというフィールドと2種類のコンストラクタ、nameにアクセスするためのSet/Getメソッドを持つごく簡単なクラスです。

EditModeでテストをするため、NewTestScriptEdit.csにテストコードを書きます

NewTestScriptEdit.cs
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace Tests {
    public class NewTestScriptEdit {
        [Test]
        public void MyClassTest1() {
            var x = new MyClass();
            Assert.IsEmpty(x.GetName());
            x.SetName("Alice");
            Assert.AreEqual("Alice", x.GetName());
        }
        [Test]
        public void MyClassTest2() {
            var x = new MyClass("Bob");
            Assert.AreEqual("Bob", x.GetName());
        }
    }
}

Assert.XXXの各評価式でテスト成否を判定します。
しかしコンパイラが 'MyClass' could not be found といってエラーを吐きます。ナンテコッタイ

前の手順で各テストのAssemblyを定義したので、今のままではMyClassNewTestScriptEditは別世界の住人になっています。MyClassを含んだAssemblyを定義し、EditTestのAssembly参照に追加して橋渡しをする必要があります。

  1. Assets/にて右クリックから[Create]>[Assembly Definition]でMyProject.asmdefを作成します。
  2. Assets/Tests/EditMode/へ移動し、EditMode.asmdefを選択します。
  3. 右のinspectorの Assembly Definition References の[+]を押し、リストにMyProject.asmdefを追加し、[Apply]します。

これにて、NewTestScriptEditからMyClassが参照できるようになり、コンパイルとテスト実行ができるようになりました。
スクリーンショット 2019-02-22 22.51.21.png

このAssembly参照の追加はPlayModeのNewTestScriptPlayでも同様に必要です。

余談

例なので簡単なテストということもありますが、どんなに簡単なテストでもあると無いでは天地の差です。 冒頭の定義ならテストが無ければ問答無用でレガシーコード認定です。
また、簡単でも1度テストを書けば、以降の開発でもテストを書かなきゃという意識を自分にもメンバーにも植え付けることが出来ます。

PlayModeのテスト

PlayModeでのテスト実行はシーンを動かしてこそだと思ったので、簡単なプロジェクトを作成しました。
naninunenoy/unity-2018-TestRunner

スクリーンショット 2019-02-22 22.57.28.png

矢印のキーに合わせて白のCube(以降は 豆腐 と呼ぶ)が移動するシーンです。また、豆腐が画面外に出ないように抑制する仕様を持っています。

Test RunnerのPlayModeで実行するTest Scriptです
Assets/Tests/PlayMode/SampleSceneTest.cs
移動判定をInterfaceで分離し、キー入力をテストで模擬するテクニックを使っています。

Unity EditorでSceneの挙動を見ながらTest RunnerのPlayModeのテストを実行することができます。
playmode.gif

テストを導入するメリット

結構な手順があり、プロジェクトに.asmdefを入れるとAssemblyを意識した開発を強いられるので導入するをのためらう人もいるかもしれませんが、その手間を補って余りあるメリットがあるとも考えています。

安心感をもてる

機械的なテストを網羅的に一通り流すことで、想定通りの実装ができているという自信になります。また、バグ修正によるデグレを検出でき、事前に防ぐことが出来るかもしれません。

しかしながら、どんなにテストを行おうともバグが無いことの証明にはなりません。白いカラスが存在しないことを証明するのがほぼ不可能なのと一緒です。

リファクタのしやすさ

汚いコード(自分が気に入らないコードとも言う)はリファクタリングしたくなりますが、動いているコードを変更することはデグレのリスクが伴います。テストが通ることを担保しながらリファクタリングができればその心配がなくなります。事前にテストを作っておけば自分や後任の人がコードをリファクタリングする助けになります。

テストを意識した設計

通常のシーケンスしか意識しないプログラムは要求さえ満たせばいいやという意識になり、汎用性に欠けがちになります。自分の実装するクラスや機能がテストからも呼び出されることを考慮する必要に迫られれば、DIなど新しい設計思想を取り入れるきっかけにもなります。

PlayModeの例で作ったプロジェクトでの豆腐を動かす処理を完全にInput.KeyDownに依存させてしまうと該当シーンのテストが困難になります。しかし、この処理をInterfaceに抽象化することで、テストで自由に移動を行うことを可能にしています。また、この設計ならInput.KeyDownからゲームパッドで操作するように仕様追加が起こった際にも改修がスムーズにいくかもしれません。

おわりに

ソフトウェアのテストに関しては捉え方が人それぞれだと思います。
バグはコードレビュー+人力による確認で潰すという方針もあるでしょう。または、アプリの仕様上自動テストが厳しい箇所があるのかもしれませんし、会社(プロジェクト)の文化も関わってくるでしょう。
しかし、Unityという開発環境を利用しているのであれば、Test Runnerをいう仕組みを利用しない手はないと考えます。まずはごく簡単なテストから初めてみてはどうでしょうか?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityのいろいろなTips

はじめに

当たり前に設定していることでも忘れてしまうことがよくあるので備忘録もかねて記事にしました。

Unityのバージョンは、2017.4 を基準に作成してあります。

画面サイズを変更するとUIが崩れる

Canvas Scalerが"Constant Pixel Size"になっているのが原因。

下の画像のように"UI Scale Mode"を"Scale With Screen Size"にするとよい。

また、"Reference Resolution"にはメインで使用する解像度を指定しておく。

※ただし、アスペクト比を変える場合はここの設定だけでは不十分

スマートフォンの複数バージョン対応や、縦横持ち対応などのアスペクト比を変えた場合のUIの設計は、

Unity公式ドキュメントに 複数解像度のためのUI設計 がありますので、そちらを参照ください。
cc9009570527503c5835bc7e32f476e0.png

スクリプト上でUIを生成した時に原点がずれる

例えば、

Image img = new GameObject("img").AddComponent<Image>();
img.rectTransform.position = Vector2.zero;

と、しても原点に生成されないときがあります。

UIの複数の解像度にも対応した相対的な位置は、"anchoredPosition"を使用します。

Image img = new GameObject("obj").AddComponent<Image>();
img.rectTransform.anchoredPosition = new Vector2(0,0);

Publicな変数がInspectorでも変更できてしまう

宣言した変数上に"[System.NonSerialized]"を書けばInspectorに表示されなくなります。

Editorで変更する仕様でない限り、書いておけば他人が見たときに外から読み取りたい旨がわかるようになります。

[System.NonSerialized]
Public int hoge = 0;

Privateな変数をInspectorに表示させたい

表示させたい変数の上に"[SerializeField]"を書く。

Editor上で値を書き換えたいときにPublicだと外から読み(書き)たいのか、Inspectorから指定したいのかわからないので、privateな変数でも大丈夫ならこちらの方法で書くといいでしょう。

[SerializeField]
private int hoge = 0;

フレームレートが落ちる(カクカクする)とキャラクターのスピードが遅くなる

Unity上でスクリプトを作成するとデフォルトでUpdate()が生成されてしまうため、そこに書きがちですが、Updateはフレームレート依存な関数なので、移動などの物理的な挙動に関してはFixedUpdate()に書くか、Update()に書く場合はTime.deltaTimeをかけるといいです。
参考までに

void Update()
{
  gameObject.transform.position += Vector3.forward * Time.deltaTime;
}

もしくは、

void FixedUpdate()
{
 gameObject.transform.position += Vector3.forward;
}

他のScriptから参照したい

UnityというかC#の話なうえ、ケースバイケースでその時によって変わりますが、使い分けてください。

当たり前に使うことですが、なぜか調べてもなかなか出てこない上にGameObject.Find()が目立っている気がしたのでいろいろな方法を載せておきます。

ManagerとしてSingletonにする

Enemyとか、Itemなど同じものが複数個生成される場合では使えませんが、GameManager等の一つしか生成されないスクリプトによく使う方法です。

hogeManager.cs
public int hoge = 100;

public static Instance;
void Awake()
{
 if(Instance == null) Instance = this;
}

void OnDisable()
{
 Instance = null;
}

とし、呼び出すほうでは、

void Start()
{
 Debug.Log(hogeManager.Instance.hoge); //100
}

とすると簡単に使用できます。Singletonの詳しい使い方や組み方、MonoBehaviourとのより良い共存方法などは調べると沢山出てきます。

そちらのほうが丁寧に分かりやすく解説されていると思いますので、各自で調べてみてください。

FindWithTag()等で保持しておく

参照したいScriptがついているGameObjectにTagをつけて、StartやAwakeで保持しておく方法。

GameObject.Find()でも大丈夫ですが、重たい処理な為、特に利用が制限される場合じゃなければFindWithTag()を利用するのが無難だと思います。

HogeScript hoge;

void Start()
{
 hoge = GameObject.FindWithTag("hoge").GetComponent<HogeScript>();
}

Inspectorで直接アタッチする

SerializeField等でInspectorに表示させ、ドラッグアンドドロップでアタッチする方法です。

直感的でわかりやすく手間のかからない方法ですが、プロジェクトファイルを移動した際にアタッチが外れることが多々あります。

接触したオブジェクトに送る

上記と違い、極端に限定的ですが、Enemyに当たったらダメージを与えるとか、Itemに触れたらインベントリに入れるなど、

接触した時に接触したもののスクリプトを呼びたい時に使う方法です。

void OnTriggerEnter(collider col)
{
 if(col.tag == "enemy")
 {
  col.GetComponent<enemy>().Damage();
 }
}

あとがき

もっといろいろ書きたいことがあった気がしますが、思い出せないのでその都度更新していく予定です。

それではよきUnityライフを!

参考文献

Unity - Manual

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VuforiaのImageMarkerをターゲットにする場合に認識精度を上げるためのTips

今時マーカーよりもマーカーレスな世の中ですが、VR空間で手に持つ物体のトラッキングなど限定された環境下ではマーカーの方が扱いやすい場合があります。そういったときの設定の仕方をまとめています。
主に以下の抄訳です。

参考

Image Targets
https://library.vuforia.com/content/vuforia-library/en/articles/Training/Image-Target-Guide.html

Optimizing Target Detection and Tracking Stability
https://library.vuforia.com/articles/Solution/Optimizing-Target-Detection-and-Tracking-Stability.html

サポートされる画像:JPG,PNGでグレースケールもしくはRGB。サイズは2MB以下
Unityで自身のプロジェクトに入れる方法は以下が分かりやすいです。

https://qiita.com/takako_sudou/items/ec2081cc4d9c4abb1c8f

主にWebカメラなどで撮影するので周辺の環境はほどよく明るくスポットライトのような一部に光が集中しているよりも拡散している方がよいです。

Extended Trackingとは

これは、マーカーの周囲の特徴も使ってARカメラのトラッキングを行うモード出会って、ARマーカーを動かして使いたいときには必ずOffにする。

アップロード時の画像は横幅320ピクセル以上を推奨。320ピクセル以下だとアップロード後にサーバーサイドでスケーリングが行われて画像の持つ特徴量が減ってしまうことがあります。

画像のレーティング

画像は1から5つ星でレーティイングされますが、細かいものが写っていること、コントラストが良好なこと、繰り返しパターンがないことがレーティングの基準となっています。

トラッキング用カメラの設定

カメラは常にピントが合うことが重要なのでコンティニュアスオートフォーカスモードがあればそれを使うこと。VuforiaでもAPIを提供しているので以下を参照のこと。
https://library.vuforia.com/articles/Solution/Working-with-the-Camera.html

カメラと対象のターゲットとの距離を10で割ると印刷する画像の最小サイズを見積もることが出来ます。例えば距離が2Mであれば20CM幅必要になるということです。もちろん目安なので実際に試してみることが重要です。

以下のサイトとかでprofiles.xmlを設定してるけどインポートしただけでは存在しないし、入れても反映しない。今は無効化されてる?
https://gitlab.cs.ucl.ac.uk/vania/team27website/blob/d8e1335d1a77dd9a6ce215ada1b08e0bd479e51a/project/Assets/Vuforia/Editor/WebcamProfiles/profiles.xml

フォーラムの以下に回答がありました。ファイルの場所と名前が変わっているようです。現在は「webcamprofiles.xml」が以下のフォルダに入っています。(2017.4.1f1現在)
Unity\Editor\Data\PlaybackEngines\VuforiaSupport\VuforiaResources

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityPurchasing: InitializationFailureReason.PurchasingUnavailable

The official document says that when this error happens it means

IAP may be disabled in security settings: iOS devices only
The system purchasing library may be outdated

My case did not fall into any of the above but simply because I have not logged into my Google account on the test device yet.
If you encounter the above error on Android, it may help to check if the Play Store has been set up properly before trying anything else.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む