FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »Unityリファレンス
このページの記事一覧

【Unity】【C#】ゲームパッドの右スティックを使えるようにする  


 基本的にはこちらの資料そのままという感じだが、もっとざっくりと視覚的に見たいときの自分用メモ。なので詳しく知りたいときは以下のURLを参照して欲しい。まぁ、内容的に難しくはないのだが、実は「ゲームパッドの機器によって設定が異なる」という問題があることを知ったので、コードより手順の方を簡単に書いておこうかと。

Unityでゲーム開発 -左右スティックを使う方法-

(※) Unity 2019.2.21f1 / Windows10(x64) で確認



■InputManager で右スティック用設定を作っておく

 まずは、メニューから「Edit>Project Settings...」を開き、左のタブから「Input」を選択する。

 そしたら、一番上の「Size」を+2個し、右スティック用の設定を作る(※水平・垂直2方向の場合)。

 名前「Name」はコードで使うので、自分でわかりやすい名前を付けておく

 ここでは「Axis」の項目で「4th axis(Joystick)」「5th axis(Joystick)」を選択しているが、ここが前述した「機器によって違う」項目であり、ランタイム時にユーザーに設定して貰うしかないので、とりあえず開発テスト用の機器に合わせたもので良い(わからなければ、この設定でも良い。※ここでは「Logicool Rumble Gamepad F510」を使用例に用いている)。

 注意点としては「Invert」(値の正負反転)はオフにしておいた方が良いだろう。これもゲームパッド機器によって違うので、アプリ内で設定できるようにしておく必要がある





■右スティックをコードで取得する

 先ほど InputManager で付けた名前を使って、コードから入力値を取得する。取得できる値の範囲は -1~0~+1 だ。「Invert」がオンになっている場合は、正負が反転する。ここでは InputManager での「Invert」はオフであるとして、アプリ内にユーザーが設定できる UI などを置いて、それがオン・オフできると仮定しておこう(UI-Toggle.isOn 等が簡単)。そしてそれらをプロパティで表わすとする。尚、私は「Logicool Rumble Gamepad F510」という機器を使ってテストしているので、それに合わせてデフォルト値を設定しているが、開発環境によって任意で良い。

●右スティックの入力を取得する例(※ .NET 4.x 以降)
using UnityEngine;

//反転オプションプロパティ (※Logicool Rumble Gamepad F510 でのデフォ例。任意で良い)
bool InvertH { get; set; } = false; //水平方向反転オプション (UI などでユーザー設定できるようにしておく)
bool InvertV { get; set; } = true; //垂直方向反転オプション (UI などでユーザー設定できるようにしておく)

//右スティックの入力による方向の取得
float h = Input.GetAxis("HorizontalStickR"); //水平方向 ※InputManager で付けた名前
float v = Input.GetAxis("VerticalStickR"); //垂直方向 ※InputManager で付けた名前
Vector2 dir = new Vector2(h * (InvertH ? 1 : -1), v * (InvertV ? 1 : -1));

//方向による処理など・・・
Debug.Log($"h = {h}, v = {v}, dir = {dir}");

 もちろん、Input.GetAxis() の代わりに Input.GetAxisRaw() を用いても良い。その場合は中間値がなく、値が -1, 0, +1 のいずれかになる(つまり量がなく、方向だけ取得する感じになる)。



■アプリ起動前のダイアログで右スティックを設定する

 なんからのテストできるものを作ったら、アプリをビルドしてみよう。

 右スティックを設定するには、起動時に出るダイアログで「Input」タブに切り替え、InputManager で付けた名前をダブルクリックする。そしたら「Input Configuration」の小さなボックスが出るので、このときに、右スティックを利用する方向へ倒せば登録される(表示も変わる)。この操作をユーザーにやって貰う必要がある(なので、説明書とか必要だよね…)。

 登録完了したら、「Play!」してアプリ内で確認してみよう。

●名前をダブルクリックすると、「Input Configuration」ボックスが出る


●右スティックをそれぞれ利用する方向へ倒すと(上or下/左or右)登録される


 尚「Input Configuration」で設定した値は、レジストリ(Windows)で「__Input Joystick Axis HorizontalStickR_~」のように記録されているようだ(axis3 なら値は3 のように)。なので、一度設定したら、毎回設定する必要なく、継続して使える。





