20201208のUnityに関する記事は15件です。

Unity 超初心者が Firebase でアプリ開発する際に必要になるスキル

こちらは Unity #2 Advent Calendar 2020 の 8日目の記事です

また今回は、解説が長くなってしまうため前編と後編に分けることにしますmm
後編では、Firebaseの実装関連を全て Firebase Advent Calendar 2020 の16日目に投稿予定の記事『UnityとFirebaseでアプリ開発する際にユーザーデータの取り扱い方まとめ(仮)』でまとめようと思います。

概要

今回は、タイトルの通り超初心者がUnity/C#を学び、さらにFriebaseを使ってアプリ開発したい人向けとなります。
プログラミング初心者でもUnityは、書籍が豊富なだけでなく、Asset StoreUnity LearnUnity Japan YouTubeチャンネル などUnityコンテンツの制作・開発においてとてもサポートが豊富なので、熱意があれば小学生から高齢者まで幅広くもの作りができる、あらゆる可能性を持ったツールだと思います。

私は、とあるサービスでUnityのインストラクターをやっているのですが、担当している生徒さんはほとんどプログラミング初心者でUnityを学びつつC#/プログラミングも学んで、その上で目的であるゲーム作りやVR/AR作りなども学んでいるため、学ぶことが多くお手上げ状態になってしまいます。
どんなに学びやすい環境でもスキルを身に付ける量が多いと、どうしてもモチベーションが下がってしまいますよね。

そこで今回は、初心者がUnityでiOS/Androidのアプリ開発を目指している方にだけターゲットになってしまいますが、「どうぶつタワーバトル」や「飛べゴリラ」などのインディー/カジュアルゲームの開発でコスパ最強な mBaaSFirebase について、活用方法や必要になってくる知識のサポートについて解説していきたいと思います!

定義

各用語について本記事の定義をさせてください。

  • 超初心者
    • UnityとC#を技術書やネット記事などを参考にすでに何か作っている
  • プログラミング初心者
    • ”入門”や”初心者”などが書かれている技術書をもとに何らかの言語を学んで何となく使える
  • アプリ開発
    • 本記事ではiOS/Androidのアプリ開発のことで扱います

ターゲット

改めてターゲットはこちらになります。

  • Unity超初心者
  • UnityでiOS/Androidのアプリ開発をやりたい方
  • FirebaseをUnityでどう使うのかわからない方

前提

本記事を参考にするには次の項目が問題ないことをご確認ください。

  • ボルドプラットフォームは、iOS/Androidに向けたアプリ開発が前提です。
  • 各ツールのインストールや環境設定など済んでいる。
  • C#の学習が、入門書の後半(例えば 独習 C# だと「第7~9章 オブジェクト指向構文」)まで進んでいる。
  • Unityがテーマなので、プログラミングの話はオブジェクト指向言語ベースです。
  • Firebaseの前提知識は、公式ドキュメントで一部割愛させていただきます。

やってほしいこと

今回は、Firebase Authentication (Auth)Firebase Realtime Database (Database) を活用するので、私が過去に書いた FirebaseとUnityでアプリ開発(ハンズオンみたいなやつ)を参考に FirebaseAuth.unitypackageFirebaseDatabase.unitypackage をUnityにインポートし、Firebaseの導入を済ませておいてください。

キャッチアップ

また、公式ドキュメントをもとに一度Firebaseを実際に使ってどんなものなのか知ってもらいたいです。
次の項目を一通りやってみて、途中でわからなくて止まってしまっても全部確認してやってください?

Firebase 活用方法

 

今回、AuthとDatabaseを活用する理由ですが、カジュアルなゲームや2D/3Dのコンテンツの開発でユーザーデータの登録/取得が必要になる場面が多いため、他にもFirebaseの導入が必要かもしれませんが、まずは最低限の導入から進めることをオススメします。

アプリの要件次第ですが、例えばゲームアプリの開発でランキングシステムを実装するとします。
一端末で完結するアプリ(ノベルゲームや人狼ゲームなど)であれば PlayerPrefs などを用いて、ローカルにデータを保持するだけで事足ります。
ですが、オンラインで様々なユーザーがランキングを争う要件となると、全ユーザーのゲームデータを管理する サーバー が必要となります。

そこで、FirebaseのDatabaseでゲームデータを保持させ、アプリを使用するユーザーのみDatabaseへアクセスできるようにセキュリティの担保、およびユーザーを管理するAuthを使うことで、サーバーの開発と運用をやらなくて済みます。

Auth

 

Databaseを使用するならAuth必須だと思っていいでしょう。
Firebaseはある一定から従量課金制になるので、使った分だけ請求が発生します。そのため、誰でもアクセスでき(".read": true)誰でも書き込みでき(".write": true)る脆弱なDatabaseのルールで運用してしまうと、Databaseが攻撃されて多額の請求が発生してしまう可能性が考えられます。

そのため、作るアプリを使用するユーザーのみアクセスと書き込みができる設定にしておけば不正アクセスを特定できるし、後からさらにセキュリティを強化することが可能です。

認証方式

 

本来は、SNS連携でスマートにユーザー管理したいのですが、Unityの場合はiOS/Androidともに個別対応が必要で大変なので、今回も前回同様パスワードでの認証を扱います。
 

※実装関連は後半へ続く

Database

 

ユーザーデータをDatabaseに保存することで作る機能の幅がグッと広がります。
ゲームならランキング機能やゲームデータの共有機能、ゲーム以外でもフレンド機能などを開発できるようになります。

私が過去に開催したイベント Unity Developer Jobs #1 でご登壇していただいた @tktrvr さんの 発表内容 で、アバターライブ配信プラットフォーム「トピア」 のチャット・ギフトの実装は、Firebase Realtime Databaseで実装されているとのことでした。

 

※実装関連は後半へ続く

公式ドキュメントにある各サンプルコード

さて、実際にAuthとDatabaseを扱って、Unity/C#の初心者さんはサンプルコードや公式ドキュメントの解説についてレベルが高く感じ、全然頭に入らないしどうしたらいいのかわからない状態になった方は少なくないんじゃないでしょうか。

ここからが本記事の本題でして、これから次の項目を一つ一つFirebaseに合わせて解説していきます。
Unity/C#の初心者さんは、どうキャッチアップすればUnityでFirebaseを使ってアプリ開発できるのか一緒に抑えていきましょう!

Json(JavaScript Object Notation)

Jsonは、1要素をKeyとValueで取り扱います。このValueでは、文字列や数値を取り扱える他に新たに要素を増やしたり、配列でもあたりを管理することができる上に人間がとても管理しやすいデータ型でもあります。

例えば、以下のようなテーブル情報があったとして、次のようなJsonフォーマットを文字列で管理することができます。
Json形式はフォーマットが存在するため、JSON Pretty Linter Ver3のようなWebツールを使って確認すると作業しやすいですね。

Usersテーブル

id name point
1 Tanaka 90
2 Sato 80
3 Yamada 70
4 Inoue 60

{
  "Users": {
    "1": {
      "name": "Tanaka",
      "point": 90
    },
    "2": {
      "name": "Sato",
      "point": 80
    },
    "3": {
      "name": "Yamada",
      "point": 70
    },
    "4": {
      "name": "Inoue",
      "point": 60
    }
  }
}

 
FirebaseのDatabaseでは、JsonのようなKeyとValueでやり取りします。一般的なデータベース(MySQLやPostgreSQL)は、SQL(Structured Query Language)をベースに設計されているため、SQLを学ぶ必要があります。

SQLの学習はとても難しいですが、JSONであればKeyとValueの設定さえ把握してしまえばいいので、JsonのWebツールなどを活用してサクッと身に付けましょう!

非同期

非同期は、以下のように『通常同期処理とは違って、メイン処理(メインスレッド)が終わっても別で処理(サブスレッド)が進んでいる。』だけ知り、それ以外で非同期を理解しようとしなくて大丈夫です。

// 同期処理のサンプル
void Start() {
    Debug.Log("【メッセージ表示開始】");
    Debug.Log( Message() );
    Debug.Log("【メッセージ表示終了】");
}

string Message()
{
    var stg = "■-□-■-□-■-□-■-□-■-□\n";
    stg += " +。♪*。#+。♪*。#+。\n";
    stg += "Hello World!!\n";
    stg += " +。♪*。#+。♪*。#+。\n";
    stg += "■-□-■-□-■-□-■-□-■-□\n";
    return stg;
}

/**
 * [表示結果]
 * 
 * 【メッセージ表示開始】
 * ■-□-■-□-■-□-■-□-■-□
 *  +。♪*。#+。♪*。#+。
 * Hello World!!
 *  +。♪*。#+。♪*。#+。
 * ■-□-■-□-■-□-■-□-■-□
 * 【メッセージ表示終了】
 */

// 非同期処理のサンプル
void Start() {
    Debug.Log("【メッセージ表示開始】");

    // 別処理(別スレッド)を作成
    Thread thread = new Thread(new ThreadStart(() =>
    {
        Debug.Log( Message() );
    }));
    // 別処理実行
    thread.Start();

    Debug.Log("【メッセージ表示終了】");
}

/**
 * [表示結果]
 * 
 * 【メッセージ表示開始】
 * 【メッセージ表示終了】
 * ■-□-■-□-■-□-■-□-■-□
 *  +。♪*。#+。♪*。#+。
 * Hello World!!
 *  +。♪*。#+。♪*。#+。
 * ■-□-■-□-■-□-■-□-■-□
 */

 
