20201016のUnityに関する記事は7件です。

効率的な開発必勝法 | UWA GOT Online機能紹介

プロジェクト自身に問題を抱えているかどうか関係なく、パーフォマンス改善はどの開発チームにとってもリリース前の必修課題になります。パフォーマンスの最適化は高度な診断でボトルネックを特定必要だけではなく、先見性のある保障措置を通じて、後続の最適化工程の緊迫感とパフォーマンスリスクを緩和させる必要もあります。これがUWA GOT Onlineリリースした理由です。

UWA GOT Onlineについて

UWA GOT はUWAが提供するローカルテストツールで、開発者がローカル環境で実機テストを行うとことができる他、Editorでローカルサーバーを立てパーフォマンスデータを確認することができます。UWA GOT Onlineはローカルツールと連携し、オンラインの同期機能です。開発チームはローカルのテストデータをUWAサービスサイトにアップロードし、オンラインレポートを素早く生成することができます。
1.png
「ローカル化」と「素早く」というメリット以外に、開発者たちのパフォーマンス改善のニーズを満たすため、現在「Overview診断」、「Monoメモリ分析」、「実行時アセット診断」と「Luaパフォーマンス分析」4つの診断、テストモードを提供しています。ではそれぞれの機能を見ていきましょう。
2.png
以下は4つのモードのコア機能と特徴です。項目のリンクをクリックすれば、さらに詳細機能説明を確認できます。

CPU性能診断

全般的なエンジンモジュール診断(レンダリング、Physics、パーティクル、UI、動画とLoading、一つも欠かせない)
UWA API 提供機能(気がかりな問題点の詳細まで特定し、確認できる)

Monoメモリ診断

Mono詳細割当て情報(コードに紐づくメモリ情報は一目瞭然、 逆順機能はさらに便利)
Monoメモリリークポジショニング(メモリリークリスク?本モードですべて解決)
 

実行時アセット診断

詳細なアセット使用状況診断(主要なアセットの実行情報を詳細に表示されます)
アセットメモリリークポジショニング(自由なサンプリングで自由比較、科学的な診断)
 

Lua性能診断

LuaCPU性能診断(深く、全面的なメモリ追跡、直接ボトルネックを特定)
Luaメモリ診断(メモリ割当て過ぎやメモリリークに関わらず、全部クリア)

「ローカル化」と「素早く」という自然な利点のもと、UWA GOT Onlineはプロジェクトメンバーが迅速にテスト実行することができ、さらにユーザーフレンドリーで、使用しやすい方法で開発者に表示されます。また、診断レポート中の詳細リコメンド機能で、開発・品質管理者が定期的に自分のプロジェクトのバージョンに基づいて性能追跡することができます。

もし特定なバージョンである重要な性能指標に問題を発見した場合は、タイムリーに開発チームにフィードバックし、改善や修正できます。

プロジェクトの開発は継続的に改善する必要がありますが、問題解決のよい時期ははやり問題を発見したタイミングにしかありません。こうすることで、プロジェクト後期の性能改善時間を大幅に減縮することができ、潜在的な性能リスクを低下させることにより、プロジェクト全体的な開発期間の短縮になります。

以上はUWA GOT Online機能の概要説明、もっと知りたい方はjp.uwa4d.comに登録して詳細をご覧ください。

「ローカルテスト」/「 UWA GOT Online」でそれぞれのDemoレポートをご確認ください。


UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析最適化ソリューション及びコンサルティングサービスを提供している会社でございます。

UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com

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

モバイルゲーム開発におけるPBRの適用性

今回の主な話題:モバイルゲーム開発におけるPBRの適用性、レンダリングレベルの設計方法(発展編)、ハードウェアのGPU Instancingサポートの判断方法、回避する必要がある25個のDrawCall成長問題。


レンダリング

Q1:私たちはMMOゲームを開発しています。今度は、主人公(他のプレイヤーを含む)にPBR(Untiy自身の標準)を使用したいと思います。他のキャラクターやシーンでは使用しません。パフォーマンスがこの状況に耐えられるかどうかはわかりません。また、今のモバイルゲームでのPBRマテリアルの使用状況も知りたいです。

現在、モバイルゲームでPBR素材を使用するプロジェクトは、特にMMOやRPGなどのプロジェクトで大幅に増加しており、主人公、NPC、ビッグボスはすべてPBRになっていきます。

上記の状況でのみPBRを使用する場合、一般的にキャラクターが画面に占める面積は比較的小さいため、Unityエンジンに付属のPBRはパフォーマンスに大きな問題はあまりありません。ストーリーを推進する時、画面に占める面積はより大きいですが、時間は一般的に非常に短いため、全体的なパフォーマンスにほとんど影響はありません。但し、(特にローエンドデバイスで)地形などの画面に大面積を占めるオブジェクトにPBRを使用することは、UWAにはお勧めしません。これは、確かにGPUの圧力は高くなるためです。UWA Day2018でもこの問題に対して定量分析を行いました(下図のように)。開発チームが使用中にこれを注意すべきです。そして、これも自分のプロジェクト次第です、自分が設置したローエンドデバイスでのパフォーマンス表現を確認することをお勧めします。
1-1.png
最後に、Standard Shaderを使用する場合、パフォーマンスに重点を置くだけでなく、メモリへの影響にも注意を払う必要があります。この文章を参照できます:「レンダリングレベルの設計方法」


レンダリング