(関連記事)
【Unity】【C#】CapsLock のオン・オフ状態を取得する (Windows)
【Unity】5ボタンマウスの KeyCode 図解
【Unity】【C#】KeyCode をリアルタイムで調べる / Windows 日本語キーボードでの記号の KeyCode 一覧
【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする
【Unity】【C#】長押し(ロングタップ)を取得してコールバックする
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録


関連記事
スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  C# 
tb: 0   cm: --

【Unity】【C#】CapsLock のオン・オフ状態を取得する (Windows)  


 Unity には Input クラスという入力に関するクラスが元からあるが、残念ながら「Input.GetKey(KeyCode.CapsLock)」のようにして、CapsLock の状態を取得するのは上手く行かなかった(押下した瞬間の判定なので、常に true になる)。

 また、OnGUI() での Event.capsLock というものもあるが(Update() などでは使えない=エラーが出る)、こちらも同じように押下した瞬間に true になるだけで、「オン/オフの継続している状態」が取得できない。

 なのでいつものようにググってたら、Windows API から取得する方法があったので、試してみた。しかし同時に利用するにあたって、いくつか注意する点もあったので、備忘録として残しておこう。

(※) Unity 2019.2.21f1 / Windows10(x64) で確認

 まずは Windows API から状態を取得するメソッドを static で定義しよう。以下のコードは参考資料の内容を、使い回せるように少しだけ手を加えたものだ。.NET 4.x 以降のコードになっているので注意して欲しい(.NET 3.x にしたいなら「=>」などを通常のメソッドに書き直せば使える)。

(参考)
Capslock detection ?

●Windows API から CapsLock の状態を取得する
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
using System.Runtime.InteropServices;

public static class WindowsUtil //※名前は任意
{
//https://forum.unity.com/threads/capslock-detection.538792/
[DllImport("user32.dll")]
public static extern short GetKeyState(int keyCode);

//CapsLock の状態取得
public static bool IsCapsLockOn
=> (((ushort)GetKeyState(0x14)) & 0xffff) != 0; //※.NET 4.x
}
#endif

 あとは参考資料と同じように、起動時にプロパティにでも入れておいて、キー入力のたびに反転するのが良いだろう。毎フレーム API 使うのは負荷が高そうだしね(常に型変換の必要があるので)。ここまではほとんど参考資料と同じだ。

 メインは例えば、以下のようなコードで確認できるだろう(何かキーを押したときに CapsLock のオン・オフ状態が取得できる)。

●メインでの使用例
using UnityEngine;

bool IsCapsLockOn { get; set; }

private void Awake()
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IsCapsLockOn = WindowsUtil.IsCapsLockOn; //Windows API を定義したクラス
#endif
}

private void Update()
{
if (Input.anyKeyDown)
{
if (Input.GetKeyDown(KeyCode.CapsLock))
{
IsCapsLockOn = !IsCapsLockOn;
}

Debug.Log($"IsCapsLockOn = {IsCapsLockOn}"); //※.NET 4.x
}
}

 ただこれには1つ注意点があって、「アプリから離れる → 他のアプリ上などで CapsLock を切り替える → アプリに戻る」などの操作をすると、IsCapsLock のプロパティが狂ってしまう。なので、再度アプリにフォーカスが当たったときや、アプリのポーズ復帰(Resume)のようなタイミングで再度 Windows API から取得した方が良いようだ。

●フォーカスやレジュームのタイミングで再取得例
・・・(中略 ※前述のコード)・・・
private void OnApplicationFocus(bool focus)
{
if (focus)
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IsCapsLockOn = WindowsUtil.IsCapsLockOn;
#endif
}
}

private void OnApplicationPause(bool pause)
{
if (!pause) //Resume
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IsCapsLockOn = WindowsUtil.IsCapsLockOn;
#endif
}
}

 しかし、実はまだこれでも上手く行かないときがある。何度か試してみたら、どうやら OnApplicationFocus 等と同フレームで取得すると、不正確になるようだ。なので1フレーム遅らせて取得するなどの工夫も必要になる(エディタ上では上手く行くかも知れないが、ランタイム時にはタイミングによって上手く行かないことがある=Unity ではよくある事なので覚えておくと良い)。

 1フレーム遅らせて処理する方法は色々あるので、好きにやって欲しい。コルーチンでも良いし、.NET 4.x なら async/await でも良いだろう。UniRx プラグイン使っているなら Observable.NextFrame() を使うのも良いね。以下に例を載せておくので、既存のコードとの兼ね合いで実装すると良いだろう。