非同期処理の場合、上記のように処理の結果が変わります。
そのため、いつものようにメソッドを扱う感覚でプログラミングしてしまうと、タイミングによって処理結果がバラバラになってしまって不具合の原因を作りかねません。

非同期の場合、処理がどのタイミングで結果を出してくれるのか意識するために、フローチャートシーケンス図 を書きながらプログラミングするとよいでしょう!

どこで使うのか

非同期処理が必要になる箇所ですが、Firebaseが提供している *****Async( ... ).ContinueWith( task => { 【ここの処理全て非同期処理】 } ) とAsyncが付いているメソッド全てになります。

Authでは、アカウントの作成( CreateUserWithEmailAndPasswordAsync )とログイン( SignInWithEmailAndPasswordAsync )などで非同期処理が求められ、Databaseでは、データの取得( GetValueAsync )で非同期処理が求められます。

難しいので使い方がわかればいい

  

各プログラミング言語で非同期をテーマにした書籍が出ているぐらい、初心者ベテラン関係なく、非同期はとても難しいです。なので、最低限扱えるレベル学習するだけで大丈夫ですー

Unityでの非同期の歴史

Unityの非同期には歴史がありまして、その辺は @4_mio_11 さんの 古来よりUnity非同期を実現していたコルーチンとは何者か? を参考にされてください。

ラムダ式(Lambda Expression)

先ほど解説で出た ContinueWith メソッドの引数=ラムダ式(無名関数)になります。
これも非同期同様に難しい分類なので、書き方と扱い方さえ把握すれば理解しようとしなくていいです。

ラムダ式は以下のような書き方で、ソースコードを簡略化することができます。

// 通常のメソッド
int Add(int a, int b) {
    return a + b;
}

// ラムダ式
(int a, int b) => { return a + b; };

 
ラムダが無名関数と言われているように、ラムダ式になるとメソッド名と返り値の型を明示しなくてもプログラミングとして動きます。

さらに知りたい方は、@toRisouP さんの 【C#】わかった"つもり"になれる「ラムダ式」解説 を参考にされてください。

さいごに

いかがだったでしょうか、個人的にFirebaseを扱うことでキャッチアップの量が増えてしまいますが、ここまで解説した最低限の範囲を学んで、Firebaseを用いて作っているアプリにユーザーデータを保存して取得できる機能が作れたら、Unity超初心者から卒業してるレベルじゃないでしょうか。

実装内容は、全て後編の Firebase Advent Calendar 2020 の16日目に投稿予定の記事『UnityとFirebaseでアプリ開発する際にユーザーデータの取り扱い方まとめ(仮)』でまとめさせていただきますmm

あくまでも参考程度

 

ちなみに公式ドキュメントのサンプルコードは、そのままでは扱えないコードを記載していることがためにあります。
上記のように変なところに閉じブロックがあったり、Unityではないですが、過去にiOSのネイティブアプリでFirebaseを導入した際にサンプルコードだけではエラーになって調査しながら組み込んだことがありました。

このように、公式ドキュメントであっても全て正しいわけではなかったりするので、『公式のサンプルコードなのにエラーになるなあ、日本語記事は見つからないけど海外の記事に似たようなエラーの人がいるみたい。翻訳してもわからないから直接公式に問い合わせしよー』ぐらいの感覚で気楽にやらないとやっていけませんので笑

 

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

”ARでキャラクターに現実の空間を歩かせたりするの呼吸、自己脅迫の型 with ML” を書こうとしましたがまだ何もできていません。代わりにMagicLeap UnityPackage 0.24.2の注意点を書いておきます。

はじめに

この記事はMagic Leap Advent Calendar 2020の25日目です。

記事の内容がまだ白紙です。ごめんなさい。後述の理由で止まってました。恥ずかしい。
これから関係ある(?)ことをちまちま書いていきます。

自己脅迫の型

今年のうちにはタイトル通りの内容にします!!

これは宣言です!この宣言こそ"自己脅迫の型"です!
こうしないとまだ手についてない職は何もできない!!

/* お ま け (になればいいな) */

◎MagicLeap UnityPackage 0.24.2はUnity2019に使えない!!!!

MagicLeapのアプリをUnityで制作するには専用のSDKとUnityPackageが必要です。2020年12月、Lumin SDK の最新は0.24.1ですが、MagicLeap UnityPackageの最新は0.24.2です。これはUnity2020への対応をしたUnityPackageとされています。

じゃあ最新版を使えばいいじゃん?別にグループでやっているプロジェクトでもないし。
私もそう思っていました。しかし、使っているUnityがUnity2019の場合は注意が必要です。

こちらがUnityPackage 0.24.2を使用したプロジェクト(Unity 2019.3.6f1)のConsoleです。
NIGHTMARE.png
なにやらエラーが出ています。全部「スクリプトで呼び出している特定の関数が見つからない!」というエラーです。
えらいこっちゃ。何をしても解決しません。スクリプトを探してもおかしなところはないし、それどころか、MagicLeap UnityPackageから出てきたファイルには、まだ何も触れていないのです
おかしい。

ここで私は気づくべきでした。MagicLeap UnityPackage 0.24.2には
後 方 互 換 が な い ら し い ? ? ? ! ! ! :scream: :scream: :scream: :scream: :scream: :scream:

そういうことでした。これでだいぶ長いこと作業が止まっていた自分があまりにも恥ずかしい。

早速Unityを閉じて

Asset/MagicLeap

をエクスプローラーで削除します。
そしてMagicLeap UnityPackage 0.24.1をインポートします!

0.24.2にしちゃったらやり直しですっ!もうこんな目はいやだ~~

そしたらこのようにエラーが解決します。
NIGHTMARE2.png
また別のエラーが発生していますが、実行はできますのでここでは無視します。

一応、そのエラーに関するものがこのページの本題になるはずだった話題に関連します。

付随して、最新ではないUnityPackage 0.24.1をインストールする方法をお伝えします。
いつもLumin SDKなどなどをインストールしているように

The_Lab > Project Manager

ここでUnityにチェックをいれて[Apply Changes]するのではなく、

All > Unity@ Packages > MagicLeap Unity@ Package > Available Version(s):▢

を選択します。すると過去のバージョンが選べるので 0.24.1 を選択して[ ↓ ]を押して[Apply Changes]しましょう。
あとは

C:Users\(User)\MagicLeap\tools\unity\v0.24.1\MagicLeap.unitypackage

を目的のUnity2019プロジェクトにインポートしましょう。これで問題なくLeaperライフが送れます!!

結論、Unity2019でMagicLeapアプリを開発するときはMagicLeap UnityPackage 0.24.1を使いましょう。MagicLeap UnityPackage 0.24.2はUnity2020専用と捉えた方がいいかもしれません。

以上になります。
実体験のうっかりミスなのでとってもしつこくねちっこく書いてあります。本当に申し訳ございません。

まだまだUnity経験の薄い素人の体験談になりますので、間違っているところがあればお気軽に教えてください。ご一読本当にありがとうございました。

VRChatでは FallenAngelRK というIDで活動しています。良ければお友達になって下さい。そしてUnity教えて下さい。

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

あんスタ!!MusicにおけるCRI ADX2活用事例(前編)

はじめに

Happy Elements株式会社 カカリアスタジオ Advent Calendar 2020 の 9日目 の記事です。

この記事では「あんさんぶるスターズ!!Music」におけるCRI ADX2の活用事例についてご紹介します。
本日は前編として、CRI ADX2の各種機能の活用例についてご紹介します。

CRI ADX2とは

criware_logo_seminar

CRI ADX2は、株式会社CRIミドルウェアさんが提供しているミドルウェア群の中の一つである統合型サウンドミドルウェアです。

公式サイト:https://game.criware.jp/products/adx2/

CRI ADX2の特徴

ただサウンドを再生するだけであれば、Unity単体でも実装は可能です。しかし、CRI ADX2には以下のような特徴があります。

  • 専用オーサリングソフト(CRI Atom Craft)による充実した設定機能
  • 専用コーデックによる高品質な圧縮、ループ再生やストリーミング再生などの対応
  • 低遅延な音声再生機能

独自でこれらを実現しようと思うとかなりの工数が掛かることが予想されます。そこをまるごと全て外部のミドルウェアに任せることができるのは大きな魅力だと言えます。

また、モバイルゲームにおける採用実績が豊富な事もあり、安定性・サポートの品質にも一定以上の信頼があると言えます。
これらの利点を理由に「あんスタ!!Music」でも採用する事になりました。

あんスタ!!Musicにおける活用事例

「あんスタ!!Music」において、CRI ADX2の機能をどのように活用しているかの事例をご紹介します。

ノーツSEの低遅延再生

image

リズムゲームではノーツをタップしたタイミングで効果音が再生されますが、音声の再生処理には一定の遅延を伴います。また、実装方法によっては遅延の幅は大きくなってしまいます。

音声遅延の幅が大きいほどBGMとのズレが大きくなり、リズムゲームの爽快感が損なわれてしまいます。特にAndroid端末は機種によって特性の違いが大きく、それら全てに適切な対応を行うことは難しいです。

