- 2020/04/04 【Unity】【C#】ゲームパッドの右スティックを使えるようにする
- 2020/03/18 【Unity】【C#】CapsLock のオン・オフ状態を取得する (Windows)
- 2019/08/26 【Unity】5ボタンマウスの KeyCode 図解
- 2019/07/20 【Unity】【C#】KeyCode をリアルタイムで調べる / Windows 日本語キーボードでの記号の KeyCode 一覧
- 2018/01/09 【Unity】【C#】ピンチ操作を取得してコールバックする
« prev next »
【Unity】【C#】ゲームパッドの右スティックを使えるようにする 
2020/04/04 Sat [edit]
基本的にはこちらの資料そのままという感じだが、もっとざっくりと視覚的に見たいときの自分用メモ。なので詳しく知りたいときは以下の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」で設定した値は、レジストリ(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 でのコールバック実装方法とインスペクタでの登録
【Unity】【C#】CapsLock のオン・オフ状態を取得する (Windows) 
2020/03/18 Wed [edit]
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#】ゲームパッドの右スティックを使えるようにする
【Unity】5ボタンマウスの KeyCode 図解 
2019/08/26 Mon [edit]
Unity の UI 操作でも、マウスのバックボタンが使えたら少し便利かな、と思って調べてみたら、簡単だった(笑)。早速「VRM Live Viewer」に導入してみたら、結構良い感じ。クリティカルでない「はい/いいえ」の2択を「進む(フォワード)/戻る(バック)」ボタンで操作できたら、色々楽になるかもね。
何でも図や表にしておくのは、ただの私のクセ。まぁ、何度もマニュアルを見て探すのが億劫なので、一見してわかるものが欲しいだけなんだけどね(笑)。
・KeyCode
(※) Unity 2018.4.6f1 / Windows10(x64) で確認
■マウスのボタンと KeyCode 対応図

(マウス イラスト素材) 無料イラスト素材倉庫 (※少し加工)
あくまでもデフォルト状態なので、専用ドライバ等でカスタマイズしてる場合は定かではない。たぶん、ゲーミングマウスみたいな多ボタンマウスも 5~n で行けるんだろうね(※未確認)。
判定するメソッドは Input.GetKey / GetKeyDown / GetKeyUp などお好みで。
Input.GetMouseButtonDown(int) などでも引数の番号を入れれば、マニュアルには 0~2 までしか書いてないが、3, 4 でもバック/フォワード判定できるみたい。
ホイールの Input.mouseScrollDelta はオマケ(笑)。
(マニュアル)
・KeyCode
・Input.GetKey
・Input.GetKeyDown
・Input.GetKeyUp
・Input.GetMouseButton
・Input.GetMouseButtonDown
・Input.GetMouseButtonUp
・Input.mouseScrollDelta
・Input.GetAxis
(関連記事)
【Unity】【C#】KeyCode をリアルタイムで調べる / Windows 日本語キーボードでの記号の KeyCode 一覧
【Unity】【C#】長押し(ロングタップ)を取得してコールバックする
【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする
【Unity】【C#】ピンチ操作を取得してコールバックする
【Unity】【C#】ゲームパッドの右スティックを使えるようにする
【Unity】【C#】KeyCode をリアルタイムで調べる / Windows 日本語キーボードでの記号の KeyCode 一覧 
2019/07/20 Sat [edit]
以前「Windows日本語キーボードでのKeyCodeにはバグがある」みたいな記事を見たことがあったが、Unity4 時代の頃からなので、単純にキーボードによる違いと考えて、改めて確認した。特に記号の KeyCode が公式マニュアルとは少し違うので、簡単な一覧表としてまとめてみた。
・[Unity]KeyCodeについて
(※) Unity 2018.4.4f1 / Windows10 (x64) で確認
■Windows 日本語キーボードでの記号の KeyCode 一覧
基本的には公式マニュアルで十分なので、ここでは記号のみで、日本語キーボードでは見た目が違うものがわかるように挙げておこう。ショートカットキーみたいなものを作るときに、ちょっと困ったりするんだよね(笑)。
・KeyCode(公式マニュアル)
●Windows 日本語キーボードでの記号の KeyCode 一覧
キー | KeyCode | 備考 |
---|---|---|
- | Minus | |
^ | Quote | キャレット |
¥ | Backslash | 右[Shift]隣の[\_ろ]と同じになる。 |
[ | LeftBracket | |
] | RightBracket | |
@ | BackQuote | アットマーク |
; | Equals | セミコロン |
: | Semicolon | コロン |
, | Comma | カンマ(コンマ) |
. | Period | ピリオド(ドット, ポイント) |
/ | Slash | スラッシュ |
\ | Backslash | バックスラッシュ。[Backspace]隣の[¥|]と同じになる。 |
Clear | Delete | テンキー[Clear]。[Delete]と同じになる。 |
PrtScn | SysReq | Print Screen |
Menu | Menu | 右[Ctrl]隣のメニューキー |
Windowsキー | LeftCommand | Windowsアイコンのキー |
※[Shift]キーを押さない状態。
※テンキー(Keypad~)は公式マニュアルとほぼ変わらないので割愛([Clear]のみ)。
■KeyCode をリアルタイムで調べるコード
●KeyCode をリアルタイムで調べる
using System;
using System.Linq;
using UnityEngine;
public class KeycodeTest : MonoBehaviour { //※クラス名は任意
KeyCode[] keyCodes = Enum.GetValues(typeof(KeyCode)).Cast<KeyCode>().ToArray();
// Update is called once per frame
private void Update () {
if (Input.anyKeyDown) //※KeyDown のみ
{
foreach (var key in keyCodes)
{
if (Input.GetKeyDown(key))
{
Debug.Log(key);
break;
}
}
}
}
}
内容的には KeyCode の列挙型の一覧(※Array 型)を何らかのキーが押された時(Input.anyKeyDown)に調べているだけだ。ここでは KeyDown (押下した瞬間)を使っているので、押してる間ずっとにしたいなら Input.GetKey を使っても良いだろう。
Unity の場合、記号は使っているキーボードによって値が変わる可能性があるから(Mac 上では大丈夫と記事にあるが)、あまり使わない方が良いのかもね(笑)。
・Array Class
・Enum.GetValues(Type) Method
・Input.anyKeyDown
・Input.GetKey
(関連記事)
【Unity】【C#】InputSystem.Key をリアルタイムで調べる / Windows 日本語キーボードでの Key 一覧
【Unity】【C#】(旧)KeyCode と InputSystem.Key の対応
【Unity】5ボタンマウスの KeyCode 図解
【Unity】【C#】ゲームパッドの右スティックを使えるようにする
【C#】【Unity】enum 型と string, int 型の相互変換など
【Unity】【C#】ピンチ操作を取得してコールバックする 
2018/01/09 Tue [edit]
既にスワイプ、ロングタップ(クリック)は掲載しているが、スマートフォン等はピンチという操作もよく使われるので、Androidのプラグインに同梱する形で配布することにした。プラグイン自体は Android 専用のものだが、今回のピンチや既存のスワイプ、ロングタップは Unity 上でのスクリプトなので、iOS などでも使えると思う。
ついでにこのピンチを使って、オブジェクトを拡大・縮小、またはカメラを近づけたり、遠ざけたりする方法(スクリプト)も書いておこう。これらはプラグインのパッケージにデモとしてセットアップされているので、自分で作るのが面倒な人はプラグインをダウンロードして、コピペで利用しても良いと思う。

プラグインデモをダウンロード


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。
(※) Unity 5.6.3p1 / Windows10(x64) / Galaxy S7 Edge (Android 7.0) で確認
●ピンチを取得してコールバックする
using System;
using UnityEngine;
using UnityEngine.Events;
namespace FantomLib
{
/// <summary>
/// ピンチ操作を取得してコールバックする
/// 2018/01/09 Fantom (Unity 5.6.3p1)
/// http://fantom1x.blog130.fc2.com/blog-entry-288.html
///(使い方)
///・適当な GameObject にアタッチして、インスペクタから OnPinchStart, OnPinch にコールバックする関数を登録すれば使用可。
///・またはプロパティ IsPinching, Width, Delta, Ratio をフレーム毎監視しても良い(こちらの場合は使用してない状態(IsPinching=false, Width=0, Delta=0, Ratio=1)も含まれる)。
///(仕様説明)
///・内部的には画面でタッチされた2本の指の間隔をピクセル単位で取得する。ただし戻り値は画面幅で割った正規化された値とピクセルそのもので返すかを選べる(isNormalized)。
///・ピンチの操作は1本→2本となったときのみ認識する。3本以上→2本になったときは無効。
///・タッチデバイスを UNITY_ANDROID, UNITY_IOS としているので、他のデバイスも加えたい場合は #if の条件文にデバイスを追加する(Input.touchCount が取得できるもののみ)。
/// </summary>
public class PinchInput : MonoBehaviour
{
public bool isNormalized = true; //画面幅(or 高さ)で正規化した値でコールバックする(false=ピクセル単位で返す)
public bool widthReference = true; //isNormalized=true のとき、画面幅(Screen.width)を基準にする(false=高さ(Screen.height)を基準)[単位が px/Screen.width のようになる]
//認識する画面上の領域(0.0~1.0)[(0,0):画面左下, (1,1):画面右上]
public Rect validArea = new Rect(0, 0, 1, 1);
//ピンチ検出プロパティ(フレーム毎取得用)
public bool IsPinching {
get; private set;
}
//ピンチ幅(距離) プロパティ(フレーム毎取得用)
public float Width {
get; private set;
}
//ピンチ幅(距離)の直前との差分 プロパティ(フレーム毎取得用)
public float Delta {
get; private set;
}
//ピンチ幅(距離)の変化比 プロパティ(フレーム毎取得用)
public float Ratio {
get; private set;
}
//ピンチ開始コールバック
[Serializable]
public class PinchStartHandler : UnityEvent<float, Vector2> { } //Width, center(2指間の中心座標)が返る
public PinchStartHandler OnPinchStart;
//ピンチ中コールバック(伸縮率とその差分)
[Serializable]
public class PinchHandler : UnityEvent<float, float, float> { } //Width, Delta, Ratio が返る
public PinchHandler OnPinch;
//Local Values
float startDistance; //ピンチ開始時の指の距離(px)
float oldDistance; //直前の伸縮距離(px)
//アクティブになったら、初期化する(アプリの中断などしたときはリセットする)
void OnEnable()
{
IsPinching = false;
}
// Update is called once per frame
void Update()
{
//プロパティはフレーム毎にリセット
Width = 0; Delta = 0; Ratio = 1;
#if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS) //タッチで取得したいプラットフォームのみ
if (Input.touchCount == 2) //ピンチでの操作(2本指のみ)
{
//※fingerId と touches[] のインデクスは必ずしも一致しないらしいので fingerId=1 となっている方を取得(指1本→2本になったとき可能とするため)
Touch touch = (Input.touches[1].fingerId == 1) ? Input.touches[1] : Input.touches[0];
if (!IsPinching && touch.phase == TouchPhase.Began) //新しく認識したときのみ
{
//認識する画面上の領域内か?(2本の指の中心の座標を基準にする)
Vector2 center = (Input.touches[0].position + Input.touches[1].position) / 2;
if (validArea.xMin * Screen.width <= center.x && center.x <= validArea.xMax * Screen.width &&
validArea.yMin * Screen.height <= center.y && center.y <= validArea.yMax * Screen.height)
{
IsPinching = true; //ピンチ開始
//fingerId=0~1 のみ(必ず最初と2本目の指)。指3本→2本(0-2 など)は不可とする。
Width = startDistance = oldDistance = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
if (isNormalized)
{
float unit = widthReference ? Screen.width : Screen.height;
Width /= unit; //画面幅で正規化すれば、解像度に依存しなくなる
center /= unit;
}
if (OnPinchStart != null)
OnPinchStart.Invoke(Width, center); //開始時は必ず Delta=0, Ratio=1 となる
}
}
else if (IsPinching) //既に認識されているときのみ:3本→2本になったときは無効になる
{
float endDistance = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
Width = endDistance;
Delta = endDistance - oldDistance; //直前との差分
Ratio = endDistance / startDistance; //開始時のピンチ幅(px距離)を基準にした倍率になる
oldDistance = endDistance;
if (isNormalized)
{
float unit = widthReference ? Screen.width : Screen.height;
Width /= unit; //画面幅で正規化すれば、解像度に依存しなくなる
Delta /= unit;
}
if (OnPinch != null)
OnPinch.Invoke(Width, Delta, Ratio);
}
}
else //タッチが2つでないときは全て無効にする
#endif
{
IsPinching = false;
}
}
}
}
ピンチ検出の簡単な解説をしておくと、スマホなどのタッチデバイスで指を2本置いたとき、それぞれの座標からそれらの距離を取得し、指を動かすことによってその座標間の距離の差分でピンチと認識するというものである。
ただこのスクリプトではもう少し細かく検出されていて、例えば指が1本→2本となったときはピンチと認識するが、指が3本以上→2本となったときは認識しない(1本、3本以上のときは常に無視する)。
またコールバックの引数で返ってくる値は「現在のピンチの幅(距離)」「直前のピンチ幅との差分」「ピンチ開始したときの比率」と3つの値を返す。そしてそれらは実機により画面解像度が違うため、正規化オブション(isNormalized = true)により、端末の解像度で割った値を返すようにしてある(isNormalized = false にするとピクセル単位で返ってくる)。
他にもフレーム毎で取得したいときのプロパティ(IsPinching, Width, Delta, Ratio)や認識できる画面領域(validArea)などは以前に書いたスワイプ、ロングタップと同じ仕様だ。実際にこれらスクリプトを同時に使用しても問題ないように作ってある。
プラットフォームに関しては、タッチデバイスとして Android と iOS しか考えてないので、必要あればプリプロセッサ(#if 文)に条件を追加して欲しい。これらは Input.touchCount が取得できるものに限られる。しかしこのスクリプトの場合、Update() での検出部分は Input.touchCount で囲まれているので、スマホで使うこと前提なら、プリプロセッサ(#if 文)をコメントアウトしても良いだろう(Input.touchCount は PCなどでは無効になるため)。
あと、コールバックイベントはインスペクタで設定(UnityEvent)するようにしてあるが、コードだけにしたいなら、Action を使ったコールバックや、直接 delegate でコールバックするように書き換えても良いだろう。もちろんそのままで UnityEvent.AddListener を使ってコードから追加する方法もある。自由に改造して使って欲しい(※Unity でのコールバック実装は以下を参照)。
・【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
■ピンチでオブジェクトを拡大・縮小する
ここからは上記の「PinchInput」を使って、実際にオブジェクトの拡大・縮小の操作をやってみよう。このスクリプトは使い回しできるのでライブラリとして追加もしてある。考え方を理解すれば他のものに利用することも可能だろう。
|
using UnityEngine;
namespace FantomLib
{
/// <summary>
/// ピンチでスケールを変化させる(ローカルスケール)
/// 2018/01/09 Fantom (Unity 5.6.3p1)
/// http://fantom1x.blog130.fc2.com/blog-entry-288.html
///(使い方)
///・伸縮したい GameObject にアタッチして、インスペクタから PinchInput のコールバックを登録すれば使用可。
/// </summary>
public class PinchToScale : MonoBehaviour
{
public Transform target; //スケール変化させるオブジェクト
//Local Values
Vector3 startScale; //ピンチ開始時スケール
Vector3 initScale; //起動初期スケール(リセット用)
// Use this for initialization
private void Start()
{
if (target == null)
target = gameObject.transform; //指定がないときは自身を対象とする
initScale = target.localScale;
}
//width: ピンチ幅, center: ピンチの2本指の中心の座標
public void OnPinchStart(float width, Vector2 center)
{
if (target != null)
startScale = target.localScale;
}
//width: ピンチ幅, delta: 直前のピンチ幅の差, ratio: ピンチ幅の開始時からの伸縮比(1:ピンチ開始時, 1以上拡大, 1より下(1/2,1/3,...)縮小)
public void OnPinch(float width, float delta, float ratio)
{
if (target != null)
target.localScale = startScale * ratio;
}
//スケールを元に戻す
public void ResetScale()
{
if (target != null)
target.localScale = initScale;
}
}
}

仮に拡大・縮小するオブジェクトを「Cube」としたとき、インスペクタでの設定は上のようにしよう。スクリプト自体は他の GameObject にアタッチしても構わないが、「PinchToScale」の「Target」には対象となるオブジェクトを指定し、「PinchInput」のピンチ開始時のコールバック「OnPinchStart」に「PinchToScale.OnPinchStart」を登録(Dynamicの方)、ピンチ中のコールバック「OnPinch」には「PinchToScale.OnPinch」を登録(Dynamicの方)する。あとは「PinchInput」の「isNormalize」がオンになっているのを確認すればOKだ(デフォルト=オン)。
スクリプトの簡単な解説をしておくと、ピンチ開始時のコールバック(OnPinchStart)のときのピンチ幅(指と指の距離)を記録し、ピンチ中は指の開閉による距離の比率(指を開くと 2, 3, 4...倍[小数も含む]となり、指を閉じると 1/2, 1/3, 1/4,..倍[実際には小数=0.5, 0.3, 0.25,...など])で拡大・縮小を行っている。この操作には主にコールバック引数の「ratio」(比率)を使っている。また、スケール操作はローカルスケール(transform.localScale)になる。ただ1つだけ注意して欲しいのは、物理的に指を3倍、4倍と開くのはキツイけど、指を1/3, 1/4 倍と閉じるのは簡単だということだ。指の間隔を比率ではなく、線形的な差分にしたいなら、コールバック引数の「delta」(距離差分)を使っても良いが、実際に試してみると ratio(比率)の方が人間の感覚・見た目には合ってるようだ。線形でやりたい場合はカーブを間に挟んだ方が良い感じになるかも知れない。その辺りは自分で工夫してみて欲しい。
■ピンチでカメラの遠近を操作する
次に上記の「PinchInput」を使って、視点となるオブジェクトの遠近操作をやってみよう。このスクリプトは使い回しできるのでライブラリとして追加もしてある。考え方を理解すれば他のものに利用することも可能だろう。
|
using System;
using UnityEngine;
namespace FantomLib
{
/// <summary>
/// ピンチで距離を操作する
/// 2018/01/09 Fantom (Unity 5.6.3p1)
/// http://fantom1x.blog130.fc2.com/blog-entry-288.html
///(使い方)
///・カメラなどの GameObject にアタッチして、インスペクタから PinchInput のコールバックを登録すれば使用可。
///・距離は target からの直線距離となる。
/// </summary>
public class PinchToDistance : MonoBehaviour
{
public Transform target; //視点となるオブジェクト
public float speed = 2f; //変化速度
public float minDistance = 1.0f; //近づける最小距離
public bool lookAt = true; //オブジェクトの方を向く
//LocalValues
float initDistance; //起動初期距離(リセット用)
// Use this for initialization
private void Start()
{
if (target != null)
{
Vector3 dir = target.position - transform.position;
initDistance = dir.magnitude;
if (lookAt)
transform.LookAt(target.position);
}
}
//width: ピンチ幅, delta: 直前のピンチ幅の差, ratio: ピンチ幅の開始時からの伸縮比(1:ピンチ開始時, 1以上拡大, 1より下(1/2,1/3,...)縮小)
public void OnPinch(float width, float delta, float ratio)
{
if (target == null)
return;
Vector3 dir = target.position - transform.position;
float distance = Math.Max(minDistance, dir.magnitude - delta * speed);
Vector3 pos = target.position - dir.normalized * distance;
transform.position = pos;
if (lookAt)
transform.LookAt(target.position);
}
//初期の距離に戻す
public void ResetDistance()
{
if (target == null)
return;
Vector3 dir = target.position - transform.position;
Vector3 pos = target.position - dir.normalized * initDistance;
transform.position = pos;
if (lookAt)
transform.LookAt(target.position);
}
}
}

仮に視点先となるオブジェクトを「Cube」としたとき、インスペクタでの設定は上のようにしよう。スクリプト自体は他の GameObject にアタッチしても構わないが、「PinchToDistance」の「Target」には対象となるオブジェクトを指定し、「PinchInput」のピンチ開始時のコールバック「OnPinchStart」に「PinchToDistance.OnPinchStart」を登録(Dynamicの方)、ピンチ中のコールバック「OnPinch」には「PinchToDistance.OnPinch」を登録(Dynamicの方)する。あとは「PinchInput」の「isNormalize」がオンになっているのを確認すればOKだ(デフォルト=オン)。
スクリプトの簡単な解説をしておくと、ピンチ開始時のコールバック(OnPinchStart)は使ってないのだが、ピンチ中は指の開閉による直線の距離の差分(指を開くと正の値が、指を閉じると負の値が返る)で遠近操作を行っている。この操作には主にコールバック引数の「delta」(距離差分)を使っていて、直前の指と指の距離との差を利用し、オブジェクトとの距離を加減算することによって、カメラを移動する感じだ(ただし指を開いたときに近づくには距離をマイナス、指を閉じたときは遠くにするには距離をプラスなので、符号は逆)。これは「ratio」(比率)とは違って線形な値になるので、指を開くときも閉じるときも同じような相対量になる(「ratio」(比率)の解説は PinchToScale を参照)。
具体的な値については「isNormalize」がオンになっているときは画面幅で正規化(widthReference=falseのときは画面の高さで正規化)された値が返り、オフになっているときはピクセル単位で返ってくる。それは例えば、画面解像度の幅が 800px の機種と 960px の機種があったとき、ピクセル単位だと画面の半分を移動したときそれぞれ、400px と 480px と異なった値になってしまうからだ。なので、端末の画面幅で割っておけば(=正規化する)どちらも 0.5 (= 400/800, 480/960)となり、画面解像度に依存しなくなる。しかし実際のピクセル座標で欲しいときは isNormalize=false にするか、正規化された値×解像度でもピクセル単位に変換できるので、好きな方を使うと良いだろう。

プラグインデモをダウンロード


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。
(関連記事)
【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする
【Unity】【C#】長押し(ロングタップ)を取得してコールバックする
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
- 関連記事
category: Unity
thread: ゲーム開発
janre: コンピュータ
tag: Unityライブラリ Unityプラグイン InputManager FantomPlugin