●コルーチン例
・・・(中略)・・・
private IEnumerator OnApplicationFocus(bool focus)
{
if (focus)
{
yield return null;
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IsCapsLockOn = WindowsUtil.IsCapsLockOn;
#endif
}
}

//OnApplicationPause も同様


●UniRx, UniTask / async, await 例
【Unity】【UniRx】時間系のオペレータまとめ
【Unity】【C#】UniRx で「1フレームごと待機して処理」してみる





(関連記事)
【Unity】【C#】UniRx で「1フレームごと待機して処理」してみる
【Unity】【C#】KeyCode をリアルタイムで調べる / Windows 日本語キーボードでの記号の KeyCode 一覧
【Unity】5ボタンマウスの KeyCode 図解
【Unity】【C#】ゲームパッドの右スティックを使えるようにする


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  WindowsAPI  C# 
tb: 0   cm: --

【Unity】【C#】現在再生中のアニメーションの経過時間を取得する  


 なぜかググっても簡単な例が見つからなかったので、とりあえずのものを書いておく。長さの取得例は多いんだけどね。基本的には AnimatorStateInfoAnimationState あたりで取得できる情報を使えば良いだけなのだが、毎回書くのは面倒だったので、拡張メソッドを定義しておくと楽になると思う。


(※) Unity 2019.2.20f1 / Windows10(x64) で確認



■Animator から経過時間/長さを取得

●Animator から経過時間/長さを取得
using UnityEngine;

public static class Extensions //※名前は任意
{
/// <summary>
/// 現在のアニメーションの経過時間を取得する
/// </summary>
/// <param name="animator">対象のアニメータ</param>
/// <param name="layerIndex">レイヤーのインデクス</param>
/// <returns>経過時間(秒), null のときは常に 0</returns>
public static float GetCurrentTime(this Animator animator, int layerIndex = 0)
{
if (animator == null)
return 0;

AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(layerIndex); //現在のステートを取得
return stateInfo.length * stateInfo.normalizedTime;
}

/// <summary>
/// 現在のアニメーションの長さを取得する
/// </summary>
/// <param name="animator">対象のアニメータ</param>
/// <param name="layerIndex">レイヤーのインデクス</param>
/// <returns>アニメーションの長さ(秒), null のときは常に 0</returns>
public static float GetLength(this Animator animator, int layerIndex = 0)
{
if (animator == null)
return 0;

AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(layerIndex); //現在のステートを取得
return stateInfo.length;
}
}

●使用例(メインコード等)
using UnityEngine;

var animator = GetComponent<Animator>();
Debug.Log("time = " + animator.GetCurrentTime()); //※アクセスしているアニメーションが動いている方がわかりやすい
Debug.Log("length = " + animator.GetLength());

 GetLength() の方はついでに作っておいただけのものなので、実際には同時に取得したいなら、普通に AnimatorStateInfo 取得してプロパティを使った方が良いかも知れない。特に長さは途中で変わるものではないから、使い回すときには結果をキャッシュした方が良いこともあるね。

AnimatorStateInfo



■Animation から経過時間/長さを取得

●Animation から経過時間/長さを取得
using UnityEngine;

public static class Extensions //※名前は任意
{
/// <summary>
/// 現在のアニメーションの経過時間を取得する
/// </summary>
/// <param name="animation">対象のアニメーション</param>
/// <param name="allowWrapMode">ラップモードに従う:true = 永久ループ系(Loop, PingPong)のとき長さ以上も許可 / false = 曲の長さ以内</param>
/// <returns>経過時間(秒), null のときは常に 0</returns>
public static float GetCurrentTime(this Animation animation, bool allowWrapMode = false)
{
if (animation == null || animation.clip == null)
return 0;

AnimationState state = animation[animation.clip.name]; //現在のステートを取得
//WrapMode が永久ループ系(Loop, PingPong)のとき、長さ(length)より大きくなることもある
//https://docs.unity3d.com/ja/current/ScriptReference/WrapMode.html
//また、再生速度のプロパティ:speed は1(1倍)で考えているが、1でないときは time は現実時間とは違うので注意(長さに対する位置と考えるとわかりやすい)
return allowWrapMode ? state.time : (state.time % state.length);
}


/// <summary>
/// 現在のアニメーションの長さを取得する
/// </summary>
/// <param name="animation">対象のアニメーション</param>
/// <returns>アニメーションの長さ(秒), null のときは常に 0</returns>
public static float GetLength(this Animation animation)
{
if (animation == null || animation.clip == null)
return 0;

return animation.clip.length;
}
}