そこで、「あんスタ!!Music」ではノーツSEの再生にCRI ADX2のAndroid低遅延再生機能を用いています。
端末に応じた最適化の部分を全てミドルウェアに任せることができるため、他の部分の開発に集中することができました。

BGMのストリーム再生

「あんスタ!!Music」では特定の楽曲においてプレイヤーが選択したキャラクター達が歌唱を行う「歌い分け機能」が存在します。
この機能ではBGMに加えて歌い分け用のキャラクター歌唱音源×5人分の音声を同時に再生しています。
これらの音声データを全てメモリ上に乗せてしまうとそれだけで30MBほどメモリを消費してしまうことになります。
そこで、BGMについてはストリーム再生を用いることでメモリ消費量を抑えています。

  • オンメモリ再生を用いた場合

image

  • ストリーム再生を用いた場合

image

このように、ストリーム再生の場合にメモリ使用量が抑えられていることが分かります。

楽曲選択画面でのプレビュー再生

楽曲選択画面では、選択した楽曲のサビ付近がプレビューとして再生されるようになっています。

image

このプレビュー再生の位置の指定はシーケンスマーカーを用いています。
また、フェードアウトはトラックオートメーションによって制御しています。

image

このようにプレビュー範囲の切り出しを付属の専用オーサリングソフト上で直感的に設定でき、設定は音源データに埋め込まれ再生時に自動で適用されるためプログラム側での制御も必要無く、手軽にプレビュー再生が実現可能です。
また、プレビュー再生用の音源を別に用意する必要が無いため、データ量も削減できます。

その他良かった点など

試用段階から製品版と同等の物が無期限で使える

「あんスタ!!Music」の開発には最初期から数えるとおおよそ2年以上掛かったのですが、開発の早い段階から試用を申し込み、製品版と同等の物でアプリへの組み込み検証を行うことができました。
使用料金はアプリがリリースしてからの発生1となるので、料金を気にせず検証を行うことができたのはとても良かった点でした。

オーサリングソフトがMac対応

付属の専用オーサリングソフト「CRI AtomCraft」が以前はWindows専用だったのですが、V3からはWindows・Mac両対応となりました。
弊スタジオでは標準開発機としてMacを採用しているため、Macに対応したのはとても助かりました。

困った点など

運用の自動化が難しい

オーサリングソフトを用いて設定が行えるのは便利なのですが、一方で設定の自動化を行うのはやや複雑で難しいです。

個々に細かい設定の必要が無く大量に数が存在するボイスデータなどは、設定とビルドを自動化したい所です。
自動化のための方法は用意されていて、設定をCSVからインポートし、コマンドライン実行でデータ追加とビルドが可能2になっています。

ただ、このCSVの仕様3がなかなかに難解で、思い通りに自動化を行うのは少々しんどい物があります…。実際、「あんスタ!!Music」では未だ自動化ができておらず、追加の度にエンジニアがAtomCraftを起動し手動で設定〜ビルドまでを行っています。

…と思っていたのですが、つい先日(2020/12/01)に自動化のための新機能「Atom Craft ロボット」という物が追加されていました!
こちらの導入は追々検証してみたいと思います。

前編まとめ

リズムゲームにおいて音声処理はとても大事かつ大変な部分ですが、CRI ADX2を用いることでこのように様々な問題を解決することができました。
ここで紹介した他にもたくさんの機能があり、ゲーム開発に大いに役立ってくれると思われます。

無償版のCRI ADX2 LEもあり、導入のハードルはかなり低いので、音声処理の実装で困っているなら一度試してみると良いのでは無いでしょうか。

後編では、CRI ADX2をアプリに組み込むにあたって実装上工夫した点などをご紹介する予定です。

メンバー募集

Happy Elements株式会社 カカリアスタジオでは、
いっしょに【熱狂的に愛されるコンテンツ】をつくっていただけるメンバーを大募集中です!

もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください。
Happy Elements株式会社 採用特設サイト

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

【Unity】UI Elements-Editor拡張入門

UI Elementsとは

UI Elementsとは従来のエディタ拡張(IMGUI)を置き換える新たなUIシステムで次のような特徴があります。

  • システムによって、定義したUIの構造がレンダリングされる
  • 描画の内容とタイミングが最適化され、IMGUIよりもパフォーマンスが高い
  • 機能のロジックを作る作業とヒエラルキー・スタイリングを行う作業が分離される

本記事ではこのUI Elementsを使ったエディタ拡張のほんの入口をチュートリアル的に解説していきます。

環境

  • macOS Catalina 10.15.7
  • Unity2020.1.16f1

準備

エディタ拡張のためのC#スクリプトは全てEditorフォルダ内に置く必要があります。
またUXML、USSファイルについては本記事ではAssets/Editor/Resources以下に置くことにしましょう。
Assetsフォルダ内にEditorフォルダを作成し、Editorフォルダ内にScriptsフォルダとResourcesフォルダを作成して下さい。

Assets
└─ Editor
     ├── Resources
     └── Scripts

作ってみる

1.まっさらなEditorWindowを作る

まずは最も簡単なものから始めることにして、「中身が空っぽのEditorWindow」を作ってみましょう。
次のようなC#のスクリプトをEditor/Scriptsフォルダ内に作成して下さい。

using UnityEditor;

// EditorWindow を継承したクラスを作成
public class Practice : EditorWindow
{
    // メニューバーに"Practice"という項目と、その中に"Open"という項目が新たに作られ、
    // その"Open"をクリックするとShowWindow()が実行される。
    [MenuItem("Practice/Open")]
    public static void ShowWindow()
    {
        // EditorWindowを作成
        // 型引数にはこのクラスを入れる
        GetWindow<Practice>("Title"); // タイトルを指定
    }
}

そしてメニューバーで
MenuBar.png
でOpenをクリックすると、

ヌっとこのような中身が空っぽのEidtorWindowが出現します。
タイトルも反映されてますね。

ちなみにここではまだUI Elementsの機能を使っていません。

2.Labelを作る

次に先程作成したEditorWindowに"hogehoge"というLabelを1つだけ表示してみましょう。

作るもの

ここでようやくUI Elementsを使うことになります。

using UnityEditor;
using UnityEngine.UIElements; // UIElementsを使うのでusingする

public class Practice : EditorWindow
{
    [MenuItem("Practice/Open")]
    public static void ShowWindow()
    {
        GetWindow<Practice>("Title");
    }

    // 有効になった時に実行される
    private void OnEnable()
    {
        // ラベルのVisualElement
        var label = new Label("hogehoge");

        // EditorWindowのrootの子としてlabelを追加
        rootVisualElement.Add(label);
    }
}

先程のPracticeクラスに上記のようにOnEnable関数を追加して下さい。

rootVisualElementとは?

UI Elementsでは、全てのUI(ラベル、ボタン、トグル、スライダー、ボックス...)はVisualElementというクラスを継承したクラスによって表されます。
そしてVisualElementは他のVisualElementを子として持つことができ、VisualElement同士で親子関係を形成します。
例えばVisualElement1VisualElement2VisualElement3を子として持っていて、VisualElement2VisualElement4を子として持っていて... というようにVisualElementのインスタンス同士で木構造が作られます。
それら複数のVisualElementによって作られた木構造はVisualTreeなどと呼ばれています。
そしてこのVisualTreeの「根」となっているVisualElementrootVisualElementです。(上の例だとVisualElement1)
EditorWindowにUIを表示したい場合はこのrootVisualElementに子として追加する必要があります。
今回の例ではrootVisualElementLabelのインスタンスを子として追加しています。

UXMLファイルを作成する

この例ではC#のみでラベルを定義していましたが、UXMLでも行うことができます。

まず Editor/Resources 内で Create -> UI Toolkit -> UI Document で"Practice"という名前のUXMLファイルを作成しましょう。

生成されたコードの中に

<engine:Label text="hogehoge"/>

を挿入して、

<?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"
    xsi:noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd"
>
    <!-- ラベルの作成 -->
    <engine:Label text="hogehoge"/>
</engine:UXML>

のようにしてみましょう。

そしてC#のコードを次のように変更します。

Practice.cs
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public class Practice : EditorWindow
{
    [MenuItem("Practice/Open")]
    public static void ShowWindow()
    {
        GetWindow<Practice>("Title");
    }

    private void OnEnable()
    {
        // UXMLファイルを読み込む
        var visualTree = Resources.Load<VisualTreeAsset>("Practice");

        // UXMLで定義したVisualTreeを生成し、そのrootとしてrootVisualElementを設定
        visualTree.CloneTree(rootVisualElement);
    }
}

さっきと同じようにメニューバーからPractice > Open をクリックすると次のように同様に"hogehoge"というLabelが1つだけあるウィンドウが表示されます。

このようにUIの構造に関する情報をC#ではなくUXMLで定義することができます。

3.複数のLabelを作る

複数のLabelを表示してみましょう。

C#のコードはさっきと同じで、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"
    xsi:noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd"
>
    <!-- 複数のラベルの作成 -->
    <engine:Label text="hogehoge"/>
    <engine:Label text="あいうえお"/>
    <engine:Label text="ABCDE"/>
    <engine:Label text="12345"/>
    <engine:Label text="テストテスト"/>
</engine:UXML>

このように複数のLabelを表示できます。

4.Boxを作る

今作った複数のLabelをBoxで囲ってみましょう。

<?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"
    xsi:noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd"
