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

C# リストのシャッフル

0.0 はじめに リスト(List)内の要素をシャッフルする方法を調べてみました。 1.0 スクリプトでシャッフル 下記のスクリプトでシャッフル可能です。 Shuffle.cs // リストlistをシャッフルする (for降順ランダム取り) for (int i = list.Count - 1; i > 0; i--){ var j = Random.Range(0, i+1); // ランダムで要素番号を1つ選ぶ(ランダム要素) var temp = list[i]; // 一番最後の要素を仮確保(temp)にいれる list[i] = list[j]; // ランダム要素を一番最後にいれる list[j] = temp; // 仮確保を元ランダム要素に上書き } 2.0 定型文を利用したシャッフル方法 OrderByを使うためにusing System.Linq、Guidを使うためにusing Systemを追加します。 Shuffle.cs // リストをランダムに並べ替える list = list.OrderBy(a => Guid.NewGuid()).ToList(); 配列の場合は下記でOKです。 array = array.OrderBy(a => Guid.NewGuid()).ToArray();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity C# 自作Randamクラス

0.0 はじめに using Systemを追加したらRandamクラスはUnityEngineの明示が必要となります。 ほかにも方法がありますが、対策しないとエラーになります。 Systemとぶつからない様にするためのいろいろと検討しましたが、自作Randamクラス(Randクラス)を使うのが簡単でした。 1.0 スクリプト 以下はint, floatでランダムな数字を入手するための関数を入れています。 Rand.cs using UnityEngine; // using Systemを使ったときにぶつからない様にするための疑似Randamクラス public static class Rand{ /// <summary> /// minとmaxの間のランダムなintを返す。min含む、max含まない。 /// </summary> public static int Range(int min, int max) { return Random.Range(min, max); } /// <summary> /// minとmaxの間のランダムなfloatを返す。min含む、max含む。 /// </summary> public static float Range(float min, float max) { return Random.Range(min, max); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】エディタ拡張でSortingLayerのフィールドを表示する

このメニューを出したい 標準のエディタ拡張APIでは、Sorting Layerを編集するポップアップを出す手段がありません。 これを手動で出せるようにするというお話です。 方針 実はSortingLayerのリストを表示するAPIがEditorGUI.SortingLayerField()として存在しているのですが、internalに指定されているために直接使用することができなくなっています。 当初はそのままコピペして動かそうと思ったのですが、結局別のinternalなメンバーにアクセスする必要が出てきてしまったので、EditorGUI.SortingLayerField()を直接リフレクションで呼び出すことにしました。 Unityでinternalなメンバにアクセスするにはasmrefを使う方法なんかもあるのですが、今回は相手がUnityEditor.dllなので使えず。素直にリフレクションを使います。 実装 エディタ描画の度にリフレクションさせるとエディタのパフォーマンスに影響しそうな気がするので、初回だけデリゲートを動的生成して2回目以降はキャッシュを使います。 SortingLayerEditorUtility.cs using System; using System.Reflection; using UnityEditor; using UnityEngine; namespace Ruccho.Utilities { public static class SortingLayerEditorUtility { private static GUIStyle boldPopupStyle; private static GUIStyle BoldPopupStyle { get { if (boldPopupStyle == null) { boldPopupStyle = new GUIStyle(EditorStyles.popup); boldPopupStyle.fontStyle = FontStyle.Bold; } return boldPopupStyle; } } private delegate void SortingLayerFieldDelegate(Rect position, GUIContent label, SerializedProperty layerID, GUIStyle style, GUIStyle labelStyle); private static SortingLayerFieldDelegate sortingLayerFieldDelegate = default; private static bool HasPrefabOverride(SerializedProperty property) { return property != null && property.serializedObject.targetObjects.Length == 1 && property.isInstantiatedPrefab && property.prefabOverride; } public static void SortingLayerFieldLayout(GUIContent label, SerializedProperty layerID) { var hasPrefabOverride = HasPrefabOverride(layerID); var style = hasPrefabOverride ? BoldPopupStyle : EditorStyles.popup; var labelStyle = hasPrefabOverride ? EditorStyles.boldLabel : EditorStyles.label; SortingLayerFieldLayout(label, layerID, style, labelStyle); } public static void SortingLayerFieldLayout(GUIContent label, SerializedProperty layerID, GUIStyle style, GUIStyle labelStyle) { Rect rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight, style); SortingLayerField(rect, label, layerID, style, labelStyle); } public static void SortingLayerField(Rect position, GUIContent label, SerializedProperty layerID) { var hasPrefabOverride = HasPrefabOverride(layerID); var style = hasPrefabOverride ? BoldPopupStyle : EditorStyles.popup; var labelStyle = hasPrefabOverride ? EditorStyles.boldLabel : EditorStyles.label; SortingLayerField(position, label, layerID, style, labelStyle); } public static void SortingLayerField(Rect position, GUIContent label, SerializedProperty layerID, GUIStyle style, GUIStyle labelStyle) { if (sortingLayerFieldDelegate == default) { var editorGuiType = typeof(EditorGUI); var sortingLayerFieldMethod = editorGuiType.GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic); if (sortingLayerFieldMethod == null) return; sortingLayerFieldDelegate = (SortingLayerFieldDelegate) Delegate.CreateDelegate(typeof(SortingLayerFieldDelegate), sortingLayerFieldMethod); } sortingLayerFieldDelegate?.Invoke(position, label, layerID, style, labelStyle); } } } ここまでやればSortingLayerEditorUtility.SortingLayerFieldLayout() または SortingLayerEditorUtility.SortingLayerField()で呼び出しが可能です。 参考 neue cc - C#での動的なメソッド選択における定形高速化パターン
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Missingチェックを行うEditor拡張