●使用例(メインコード等)
using UnityEngine;

var animation = GetComponent<Animation>();
Debug.Log("time = " + animation.GetCurrentTime()); //※アクセスしているアニメーションが動いている方がわかりやすい
Debug.Log("length = " + animation.GetLength());

 GetLength() の方はついでに作っておいただけのものなので、実際には同時に取得したいなら、普通に AnimationState 取得してプロパティを使った方が良いかも知れない。特に長さはステートからでも clip プロパティ(AnimationClip) からも取得できるのでメソッド無くても良いしね(まぁ、Animator のと同じように統一して定義しておくと、迷わなくて済むというだけ(笑))。

 AnimationState にも AnimatorStateInfo のように正規化された時間(曲の長さを1とし、0~1で位置を表す):normalizedTime があるので、「state.length * state.normalizedTime」でも経過時間を求められる。必要あればその方法で計算をするのも良いだろう(この例では別段必要はないので time の方にした)。

AnimationState


 また、ここではアニメーションの再生速度のプロパティ:speed は1(=1倍)で考えているが、1でないときは time は現実時間とは違うので注意しよう(長さに対する位置と考えるとわかりやすい)。その場合、現実時間は以下のようにすれば求まる。

●speed が1以外のとき、現実の時間を求める
var animation = GetComponent<Animation>();
AnimationState state = animation[animation.clip.name];
float realTime = (1f / state.speed) * state.time;

 再生速度に関わらず現実時間に合わせたいなら「state.speed * state.time」で良い。まぁ、このような比率的な計算をするなら normalizedTime を使った方が直感的に書けるというメリットがある。用途に合わせて使い分けると良いだろう。







(関連記事)
【Unity】AnimationCurve をコードで初期化する
【Unity】5.6 のアニメーションは非アクティブ化でリセットされない(5.6のアニメーション挙動の違い)


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityリファレンス 
tb: 0   cm: --

【Unity】タイトルバーの「PREVIEW PACKAGES IN USE」を消す  


 Unity2018 から Unity2019 にアップグレードする際に、いくつかの問題があってなかなか移行できなかったのだが、やっと目星が付いたので、そのままプロジェクトをアップグレードしたら、タイトルバーに「PREVIEW PACKAGES IN USE」が常に出ている…。

 もちろん、自分で Preview パッケージをインストールしたならわかるが、実はせいぜい「Text Mesh Pro」ぐらいしか使ってなく、しかも Unity2018 のメニューから自動で設定されているものを使っていたので、特にバージョンは気にしてない(通常、アップグレードで自動で更新される)。

 その上、PackageManager 上で見ても「-preview~」となっているものが無い、そんな状態。

 要するにアップグレードした際に古いものが残っているらしい。しかし明示的にインストしたわけでないので、手動で削除はちと怖い。

 というわけでいつものようにググってたら、また英文記事にひっかかった。まぁ、見ればわかる程度なので、翻訳の必要はないが、同じ様にググる人はいると思うので、いつものように備忘録として残しておく。

(※) Unity 2019.2.14f1 / Windows10(x64) で確認

(参考)
What is "PREVIEW PACKAGES IN USE"

 やり方としては、

 メニューから
Window>Package Manager」を開いて、

 上部のプルダウンメニュー