>
    <!-- Boxを作成 -->
    <engine:Box >
        <engine:Label text="hogehoge"/>
        <engine:Label text="あいうえお"/>
        <engine:Label text="ABCDE"/>
        <engine:Label text="12345"/>
        <engine:Label text="テストテスト"/>        
    </engine:Box>
</engine:UXML>

先程のUXMLをこのように変更し、5つのLabelをBoxの子に設定すると、これらのLabelを囲むBoxが作られます。

5.Buttonを作る

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"
    xsi:noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd"
>
    <engine:Button text="OK"/>
</engine:UXML>

するとこのようなButtonが作られます。

この時点ではButtonをポチポチ押すこと自体はできますが、押しただけでは何の処理も実行されません。

Buttonのクリック時の処理を登録する

UXMLの方を次のように少しだけ変更し、先程作成したButtonに名前を設定しましょう。

<?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"
    xsi:noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd"
>
    <engine:Button text="OK" name="OKButton"/> <!-- nameを設定する -->
</engine:UXML>

name="OKButton"で名前を設定しています。

そしてC#の方を次のように変更して下さい。

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public class Practice : EditorWindow
{
    [MenuItem("Practice/Open")]
    public static void ShowWindow()
    {
        GetWindow<Practice>("Title");
    }

    private void OnEnable()
    {
        var visualTree = Resources.Load<VisualTreeAsset>("Practice");
        visualTree.CloneTree(rootVisualElement);

        // 型と名前を指定してButtonを取得
        var button = rootVisualElement.Q<Button>("OKButton");

        if (button != null)
        {
            // Buttonを押した時の処理を登録
            button.clickable.clicked += () => Debug.Log("OK");
        }
    }
}

はい、これでOKButtonを押したときに

このようにログが出るはずです。

6.Buttonのサイズを変更する

今作ったButton、ちょっと横に長過ぎる感じがするのでサイズを変更しましょう。

まずはUXMLを次のように変更し、先程作成した"OKButton"に対して新たにclassを定義します。

<?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"
    xsi:noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd"
>
    <engine:Button text="OK" name="OKButton" class="OKButton"/> <!-- classを定義する -->
</engine:UXML>

そして、Editor/Resources 内で Create > UI Toolkit > Style Sheet により"PracticeStyle.uss"を作成して下さい。

テンプレートのコードを削除し、次のように変更して下さい。

.OKButton {
    width: 50px;
    height: 50px;
}

C#のコードにも次のように、USSファイルを読み込んで設定する処理を追加して下さい。

Practice.cs
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public class Practice : EditorWindow
{
    [MenuItem("Practice/Open")]
    public static void ShowWindow()
    {
        GetWindow<Practice>("Title");
    }

    private void OnEnable()
    {
        var visualTree = Resources.Load<VisualTreeAsset>("Practice");
        visualTree.CloneTree(rootVisualElement);

        // USSファイルを読み込む
        var styleSheet = Resources.Load<StyleSheet>("PracticeStyle");
        // USSファイルをVisualTreeに設定
        rootVisualElement.styleSheets.Add(styleSheet);

        var button = rootVisualElement.Q<Button>("OKButton");

        if (button != null)
        {
            button.clickable.clicked += () => Debug.Log("OK");
        }
    }
}

設定完了です。ウィンドウを開くと...?

Buttonのサイズがちゃんと変わってますね。USSで設定したように、縦50px、横50pxになっているはずです。

USSの意味

今回用いたPracticeStyle.uss

.OKButton {
    width: 50px;
    height: 50px;
}

は"OKButton"という名前のクラスが付いているUIの横と縦の長さを50pxにする」という意味になります。

ここでは.OKButton.から始まっているのですが、.で始まる場合はその後に続けて書くのはUXMLで定義したクラス名でなければなりません。

他にもパターンがあり、#から始まる場合はUXMLで定義したVisualElementのnameを続けて書く必要があります。
今回の場合はVisualElementのnameも"OKButton"だったので

#OKButton {
    width: 50px;
    height: 50px;
}

これで同様にスタイルが適用されます。

また、.#も付けない場合はUIのC#におけるクラス名を書く必要があります。今回の場合はButtonなので

Button {
    width: 50px;
    height: 50px;
}

となります。
ただし、他にもButtonが存在する場合はそれらにも同じスタイルが適用されてしまうので注意が必要です。

UIのスタイルを変える場合はこんな感じでUSSで設定することができます。他にも文字の色やフォントサイズを変えたりと色々なことができるようです。(Unity - Manual: Styles and Unity style sheets)

まとめ

UI Elementsでは、UXMLでUIの構造を、USSでUIのスタイルを、C#でロジックをそれぞれ定義することによりUIを作成することができます。

参考にさせて頂いたサイト

本記事を執筆には以下のサイトを参考にさせて頂きました。ありがとうございました。

最後に

DeNA では今年、以下の 3種 のアドベントカレンダーを書いてます!それぞれ違った種類なのでぜひ見てみてください。

この記事を読んで「面白かった」「学びがあった」と思っていただけた方、よろしければ Twitter や facebook、はてなブックマークにてコメントをお願いします!
また DeNA 公式 Twitter アカウント @DeNAxTech では、 Blog記事だけでなく色々な勉強会での登壇資料も発信してます。ぜひフォローして下さい!
Follow @DeNAxTech

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

ムービー再生中にFPSを落とさせない工夫

はじめに

本記事は、サムザップ #1 AdventCalendar 2020の12/9の記事です。
昨日の記事は@kanasaki_kenjiの「最高の振動をデザインするための下準備〜UnityからCore Haptic FrameWorkを使ってみた〜」です。

導入きっかけ

私は、Unityを使ったゲーム開発をしております。
インゲームなどの60FPSをキープしたい、そしてプレー中にムービーを流したいなどの要望を受けました。
私が担当したプロジェクトでは、ムービーを再生するのに利用しているプラグインとして
CRI Manaを利用していました。

CRI Manaにはムービーをシームレスで再生するという機能などいくつかありますが、
低スペックの端末だとシームレスの再生の切り替えのタイミングでコマ落ちすることが調査して判明しました。
そこで今回どうやって工夫したかを説明したいと思います。

対処方法

いくつか試してみてので、その時のことを書きます

  • アセットバンドルとして登録されているムービーを一つに連結してしまう

こちらであれば簡単だなと思って実際にやってみたのですが、
これだとファイルサイズのでかいムービーデータになってしまい
ダウンロードが遅すぎるという欠点がでました

  • アセットバンドルとして登録されているムービーを分割した状態で並列ダウンロードさせて連結させる
        /// <summary>
        /// Save(デバイスで読み書きできるパスにセーブする)
        /// </summary>
        /// <param name="index">index</param>
        /// <param name="totalbyte">分割されたファイルのトータルバイト数</param>
        /// <param name="usmFileName">ムービーファイル</param>
        /// <param name="outputFilePath">出力先ファイルパス</param>
        /// <returns></returns>
        private async UniTask Save(int index, long totalbyte, string usmFileName, string outputFilePath)
        {
            if(File.Exists(outputFilePath))
            {
                FileInfo fileInfo = new FileInfo(outputFilePath);
                // 端末のファイルとコピー元のサイズをチェック
                if(fileInfo.Length == totalbyte)
                {
                    //同じなので終了
                    return;
                }
                // 初回のチェックの場合ファイルを削除しておく
                if(index == 0)
                {
                    File.Delete(outputFilePath);
                }
            }
            TextAsset asset = assetLoader.LoadResource<TextAsset>(usmFileName);
            byte[] bytes = asset.bytes;
            // ファイル書き込み
            using (var stream = new FileStream(outputFilePath, FileMode.Append, FileAccess.Write))
            {
                stream.Write(bytes, 0, bytes.Length);
            }
        }

こちらのSaveメソッドをusmFileNameの数だけ回し
それらをファイルごとに書き込むという手法をとりました。
更に端末にセーブした元を比較するのにファイルサイズを比較しないとアセットの差し替えに対応できないので
チェックし初回のチェックがついているときには、元のファイルを削除しておくという対応を加えました。
変更がなければ、生成しないという手法を採用しました。

これによりムービーが再生されてしまえば、シームレスで切り替えるという処理がなくなり
60FPSをキープすることが可能なりました。

まとめ

意外にこれするだけで、効果がかなりありました。もしも困っていたら試してみてください。
明日の記事は、@norimatsu_yusukeです。

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

【Unity】広告の導入方法

導入方法

UnityのServicesの部分を有効にしたら、自動で、「Package Manager」の「Advertisement」が有効になる。

アセットストアからのインポートは行わない。

Servicesの部分を設定する。

Advancedのチェックボックスは外す!!

image.png

パッケージのアップデート

Package Managerから、Advertisementを開いて、アップデートする。

初期化

一番初めのシーンで初期化を行います。
そのために、ゲームオブジェクトをはじめのシーンに用意して、そこで、下記のスクリプトを読み込みます。

広告初期化用スクリプト
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Advertisements;

public class AdsControl : MonoBehaviour, IUnityAdsListener
{
    /// <summary>
    /// 広告再生スキップフラグ
    /// </summary>
    public static bool adsSkip = false;

    /// <summary>
    /// 広告再生終了フラグ
    /// </summary>
    public static bool adsEnd = false;

    private void Awake()
    {
        //DontDestroyOnLoad(this);
    }