GameObjectに付与したスクリプトや、インスペクタウィンドウで設定したマテリアルや他のGameObjectへの参照がリンク切れになって「Missing」という表示になってしまうことってありますよね。 プレイ中に思ったように動かなくて調べたらMissingになっていたということがあるので、少しでも早く気付けるようにEditor拡張を作成しました。 バージョン2020.3.5f1で動作確認。 仕様 ヒエラルキーウィンドウに表示しているGameObjectに以下のものが存在する場合、対象GameObjectの行にワーニングアイコンを表示する。 付与されているコンポーネントにMissingなものが存在する場合。 SerializeField設定項目の中にMissingなものが存在する場合。 同時にコンソールにエラーメッセージを表示する。 GameObject単位で最初に発見したリンク切れ情報を表示する。 ポイント解説 下記の投稿と重複する部分は省きます。 コンポーネントへの参照がリンク切れになっているかを確認する方法 EditorApplication.hierarchyWindowItemOnGUIイベントに登録したイベントハンドラ内で、GameObjectに対してGetComponents<Component>()を行ってnullのものが存在する場合、そのコンポーネントはリンク切れとなっています。 コンポーネントへの参照がリンク切れになっているかを確認 private static void CheckMissingReference(int instanceID, Rect selectionRect) { // instanceIDをオブジェクト参照に変換 if (!(EditorUtility.InstanceIDToObject(instanceID) is GameObject gameObject)) return; // オブジェクトが所持しているコンポーネント一覧を取得 var components = gameObject.GetComponents<Component>(); // Missingなコンポーネントが存在する場合はエラー表示 var existsMissing = components.Any(x => x == null); if (existsMissing) { UnityEngine.Debug.LogError( gameObject.name + "のコンポーネントにMissingのものが存在します。"); } } SerializeFieldへの参照がリンク切れになっているかを確認する方法 SerializeFieldが以下の条件に当て嵌まる場合はリンク切れと見なすことができます。 SerializedPropertyのobjectReferenceValueがnull(=参照先が存在しない)なのに、FileIDは設定されている(=参照すべきものは設定されている)もの SerializeFieldへの参照がリンク切れになっているかを確認 var serializedProp = new SerializedObject(component).GetIterator(); while (serializedProp.NextVisible(true)) { if (serializedProp.propertyType != SerializedPropertyType.ObjectReference) continue; if (serializedProp.objectReferenceValue != null) continue; var fileId = serializedProp.FindPropertyRelative("m_FileID"); if (fileId == null || fileId.intValue == 0) continue; UnityEngine.Debug.LogError( component.name + "の" + component.GetType().Name + "のフィールド" + serializedProp.propertyPath + "がMissingとなっています。"); } ただし過去にUnityのバージョンアップで仕様が変更になったことがあるので、バージョンによっては他の方法が必要となるかもしれません。 ワーニングアイコン表示 ワーニングアイコンは以下の形で取得することができました。 アイコン名はこちらを参考にしましたが、Unityのバージョンによって変わるかもしれません。 ワーニングアイコン取得 Texture warningIcon = EditorGUIUtility.IconContent("console.warnicon").image; エラーメッセージ表示頻度 EditorApplication.hierarchyWindowItemOnGUIイベントでエラーメッセージの表示を行なっているので、ヒエラルキービューの再描画処理が走る都度新しくエラーが出力されます。 これがかなりの頻度なので、ヒエラルキービューを操作しているとすぐにこんな状態になってしまいます。 もう少し控えめでも良いのですが、これだけ煩かったらすぐに解消したくなるからまあいいかな、と自分を納得させています。 コード全文 Missingチェックを行うEditor拡張 #nullable enable using System.Linq; using UnityEditor; using UnityEngine; /// <summary> /// リンク切れ情報を表示する拡張機能 /// </summary> /// <remarks> /// <para>Unity2020.3.5f1で動作確認。</para> /// <para> /// <list type="bullet"> /// <item><description>コンポーネントがMissingとなっている場合は!のアイコンを表示し、エラーログも表示。</description></item> /// <item><description> /// コンポーネント内の参照項目にMissingとなっているものが存在する場合は!のアイコンを表示し、エラーログも表示。 ///(ログはコンポーネント単位で最初に見つかったもののみ表示) /// </description></item> /// </list> /// </para> /// </remarks> public static class MissingReferenceChecker { private const int IconSize = 16; private const string WarningIconName = "console.warnicon"; private const string PropertyNameOfFieldId = "m_FileID"; private static Texture? warningIcon; [InitializeOnLoadMethod] private static void Initialize() { Enable(); /* * ビルトインアイコンの呼び出し方は以下を参考にした * https://qiita.com/Rijicho_nl/items/88e71b5c5930fc7a2af1 * https://unitylist.com/p/5c3/Unity-editor-icons */ #pragma warning disable UNT0023 // Coalescing assignment on Unity objects warningIcon ??= EditorGUIUtility.IconContent(WarningIconName).image; #pragma warning restore UNT0023 // Coalescing assignment on Unity objects } private static void Enable() { EditorApplication.hierarchyWindowItemOnGUI -= CheckMissingReference; EditorApplication.hierarchyWindowItemOnGUI += CheckMissingReference; } private static void CheckMissingReference(int instanceID, Rect selectionRect) { // instanceIDをオブジェクト参照に変換 if (!(EditorUtility.InstanceIDToObject(instanceID) is GameObject gameObject)) return; var pos = selectionRect; pos.x = pos.xMax - IconSize; pos.width = IconSize; pos.height = IconSize; // オブジェクトが所持しているコンポーネント一覧を取得 var components = gameObject.GetComponents<Component>().ToList(); // Missingなコンポーネントが存在する場合はWarningアイコン表示 var existsMissing = components.RemoveAll(x => x == null) > 0; if (existsMissing) { UnityEngine.Debug.LogError(gameObject.name + "のコンポーネントにMissingのものが存在します。"); DrawIcon(pos, warningIcon!); } else { foreach (var component in components) { // SerializeFieldsにMissingなものが存在する場合はWarningアイコン表示 var existsMissingField = ExistsMissingField(component); if (existsMissingField) { DrawIcon(pos, warningIcon!); } } } } /// <summary> /// コンポーネントの設定値にMissingなものが存在するかどうかを確認する /// </summary> /// <param name="component">確認対象のコンポーネント</param> /// <returns>MissingなSerializedFieldが存在するかどうか</returns> /// <remarks> /// 以下の条件を満たす場合はMissingと見なす。Unityのバージョンが変わると変更になる可能性有。 /// <list type="bullet"> /// <item><description><see cref="SerializedProperty.propertyType"/>が<see cref="SerializedPropertyType.ObjectReference"/></description></item> /// <item><description><see cref="SerializedProperty.objectReferenceInstanceIDValue"/>がnull</description></item> /// <item><description>fileIDが0ではない</description></item> /// </list> /// </remarks> private static bool ExistsMissingField(Component component) { var ret = false; var serializedProp = new SerializedObject(component).GetIterator(); while (!ret && serializedProp.NextVisible(true)) { if (serializedProp.propertyType != SerializedPropertyType.ObjectReference) continue; if (serializedProp.objectReferenceValue != null) continue; var fileId = serializedProp.FindPropertyRelative(PropertyNameOfFieldId); if (fileId == null || fileId.intValue == 0) continue; UnityEngine.Debug.LogError( component.name + "の" + component.GetType().Name + "のフィールド" + serializedProp.propertyPath + "がMissingとなっています。"); ret = true; } return ret; } private static void DrawIcon(Rect pos, Texture image) { GUI.DrawTexture(pos, image, ScaleMode.ScaleToFit); } } コンポーネントアイコン表示機能と併用 こちらに記載した、ヒエラルキーウィンドウにコンポーネントのアイコンを表示するEditor拡張と合わせると、こんな風になります。 コンポーネントアイコン表示機能と併用 #nullable enable using System.Linq; using UnityEditor; using UnityEngine; /// <summary> /// Hierarchyウィンドウにコンポーネントのアイコンを表示する拡張機能 /// </summary> /// <remarks> /// <para>Unity2020.3.5f1で動作確認。</para> /// <para> /// <list type="bullet"> /// <item><description>Transform以外のコンポーネントのアイコン表示。</description></item> /// <item><description>スクリプトのアイコンは複数付与されていても1つのみ表示。</description></item> /// <item><description>コンポーネントが無効になっている場合はアイコン色が半透明になっている。</description></item> /// <item><description>コンポーネントがMissingとなっている場合は!のアイコンを表示し、エラーログも表示。</description></item> /// <item><description> /// コンポーネント内の参照項目にMissingとなっているものが存在する場合は!のアイコンを表示し、エラーログも表示。 ///(ログはコンポーネント単位で最初に見つかったもののみ表示) /// </description></item> /// <item><description>ヒエラルキーウィンドウで右クリックで表示されるメニュー「コンポーネントアイコン表示切替」の選択で表示/非表示の切替可能。</description></item> /// </list> /// </para> /// </remarks> public static class ComponentIconDrawerInHierarchy { private const int IconSize = 16; private const string MenuPath = "GameObject/コンポーネントアイコン表示切替"; private const string ScriptIconName = "cs Script Icon"; private const string WarningIconName = "console.warnicon"; private const string PropertyNameOfFieldId = "m_FileID"; private static readonly Color colorWhenDisabled = new Color(1.0f, 1.0f, 1.0f, 0.5f); private static Texture? scriptIcon; private static Texture? warningIcon; private static bool enabled = true; [InitializeOnLoadMethod] private static void Initialize() { UpdateEnabled(); /* * ビルトインアイコンの呼び出し方は以下を参考にした * https://qiita.com/Rijicho_nl/items/88e71b5c5930fc7a2af1 * https://unitylist.com/p/5c3/Unity-editor-icons */ #pragma warning disable UNT0023 // Coalescing assignment on Unity objects scriptIcon ??= EditorGUIUtility.IconContent(ScriptIconName).image; warningIcon ??= EditorGUIUtility.IconContent(WarningIconName).image; #pragma warning restore UNT0023 // Coalescing assignment on Unity objects } [MenuItem(MenuPath, false, 20)] private static void ToggleEnabled() { enabled = !enabled; UpdateEnabled(); } private static void UpdateEnabled() { EditorApplication.hierarchyWindowItemOnGUI -= DisplayIcons; if (enabled) EditorApplication.hierarchyWindowItemOnGUI += DisplayIcons; } private static void DisplayIcons(int instanceID, Rect selectionRect) { // instanceIDをオブジェクト参照に変換 if (!(EditorUtility.InstanceIDToObject(instanceID) is GameObject gameObject)) return; var pos = selectionRect; pos.x = pos.xMax - IconSize; pos.width = IconSize; pos.height = IconSize; // オブジェクトが所持しているコンポーネント一覧を取得 var components = gameObject .GetComponents<Component>() .Where(x => !(x is Transform || x is ParticleSystemRenderer)) .Reverse() .ToList(); // Missingなコンポーネントが存在する場合はWarningアイコン表示 var existsMissing = components.RemoveAll(x => x == null) > 0; if (existsMissing) { UnityEngine.Debug.LogError(gameObject.name + "のコンポーネントにMissingのものが存在します。"); DrawIcon(ref pos, warningIcon!); } var existsScriptIcon = false; foreach (var component in components) { // SerializeFieldsにMissingなものが存在する場合はWarningアイコン表示 var existsMissingField = ExistsMissingField(component); if (existsMissingField) DrawIcon(ref pos, warningIcon!); Texture image = AssetPreview.GetMiniThumbnail(component); if (image == null) continue; // Scriptのアイコンは1つのみ表示 if (image == scriptIcon) { if (existsScriptIcon) continue; existsScriptIcon = true; } // アイコン描画 DrawIcon(ref pos, image, component.IsEnabled() ? Color.white : colorWhenDisabled); } } /// <summary> /// コンポーネントの設定値にMissingなものが存在するかどうかを確認する /// </summary> /// <param name="component">確認対象のコンポーネント</param> /// <returns>MissingなSerializedFieldが存在するかどうか</returns> /// <remarks> /// 以下の条件を満たす場合はMissingと見なす。Unityのバージョンが変わると変更になる可能性有。 /// <list type="bullet"> /// <item><description><see cref="SerializedProperty.propertyType"/>が<see cref="SerializedPropertyType.ObjectReference"/></description></item> /// <item><description><see cref="SerializedProperty.objectReferenceInstanceIDValue"/>がnull</description></item> /// <item><description>fileIDが0ではない</description></item> /// </list> /// </remarks> private static bool ExistsMissingField(Component component) { var ret = false; var serializedProp = new SerializedObject(component).GetIterator(); while (!ret && serializedProp.NextVisible(true)) { if (serializedProp.propertyType != SerializedPropertyType.ObjectReference) continue; if (serializedProp.objectReferenceValue != null) continue; var fileId = serializedProp.FindPropertyRelative(PropertyNameOfFieldId); if (fileId == null || fileId.intValue == 0) continue; UnityEngine.Debug.LogError( component.name + "の" + component.GetType().Name + "のフィールド" + serializedProp.propertyPath + "がMissingとなっています。"); ret = true; } return ret; } private static void DrawIcon(ref Rect pos, Texture image, Color? color = null) { Color? defaultColor = null; if (color.HasValue) { defaultColor = GUI.color; GUI.color = color.Value; } GUI.DrawTexture(pos, image, ScaleMode.ScaleToFit); pos.x -= pos.width; if (defaultColor.HasValue) GUI.color = defaultColor.Value; } /// <summary> /// コンポーネントが有効かどうかを確認する拡張メソッド /// </summary> /// <param name="this">拡張対象</param> /// <returns>コンポーネントが有効となっているかどうか</returns> private static bool IsEnabled(this Component @this) { var property = @this.GetType().GetProperty( "enabled", typeof(bool)); return (bool)(property?.GetValue(@this, null) ?? true); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

公式サイト「初めてのVRアプリを構築する」をやってみる

2021/10/12 4つのマテリアルを作った 2021/10/10 これの前作:YouTubeみながらQuest2のBuild and Runできた 着手 初めてのVRアプリを構築する 前提条件の確認 Unity設定を構成するを順番に行ったが、以下の2箇所、不安がある ■ビルトインXRプラットフォーム統合を使用する: ■Androidマニフェストファイルを生成する 後日は、ここから再開する:基本概念
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Oculus Quest】AppLabでコンテンツをリリースする手順

AppLabの登場により、Oculusでの配信のハードルが下がったということで、力試しのつもりで申請してみました。 ここでは、AppLabにアプリを申請する手順とポイントをまとめたいと思います。(通常のAPKファイル作成については、詳細触れていませんのでご了承ください) 申請にあたっては、下記の記事を参考にさせていただきました。(いつもお世話になっています!) また、公式ガイドラインは一度目を通しておくべきかと思いますが、その中でも個人的に参考になったページを下記にピックアップしました。 Oculus公式ガイドライン Questバーチャルリアリティチェック(VRC)ガイドライン リリースビルドのためのアプリマニフェスト(※1) Androidアプリケーションの署名(※2) Oculusストアアセットデザインガイドライン(※3) 目次 〜大まかな流れ〜 APKファイルの作成 1. ビルド設定で押さえるポイント 2. Bundle Version Code の設定 3. 不要なマイク使用権限を削除する 4. Androidアプリの署名(Keystore作成) 5. AndroidManifestの作成 6. App ID の設定 申請 ① ビルド ② 説明 ③ スペック ④ 追加情報 ⑤ アセット ⑥ コンテンツ評価 ⑦ 価格 リジェクトの対応(修正) 1. 修正項目の修正 2. 再ビルド (リリース) アプリのプロモーション(任意) 1. Oculusキーの発行 APKファイルの作成 まずはAPKファイルの作成です。 申請するためのAPKファイルでは、非公開のAPKファイル作成時にはしなくてよい作業も必要になります(ビルド時の設定のほかアプリの署名やマニフェストの作成など)。実際にやってみて、いつもの手順(非公開のAPKファイル作成時)以外で必要な作業やポイントを書いておきたいと思います。 1. ビルド設定で押さえるポイント 下記は必須です。まぁ、だいたいこの設定でビルドしているかな、と思いますが。。 IL2CPP Minimum API Level:23以上 Install Location (インストール場所):Automatic (自動) (※1)リリースビルドのためのアプリマニフェスト 参考。 2. Bundle Version Code の設定 このまんまなので、フレームシンセシス様の引用で恐れ入ります。。 Oculus のダッシュボードは Project Settings の Player > Other Settings > Bundle Version Code の数値でアプリのバージョンを識別しています。新しいバージョンの apk ファイルをアップロードするたびに Bundle Version Code の数値を増やしてビルドする必要があります。 (Unity + Oculus Quest 2 開発メモより) (※1)リリースビルドのためのアプリマニフェスト 参考。 なので最初は、 Version:0.1 Bundle Version Code:1 などでOK。申請する際にAPKファイルを再アップする時は、Bundle Version Code:2,3,...といった具合に数値を上げます。 【参考】 Version:ストアなどユーザーが見られるリリースバージョン Bundle Version Code:ビルドの区別をするためのバージョン(ビルド番号) ※アプリのバージョニングについては、下記が分かりやすく解説されています。 https://developer.android.com/studio/publish/versioning https://qiita.com/nkjzm/items/492cfc1103b8a124770a 3. 不要なマイク使用権限を削除する 「マイクを使用しないアプリなのに権限があるとリジェクトされる」とのことなので、マイクを使用しない場合は注意が必要です。 こちらも、フレームシンセシス様の記事を引用させていただきます。 対処方法としては、もし Oculus/LipSync フォルダと Oculus/Avatar フォルダの機能を使用していなければこれらを削除するのが簡単です。 (Unity + Oculus Quest 2 開発メモより) 4. Androidアプリの署名(Keystore作成) またまた、フレームシンセシス様の引用ですみません。。 apk ファイルをビルドする際に署名が必要です。 Project Settings の Player > Publishing Settings > Keystore Manager… で署名ファイル(*.keystore)を作成して署名します。アプリのアップデートの際は同じ署名ファイルで署名する必要があります。設定したパスワードを忘れないように注意してください。プロジェクトファイルには保存されません。 (Unity + Oculus Quest 2 開発メモより) (※2)Androidアプリケーションの署名 参考。 5. AndroidManifestの作成 Oculus > Tools > Create store… を実行するだけです。 Assets\Plugins\Android の中に、AndroidManifest.xmlが作成されます。 (※1)リリースビルドのためのアプリマニフェスト 参考。 生成されたファイルを開くとこんな感じに記載されています。 6. App ID の設定 最後まで、引用させていただきすみません。(ありがとうございます) apk ファイルをビルドする際に App ID を設定する必要があります。 Oculus のダッシュボードでアプリを作成して Getting Started API のページを開き、取得した App ID を Oculus > Platform > Edit Settings の Application ID のところに設定します。 (Unity + Oculus Quest 2 開発メモより) 申請 いよいよアプリの申請です。 「Oculus開発センター」のダッシュボードにアクセスします。 https://developer.oculus.com/manage/ 「新しいアプリを作成」をクリック アプリ名:(任意で入力) プラットホーム:Quest(App Lab)を選択 「作成」をクリック(アプリのページができます) 右上の「申請の詳細に移動」をクリックすると、下図のような画面になると思います。(こちらは申請済みアプリの画面ため全て「済み」になっていますが、最初はチェックが入っていない状態です) 「申請を開始」をクリックすると、左列の上から各項目順に、申請内容が表示されます。 ①ビルド 右上の「新しいビルドをアップロード」から、作成したAPKファイルをアップロードします。(ブラウザからアップロード出来るのは400MBまでのようです) APKファイルが正しく作成されていないと(アプリの署名やマニフェストなど含め)、ここでエラーが出ます。逆に、アップロードできなければ、そのAPKファイルは申請要件を満たしていないということが分かるので、確認にもなります。 無事アップロードに成功したら、以降ではリリースするのに必要な各種設定を行なっていきます。 「保存して続行」で次の項目へいけます。 左列の項目を選べば、順番通りでなくてもOK。 「下書きを保存」で、ダッシュボード内に保存されるので、申請途中で中断しても大丈夫です。 ②説明 アプリ名や説明を入力していきます。 アプリ名 簡単な説明 詳細 言語はデフォルトでは英語だけですが、「言語を管理」から新たに言語追加出来ます。 ③スペック ゲームモードや対応言語などを設定していきます。(「ジャンル」は3つ設定可能) ④追加情報 開発者情報や連絡先等を設定していきます。 ウェブサイトやプライバシーポリシーへのリンクが必要なので、無い場合は要作成。 ⑤アセット ロゴやスクショ、PVなどを設定していきます。 予告編動画以外は必須のようです。 ◆ロゴとアイコン ロゴ(1440x1440) アイコン(512x512) ロゴは、「アスペクト比: 変数、9000 x 1440px (最大) (32-Bit PNG 透過)」と書かれており、英語の説明を読むと、アスペクト比は特に指定なしでサイズは最大9000 x 1440pxの32ビット透過PNGと読めるのですが、手持ちのPNGをいろいろ入れてみても「使用できない画像サイズです」というそっけないエラーで拒否されます。結局 1440x1440 の透過PNGだけがうまく行きました。 ◆カバーアート 横型(2560x1440) 正方形(1440x1440) 縦型(1008x1440) ミニ横型(1008x360) ◆PDPアート ヒーローカバー(3000x900) ◆スクリーンショット 画像5点(2560x1440) ◆予告編(※任意。無くてもOK。) 動画(アスペクト比: 16:91280 x 720px (最小サイズ) (H.264とAAC音声によるMPEG-4コンテナ)) カバー画像(2560x1440) (※3)Oculusストアアセットデザインガイドライン 参考。 ⑥コンテンツ評価 IARC証明書を用いてアプリの対象年齢を設定していきます。 ちなみに、IARC証明書は質問に答えるだけですぐに、しかも無料で作成出来ます。 IARC証明書を作成する場合は「新しい証明書をリクエスト」を押します。 既存の証明書を使用するには、証明書IDを入力すれば良いようです。 ⑦価格 アプリの価格を設定していきます。(無料にも出来ます) ちなみに、リリース後の変更はメールでやらないといけないようです。 submissions@oculus.comにメールを送信することで価格の変更を手動でリクエストできます。 全ての入力が完了し、チェックマークが付いたら送信の「ストアに送信」から申請します。 「アプリを申請していただきありがとうございます」という件名で、本文なしのメールが届きます。 リジェクトの対応(修正) 審査結果はメールで通知されます。 私の場合は、申請から25日目に以下の件名のリジェクトメールが届きました。 1. 修正項目の修正 件名:「HALLO EINSTEIN」への変更リクエスト メール本文の「here」から審査結果にアクセスし、内容を確認します。 私の場合、以下のような修正を求められました。 60fpsを大幅に切っているところがある UIのボタンを押しても効かないときがある (もう一点あった気がしますが忘れました...が、すべて基本的なことでした...orz) ちなみに、どの場面で、など結構詳しく書いてくれているので、該当箇所を見つけるのに苦労はしないと思います。 2. 再ビルド 修正が終わったら、再度ビルドしてAPKファイルを作成します。このとき、Bundle Version Codeの数値を上げてビルドしてください。 Bundle Version Code:(前回が1なら、2に上げる) ※APKファイルの作成 / 2. Bundle Version Code の設定 を参照。 修正ファイルが作成できたら、再び「アプリの申請」の一番目の項目「ビルド」に行き、「新しいビルドをアップロード」から、修正したAPKファイルをアップロードします。 ※申請 / ①ビルド を参照。 他の項目は設定済なので、最後の「送信」から、再度「ストアに送信」します。 アプリのプロモーション(任意) 無事に審査を通過してリリースされると、Oculusの公式ストアから検索できるようになります。(AppLabでリリースされた作品は、完全一致でないと検索に出てこないので注意) リリース後のプロモーションについては、いろいろな方法があると思いますし自由ですが、「Oculusキーの発行」について、ハマった箇所があったので記載しておきたいと思います。 1. Oculusキーの発行 「Oculusキー」とは、開発者側で作成して自由に配布できる、ダウンロードコードのことです。公式サイトでは、下記のように説明されています。 Oculusキーは、アプリ承認後、必要に応じて生成できる25文字の英数字コードです。Oculusキーの発行権を得てキーを発行し配布すると、キーを受け取った人はOculusアプリで[コードを利用]セクションにキーを入力することにより、アプリまたはアプリ内購入アイテムをダウンロードできます。キーは、販売、贈呈のほか、印刷してオフラインで配布するなど、開発者の都合に合わせて配布することができます。キーはメディアにレビュー用の見本を渡す場合などにも便利です。 こちらのページで作成方法も記載されているのですが、作成ボタン(①)から開いたウィンドウで必要項目を記入するところまでは問題ないと思います。 その後、下記のように説明されているのですが、 6.完了したら[送信]をクリックします。250以下のキーを作成した場合は、キーのリストが表示されます。各キーのフォーマットは5x5です(例: 3YEBU-GCRKT-SHHHH-PYE0T-KVW7S)。 7.このページを閉じるとキーを取得できなくなるため、キーをコピーして安全な場所に保存します。 [送信]ボタンがないので作成ボタン(②)を押すと、「キャンペーン名」がリストアップはされるものの、「キーのリスト」は表示されません。。あたかもこのページにキーが表示されるような書き方なのですが、実はコードが記載されたテキストファイルが自動でダウンロードされるようです。(ダウンロードフォルダを確認してみてください。) これに気づくまでにかなりの時間を要したので、参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】パッケージマネージャ トラブルシューティング

[Addressable]ArgumentException: An item with the same key has already been added. Key:****** が発生した際にKey:******を含むファイルが存在しない。 Unity2019.4.23f1で発生 本来であれば、重複したAddressableAssetSettingsの定義を片方削除するとエラーを解除できるのですが、そもそもキーを含むファイルが存在しないケースが発生しました。 ⇨Unity再起動でエラーから復旧しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む