Q2:UWA Day 2018大会に「DrawCallの上昇を導く原因は25種あります。」と紹介してくださいましたが、具体的に何かありますと知りたいです。

バッチ処理が失敗する理由は次のとおりです。

1.Additional Vertex Streams — 追加の頂点ストリーム—オブジェクトはadditionalVertexStreamsを使用して、追加の頂点情報ストリームを設定しました。

2.Deferred Objects on Different Lighting Layers — オブジェクトは異なる照明レイヤーにあります。

3.Deferred Objects Split by Shadow Distance — 2つのオブジェクトの一方はシャドウ距離内にあり、もう一方はそうではありません。

4.Different Combined Meshes — オブジェクトは別の合併した静的メッシュに属しています。

5.Different Custom Properties — オブジェクトは異なるMaterialProperyBlockを設定しました。

6.Different Lights — オブジェクトは異なるフォワードライト(Forward Light)の影響を受けました。

7.Different Materials — オブジェクトは異なるマテリアルを使用しました。

8.Different Reflection Probes — オブジェクトは異なる反射プローブ(Reflection Probe)の影響を受けました。

9.Different Shadow Caster Hash — オブジェクトは別のシャドウキャスティングシェーダーを使用するか、異なるシェーダーパラメーター/キーワードを設定しました。これらのパラメーター/キーワードはシャドウキャスティングパスの出力に影響します。

10.Different Shadow Receiving Settings — オブジェクトは異なる「Receive Shadows」パラメーターを設定しましたか、一部のオブジェクトがシャドウ距離内にあり、他のオブジェクトは距離外にあります。

11.Different Static Batching Flags — オブジェクトは異なる静的バッチ設定を使用しました。

12.Dynamic Batching Disabled to Avoid Z-Fighting — Player Settingsに動的バッチ処理をオフにするか、現在の環境で深刻な衝突を回避するために一時的にオフにしました。

13.Instancing Different Geometries — GPU Instancingを使用して異なるグリッドまたはサブグリッドをレンダリングしました。

14.Lightmapped Objects — オブジェクトは異なるライトマップを使用しましたか、同じライトマップ内で異なるライトマップUV変換関係があります。

15.Lightprobe Affected Objects — オブジェクトは他のライトプローブ(Light Probe)の影響を受けました。

16.Mixed Sided Mode Shadow Casters — オブジェクトの「Cast Shadows」設定が異なります。

17.Multipass — オブジェクトは複数のPassを持つシェーダーを使用しました。

18.Multiple Forward Lights — オブジェクトは複数のフォワードライトレンダリングの影響を受けます。

19.Non-instanceable Property Set — instancedシェーダーにnon-instancedを設定しました。

20.Odd Negative Scaling — オブジェクトの拡大縮小は、(1、-1,1)などの変な負の値であります。

21.Shader Disables Batching — シェーダーは「DisableBatching」タグを使用してバッチ処理を閉じます。

22.Too Many Indices in Dynamic Batch — 動的バッチ処理のインデックスが多すぎます(32kを超える)。

23.Too Many Indices in Static Batch — 静的バッチ処理の組み合わせメッシュインデックスが多すぎます。OpenGL ESの場合は48k、OSXの場合は32k、その他のプラットフォームの場合は64kです。

24.Too Many Vertex Attributes for Dynamic Batching — 動的バッチ処理したいサブメッシュには900を超える頂点属性があります。

25.Too Many Vertices for Dynamic Batching — 動的バッチ処理したいサブメッシュには300を超える頂点があります。


GPU

Q3:「ES 3.0をサポートできる」と宣伝していますが、実際にはGPU Instancingをサポートしていない国産Android設備はたくさんあることを見つけました。では、ハードウェアがGPU Instancingをサポートしているかどうかをどのように判断しますか?

SystemInfo.supportsInstancingを使うことをお勧めします。

https://docs.unity3d.com/ScriptReference/SystemInfo-supportsInstancing.html


レンダリング

Q4:前回の記事「レンダリングレベルの設計方法」の続きに、更に2人の業界の有名人が彼らの見解を共有しました。

王亮:私たちのプロジェクトはまだ開発中であり、同じプロセスを経て、最終的に同様のファイルを作成しました。私の経験では、最終的に生成するのは一つのテーブルであるように感じますが、各携帯機種はこのテーブルによってデフォルト設定を提供できます。但し、これは自分のプロジェクト次第で、ハイ/ミドル/ローエンドモデル、FPSの目標、レンダリング効果の違いにより、結果は大きく違う場合があります。

私の考えを共有します。

1.先ずは、テストケースを決定することです。

メインシティーや違う戦闘スタイルの最高面数シーンを例として選択します。
2-1.png

2.キャラクターに違う戦闘スタイルの最高面数キャラを選択します。

3-1.png

3.次に、テストステージを設定して、サンプリングスクリプトを書きます。

このテストデータは、スクリプトを使用してフレーム数を記録します。
テスト方法はCheatに統合されており、指定された時間のフレーム範囲と平均フレームをテストして、パフォーマンスを大まかに説明するために使用されます。
フルモンスターレンダリング:主人公は雑魚敵を一周して、テストされた雑魚敵たちを全員集まって、同一画面で全体レンダリングを保証します。
フルモンスタースキル発動:できるだけ多くの雑魚敵を攻撃して、粒子の圧力を確認します。

4.テスターさんに例の組み合わせをテストすることをお願いします。デートを収集します。