    // Start is called before the first frame update
    void Start()
    {
        // ゲームIDをAndroidとiOSで分ける
#if UNITY_ANDROID
        string gameID = "XXXXXXX";
#else
        string gameID = "XXXXXXX";
#endif
        //広告の初期化
        Advertisement.Initialize(gameID, testMode: true);

        //広告関連のイベントが発生するように登録
        Advertisement.AddListener(this);

        Invoke("ShowMovieAd", 10f);
    }

    public static bool ShowMovieAd(string adsPlacement)
    {
        //広告全体の準備が出来ているかチェック
        if (!Advertisement.IsReady())
        {
            Debug.LogWarning("動画広告の準備が出来ていません");
            return false;
        }

        //表示したい広告の準備が出来ているかチェック
        var state = Advertisement.GetPlacementState(adsPlacement);
        if (state != PlacementState.Ready)
        {
            Debug.LogWarning($"{adsPlacement}の準備が出来ていません。現在の状態 : {state}");
            return false;
        }

        // フラグ初期化
        adsSkip = false;
        adsEnd = false;

        //広告表示
        Advertisement.Show(adsPlacement);
        return true;
    }

    //=================================================================================
    //イベント
    //=================================================================================

    //広告の準備完了
    public void OnUnityAdsReady(string placementId)
    {
        Debug.Log($"{placementId}の準備が完了");
    }

    //広告でエラーが発生
    public void OnUnityAdsDidError(string message)
    {
        Debug.Log($"広告でエラー発生 : {message}");
        adsEnd = true;
    }

    //広告開始
    public void OnUnityAdsDidStart(string placementId)
    {
        Debug.Log($"{placementId}の広告が開始");
    }

    //広告の表示終了
    public void OnUnityAdsDidFinish(string placementId, ShowResult showResult)
    {
        Debug.Log($"{placementId}の表示終了");
        adsEnd = true;
        switch (showResult)
        {
            //最後まで視聴完了(リワード広告ならここでリワード付与する感じ)
            case ShowResult.Finished:
                Debug.Log("広告の表示成功");
                break;
            //スキップされた
            case ShowResult.Skipped:
                Debug.Log("広告スキップ");
                adsSkip = true;
                break;
            //表示自体が失敗した
            case ShowResult.Failed:
                Debug.LogWarning("広告の表示失敗");
                break;
        }
    }
}

他のシーンからの呼び出し

別のシーンでAdsの初期化を実行していて・・・
その後、オブジェクトは破棄されている場合でも、スクリプトは実行されているので、Advertisementクラスを呼び出すことができる。

参考URL

https://kan-kikuchi.hatenablog.com/entry/Unity_Monetization_SDK

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

【Unity】アニメーションによってポジションやローテーションがずれる時(自分用)

“shot” 2020-12-08 13.12.30.png

・Root Transform Rotation
・Root Transform Position(Y)
・Root Transform Position(XZ)

の「Bake Into Pose」にチェックを入れたらアニメーションを連続再生してもポジションやローテーションがずれることはなかった。

Unity参考リファレンス

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

UnityプロジェクトでPerforceを扱う方法

Applibot Advent Calendar 2020」 8日目の記事になります。
前日は @fu11mated さんの
行動ログを吐き出すバッチを作ったら便利だった話 という記事でした!

はじめに

大規模なゲームの開発においては、膨大なリソースを管理する方法についても熟慮する必要があります。リソースをバージョン管理するためのシステムは複数ありますが、私の所属しているプロジェクトではPerforceを選択しています。Perforceの選定理由としてはこちらが参考になると思います。 今回は、このPerforceを用いたUnityでの開発方法について軽くお話しさせていただきたいと思います。Perforceに対する基本的な知識がある前提でお話ししますので、ご了承ください。

クリエイターの作業フローについて

下記はUnityを用いたプロジェクトにおける作業フローを簡単に図示した例になります。
image.png
ここでポイントとなるのが、この辺↓です。
この辺.png

この図の場合では、クリエイターはDCCツール(Mayaなど)で作ったアセットをUnityに取り込み、Unity上で諸々の設定をした後でPerforceにサブミットする必要があります。
この作業はUnityとP4V(Perforceを扱うためのGUIツール)で行うことができます。

ここで考えていただきたいのが、クリエイターは大量のアセットを裁くためにこの作業を繰り返し繰り返し行うということです。
つまり、UnityとP4Vをいったり来たりするのでは無駄な作業が大量に発生してしまいます。

そこで、Unityのみでアセットに対する作業からPerforceへのサブミットまで行うことができれば作業を効率化することができます。
しかしここで問題になるのが、Unityに標準搭載されているPerforceを扱うための機能(公式の呼び方ではないのですが仮にP4Unityと呼ぶことにします)にちょっとしたクセがあることです。

P4Unityのクセというのが、「Perforceでプロジェクト全体を管理することを前提としている」ことです。
つまり、ソースコードをGit管理している状況では使えないということです。

かと言って、ソースコードをPerforceで扱うのは得策とは言えません。
と言うよりコードレビューなどできないので実質不可能です。

なのでソースコードはGit管理しつつ、アセットのみPerforce管理にしたいと考えます。
Git管理されているUnityプロジェクトの中のフォルダの一つがPerforceで管理されているという状況が理想です。

それではその状況にするための方法を説明していきます。

UnityでPerforceを扱えるようにするための設定

Unityプロジェクトの中のフォルダの一つがPerforceで管理されている状況にするためにはまずストリームのリマップ設定をする必要があります。ストリームのリマップ設定はP4Vで行います。

  1. P4Vで該当ディポのワークスペースを開きます。
  2. View→StreamでStreamタブを開きます。
  3. 該当ストリームを右クリック → Edit Stream *** を選択します。
  4. Advanced → Remappedに ... Assets/[アセットを入れるフォルダの名前]/... を設定します。 スクリーンショット 2020-12-08 10.46.32.png

これでリマップ設定ができました。

次にワークスペースのルートディレクトリをUnityプロジェクトのルートディレクトリに一致させます。(この作業はUnityでPerforceを扱いたい人それぞれが行う必要があります。)

  1. P4Vで該当ディポのワークスペースを開きます。
  2. View→WorkspaceでWorkspaceタブを開きます。
  3. 該当ワークスペースを右クリック → Edit Workspace *** を選択します。
  4. Workspace RootにUnityプロジェクトのルートディレクトリを設定します。 スクリーンショット 2020-12-08 10.54.43.png

これでUnityプロジェクトの中のフォルダの一つがPerforceで管理されている状況になりました。

最後にUnityでPerforceの設定を行います。

  1. Unityで該当プロジェクトを開きます。
  2. Edit→Project SettingsでProject Settingsウインドウを開きます。
  3. Project Settingsウインドウの左タブからVersion Controlを選択し、ModeでPerforceを選択します。
  4. Perforceの設定を入力してRecconectを押します。
  5. Connectedと表示されたら完了です。 UnityのPerforce設定.png

これでUnity上でPerforceを扱うことができるようになります。

P4Unityの操作方法

ファイルの最新化を行いたい場合の操作は以下のようになります。

  1. Projectタブの更新したいディレクトリまたはファイルを右クリック
  2. Version Control→Get Latestを選択 ファイル更新.png

このように、P4Unityを導入すると右クリックによって簡単に操作ができるようになります。

まとめ

UnityでPerforceを扱うための方法をご紹介しました。これを読んでいただければ、特に難しくないことがわかるかと思います。導入が難しくてUnityプロジェクトでのPerforceの導入を諦めてた方に、少しでもお役に立てれば幸いです。

おわりに

Applibot Advent Calendar 2020」 8日目の記事でした!
明日は @kbt_ さんです!

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

Unity 2020から自動実装プロパティのバッキングフィールドにSerializeField属性をつけるとプロパティ名が表示される

PONOS Advent Calendar 2020の10日目の記事です。
昨日は@kerimekaさんの【Android】root判定によるチート対策でした。


Unity 2019以前

C# 7.3から自動実装プロパティのバッキングフィールドに属性をつけられるようになりましたが、SerializeField属性をつけるとInspectorの表示が残念なことになってました。

public class Sample : MonoBehaviour
{
    [field: SerializeField]
    public int ValueA { get; private set; }
}

こう書くと
property-backing-field-2019.png
こう表示されてしまう。

ラベルだけ上書きするPropertyDrawerを自作したら使い勝手が良くなりそうな気がしますが、実際はうまくいきません。
そのあたりは下記の記事で説明されています。
Unityで自動実装プロパティに表示名を指定する

Unity 2020

2020からは

public class Sample
{
    [field: SerializeField]
    public int ValueA { get; private set; }
}

と書くと
property-backing-field.png
こう表示されるようになりました。

ちなみに表示名はプロパティ名ですがyamlに保存されるのはバッキングフィールドの名前です。
sample-prefab-yaml.png

結論

Unity 2020から自動実装プロパティのバッキングフィールドにSerializeField属性をつけるとプロパティ名がいい感じに表示されるようになりました?


本当はInspectorでいい感じに表示する拡張を公開する予定だったのですが、2020でチェックしていたところ何もしなくてもプロパティ名が表示されたのでやめました。


明日は@kenta-sanです!

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

Unityプロジェクトにおけるプルリクエスト時の自動テスト