Advanced>Reset Packages to defaults

 すればOK。

 注意点としては、自分でパッケージをカスタマイズしている場合は消えてしまう可能性大なので、気を付けた方が良いかも。


 「manifest.json」の差分を見てみると、いくつかパッケージが消えてるみたい。Unity 自体も今後はパッケージでコンポーネントを供給するようになって来るみたいだから、たまにこの作業は必要になるかもね。同じパッケージの利用なら自動アップグレードされるが、他のパッケージに統合されたり、Unity ビルトインに仕様変更されたりと、次のバージョンと比較できなくなったら、そのまま残ってしまうことがあるっぽい。


 今後は github からも直接 PackageManager でインストもできるようになるみたいだし、毎回 AssetStore にバージョン確認しなくて済むのは良いんだけどね。全自動というわけにはいかないか…(笑)。





(関連記事)
【Unity】【Android】Unity2019 へのアップグレードの問題点 メモ
【Unity】【Android】2019.2.0 でパッケージ名(Bundle Identifer)でアンダーバーが使えない
【Unity】Unity2018 でビルドエラー「CommandInvokationFailure: Gradle build failed.」が出る
【Unity】IncrementalCompiler でのエラー:Unloading broken assembly Packages/com.unity.incrementalcompiler/Editor/Plugins/Unity.PureCSharpTests.dll, this assembly can cause crashes in the runtime
【Unity】EventTrigger のコールバック引数変わった?


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityトラブルシューティング  Unityリファレンス 
tb: 0   cm: --

【Unity】UI のフォーカスを外すコードと「Attempting to select while already selecting an object.」  


 最近まで気が付かなかったのだが、Unity の UI を使っていると、例えばマウスでボタンをクリックした後に、キーボードの [Enter] キーを押すとそのままフォーカスが残っていて、無駄に連打されてしまうことがある。

 そこでフォーカスを外す方法を調べたら「EventSystem.current.SetSelectedGameObject(null) を使うと良い」とあったので使ってみたのだが(※interactable を一旦 false にする手もあるが、元に戻す必要がある)、ごく稀にタイミングによっては以下のエラーが出ることがある。

Attempting to select while already selecting an object.
UnityEngine.EventSystems.EventSystem:SetSelectedGameObject(GameObject)
・・・(中略)・・・
UnityEngine.EventSystems.EventSystem:Update()

(※) Unity 2018.4.13f1 / Windows10(x64) で確認

 最初の1行(Attempting to select while already selecting an object.)を google翻訳にかけると…

既にオブジェクトを選択しているときに選択しようとしています。

 いまいち意味がわからなかったので「Attempting to select while already selecting an object.」でググってみると、以下の記事が出てきた。

選択中扱いとするゲームオブジェクトの管理
Event System SetSelectedGameObject Error, but code still runs fine.
Unity UI 4.6 inputfield bug or behavoir?

 ああ、なるほど「現在選択中のオブジェクトと EventSystem.SetSelectedGameObject」 の引数のオブジェクトと比較して、同じだったらエラー出してる」んだね、と理解できた。

 また、フォーラムでは「EventSystem.alreadySelecting」(EventSystem が SetSelectedGameObject に存在する場合は true を返す)を使うと良い、みたいなことが書かれてあったので、試してみたのだが、「フォーカスを外す」ときの引数に null を渡す場合、やはりたまにエラーが出ることがある。まぁ、Debug.LogError なら実害は無いのだが、なんとなく気持ち悪いので、他の方法がないかと試してみたら、以下のコードで上手くいった。

//現在アクティブとみなされる GameObject が null でないとき
if (EventSystem.current.currentSelectedGameObject != null)
{
EventSystem.current.SetSelectedGameObject(null); //フォーカスを外す
}

EventSystem.currentSelectedGameObject

 まぁ、使っているコードが EventSystem の static なメソッドのみなので、static なクラスに関数を作っても良いと思う。地味な挙動だが、かえってサンプルコードみたいのが出て来なかったので、備忘録として残しておく。





(関連記事)
【Unity】Unity2018 でビルドエラー「CommandInvokationFailure: Gradle build failed.」が出る
【Unity】IncrementalCompiler でのエラー:Unloading broken assembly Packages/com.unity.incrementalcompiler/Editor/Plugins/Unity.PureCSharpTests.dll, this assembly can cause crashes in the runtime
【Unity】【Android】2019.2.0 でパッケージ名(Bundle Identifer)でアンダーバーが使えない
【Unity】EventTrigger のコールバック引数変わった?


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  Unityトラブルシューティング  uGUI 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR