- 投稿日:2020-06-30T23:14:22+09:00
【Unity:Log】Android Logcat
- 投稿日:2020-06-30T16:18:21+09:00
【Blender】Blenderでメッシュから服をつくるにあたり、画像で備忘録【メモ】
- 投稿日:2020-06-30T14:55:45+09:00
UnityでXBOXコントローラーを振動させる
忘れそうなので自分用メモとして残しておきます。
Unity 2019.4で確認済みです。今回はこちらのXInputDotNetというライブラリを使用させていただきました。
とても助かりました。感謝。
https://github.com/speps/XInputDotNet.git手順
https://github.com/speps/XInputDotNet/releases
上にある最新のunityPackageをプロジェクトにインポートして
VibrationTest.csXInputDotNetPure.GamePad.SetVibration(0,5,5);この一行で振動してくれました。お手軽。
左右別々のモーターをそれぞれ動かすことができるようです。例
例えばこんな風にテスト用のスクリプトを書いてみました。
VibrationTest.csusing System.Collections; using UnityEngine; using XInputDotNetPure; public class VibrationTest : MonoBehaviour { [SerializeField] float right_power = 1; [SerializeField] float left_power = 1; [SerializeField] float duration = 0.5f; void Update() { if (Input.GetButtonDown("Fire1")) { StartCoroutine("RightVibration"); } if (Input.GetButtonDown("Fire2")) { StartCoroutine("LeftVibration"); } } IEnumerator RightVibration() { GamePad.SetVibration(0, 0, right_power); yield return new WaitForSecondsRealtime(duration); GamePad.SetVibration(0, 0, 0); } IEnumerator LeftVibration() { GamePad.SetVibration(0, left_power, 0); yield return new WaitForSecondsRealtime(duration); GamePad.SetVibration(0, 0, 0); } }今のところ0をセットすることで振動を止めていますが、これでいいのでしょうか…
参考
- 投稿日:2020-06-30T11:33:43+09:00
Unity+ADX2環境でエディタからプロジェクト設定情報(acf)を確認する
acfファイルの中身をエディタで表示&クリップボードにコピー
.acfファイルはADX2から出力される、ADX2のプロジェクト設定が入ったファイルです。
Atom Craftでは「プロジェクトツリー」の「全体設定」以下で設定する項目となっており、ゲームプロジェクト全体で使用するADX2の設定情報になります。Atom Craftを開けばどんな設定になっているかすぐに確認できます。しかしながら、複数人で作業している場合や、今作業している環境にAtom Craftやプロジェクトファイルがない状態のときは、出力されたacfファイルの中身がどんなものであるかはわかりません。そこで、Unity Editor上でacfの中身を取り出しておき、作業者がAtom Craftを開かずとも設定が簡単に確認できることを目指します。
今回は「カテゴリーの情報」と「AISACコントロールの情報」の2つを表示するウィンドウをエディタ拡張で作成します。
ソースコードとunitypackage
Unity ADX2 Acf Viewer
https://github.com/TakaakiIchijo/UnityADX2AcfViewer確認環境
Unity 2019.4.1f1
ADX2 LE SDK 2.10.05Scriptable Objectでacf情報を保存する
ADX2 LEでは、エディタで一度ゲームを実行するとacfファイルの中身をADX2のランタイムが読み込む仕組みになっています。
これから作成するACF Viewerもゲームを実行してからデータ読み込み、という手続きになりますが、エディターを起動するたびに読み込むのは面倒です。そこで、一回読み込んだacfのデータはScriptable Objectとして保存しておき、次回起動時にはこのScriptableObjectからデータを読み込むことにします。「カテゴリー」と「AISACコントロール」のプロパティを保存するScriptable Object
は次の通りです。AcfDataSo.csusing System.Collections.Generic; using UnityEngine; using ACFDataClass; public class AcfDataSo : ScriptableObject { public List<CategoryInfo> categoryInfoList = new List<CategoryInfo>(); public List<AisacControlInfo> aisacControlInfoList = new List<AisacControlInfo>(); } namespace ACFDataClass { [System.Serializable] public class CategoryInfo { public string name; public uint groupNo; public uint id; public uint numCueLimits; public float volume; public CategoryInfo(uint groupNo, uint id, string name, uint numCueLimits, float volume) { this.groupNo = groupNo; this.id = id; this.name = name; this.numCueLimits = numCueLimits; this.volume = volume; } } [System.Serializable] public class AisacControlInfo { public string name; public uint id; public AisacControlInfo(string name, uint id) { this.name = name; this.id = id; } } }ウィンドウが有効になったらScriptable Objectを作成する
次に、保存用のScriptableObjectのファイルをエディタ上で生成します。
EditorWindowのOnEnableメソッドで、指定のパスにScriptableObjectが存在するか確認し、なければ新しく作ります。CriAtomAcfViewerWindow.csusing System; using System.Collections.Generic; using System.Linq; using ACFDataClass; using UnityEditor; using UnityEngine; public class CriAtomAcfViewerWindow : EditorWindow { const string acfScriptableObjectPath = "Assets/Editor/CriWare/CriAtom/acfDataSo.asset"; private AcfDataSo acfDataSo; [MenuItem("Window/CRIWARE/Open Acf Viewer Window", false, 100)] static void OpenWindow() { GetWindow<CriAtomAcfViewerWindow>("Acf Viewer"); } private void OnEnable() { acfDataSo = AssetDatabase.LoadAssetAtPath<AcfDataSo>(acfScriptableObjectPath); if (acfDataSo == null) { acfDataSo = CreateInstance<AcfDataSo>(); AssetDatabase.CreateAsset(acfDataSo, acfScriptableObjectPath); AssetDatabase.Refresh(); } } //以下略ADX2のランタイムからacf情報を読み込む
ランタイムからのacf情報読み込みは、CriAtomExAcfDebugクラスを経由して行います。
カテゴリー情報はGetNumCategoriesでカテゴリーの数を取得し、GetCategoryInfoByIndexでカテゴリの情報を取得できます。CriAtomAcfViewerWindow.csprivate List<CategoryInfo> LoadCategory() { List<CategoryInfo> categoryInfoList = new List<CategoryInfo>(); for (ushort i = 0; i < CriAtomExAcfDebug.GetNumCategories(); i++) { CriAtomExAcfDebug.GetCategoryInfoByIndex(i, out var categoryInfo); categoryInfoList.Add(new CategoryInfo( categoryInfo.groupNo, categoryInfo.id, categoryInfo.name, categoryInfo.numCueLimits, categoryInfo.volume)); } return categoryInfoList; }AISACコントロール情報も全く同様に、GetNumAisacControlsで数を取得し、GetAisacControlInfoで情報を取得できます。
CriAtomAcfViewerWindow.csprivate List<AisacControlInfo> LoadAisacControlList() { List<AisacControlInfo> aisacControlInfoList =new List<AisacControlInfo>(); for (ushort i = 0; i < CriAtomExAcfDebug.GetNumAisacControls(); i++) { CriAtomExAcfDebug.GetAisacControlInfo(i, out var aisacControlInfo); aisacControlInfoList.Add(new AisacControlInfo( aisacControlInfo.name, aisacControlInfo.id)); } aisacControlInfoList.Sort((a,b) => (int)a.id - (int)b.id); return aisacControlInfoList; }ウィンドウの各要素の描画
次に、ウィンドウを構成する各要素の描画処理を書いていきます。
acf読み込みボタンの描画
「Load ACF data from runtime」ボタンは、EditorApplication.isPlayingを使ってゲームが実行中かどうかを判定し、実行中のみacfの読み込み処理を行います。
先ほど作成したScriptableObjectにカテゴリの情報、アイザックの情報を読み込み、保存します。CriAtomAcfViewerWindow.csprivate void DrawLoadButton() { GUILayout.BeginHorizontal(); { if (GUILayout.Button("Load ACF data from runtime", GUILayout.Width(200), GUILayout.Height(50))) { if (!EditorApplication.isPlaying) { Debug.LogError("Please load acf data in editor playing"); } else { acfDataSo = AssetDatabase.LoadAssetAtPath<AcfDataSo>(acfScriptableObjectPath); acfDataSo.categoryInfoList = LoadCategory(); acfDataSo.aisacControlInfoList = LoadAisacControlList(); EditorUtility.SetDirty(acfDataSo); AssetDatabase.SaveAssets(); } } //中略 } GUILayout.EndHorizontal(); }カテゴリー情報の項目名を描画
カテゴリー情報を表示する箇所の、「項目名」部分を描画します。
これらはボタンで構成されており、クリックすることでそれぞれの項目の昇順で並び替えを行うことができます。CriAtomAcfViewerWindow.csprivate void DrawCategoryItemNames() { List<CategoryInfo> categoryInfoList = acfDataSo.categoryInfoList; GUILayout.BeginHorizontal(); { GUIStyle style = new GUIStyle(EditorStyles.miniButtonMid); style.alignment = TextAnchor.LowerLeft; if(GUILayout.Button("Category Name", style)) { categoryInfoList.Sort((a,b) => String.CompareOrdinal(a.name, b.name)); } if(GUILayout.Button("Volume", style, GUILayout.Width(80))) { categoryInfoList.Sort((a,b) => (int)a.volume - (int)b.volume); } if(GUILayout.Button("Cue Limit", style, GUILayout.Width(80))) { categoryInfoList.Sort((a,b) => (int)a.numCueLimits - (int)b.numCueLimits); } if(GUILayout.Button("Id", style, GUILayout.Width(80))) { categoryInfoList.Sort((a,b) => (int)a.id - (int)b.id); } if(GUILayout.Button("Group No", style, GUILayout.Width(80))) { categoryInfoList.Sort((a,b) => (int)a.groupNo - (int)b.groupNo); } } GUILayout.EndHorizontal(); }カテゴリーのリストを描画
カテゴリー情報のリストを順番に描画します。「カテゴリ名」の部分はボタンになっておおり、クリックするとそのカテゴリ名をクリップボードにコピします。
EditorGUIUtility.systemCopyBuffer を使うことで、簡単にクリップボードにアクセスできます。
ラジオボタンにしている理由は、見た目がリストっぽくなるためです。
クリックされた行はインデックスを保持しておき、黄色で表示します。CriAtomAcfViewerWindow.csprivate int selectedInfoIndex; //中略 private void DrawCategoryList() { List<CategoryInfo> categoryInfoList = acfDataSo.categoryInfoList; for(int i = 0; i < categoryInfoList.Count; ++i) { EditorGUILayout.BeginHorizontal(); if (this.selectedInfoIndex == i) { GUI.color = Color.yellow; } else { GUI.color = Color.white; } var categoryName = categoryInfoList[i].name; if (GUILayout.Button(categoryName, EditorStyles.radioButton)) { EditorGUIUtility.systemCopyBuffer = categoryName; this.selectedInfoIndex = i; Debug.Log("Saved to clipboard " + "\""+categoryName+ "\""); } GUILayout.Label(categoryInfoList[i].volume.ToString(), GUILayout.Width(75)); string numCueLimits = categoryInfoList[i].numCueLimits == UInt32.MaxValue ? "Unlimited" : categoryInfoList[i].numCueLimits.ToString(); GUILayout.Label(numCueLimits, GUILayout.Width(75)); GUILayout.Label(categoryInfoList[i].id.ToString(), GUILayout.Width(75)); GUILayout.Label(categoryInfoList[i].groupNo.ToString(), GUILayout.Width(70)); EditorGUILayout.EndHorizontal(); } GUI.color = Color.white; }キューリミットの値については、リミットを設定していない場合はuintの最大値( 4,294,967,295)が入るため、これと一致した場合は「Unlimited」と表示するようにしています。
AISACコントロール情報の項目名を描画
AISACコントロールについてもカテゴリと同様です。項目名をボタンで表示し、クリック時にソートを行います。
CriAtomAcfViewerWindow.csprivate void DrawAisacControlItemNames() { List<AisacControlInfo> aisacControlInfoList = acfDataSo.aisacControlInfoList; GUILayout.BeginHorizontal(); { GUIStyle style = new GUIStyle(EditorStyles.miniButtonMid); style.alignment = TextAnchor.LowerLeft; if (GUILayout.Button("Aisac Control Name", style)) { aisacControlInfoList.Sort((a,b) => String.CompareOrdinal(a.name, b.name)); } if (GUILayout.Button("ID", style, GUILayout.Width(80))) { aisacControlInfoList.Sort((a,b) => (int)a.id - (int)b.id); } } GUILayout.EndHorizontal(); }AISACコントロールのリストを描画
AISACコントロールの表示にはオプションとして「デフォルトのAISAC値を表示するかどうか」というチェックボックスを設けます。「AisacControl_00」のフォーマットでデフォルトでは16個用意されているため、これをリスト上で見せるか見せないかを切り替えます。
CriAtomAcfViewerWindow.csbool hideDefaultAisacControls = true; //中略 private void DrawAisacControlList() { List<AisacControlInfo> list = acfDataSo.aisacControlInfoList; if (hideDefaultAisacControls) { list = list.Where(a => a.name.Contains("AisacControl_") == false).ToList(); } for(int i = 0; i < list.Count; ++i) { EditorGUILayout.BeginHorizontal(); if (this.selectedInfoIndex == i) { GUI.color = Color.yellow; } else { GUI.color = Color.white; } var aisacName = list[i].name; if (GUILayout.Button(aisacName, EditorStyles.radioButton)) { EditorGUIUtility.systemCopyBuffer = aisacName; this.selectedInfoIndex = i; Debug.Log("Saved to clipboard " + "\""+aisacName+ "\""); } GUILayout.Label(list[i].id.ToString(), GUILayout.Width(70)); EditorGUILayout.EndHorizontal(); } GUI.color = Color.white; }OnGUIで各描画パーツを呼んでウィンドウを描画する
最後にOnGUIの中でDrawLoadButton、DrawCategoryItemNames、DrawCategoryList、DrawAisacControlItemNames、DrawAisacControlListをスクロールビューで囲みながら順番に呼びます。
CriAtomAcfViewerWindow.csprivate Vector2 scrollPos_Window, scrollPosCategory, scroppPosAisacControl; //中略 public void OnGUI() { DrawLoadButton(); if (acfDataSo == null) return; this.scrollPos_Window = GUILayout.BeginScrollView(this.scrollPos_Window); { DrawCategoryItemNames(); float categoryHeight = this.position.height - 300.0f; if (categoryHeight < 100.0f) categoryHeight = 100.0f; scrollPosCategory = EditorGUILayout.BeginScrollView(scrollPosCategory, GUILayout.Height(categoryHeight)); DrawCategoryList(); EditorGUILayout.EndScrollView(); DrawAisacControlItemNames(); float aisacControlHeight = this.position.height - 300.0f; if (aisacControlHeight < 100.0f) aisacControlHeight = 100.0f; scroppPosAisacControl = EditorGUILayout.BeginScrollView(scroppPosAisacControl); DrawAisacControlList(); EditorGUILayout.EndScrollView(); } GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(2)); hideDefaultAisacControls = GUILayout.Toggle(hideDefaultAisacControls, "Hide default name AISAC controls"); GUILayout.Space(10); EditorGUILayout.EndScrollView(); }一番下でGUILayout.Toggleを使って、デフォルトのAISACコントロール名を表示するかしないかのオプションを描画しています。
拡張性
今回は「カテゴリー」「AISACコントロール」のみを表示しましたが、acfにはほかの情報も含まれています。
DSPバス設定やゲーム変数、セレクタなどが取得できますが、データの取得と描画方法は全く一緒です。プロジェクトに応じて必要な情報を見られるウィンドウを作ってみましょう。
また、保存したScriptable ObjectはEditor以外の位置に配置すればランタイムでも利用できます。
- 投稿日:2020-06-30T00:20:32+09:00
Unity メモ
キー入力
Input.GetKeyDown( KeyCode.Space );カメラをプレイヤーに追従させる
CameraController.cspublic class CameraController : MonoBehaviour { GameObject player; void Start() { this.player = GameObject.Find("player"); } void Update() { Vector3 playerPos = this.player.transform.position; transform.position = new Vector3( transform.position.x, playerPos.y, transform.position.z ); } }