第二回のテストレポートを選択して表示します。
実際、その間にいくつかの繰り返しがありました。テーブルの最後の列は、フレームターゲットに対して、配置を繰り返し調整して作成されたデータです。
4-1.png
5-1.png
6-1.png

5.最後に、テストデータに従って格付け表の配置を調整します。

最終的にできた配置は次のように、赤い部分は今回のテストで調整、および新たに追加した内容です。
7-1.png

そして、このテストで見つかった問題。

1.GPU関連の指標を収集してみて、Androidモデルに対して詳しく格付けをします。(Meizu M721Q CPU3G GPU1Gの場合、高画質を選択すれば20FPS未満になります。GPU指標は格付け標準を決定するのに十分ではありません。)
2.Unity DIを統合して、パフォーマンスの大きなデータを収集し、第1段階の格付け効果を観察して、FPSとメモリ/ GPU /画面サイズの相関関係を観察します。
3.Unity AutoTune SDKを統合して、格付けパラメータの動的配置を実現します。
4.Settingsインターフェースを追加し、王者榮耀を参照できます。PlayerPrefsはローカルに保存されており、ロジックを変更する必要があります。 ZhangWeiZhi、LvFujiaoをキャッチします。 (ロジック部分は完了しました。)
5.WP_02 / 03は01より頂点のみ多くなります。
A:「LOD格付けスクリプトで、鳥や木などのローエンドデバイスで非表示にできるオブジェクトをフィルタリングすることはできます。」をFに指示します。(完了)
B:直接にmeshbakerモデルで圧縮することを試します。(MeshBasker、頂点カラーベーキングプラグイン、SimpleLODリダクションなどのプラグインプロジェクトには適用ではありません。)
6.LODSettingテーブルで特定のAndroidモデルを削除し、iOSデバイスリストを更新します。(完了)

文雅:プロジェクトのLODルールも整理しましたが、問題主が言ったレンダリングレベルに限らずかもしれません。優れたLODシステムには、アーティストがアセットに対して多くのアセット格付け作業を行う必要があり、プログラムが完全なLODフレームワークと補助ツールを作る必要もあります。毎回追加の機能モジュールごとにLODを考慮する必要があります。

一、プロジェクトのLOD格付けとパフォーマンス基準を決定する方法

LOD(Level of Detail)、こちらの「D」は「Distance」ではなく「Detail」を代表します。つまり、距離に制限されなく、すべてのゲーム画面とゲーム機能の詳細は等級を付けられます。

1、パフォーマンス標準は如何やって決定しますか?

等級を付ける前に、目標(省エネルギー/正常的なゲーム体験/得意なこと)を確認する必要があります。どの型番にで実行するパフォーマンス効果と機能、およびどのパフォーマンス目標(フレームFPS、メモリ使用量、Drawcall、 同じ画面上の三角形の数など)。
◆経験のまとめ:
1)画質の表現力とパフォーマンスコストが矛盾している。
2)継続的なメンテナンスが必要であり、関する機能システムのデザインでは、異なる画質を考慮する必要があります。
3)最低画質はプレイアビリティを犠牲して、LODフレームワークの複雑さを増し、最も単純で暴力的な方法を使用して対処します。

◆互換性の問題:
1)低画質に考慮すべき問題。
8枚以上のテクスチャを定義するShaderをサポートしていない場合があります
ETC2形式のテクスチャをサポートしない場合があります
OpenGLES3.0をサポートしない場合があります
2)高画質に考慮すべき問題
PBR、線形空間をサポートする
変なバグ
3)各画質での互換性の問題を解決し、ブラックリストとホワイトリストを確立する必要があります。

2、等級づけられるディテールを見つける方法は?

◆コストの高いポイントを先に探す
後処理効果Bloom、HDR、ToneMapping、MotionBlur、DOFなど。
リアルタイムのライトとシャドウ、水面でのリアルタイム反射
PBR物理照明
Ragdoll、DynamicBonesなどの物理システム
昼と夜のサイクル、特殊効果気象システム

◆ディテールモジュールのLOD機能の考え
シーン/キャラクター/特殊効果/カメラ相関/他のシステムモジュール

二、LODモジュールの詳細化

1、シーン相関

8-1.png
◆ Shader LOD
●shader.globalMaximumLODで異なる画質のLOD値を指定します。
●Shader内に複数のSubShaderを定義して、計算とテクスチャサンプリングを1つずつ削減します。

Shader LODの使用に一つの問題があります。Propertiesで定義されたテクスチャは、低レベルのSubShaderでサンプリングおよび計算されませんが、それでもメモリを消費します。キャラクターと同じに、LODフレームワークをデザインする時に、2つのprefabを考えて、シーンをサポートする時にローエンドモデルのためにもう一つのロープロファイルシーンファイルを準備することをお勧めします。

◆制作する時にハイモデルとローモデルを合理的に使用する

◆シーンオブジェクトをLevel_1、Level_2、Level_3に等級付ける
●シーン特殊効果、シーンアニメーションオブジェクトなどが含まれ、違うレベルで表示/非表示します。
最初の頃は、距離を使用してシーン特集効果の表示を制御していました。
欠点:
1.リアルタイム監視距離のコスト
2.各シーンは、適切な距離値で別々に配置する必要があり

等級を使用して制御する方がより直接的です。LODを作成するときには、最も単純で暴力的な方法を最優先され、これでアートの仕様はそれほど複雑ではなく、後の段階で過度のメンテナンスを行う必要はありません。