本記事は QualiArts Advent Calender 2020 9日目の記事です。

昨日は @asakuranobuharu さんの QUBEという取り組み でした。
明日は @Koheinimi さんの 新規開発ノウハウのデータベース化 です。

はじめに

筆者が所属しているQualiArtsのUnityプロジェクトでは、GitHubでプルリクエストが作成されたときにJenkinsで自動的にテスト(以降、PRテスト)を行う仕組みを作っています。
この記事では、本PRテストの中で具体的にどのようなテストを行っているかを紹介します。
本PRテストではJenkinsでテストを実行していますが、他のCIツールの場合でもどのようなテストをするかのアイディアは活かせるかと思います。
UnityのバージョンはUnity2020.1で試しています。

PRテストの実現方法

本PRテストを実現するため、Jenkinsのプラグインの1つであるPull Request Builder Pluginを使っています。

導入方法に関しては、
https://qiita.com/kompiro/items/a097b3b36aa30bc751c6
が参考になります。

このプラグインを使えば、特定のリポジトリでプルリクエストが作成されたときに自動的にジョブが実行され、結果がプルリクエストにコメントの形でフィードバックされるようになります。
またテストに通ったかどうかはプルリクエストのチェックステータスにも反映されます。
テストが通ると次の図のように表示されます。

image.png

テストに通らなかったら修正を加えてプッシュすることになりますが、修正がプッシュされるとまた自動的にテストが実行され結果が返ります。
テストに通ったら(加えて、所定の人数の承認が得られたら)マージ可能というルールにしています。

PRテストの内容

それでは早速、本PRテストで行っている具体的なテストの内容(の一部)を紹介していきます。

コンパイルエラー、警告のチェック

まず、プルリクエストのブランチにおいてプロジェクトをUnityで開き、コンパイルされた結果エラーまたは警告が出たらテストが失敗になるようにしています。
そのためにはコンパイルエラーと警告を抽出する必要がありますが、うまく抽出する方法が分からなかったのでUnityのログから無理やり抽出するという方法をとっています。

具体的には、まず次のようにUnityをコマンドラインから実行し、プロジェクトを開いて Editor.log にログを出力します。
(コマンドはイメージなので実際とは多少異なります。)

$ /path/to/Unity -batchMode -quit -logFile Editor.log -projectPath TestProject

コンパイルエラーや警告の例を示すために、次のようなコードをプロジェクトに入れておきました。

HogeScript.cs
using UnityEngine;

public class HogeScript : MonoBehaviour
{
    void Start()
    {
        int unused;
        Debug.Log(fuga);
    }
}

コンパイルエラーや警告が起きると、次のようなログがEditor.logに出力されます。

Assets/HogeScript.cs(8,19): error CS0103: The name 'fuga' does not exist in the current context
Assets/HogeScript.cs(7,13): warning CS0168: The variable 'unused' is declared but never used

fugaという名前は存在しないというエラーと、unusedという変数は使われていないという警告です。
ログからこのようなエラーや警告を正規表現等を使って抽出し、何らかのエラーや警告が出ていたらテスト失敗とします。

実際にエラーや警告が発生してテストが失敗した場合、次の画像のようにプルリクエストのコメントにメッセージを載せます(画像は一部改変しています)。

ただし、警告に関しては外部ライブラリなどで引っかかってしまうことがあるので、テストを失敗にする対象となるファイルはプロジェクト固有のファイルに絞っています。
警告すら許さないというのはやり過ぎに思えるかもしれませんが、asyncメソッドをawaitしていないパターンなどの発見に役立っています。

コーディング規約のチェック

少し前の記事になりますが、筆者は過去に次のような記事を投稿しました。
C#静的解析によるコーディング規約チェッカーを作った話 | CyberAgent Developers Blog

この記事の中で、NRefactoryというライブラリでC#コードを静的解析し、命名規則などのコーディング規約をチェックする仕組みを紹介しています。
現在NRefactoryはメンテナンスされておらず、C#コード解析ライブラリとしてはMicrosoft製のRoslynが標準になっているため、この記事で実装していたコーディング規約チェッカーもRoslynに移行しています。

そのRoslyn製のコーディング規約チェッカーを本PRテストの中に入れており、対象のプルリクエストにおいて差分のあるファイルに対してのみ構文解析によるチェックを行っています。
ソリューション全体を読み込んで意味解析まで行えば、より柔軟で詳細なチェックも可能になるはずですが、そちらは今後の課題としております。

Unity Test Runnerによるテスト

Unityには Unity Test Runnerという、単体テストを実行するための仕組みが備わっています。
本PRテストでは、このUnity Test Runnerによるテストも走らせています。

Unity Test Runnerに関して詳しくは、
【Unity】Unity Test Runner(Test Framework)入門 - インストールから基本的な使い方・注意点まとめ - LIGHT11
などを参照ください。
Unity Test RunnerにはEdit ModeテストとPlay Modeテストが存在し、それぞれUnityエディタをPlayしていない状態でのテストとPlayしている状態でのテストを意味します。

例えば、次のようなコードを記述すればUnity Test Runnerのウィンドウからテストを実行できます。

NewTestScript.cs
using NUnit.Framework;

namespace Tests
{
    public class NewTestScript
    {
        [Test]
        public void HogeTest()
        {
            Assert.IsTrue(false); // テスト失敗!
        }
    }
}

↓Unity Test Runnerのウィンドウ

このUnity Test Runnerによるテストですが、コマンドラインから実行することもできます。

# Edit Modeテスト
$ /path/to/Unity -batchMode -projectPath TestProject -runEditorTests -editorTestsResultFile EditModeTestResult.xml
# Play Modeテスト
$ /path/to/Unity -batchMode -projectPath TestProject -runTests -testPlatform PlayMode -testResults PlayModeTestsResult.xml

上記のコマンドにより、Edit ModeテストとPlay Modeテストを実行でき、実行結果がコマンドで指定したファイル(EditModeTestResult.xmlPlayModeTestsResult.xml)に出力されます。
このコマンドの結果をもとにテスト結果を判定し、失敗した場合は具体的にどの項目で失敗したかの情報をエンジニアに提供します。

Unity Test Runnerによる単体テストの中身

Unity Test Runnerでテストを行うためには、当然ながらどのようなテストを行うか自分たちで記述する必要があります。
ゲームクライアント開発では特に顕著な問題だと思うのですが、テストを書くこと自体が高コストであったり、仕様変更が多かったり、そもそも処理が複雑に絡み合いすぎてテストが意味をなす状況が少なかったりという問題あるため、テストを上手に書く難易度は高いのが現実です。

筆者が所属しているプロジェクトでは、下記のような条件を満たしたテストが記述されているイメージです。

  • (ほぼ)単純なロジックのみで閉じた処理
  • 今後何か手を入れたときにバグる可能性がありそうで不安な処理
  • エンジニアにテストを書くモチベーションと時間がある

具体的に実装されているテストとしては、文字列の単純な変換ロジックや、ゲーム内のパラメータ計算や、アセットのロード処理などがあります。

Missing参照を持つプレハブの検出

本PRテストでは、Missing参照を持つプレハブの検出も行っています。
ここでMissing参照とは、プレハブにシリアライズされた値の中で参照先が不在になってしまっているもののことを指します。
例えばローカルで作業していたときには画像を参照していたがその画像をコミットし忘れたときなどにMissing参照が生まれます。

次の画像の赤枠で囲まれた部分のようにインスペクターでMissingと表示されている状態がMissing参照になっている状態です。

Missing参照の検出方法は、
【Unity】2018.3 で Missing References が検出できなくなった時の対処方法 - コガネブログ
を参考にしています。
この記事にあるように、Unity 2018.3以降では従来の検出方法ではうまくいかなくなる可能性があるので注意が必要です。

本PRテストでは、このMissing参照の検出を、対象のプルリクエストで差分のあったプレハブ(内の全てのコンポーネント)に対して行っています。
差分のあったプレハブに対してのみに限定しているのはPRテストの実行時間を短縮するためですが、これだけではプロジェクト内のMissing参照を完全に検出することはできません。
そのため、プロジェクト内のMissing参照を完全に検出したいということなら、毎日1回バッチ処理のような形でプロジェクト全体のプレハブを対象にしたMissing参照の検出を行って結果をSlack等に通知するような仕組みを作ると良いと思います。

PRテストの効果

これまで述べてきたPRテストですが、これによって実際に多くの良い効果が得られていると感じています。

まず第一に、コードレビューのコストが減るということです。
特に、コーディング規約違反の指摘というのは機械的で面倒な作業です。
本PRテストでは、機械的に分かる部分に関しては自動的に指摘してくれるため、人間はより本質的で高度なレビューに時間を割くことができます。
また、単純なミスに関しては人間から指摘されるより機械に指摘された方が、修正する側も余計なストレスもなく済むという利点もあります。

別の効果として、マージされたものの実際に手元に落としてきたら動かないというような事態もかなり減らすことができます。
普通は手元で動作確認をしてからプルリクエストを作るはずなので、コンパイルエラーが起こることなんて無いのではないかと思われるかもしれませんが、何らかのコミット漏れやちょっとしたミスによりコンパイルエラーが起きてしまうことは意外とあります。
もしそのような状態のものが開発の本流のブランチにマージされてしまうと、多くのエンジニアの作業を一時的に止めてしまったり、アプリのビルドが滞ってしまったりということになります。
PRテストによって、動作に関してのある程度の担保がされているので、開発をスムーズに進められていると感じます。

