UnityでiOS/Android/Editorの共通タッチ処理を実装する
↑こちらの記事が思ったより需要があるようなんで、簡単にAsset化してみた。
GodTouchとは
Unityエディタ上でタッチの動作を確認できるAssetです。iOS/Android/エディタで同じ挙動になります。
基本的にQiitaの記事内容を整理してAsset化しただけです。あくまで簡単にタッチの動作を確認するAssetで、複雑なタッチ操作は想定していません。
動作環境
Unity5.0+
iOS 6.0+
Android 2.3+
※Unity5.0で作成したので上記条件ですが、実際にはUnityバージョンに依存しないと思います(自分がUnity3系の頃から使ってるものなので)
使用方法
- GodTouch.unitypackage をインポート
- スクリプトから以下のように処理する
Demo.cs
using UnityEngine;
namespace GodTouches
{
public class Demo : MonoBehaviour
{
public Transform Move;
Vector3 startPos;
void Start()
{
startPos = Move.position;
}
void Update()
{
// タッチを検出して動かす
var phase = GodTouch.GetPhase ();
if (phase == GodPhase.Began)
{
startPos = Move.position;
}
else if (phase == GodPhase.Moved)
{
Move.position = GodTouch.GetPosition();
// Move.position += GodTouch.GetDeltaPosition();
}
else if (phase == GodPhase.Ended)
{
Move.position = startPos;
}
}
}
}
プログラム
GodTouch.cs
using UnityEngine;
namespace GodTouches
{
/// <summary>
/// GodTouch
/// </summary>
public static class GodTouch
{
/// <summary>
/// Androidフラグ
/// </summary>
static readonly bool IsAndroid = Application.platform == RuntimePlatform.Android;
/// <summary>
/// iOSフラグ
/// </summary>
static readonly bool IsIOS = Application.platform == RuntimePlatform.IPhonePlayer;
/// <summary>
/// エディタフラグ
/// </summary>
static readonly bool IsEditor = !IsAndroid && !IsIOS;
/// <summary>
/// デルタポジション判定用・前回のポジション
/// </summary>
static Vector3 prebPosition;
/// <summary>
/// タッチ情報を取得(エディタとスマホを考慮)
/// </summary>
/// <returns>タッチ情報</returns>
public static GodPhase GetPhase()
{
if (IsEditor)
{
if (Input.GetMouseButtonDown(0))
{
prebPosition = Input.mousePosition;
return GodPhase.Began;
}
else if (Input.GetMouseButton(0))
{
return GodPhase.Moved;
}
else if (Input.GetMouseButtonUp(0))
{
return GodPhase.Ended;
}
}
else
{
if (Input.touchCount > 0) return (GodPhase)((int)Input.GetTouch(0).phase);
}
return GodPhase.None;
}
/// <summary>
/// タッチポジションを取得(エディタとスマホを考慮)
/// </summary>
/// <returns>タッチポジション。タッチされていない場合は (0, 0, 0)</returns>
public static Vector3 GetPosition()
{
if (IsEditor)
{
if (GetPhase() != GodPhase.None) return Input.mousePosition;
}
else
{
if (Input.touchCount > 0) return Input.GetTouch(0).position;
}
return Vector3.zero;
}
/// <summary>
/// タッチデルタポジションを取得(エディタとスマホを考慮)
/// </summary>
/// <returns>タッチポジション。タッチされていない場合は (0, 0, 0)</returns>
public static Vector3 GetDeltaPosition()
{
if (IsEditor)
{
var phase = GetPhase();
if(phase != GodPhase.None)
{
var now = Input.mousePosition;
var delta = now - prebPosition;
prebPosition = now;
return delta;
}
}
else
{
if (Input.touchCount > 0) return Input.GetTouch(0).deltaPosition;
}
return Vector3.zero;
}
}
/// <summary>
/// タッチ情報。UnityEngine.TouchPhase に None の情報を追加拡張。
/// </summary>
public enum GodPhase
{
None = -1,
Began = 0,
Moved = 1,
Stationary = 2,
Ended = 3,
Canceled = 4
}
}
ClickLongPressDrag.cs
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
namespace GodTouches
{
/// <summary>
/// 「クリック」「長押し」「ドラッグ」サンプル
/// </summary>
public class ClickLongPressDrag : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
/// <summary>
/// イベントタイプ
/// </summary>
public enum EventType { None, Pressed, Click, LongPress, Drag }
/// <summary>
/// クリック・ドラッグ判定距離
/// </summary>
public float CheckDistance = 30;
/// <summary>
/// 長押し判定時間
/// </summary>
public float CheckTime = 0.3f;
/// <summary>
/// サンプル表示テキスト
/// </summary>
public Text Text;
/// <summary>
/// イベントタイプ
/// </summary>
EventType type;
/// <summary>
/// イベント実行中かどうか
/// </summary>
bool isRunning;
/// <summary>
/// 押された時の開始ポジション
/// </summary>
Vector3 startPos;
/// <summary>
/// 押された時の開始時間
/// </summary>
float startTime;
/// <summary>
/// イベント実行中かどうか
/// </summary>
public bool IsRunning { get { return isRunning; } }
/// <summary>
/// イベントタイプ設定
/// </summary>
/// <param name="type">イベントタイプ</param>
void SetType(EventType type)
{
this.type = type;
Text.text = type.ToString ();
}
/// <summary>
/// 押された
/// </summary>
/// <param name="e">PointerEventData</param>
public void OnPointerDown (PointerEventData e)
{
isRunning = true;
SetType(EventType.Pressed);
startPos = GodTouch.GetPosition ();
startTime = Time.time;
}
/// <summary>
/// 更新処理
/// </summary>
void Update()
{
if (type == EventType.Pressed)
{
// 押されてる
var pos = GodTouch.GetPosition ();
var dx = Mathf.Abs(pos.x - startPos.x);
var dy = Mathf.Abs(pos.y - startPos.y);
var dt = Time.time - startTime;
if(dx > CheckDistance || dy > CheckDistance)
{
// 一定距離動いていたらドラッグ実行
SetType(EventType.Drag);
}
else if(dt > CheckTime)
{
// 一定時間経過していたら長押し実行
SetType(EventType.LongPress);
}
}
else if (type == EventType.Drag)
{
// ドラッグ中(動かす)
transform.position = GodTouch.GetPosition();
}
}
/// <summary>
/// 離された
/// </summary>
/// <param name="e">PointerEventData</param>
public void OnPointerUp (PointerEventData e)
{
if (type == EventType.Pressed)
{
// 他のイベントが未入力ならクリック実行
SetType (EventType.Click);
}
else
{
// イベント初期化
SetType (EventType.None);
}
isRunning = false;
}
}
}
GodTouch.cs
がAssetのスクリプト。
ClickLongPressDrag.cs
は、試しにAssetを使って実装した「クリック」「長押し」「ドラッグ」のサンプル。クリックとか普通に処理するならuGUIのButton.onClick
を使えばいいと思うけど、クリック+長押し+ドラッグを同時に処理したいとか複雑になってきたらこんな風にもできるよーというサンプル。
Unityの正しい使い方?
Unityすごい便利で、1つのことを実現するのにもいろいろな方法が用意されている。
ただその反面、使用者によって実装がまちまちになりがちで、何が正しいUnityの使い方か分からない。
自分が最近思うのは、大きい単位では「Asset化」、小さい単位では「Component化」が良いのではないか、と。
実際、Asset化するようになって、自分が組まなければいけないものが減っていて、随分楽になった。修正の際も、処理が完全分離してるからやりやすい。
どんなに細かいものでも、Asset化しておくと、のちのち幸せになれるかもしれない。
最後に
簡単にUnityエディタ上でタッチ動作を確認したい人はどうぞ。
改変自由に。ただし修正依頼は受け付けてないので、その場合はフォークしてね☆
https://github.com/okamura0510/GodTouch
〜オセロを作りながらゲームのプログラムを学ぼう〜