◆シーン照明スイッチ
●Light / Light_Highには、リアルタイム照明がキャラクターとシーンシャドウの描画での制御が含まれます。
●シーンライトマップの切り替え

◆オブジェクトのLayer層がシャドウを決定する
●シーンファイルで設定されたCast ShadowsとReceive Shadowsはベイク用に設定されているため、保存に不便です。
●さまざまなLayerを設計してオブジェクトがシャドウを生成して受け入れるかどうかを判断します。

◆キャラクターが生成したシャドウ
●高配置時にプレーヤーとNpcキャラクターのLayerをShadowに変更し、低配置時にプレーヤー/ Npcに変更します。
●中配置時にプレイヤー自身とBossキャラクターのLayerをShadowに変更し、他のプレイヤーがPlayerに変更します。
●シャドウ層はリアルタイムシャドウを描画します。Player/ Npcはディスクを使用して足の裏にあるシャドウを描画します。

◆キャラクターがシャドウを受け取る
●高画質時のみシャドウを受け取ることをオンにする

◆トリミング距離、霧効果距離
●数値の異常を防ぐために、トリミング距離と霧効果距離がパーセンテージに応じて減らすアルゴリズムをデザインする

◆後処理効果
●全局後処理
全局後処理で後処理のON /OFFを管理し、カスタムオプションを制御します。

●シーンの後処理
シーンに「ハイ」と「ロー」の二つの後処理案を配置します。低配置にColorGradingのみ使用します。
UIおよびプロットアニメーションの後処理は、高配置のみにONする
例えば、UIで使用されるBloom、プロットで使用されるRadialBlurモーションブラーなど。

◆単一シーンの特殊効果等級
●配置表でシーンのタイプと、同じ画面に表示できる特殊効果の数やレベルを定義します。

2、キャラクター相関

9-1.png
◆キャラクター材質、シェーダー、テクスチャ
●配置表で異なる画質で異なるprefabをコールします
●A.prefabとA_Low.prefabはA.matとA_Low.matを使用します
●A_Low.matがShaderを使用して計算とテクスチャサンプリングを削減します
●A_Low.matが_MainTexを使用して低精度のテクスチャを置き換えます

◆ LODGroup
●キャラクターのモデル面数が上昇した後、ハイモデルとローモデルの2つのギアを作成し、LODGroup機能で距離次第でダウングレードします

◆ SubShader
●高配置と中配置のライトモデルの切り替え、PBRはBlinn-Phongに切り替えます

◆キャラクター部品
●部品は配置表に空と配置でき、低配置時にはバックペンダントを表示しません

◆キャラクターボーンSkin Count
●Skinがサポートする骨の最大数が2-Bones 1-Bonesに下げります
●DynamicBoneが物理システムに基づく動的ボーン効果のスイッチ
●Npc死亡動作が物理システムに基づく表現のスイッチ

◆同じ画面のキャラクター数
●異なる画質が異なる「同じ画面のキャラクター最大数」を設置します
●ロジックタスクのないクライアントNPCのスイッチ

3、特殊効果相関

10-1.png
◆天気システム
●異なる大きさの粒子システムを作成する

◆足音効果
●フットステップエフェクトのオンとオフを切り替え、プレーヤー自身や他のプレーヤーとは別に制御でき

◆ドロップ効果
●複雑な特殊効果は簡略化バージョンを作成できます

◆スキル効果
●配置表を通じて、異なる画質で異なるPrefabをコールします

◆スキル特殊効果の基準
●階段式コントロール効果のパフォーマンスコストを生成します
●ツールの支援で* _Low.Prefabを生成します
●*_Low.Prefabのパフォーマンスコストを厳密に制御します

4、他のモジュール

11-1.png
◆レンダリングの解像度
●異なるレンダリング解像度を使用し、最大解像度を1080pに制限します

◆オープンパースペクティブ
●視点の上下左右と回転
●カメラの最大距離と最小距離

◆スクリプトコントロール
ある状況では、4つの画質を切り替えることができる配置を作成する必要があり、スクリプトで制御します
●Prefab切替え
●Material切替え

5、LODに適さないシステムモジュール

◆UI
●UIのピクセルと特殊効果は、表示、非表示、最適化には適していません
●UIシーンは、ベーキングやリアルタイムのライト切替えには適していません
●UIキャラクターは、インターフェイスの重要性に応じて、高レベルまたは低レベルのどちらを使用するかを選択できます
●ロードとアンロードを管理した後、高配置を低配置のUIアセットの切り替えを考えられます

◆ストーリー


UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析最適化ソリューション及びコンサルティングサービスを提供している会社でございます。

UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com

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

【Unity】スタックトレースを有効にしてNativeArrayのメモリリークを探す

ちょっと詰まったのでメモ。
UnityのバージョンはUnity2020.2.0a。

Job SystemでNativeArrayを使うスクリプトをオブジェクトに付けてゲームを再生すると次のようなエラーが出た。

A Native Collection has not been disposed, resulting in a memory leak. Enable Full StackTraces to get more details.

コンソールのメニューのStack Trace LoggingからすべてのスタックトレースをFullに変更した。image.png

しかしエラーの表記はさっきのまま変わらず詳細は表示されない。

解決策

NativeArrayのスタックトレースはコンソールのメニューからいじれるものとは別らしい。NativeLeakDetection.ModeEnabledWithStackTraceに変更してやればよい。