おわりに

本記事では、UnityプロジェクトにおけるPRテストの事例を紹介しました。
Unityプロジェクトにおいて具体的にどのようなPRテストを行えば良さそうかの参考にして頂ければと思います。
また、この記事では紹介されていないもので、このようなテストを行うと効果があるといった情報を頂けると非常に嬉しいので、ぜひコメント等で教えて頂ければと思います。

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

音ゲーのタイミング計算

音ゲーを作ったのでその時の知見をまとめておきます

前提として今回は一定の速度でノーツが移動します
beatmaniaIIDXのSOFT LANDING ON THE BODYのようなソフランは考慮していませんが、最初からノーツスピードが遅く流れてくる太鼓の達人のようなソフランは対応できます

ノーツの移動
update(){
 this.gameObject.transform.Translate(0, "ノーツの移動速度" * Time.deltaTime, 0);
}

//ノーツの移動速度の計算方法
speed  = "曲のノーツスピード" * "プレイヤーが設定したノーツスピード" * "曲のBPM" / 60;

原型
座標 = "曲のBPM" / 60 * "ノーツをタップする時間" * "曲のノーツスピード" + "曲のオフセット";
プレイヤーが設定をいじる場合
座標 = "曲のBPM" / 60 * "ノーツをタップする時間" * "曲のノーツスピード" * "プレイヤーが設定したノーツスピード" + "曲のオフセット" + "プレイヤーの設定したオフセット";
//この際、曲のノーツスピードは基本値を1にしておくこと

このように座標の計算ができる

おまけ

スコア計算について

最終的に1000000になるように1ノーツあたりのスコアを計算して足していく方法が最初に思いついたが、細かすぎる計算はあまりきれいに出ない
何なら1000000にならないことが多々あった

別の方法として割合で計算する方法がある

score = (int)((float)1000000 * (((((float)"最高判定の数" * (float)2) + (float)"ひとつ下の判定" )/ ((float)"すべてのノーツ数" * (float)2))));

参考元

小節数から再生時間を計算する

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

xR開発をしたい!Unity未経験者のための学習ロードマップ

はじめに

なぜこの記事を書くのか

私は2020年4月、IT初心者の状態からARやMR、VRの分野に興味をもち勉強を始めました。プログラミング初心者なので流行りのスクールに通うか悩みましたが、独学の道を選びました。学習をはじめて半年たった今は、目標だったxRのお仕事に携わることができています。独学でも、できるだけ効率的に、ローコストで学習するためのロードマップをこの半年間を振り返って作成したので共有します。

目次

1.この記事を読んだ方がいい人
2.Unity学習ロードマップの全体像
3-1.インタラクティブ性のあるxRを制作したい場合の学習ロードマップ
3-2.炎・かめはめ波のような流動的な表現ができるようになりたい場合の学習ロードマップ
4.Oculus Questなどのハードデバイスにbuildする方法を調べる

1. この記事を読んだ方がいい人

全くプログラミングをやったことがない人で、Unityを用いたゲーム開発や、xR開発をやってみたい!
という人を想定しています。
筆者は文系大学卒で、プログラミングは一切やったことがありませんでした。(Excelはできる)

2.Unity学習ロードマップの全体像

3Dモデルを自由に動かすxRを開発したいのか、ライブ演出や炎のような流動的な表現をしたいのかで、学習の時間を費やす部分が変わります!
無題_3.jpg

3-1.ホログラムを自由に動かしたり、xRのゲームを制作したい場合

ロードマップ
1. Unityに慣れる
2. C#の学習をする
3. Dotweenの学習をする
4. オリジナル作品をつくる

インタラクティブなxRを開発したいと思ったら、プログラミングが必要です。Unityでは、C#言語で開発を行います。
c#言語の学習でオススメなのが、UdemyのUnity3D入門の決定版!RPG開発の基本をUnityインストラクターと共に進めるハンズオンコース[スタジオしまづ]です。

[スタジオしまづ]の動画で覚えたほうがよいポイント
・Unityのインストール方法
・Unityの基本的な機能
・C#の基本
・3Dモデルを動かす方法
・クラスの作成方法(スクリプトはどのような単位で作成しているか)
・UIの作成方法

「スタジオしまづ」は他にもいくつか講座があり、Unityの基本やC#の基本の解説は他の講座にも付いているようなので、興味のあるテーマがあれば他の講座で学習するのもよいと思います。

次に、Dotweenというアセットについて学習すると、オブジェクトにアニメーションをつけることができて楽しいと思うので、ぜひググってみてください!

ここまでやったら、もうオリジナルの開発ができると思います!
当然のようにわからないことが出てくると思いますが、Qiitaやteratailに質問すると教えてくれる優しい方がいます。

4.Oculus Questなどのハードデバイスにbuildする方法を調べるに進みましょう。

3-2.炎、かめはめ波やライブ演出のような表現がやりたい場合

ロードマップ
1. Unityに慣れる
2. パーティクルの学習をする
3. オリジナル作品をつくる

まずは、Unityというソフトウェアをさわってみましょう。このステップでオススメなのはUdemyのユニティちゃんが教える!初心者向けUnity講座です(無料!)。

C#の解説もしてくれますが、やらなくてもいいと思います。なんとなくUnityを触ったと思ったら、次のステップに行きましょう!

「ユニティちゃんが教える!初心者向けUnity講座」で覚えたほうがよいポイント
・Unityのインストール方法
・Unityの基本的な機能
・3Dモデルはどのように表示するのか
・スクリプトファイルはどのように作成するのか

次に、チュートリアルを見ながら、炎やかめはめ波のようなものとつくります!
炎・かめはめ波のような表現は、パーティクルシステムで作られています。
パーティクルシステムを操作するには、3つの方法があります。
1.Shurikenを使用する
2.シェーダー言語を使用する
3.Shader Graphを使用する
順に説明します。

1.Shurikenを使用する

Shurikenは、パーティクルと呼ばれる小さな2Dの画像をアニメーション化することによって、炎などの流体をシュミレーションするシステムの名前です。Unityに標準で備わっています。プログラミン言語は使いません。上にあげた3つの方法の中では、一番とっつきやすいです。
とりあえず触ってどういうものか知りたいという方はSTYLYに掲載されているこちらの記事の記事をみながら、「波」を作成してみるのがおすすめです!
本格的にやってみたい!と思ったら、
Unityゲームエフェクトガイドの本で学習するのがおすすめです!

2.シェーダ言語を使用する(最初はオススメしない)

HLSLというシェーダ言語と、HLSL言語とUnityを橋渡しするための「ShaderLab言語」という言語を使用して、「シェーダ」を作成します。
シェーダは、カラー情報や、UVスクロール、テクスチャなどの情報をもっていて、描画方法を調整することができます。
シェーダ言語で作成したシェーダとParticle(Shuriken)を組み合わせることで様々な表現ができます。

プログラミング初学者には泥沼です。筆者もまだ全然使いこなせていませんが、初心者の学習のステップとしては
1.描画の仕組みを理解する
2.シェーダー言語を理解する(沼)
になるのかと思います。
1.描画の仕組みを理解するには、Unity Japanのyoutubeシェーダを書けるプログラマになろう#1シェーダを理解しようがオススメです!
2に関しては、つよいエンジニアの方のブログを漁るのが良さそうだなあという気が。。。
チャレンジしたい方はSTYLYに掲載されているこちらの記事の記事をやってみると良いかもしれません!

3.Shader Graphを使用する

シェーダー言語を用いて作成していたシェーダをノードベースで作成できるようにしたものがShader Graphです。
とっかかりは、Unity道場のyoutubeがオススメです。Shurikenと組み合わせて使います。シェーダはShurikenの機能だけで作成するよりも高度なものが作成できるといわれています。
ShaderGraphもしっかり学習したいと思ったら、Unityゲームエフェクトガイドの本がおすすめです。

パーティクルについて学習できたら、チュートリアルとは少し違う値や色にするなどしてオリジナル作品を作りましょう!

4.Oculus Questなどのハードデバイスにbuildする方法を調べる

わーい、開発したものを実際に動かしてみましょう!すごく感動します!

Oculus Questはこりんさんのブログに神様のようなbuild記事があります。
HoloLensはMicrosoftのチュートリアルがわかりやすかったです。
iPhoneにビルドしたい人はARKit、androidにbuildしたい人はARCoreで調べてみましょう!

10.おわりに