SetupNativeLeakDetection.cs
using UnityEngine;
using Unity.Collections;

public class SetupNativeLeakDetection : MonoBehaviour
{
    void Start()
    {
        NativeLeakDetection.Mode = NativeLeakDetectionMode.EnabledWithStackTrace;
    }
}

エラー表示は次のようになった

A Native Collection has not been disposed, resulting in a memory leak. Allocated from:
Unity.Collections.NativeArray`1:.ctor(Int32, Allocator, NativeArrayOptions) (発生した関数名) (at (ソースのパス):(行数))

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

【echoAR】echoARのサンプルシーンを試してみた【Unity】

はじめに

今回はノンコーディングでWebARができるということでechoARというサービスを試してみます。

echoARとは

AR、VR用のクラウドプラットフォーム。
ドキュメントやチュートリアル豊富に提供されており、Unity、flutter、NodeJSなど複数のSDKも公開されている。

①ホームページにアクセス

https://www.echoar.xyz/

Start Now for Freeからアカウントを作成します。

アカウントには以下の3種類がありますので、適切なプランを選択する。
今回は個人で試すだけなので、individualsを選択しました。
価格設定に関しての詳細は公式ホームページに記載されてます。
https://www.echoar.xyz/pricing

コンソールに移動

アカウントを作成すると、登録メールアドレスにメールが届き、コンソールに移動することができます。

今回は、チュートリアルからCOVID-19ビジュアライザーの作り方をやってみます。

Githubに移動

チュートリアルをクリックすると、サンプルシーンのGithubに遷移します。
https://github.com/echoARxyz/Unity-echoAR-demo-COVID19

Unityで新規プロジェクト作成

GithubにUnityの対応バージョンが記載されていないため、とりあえずLTSで作成しますか。
ってことで2019.4.0f1で作成しました。

echoAR UnitySDKをインストール

UnitySDKをインストールします。
コンソール内からインストールできるんですね。

Unityパッケージがダウンロードできるので、
作成したUnityプロジェクトに入れます。

Githubからサンプルシーンをプロジェクトに追加

Unity-echoAR-demo-COVID19をZIPでダウンロードし、
Unityプロジェクトに追加します。

んー、エラーが出ましたね

既にありますよ!!ってCustomBehaviourからエラーを吐いてます

よく見たらサンプルシーンの方で上書きしてくださいとの記載があり、echoAR UnitySDKとサンプルシーン両方にCustomBehaviourがあったので、1つ消しました。ってことで解決。

APIキー入力

echoAR/Examplesにあるsumpleシーンを開きます。
ここで再生を押しても下記エラーが表示されます。

Key '' not found!

APIキーが見つからないということなので、
APIキーを入力しましょう。

APIキーはコンソールの上部に記載されてます。
また、アカウント登録した際に届いたメールにも記載されてます。

これをコピーし、echoARオブジェクトのAPIKeyに貼り付けましょう。

echoARにモデルを追加する

echoARブラウザに戻り、「クラウド追加」から
サンプルシーンのmodelにあるearth_obj.glbを入れます。

「どこでも」を選択し、完了する。

データを入れる

サンプルシーンのmodelにあるmetadata.csvを入れます。
この時、データに事前に追加したモデルの情報が追加されてますので、そこに追加し、モデルと紐付けます。


データの追加、削除等は公式のチュートリアルに詳しく記載されております。
https://docs.echoar.xyz/web-console/manage-pages/data-page/how-to-add-data#adding-metadata

Unityで実行する

UnityEditorで実行してみます。
無事にモデルが表示できました。

Androidで確認する

何のエラーも出ず、ビルド、確認できました。

マーカー認識でオブジェクトを表示してみる

マーカー認識には2種類あります。

名前 アクション
See on the floor マーカー認識後、認識した平面にオブジェクトを配置する
See on an Image マーカー認識後、マーカーからの相対位置でオブジェクトを配置する

登録したモデルの下記ボタンを押して、QRコードを表示します。

そのQRコードをスマートフォンで読み込むことでカメラが立ち上がります。

以下はiOS、Androidでマーカーを認識した際画像

Android See on the floor

Android See on an Image

iOS See on the floor

iOS See on an Image

Android、iOS両方で確認できました。

まとめ

individuals(無料版)で出来ること 備考
echoARのクラウド使用 モデルデータなど諸々の管理ができる。各々に割り当てられたAPIキーで取得可能
平面検知表示(WebAR) Androidはmodel-viewer、iOSはARQuickLookが起動する
マーカー表示(WebAR) 正面、鋭角からの精度は良かったが、ライブラリ未確認

平面検知表示、マーカー表示は本当にノンコーディングで出来ました。
また、公式チュートリアルやドキュメントが豊富にあったため、比較的容易に実装できました。

おわりに

今回はechoARの複数ある中の1つのサンプルを試しました。
今後もARに関する記事を書いていきたいと思います。

参考

echoAR公式:https://www.echoar.xyz/
echoAR公式ドキュメント:https://docs.echoar.xyz/
Unity-echoAR-demo-COVID19 Github:https://github.com/echoARxyz/Unity-echoAR-demo-COVID19

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

[Unity]新しいスクリプトファイルの作成場所をScriptsフォルダに変更にしたい

UnityのInspectorのAdd Componentで新しいスクリプトファイルを作成するとプロジェクト直下にファイルが作成される。

image.png

これをScriptsフォルダに自動作成されるように設定できないか調査した。

方法

https://answers.unity.com/questions/614348/change-default-script-folder.html

UnityのQAサイトで調べたところ、スクリプトの追加をInspector > Add Componentではなく、フォルダを右クリックして作成すればScriptsフォルダにファイルが作成されるようだ。

  1. Scriptフォルダを右クリック image.png
  2. Create > C# Script を選択 image.png
  3. Scriptsフォルダ直下に新しいファイルができた image.png

今後はこの方法で新規スクリプトファイルを作っていこうと思う。

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

【Unity】 iOS端末のミュージックライブラリから曲を取得して再生させる

UnityでiOS端末のミュージックライブラリの曲が必要になったのですが、参考になるサイトの情報が不足してエラーまみれになったり、記事自体が古かったので備忘録としてまとめます。

Objective-Cは、触って4日間の超初心者クオリティなので無駄なところがあるかもしれません。

AppleMusicの曲やDRMで保護がかかった曲は再生できないのでご了承ください。
(iTunesで購入した曲やCDからインポートした曲の再生は確認済み)

取得までの流れ

  1. 直接はUnityからアクセス出来ないのでObjective-Cでミュージックライブラリにアクセス
  2. Objective-Cでアクセスできても曲自体は直接取ることができないのでアプリのDocumentsフォルダ以下に曲をwav形式でエクスポート
  3. DocumentsフォルダならUnityから直接アクセス出来るのでエクスポートしたwavファイルをWWWを用いてAudioClipに変換
  4. 取得したAudioClipをAudioSourceにセットして再生!

今回はミュージックライブラリから1曲だけランダムで取得して再生するまでやっていきたいと思います。

ミュージックライブラリにアクセスしてエクスポートするObjective-Cのファイルを準備

まずは、Unityのプロジェクトを作成して、「Asset」フォルダの中に「Plugins」フォルダを作成し、その中に「iOS」フォルダを作成、そしてその中に今回は「MusicLibraryMediaPicker.mm」と言う名前のファイルを作成します。
(.mmファイルは直接作成できないと思うので一旦外部のテキストエディタでファイルを作成してドラッグ&ドロップしてインポートする方がいいと思います。)
image.png

Objective-Cファイルを操作するC#のファイルを作成

そのままでは直接Objective-Cのファイルは操作できないので今回は操作する用のC#スクリプトを「Asset」フォルダ直下に「MediaController.cs」と言う名前で作成します。

image.png

曲を再生するAudioSourceと曲名を表示するTextを作成

AudioSourceとTextを作成します。
image.png

Objective-Cの中身を作成

次はミュージックライブラリからアプリのDocumentsフォルダにエクスポートする処理を先ほど作成したObjective-Cのファイル(MusicLibraryMediaPicker.mm)に下記のソースをコピペしましょう。

こちらのサイトを参考にして私がエラー&バグを修正+加筆したソースを載せます。記事が8年前とかなり古めですがとても参考になりました。
(そのままではエラーが出るかもしれないですが今は放置で大丈夫です)

MusicLibraryMediaPicker.mm
# import <Foundation/Foundation.h>
# import <MediaPlayer/MediaPlayer.h>
# import <AVFoundation/AVAudioFile.h>
# import <AVFoundation/AVAudioEngine.h>
# import <AVFoundation/AVFoundation.h>
# import <AVFoundation/AVAssetReader.h>
# import <AVFoundation/AVAssetWriter.h>

extern "C" {

    // プロパティ
    BOOL do_export;
    long song_id;
    NSString* song_name;

    // 関数のプロトタイプ宣言
    void exportRandomToItem();
    long getSongId();
    char* getSongName();
    BOOL getDoExport();


    /***************************************************
     * MPMediaItemをwav形式でDocumentフォルダに出力する関数
     * @param item 出力したい曲のMPMediaItem
     * @return 正しく出力できたらYESを返す
     ***************************************************/
    BOOL exportItem (MPMediaItem *item) {
        // エクスポートフラグを立てる
        do_export = YES;
        // エラー表示用の変数
        NSError *error = nil;
        // WAVEファイルのフォーマット
        NSDictionary *audioSetting = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [NSNumber numberWithFloat:44100.0],AVSampleRateKey,
                                      [NSNumber numberWithInt:2],AVNumberOfChannelsKey,
                                      [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
                                      [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
                                      [NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey,
                                      [NSNumber numberWithBool:0], AVLinearPCMIsBigEndianKey,
                                      [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
                                      [NSData data], AVChannelLayoutKey, nil];
        // 指定ファイルまでのパス
        NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
        // ↑の*urlからメディアデータへのアクセス用リンクを作成
        AVURLAsset *URLAsset = [AVURLAsset URLAssetWithURL:url options:nil];
        if (!URLAsset) {
            do_export = NO;
            return NO;
        }
        // ↑で作ったリンクをもとに指定されたアセットからメディアデータを読み取るアセットリーダーを返します。
        AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:URLAsset error:&error];
        if (error) {
            do_export = NO;
            return NO;
        }
        // メディアタイプのコンポジショントラックの配列を返す。
        NSArray *tracks = [URLAsset tracksWithMediaType:AVMediaTypeAudio];
        if (![tracks count]) {
            do_export = NO;
            return NO;
        }
        // アセットトラックからミックスされたオーディオデータを読み取る。
        AVAssetReaderAudioMixOutput *audioMixOutput = [AVAssetReaderAudioMixOutput
                                                       assetReaderAudioMixOutputWithAudioTracks:tracks
                                                       audioSettings:audioSetting];
        if (![assetReader canAddOutput:audioMixOutput]) {
            do_export = NO;
            return NO;
        }
        // 実際にミュージックデータを読み込む
        [assetReader addOutput:audioMixOutput];
        if (![assetReader startReading]) {
            do_export = NO;
            return NO;
        }
        // パスを作成
        NSArray *docDirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *docDir = [docDirs objectAtIndex:0];
        NSString *outPath = [[docDir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@", [item valueForProperty:MPMediaItemPropertyPersistentID]]]
                             stringByAppendingPathExtension:@"wav"];
        // 書き込みファイルまでのパスまでのリンクを作成
        NSURL *outURL = [NSURL fileURLWithPath:outPath];
        // ↑で作ったリンクをもとに指定されたUTIで指定された形式で、指定されたURLで識別されるファイルに書き込むためのアセットライターを返します。
        AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:outURL
                                                              fileType:AVFileTypeWAVE
                                                                 error:&error];
        if (error) {
            do_export = NO;
            return NO;
        }
        //ファイルが存在している場合は削除する
        NSFileManager *manager = [NSFileManager defaultManager];
        if([manager fileExistsAtPath:outPath]) [manager removeItemAtPath:outPath error:&error];
        if (error) {
            do_export = NO;
            return NO;
        }
        // データを書き込みする際に利用する
        AVAssetWriterInput *assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio
                                                                                  outputSettings:audioSetting];
        // リアルタイムで入力するか
        assetWriterInput.expectsMediaDataInRealTime = NO;
        if (![assetWriter canAddInput:assetWriterInput]) {
            do_export = NO;
            return NO;
        }
        // 書き込む情報を追加する
        [assetWriter addInput:assetWriterInput];
        if (![assetWriter startWriting]) {
            do_export = NO;
            return NO;
        }
        // コピー処理
        // ARCをオフにしているので自分で参照カウントを+1する
        [assetReader retain];
        [assetWriter retain];
        // 設定した情報を実際に書き込みを開始する
        [assetWriter startSessionAtSourceTime:kCMTimeZero];
        // 非同期処理
        dispatch_queue_t queue = dispatch_queue_create("assetWriterQueue", NULL);
        [assetWriterInput requestMediaDataWhenReadyOnQueue:queue usingBlock:^{
            while ( 1 ) {
                // ファイルの書き込みが出来るか
                if ([assetWriterInput isReadyForMoreMediaData]) {
                    // サンプルバッファーを出力用にコピーする
                    CMSampleBufferRef sampleBuffer = [audioMixOutput copyNextSampleBuffer];
                    if (sampleBuffer) {
                        // サンプルバッファーを追加する
                        [assetWriterInput appendSampleBuffer:sampleBuffer];
                        // オブジェクトを解放する
                        CFRelease(sampleBuffer);
                    } else {
                        // バッファーを追加出来ないようにする
                        [assetWriterInput markAsFinished];
                        break;
                    }
                }
            }
            // ディスパッチオブジェクトの参照(保持)カウントを減少させます。
            [assetWriter finishWriting];
            // ARCをオフにしているので自分で参照カウントを-1する
            [assetReader release];
            [assetWriter release];
            do_export = NO;
        }];
        dispatch_release(queue);
        return YES;
    }


    /**************************************
     * ランダムで曲をエクスポートする
     * @return エクスポートが完了したらYESを返す
     **************************************/
    void exportRandomToItem() {

        /// 曲情報を取得する処理
        MPMediaQuery* songQuery = [MPMediaQuery songsQuery];

        // 使える曲の配列
        NSMutableArray<MPMediaItem*>* array = [[NSMutableArray<MPMediaItem*> alloc] init];

        // ここでiCloudにしかない曲を弾く
        [songQuery addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];
        NSArray *songlists = songQuery.collections;

        // 使える曲リストを作成
        for ( int i = 0; i < [songlists count]; i++ ) {
            MPMediaItemCollection* songlist = [songlists objectAtIndex:i];
            MPMediaItem* item = [songlist representativeItem];
            if ( ![item hasProtectedAsset] ) [array addObject:item];
        }

        // 曲をエクスポート
        NSUInteger index = arc4random_uniform([array count]);
        MPMediaItem* item = [array objectAtIndex:index];
        song_id = [[item valueForProperty:MPMediaItemPropertyPersistentID] longValue];
        song_name = [item valueForProperty:MPMediaItemPropertyTitle];
        exportItem(item);
    }


    /************************************
     * セットされている曲のIDを取得する関数
     * @return セットされている曲のIDを返す
     ************************************/
    long getSongId() {
        return song_id;
    }


    /****************************************
     * セットされている曲のタイトルを取得する関数
     * @return セットされている曲のタイトルを返す
     ****************************************/
    char* getSongName() {
        return strdup([song_name UTF8String]);
    }


    /*******************************
     * コピー中かどうか判定する関数
     * @return コピー中ならYESを返す
     *******************************/
    BOOL getDoExport() {
        return do_export;
    }
}

このままではUnityから呼び出す事ができないので次はC#にObjective-Cと連携させる処理を作成します。

C#の中身を作成

ここでは、Objective-Cのコードを実行させたいので先ほど作成したC#のファイル(MediaController.cs)に下記のソースをコピペして実行できるようにしましょう。

MusicLibraryMediaPicker.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using UnityEngine.UI;
using System.IO;

public class MediaController : MonoBehaviour {

    private AudioSource audio;
    private Text text;

    #if UNITY_IOS
        [DllImport("__Internal")]
        public static extern void exportRandomToItem();

        [DllImport("__Internal")]
        public static extern long getSongId();

        [DllImport("__Internal")]
        public static extern string getSongName();

        [DllImport("__Internal")]
        public static extern bool getDoExport();
    #endif

    // Use this for initialization
    void Start () {

        // プロパティを取得
        audio = GameObject.Find("Audio Source").GetComponent<AudioSource>();
        text = GameObject.Find("Text").GetComponent<Text>();

        // ループ再生するようにする
        audio.loop = true;

        // コルーチンを開始
        StartCoroutine(MusicImport());
    }

    IEnumerator MusicImport() {

        text.text = "楽曲エクスポート中";

        // 曲エクスポートを開始
        exportRandomToItem();

        yield return new WaitForSeconds(0.25f);

        // 曲エクスポート完了まで待つ
        while ( getDoExport() ) yield return new WaitForSeconds(0.25f);

        text.text = "楽曲インポート中";

        // Documentsにある曲を取得
        string path = Application.persistentDataPath + "/" + getSongId() + ".wav";
        WWW www = new WWW("file://" + path);

        // インポートが完了するまで待つ
        while ( !www.isDone ) yield return new WaitForSeconds(0.25f);

        audio.clip = www.GetAudioClip(false, false);

    text.text = "再生します!";

        audio.Play();

        text.text = getSongName();

        // wavファイルを削除
        System.IO.File.Delete(path);
    }
}

これでソースコードの準備は完了です!!

C#とゲームオブジェクトを連携

先ほどObjective-Cを動かす為のC#ソースコードを作成しましたが、そのままでは動いてくれないのでUnityのオブジェクトにアタッチしてアプリ起動時に実行されるようにします。
なので今回は「Main Camera」のゲームオブジェクトにC#のスクリプトをアタッチしましょう!
image.png
Main CameraにMediaController.csをアタッチ

ビルド!

これでもうUnityでの準備は完了なので、「PlayerSettings」を各自の環境に設定してビルドしましょう!!
(PlatformがiOSになってない方は先にiOSにSwitch Platformで切り替えてからビルドしてください)
image.png

実行!!

そのままでは実行できないので、いくつか設定する必要があります。

  • Info.plistにミュージックライブラリにアクセスする為の設定を追加
    1. 左にあるInfo.plistを選択して、「Information Property List」の右にある「+」を押す
    2. すると入力ボックスが現れるのでそこに「Privacy – Media Library Usage Description」を入力して右側に適当な文字列を入力します。

これで、アプリ起動時に端末のミュージックライブラリへアクセス許可を選択させるポップアップを表示させる事ができます。
image.png

Info.plistに設定を追加する

  • 作成したObjective-CファイルのARCを無効にする

次は設定がちょっと厄介で画像のように、左の「Unity-iPhone」を押し真ん中上部付近の「Build Phases」を押して「Compile Sources」のボタンを押しましょう。
image.png

下にスクロールすると先ほど作成した「MusicLibraryMediaPicker.mm」があるのでダブルクリックして画像のように「-fno-objc-arc」と入力しましょう。
image.png

これで、準備は完了であとは自分の開発者アカウントでSigningして実行しましょう!!

動かした様子

IMAGE ALT TEXT HERE

作成したソースコード&プロジェクト

作成したソースコードとプロジェクトは、GitHubにアップので良かったらどうぞ。

https://github.com/reishisu/ImportiOSMusicLibrary

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

【Unity】WebGLにビルドする方法 画像付き

始めに

unityroomにアップロードしたいと思い、自分のつくったUnityのゲームをunityroomにあげるまでの方法を備忘録として残しておきます。
実際に上がったゲームはこちら
https://unityroom.com/games/2d_side_scroll_action

※Unityのバージョンは2019.1.5f1

方法

① File > Build Settingsを選択

自分の作りたいゲームがUnityで完成したら、画面右上のメニューからBuild Settingsを選択します。
image.png

② WebGLを選択し、Switch Platformを選択

image.png
WebGLの横にUnityのアイコンがつけばSwitch完了です。

※Switch Platformが選択できない場合
自分の場合、Switch Platformが半透明になっており選択ができませんでした。
その場合、Unity HubからWebGLのモジュールのインストールが必要になります。

Unity HubからWebGLのモジュールをインストールする

Unity Hubの画面から Installsから作成したゲームのバージョンのアイコンの右上縦3つの点をクリックし、「Add Modules」をクリック
image.png

「WebGL Build Support」をチェックし、DONEをクリック。
するとインストールが始まると思います。
image.png
インストールが完了したら②の画面に戻りましょう。
戻ってもSwitch Pratformが選択できな場合はUnityを一度終了し、もう一度起動してみてください。

③Buildを選択

保存場所を聞かれるので、任意の場所を選択してください。
そこにBuildのフォルダーができれば完了です。
※数分かかる可能性があります。
image.png

unityroomへのアップロード方法

こちらが大変参考になりました。
https://blog.naichilab.com/entry/how-to-upload-unityroom

参考記事

【unity】WebGLビルド方法
https://blog.naichilab.com/entry/2017/04/29/125527

Unity WebGLビルド
https://pgintro.net/unity/build/webgl/#anchor_unity_hub_install

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