・ここに書いてる内容しか勉強してないのかな?
→ほかにもたくさんやりました。。本もたくさん買ったし、Unityの公式チュートリアル、ほかのUdemy講座もやりましたが、一番効果があったなと感じたものをピックアップして記載しました!
・なぜUnityを選択して学習したのかな?
→2020年4月時点で所有していたPCのメモリが8GBでUnrealEngineに耐えられる気がしませんでした!Unityも重たかった。私はHoloLens開発が一番やりたいので、今はWindowsPCでAsusのこのモデルを使っています
・3Dモデルはどのように勉強するのかな?
Unity Asset Storeというところで、3Dモデルやエフェクトを配布していて、無料でダウンロードできるものもあるので、必ずしもモデリングが自分でできる必要はないとおもいます!
ですが、自分でモデルを作成したい場合はblenderがオススメです。
無料だし、youtubeにおすすめの動画があります。(ワニさんのblender講座

(地味だけど私の学習成果。blenderでモデル作成もしてみたよ。)→①シーソーにオブジェクトを配置してバランスをとるゲーム②現実空間に出現させたサトシ

この記事が参考になったら、LGTMクリックいただけると嬉しいです!

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

【初学者向け】 Joystickを使ったアニメーション実装

Unityちゃんをアニメーション付きでjoystickで動かしてみましょう!
RPGとか3Dステージでプレイヤーを動かしたい時に使えると思います!

初心者向けでつくっているので細かく解説しています?

こんなんやってみます

ezgif.com-gif-maker.gif

実装手順

① UnityちゃんJoyStickをアセットストアからUnityに追加

  >>> アセットストアからアセットをUnityに追加する方法

スクリーンショット_2020-12-05_10_57_20.png

この状態になっていればOKです!

② Unityちゃんの設定

まずはUnityちゃんから
  スクリーンショット_2020-12-05_13_40_18.png
インポートしたUnityちゃんのフォルダを展開して、その中にある「UnityChan」というPrefabをHierarchyビューにドラッグ&ドロップしましょう!

Animatorを覗くといっぱいAnimationが入っているのがわかります!
このようにAssetにはすでにアニメーションがいくつも入っているんです!

今回はここに入ってるアニメーションを使って機能の実装をしてみましょう

③ Animatorの作成

新しくAnimatorを作って、自分好みのアニメーションを実装していきます。
スクリーンショット_2020-12-05_14_01_31.png

Projectから新しくAnimatorControllerを作成して、今回は名前を「MyAnimator」にしました

スクリーンショット_2020-12-05_14_04_28.png

そうしたら、「MyAnimator」をUnityちゃんを選択した状態でInspectorにあるAnimatorのControllerの中にドラッグ&ドロップします。

そうすると左にAnimatorの表示がきれいになっているとおもいます。そこに新しくAnimationを組み合わせていれます!

④Animation実装

お次は、アニメーション!
Unityちゃんは非常に便利でいっぱいアニメーションがすでに入っているのでそれを使っていきます。今回は止まった状態と歩いている状態でのアニメーションをつけていきましょう。

TestScene_-_unityChan_-_PC__Mac___Linux_Standalone_-_Unity_2019_4_8f1_Personal__Personal___Metal_-2.png

スクリーンショット_2020_12_08_1_31.png

これができたらAnimatorの中にあるAnimationのところで右クリックして「MakeTransition」をしこの二つをつなげます。
その後の設定は以下の画像の通り。
スクリーンショット_2020_12_08_1_38.png

これで終わり!

⑤JoyStickをいれます

スクリーンショット_2020_12_08_1_45.png

JoyStickPackに入っているものは全部で4種類!
違いについて、詳しくはこちら

今回はFixedJoyStickをいれます(個人的な好み)

これでUnity側の準備は終わり!

⑥コードをかく

PlayerScriptとというのを使って以下のコードを書いていきます。

PlayerScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerScript : MonoBehaviour
{
    public FixedJoystick joy; // ここは入れたJoyStickの種類によって変えてください!
    public float speed; //Unity側で自由に調整して下さい
    Animator ani;

    float degStop;

    // Start is called before the first frame update
    void Start()
    {
        ani = this.gameObject.GetComponent<Animator>(); //UnityChanについているAnimatorを取得
    }

    // Update is called once per frame
    void Update()
    {

       Move();
    }

    void Move()
    {

        float dx = joy.Horizontal; //joystickの水平方向の動きの値、-1~1の値をとります
        float dy = joy.Vertical; //joystickの垂直方向の動きの値、-1~1の値をとります

        float rad = Mathf.Atan2(dx-0, dy-0); //  原点(0,0)と点(dx,dy)の距離から角度をとってくれる便利な関数

        float deg = rad*Mathf.Rad2Deg; //radianからdegreenに変換します

        this.transform.rotation = Quaternion.Euler(0, deg,0); //Unityちゃんの向きを先ほど取得した角度に当てはめて代入します。今回はy軸方向が回転軸になります。

        if(deg!=0) //joystickの原点と(dx,dy)の2点がなす角度が0ではないとき = joystickを動かしている時
        {
            ani.SetBool("Walk",true); //wait→walkへ
            this.transform.position += this.transform.forward  *speed* Time.deltaTime; //正面方向へプレイヤーを移動させ続ける

            degStop = deg; //停止前のプレイヤーの向きを保存
        }
        else //joystickの原点と(dx,dy)の2点がなす角度が0の時 = joystickが止まっている時
        {
            ani.SetBool("Walk",false); //walk→waitへ

            this.transform.rotation = Quaternion.Euler(0,degStop,0); //停止時のプレイヤーの動きの向きを設定
        }
    }
}

詳しくはコメントアウトに書いてありますのでよんでみてください。

⑦動きの原理

こんな感じです!シンプル!
TestScene_-_unityChan_-_PC__Mac___Linux_Standalone_-_Unity_2019_4_8f1_Personal__Personal___Metal_-3.png

⑧Unity側でjoystickを紐付け

TestScene_-_unityChan_-_PC__Mac___Linux_Standalone_-_Unity_2019_4_8f1_Personal__Personal___Metal_-5.png

speedとかもいい感じに設定してみる

プレビューするといい感じに動くと思います?

もし歩くから走るとかのアニメーションを実装したいとかあったら、(dx,dy)の値がいくつ以上だったら走るとかにコードを書き加えていくとよりそれっぽいものが実装できます!

とりあえずここまでお疲れ様でした!

終わりに

普段はGeekSalonという大学生限定プログラミングコミュニティでメンターをしております。
Unityを使ったGameアプリ開発のコースがあるので
もし興味ありましたら下のURLからぜひ!!
?詳細はこちらから?

https://bit.ly/3f8zWgJ

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

Xamarin.Android で Unity as a Library (うまくいった話)

この記事は、Xamarin Advent Calendar 2020の8日目の記事です。

はじめに

Xamarin.Android で Unity as a Library (をやろうとしてうまくいかなった話) の記事を読んで、できないことはないよなと思い、自分でも試してみることにしました。

結果

記事の手順でできました。

試した環境は以下の通りです。

  • macOS Big Sur
  • Unity 2019.4.16f1
  • Android Studio 4.1.1
  • Visual Studio for Mac 8.8.3

実機(UMIDIGI A7 Pro)で試しています。また、デバッグ実行だとUnityの画面が表示されなかったのですが、デバッグなし実行だとうまくいきました。

Screenshot_20201208-001920.png

コード

こちら
記事にあったコードを拝借させていただきました。

おわりに

どうしてうまくいかなかったのかは定かではありませんが、ひとまず、Xamarin.Androidでも、Unity as a Libraryは使えそうです。

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

複数のInspectorウインドウを一斉に操作する機能を作成する

PONOS Advent Calendar 2020の7日目の記事です。

昨日は@nissy_gpさんのNode.js+Sequelize+MySQLでプライマリーキーをUUIDにするでした。

はじめに

前回の記事で複数のInspectorウインドウを一度に開く機能を実装しました。
今回は開かれているすべてのInspectorウインドウに対して一括で処理を実行する方法を紹介します。

実装

すべてのInspectorウインドウを取得する

現在Unityエディタ上で開かれているすべてのInspectorウインドウを取得するためにはInspectorWindow.GetAllInspectorWindows()メソッドを利用します。

UnityCsReference/InspectorWindow.cs at master · Unity-Technologies/UnityCsReference · GitHub

UnityEditorにinternalとして定義されている InspectorWindowクラスの機能を直接呼び出すことはできないため、前回の記事と同様にリフレクション機能を使ってメソッド実行します。

var inspectorWindowType = Assembly.Load("UnityEditor").GetType("UnityEditor.InspectorWindow");
var mathodInfo = inspectorWindowType.GetMethod("GetAllInspectorWindows", BindingFlags.NonPublic | BindingFlags.Static);
var inspectorWindows = mathodInfo.Invoke(null, null) as EditorWindow[];

これで、開いているInspectorウインドウの配列を取得することができました。
なお、その後のウインドウ操作を行いやすいように、EditorWindowの配列として取得しています。

あとはこの配列内の要素を操作をしていけばOKです。

すべてのInspectorウインドウを閉じるサンプル

サンプルとして「開いているすべてのInspectorウインドウを閉じる機能」のコードを掲載します。

static void CloseAllInspector()
{
    var inspectorWindowType = Assembly.Load("UnityEditor").GetType("UnityEditor.InspectorWindow");
    var mathodInfo = inspectorWindowType.GetMethod("GetAllInspectorWindows", BindingFlags.NonPublic | BindingFlags.Static);
    var inspectorWindows = mathodInfo.Invoke(null, null) as EditorWindow[];

    foreach (var inspectorWindow in inspectorWindows)
    {
        inspectorWindow.Close();
    }
}

まとめ

今回紹介したInspectorWindow.GetAllInspectorWindows()メソッドを利用することで、開いているInspectorウインドウを一括で操作する機能を容易に作成することができます。
開発中、オブジェクトの比較用に頻繁に追加 / 削除されるウインドウなので、こういった機能を利用して効率的に操作していきたいですね。

明日は@nissy_gpさんです!

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