【Unity】フリックカレンダーの修正と改良

前回記事の続きとなります。

フリックでスクロールするカレンダーができたのですが、
タッチパネル上から指が離れるとドロップのメソッドが呼び出せない。

これは操作系としては欠点なんで、修正案を考えてみました。

あと、Drop時にタッチパネルを、強制的に指定座標まで移動させていますが、
瞬間移動するので、スクロールしているように見えません。
この辺りも改良する必要があるので、同時に考えてみたいと思います。


まず、おさらいなんですが…

オブジェクトをドラッグする際、
必ず指やマウスがオブジェクト上にあると思います。

しかし、今回のドラッグは縦移動を制限しているので、
上下に指を動かすとオブジェクトから離れた状態になります。

EventtriggerのDropは、あくまで、
オブジェクト上の指が、Dropした時だけ呼び出されるようになっています。

オブジェクト上に指がない状態では、
Dropしても呼び出されない仕様なんですね~ (T-T) グスッ

なので、タッチパネルから外れた所でDropすると
カレンダーが変な位置で停止します。


と言う事で、
オブジェクト上に指がある事を認識させる必要があります。

まず、ドラッグの範囲を限定しようかと考えたのですが、
Androidの場合、画面サイズによってScreenWorldのポイントが変化します。

座標からドラッグできる範囲を指定するのは、
かなり面倒な計算が必要になるので、実現するのが難しいです。

そこで、いろいろと調べてみました。

のっぴの備忘録さん
オブジェクト上にあるマウスの状態によって実行される関数
有益な情報ありがとうございます。m(_ _)m

オブジェクト上にマウス(指)がある時に呼び出される関数が
いろいろ用意されてるようなので、使ってみたいと思います。

まず、OnMouse関数はColliderがいると説明されているので、
準備する事に…
Fカレンダー⑫.jpg
タッチパネルにColliderをアタッチします。
Colliderのサイズはパネルと同じにします。

ついでなんですが、タッチパネルが大き過ぎたので、
カレンダー枠1枚分の大きさに修正します。

タッチパネルのColliderは、下にあるカレンダー枠のColliderと
常に接触していますが、トリガーにするにチェックを入れなければ、
反応はしないので、問題はないと思います。

気になる方は、Colliderレイヤーを変更して接触しないようにしてみて下さい。
ちなみに
Fカレンダー⑬.jpg
ケロ仕様は、UIとDefaultのレイヤーを切っています。
タッチパネルのレイヤーをデフォルトにして、
他のColliderは、UIのままにしています。


Colliderとレイヤーの準備ができたので、Eventtriggerを追加します。
Fカレンダー⑭.jpg
追加するイベントは二つ、PointerEnterとPointerExitです。

PointerEnterは、オブジェクト上にマウスがある時は、常に呼び出されます。
PointerExitは、オブジェクト上からマウスが離れた時に呼び出されます。

この二つを使ってドラッグを制限してみます。
DragManager

private bool onMouse = false; //タッチパネル上にマウスがあるか

//ドラッグ時の処理
public void OnMouseDrag()
{
if (onMouse == false)
{
transform.localPosition = origin;
sensorParent.SetActive(true);

return;
}
       途中省略
}

//タッチパネル上にマウスや指があるか
public void OnMouseEnter()
{
onMouse = true;
}

public void OnMouseExit()
{
onMouse = false;
} 

タッチパネル上にマウスがあるか判断できるようになったので、
無い場合は、ドラッグをキャンセルするようにしました。

マウスが外れてドラッグキャンセルが掛かった場合、
・タッチパネルが原点に戻ってない
・センサーペアレントが非アクティブのまま

なので、二つを復旧させます。

これで、タッチパネルからマウスが外れた場合、
カレンダーがズレて停止するを回避できそうでっす。

実際にワザとタッチパネルから外してみると、
問題なく原点に戻るようになりました。ヽ(´▽`)/~♪


さてさて、問題点も修正できたので、
ここからは、スクロールの瞬間移動を改良して、動きが分かるようにしてみます。

Drop時に指定座標まで移動させると言う事で、移動と言えばDoTween!
DoTweenをインポートします。

DoTweenの設定は割愛して、タッチパネルの移動処理を修正します。
DragManager

using DG.Tweening;

public class DragManager : MonoBehaviour
{
[Header("送りスピード")]
public float scrollSpeed;

//各座標データ
private Vector3 origin = new Vector3(0f, 0f, 0f); //原点
private const float rightSide = 720f; //右側
private const float leftSide = -720f; //左側
private const float zero = 0.0f; //原点やYZ軸用

// Update is called once per frame
void Update()
{
//タッチパネルの原点出し
if (transform.childCount == 0 && originPos == false)
{
transform.localPosition = origin;
originPos = true;
}
}

//ドロップ時の処理
public void OnMouseUpAsButton()
{
sensorParent.SetActive(true);

if (transform.localPosition.x <= -200)
{
transform.DOLocalMove(new Vector3(leftSide, zero, zero), scrollSpeed);
originFlag = false;
}
else if (transform.localPosition.x >= 200)
{
transform.DOLocalMove(new Vector3(rightSide, zero, zero), scrollSpeed);
originFlag = false;
}
else
{
transform.DOLocalMove(new Vector3(zero, zero, zero), scrollSpeed);
}
}
}
DoTweenは、Vector変数で代入できないようなので、
座標変数を個別に用意して代入します。

スクロールの送りスピードを0.2に設定してPLAYでっす。

この状態で試しに動かした所、タッチパネルが右や左に移動した所から
原点に戻らなくなりました。((T.T )( T.T) オロオロ

原因を考えてみました。
・タッチパネルが指定位置に到着する前に枠とセンサーが接触する
・originFlagがtrueになるとUpdate内の関数は呼び出されない
・DoTweenで移動中のタッチパネルはTransformを受け付けない

と言う事で、
タッチパネルが指定位置に到着する前にoriginFlagがtrueになります。

originFlagがtrueになると、原点出しは機能しないので、
タッチパネルがDoTweenで指定した位置で停止したままになる。

また、この事で分かったのですが、DoTweenが働いている間、
Transformは受け付けないようです。

このままでは、原点出しができない… (T-T) グスッ

センサーに接触した所でDoTweenを停止できればなんとかなるかもと言う事で、
DoTweenを停止させる方法があるか調べてみました。

kanのメモ帳さん
DOTweenのTweenerを使って途中終了、 一時停止、再開、リスタート【Unity】【DOTween】
いつも有益な情報ありがとうございます。m(_ _)m

DoTweenを停止させるにあたって、Updateの処理も変える必要が出てきたので、
少し修正をします。
DragManager

private Tweener tweener; //DoLocalの実行状態

// Update is called once per frame
void Update()
{
//タッチパネルの原点出し
if (transform.childCount == 0 && transform.localPosition != origin)
{
tweener.Kill(true);
transform.localPosition = origin;
}
}

//ドロップ時の処理
public void OnMouseUpAsButton()
{
sensorParent.SetActive(true);

if (transform.localPosition.x <= -200)
tweener = transform.DOLocalMove(new Vector3(leftSide, zero, zero), scrollSpeed);

else if (transform.localPosition.x >= 200)
tweener = transform.DOLocalMove(new Vector3(rightSide, zero, zero), scrollSpeed);

else
transform.DOLocalMove(new Vector3(zero, zero, zero), scrollSpeed);
}

原点復帰にフラグを利用してましたが、
DoTweenの移動処理の間に、Update内の関数が呼び出されると、
フラグが立って、それ以上、関数が呼び出されなくなります。

なのでoriginFlagを無くして、
代わりにタッチパネルの座標を読み取るように変更しました。

タッチパネルが空で原点に戻ってない間は、
DoTweenの停止と原点出しをするようにしています。

ここまでくれば実際に動きを確認してみます。
少し流れる感じなんですが、やはり一瞬で動くように見えます。

恐らくセンサーサイズが大きいので、
少し動くと当たってしまい、枠がセンサーポジションに
移ってしまうからだと思います。

センサーサイズを小さくして様子を見てみます。
Fカレンダー⑮.jpg
サイズを10×10に変更して、送りスピードも0.25に変更します。

タッチパネルからマウスが外れた時の処理も含めて
動画にしたのでどうぞ。

キャプチャのせいでカーソルと枠がズレて見えますが
枠から離れたら原点復帰しております(;^_^A

無事に完成する事ができました。ヽ(´▽`)/~♪


最後に完成版のスクリプトを
DragManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class DragManager : MonoBehaviour
{
//パブリック
//------------------------------
[Header("各枠")]
public GameObject[] frameObj;
[Header("センサーペアレント")]
public GameObject sensorParent;
[Header("送りスピード")]
public float scrollSpeed;



//プラベート
//------------------------------

private bool onMouse = false; //タッチパネル上にマウスがあるか
private Tweener tweener; //DoLocalの実行状態


//ドラッグの各変数
private Vector3 offset; //タッチ位置とのオフセット
private float axisY = 0f; //タップ時のY座標
private const float axisZ = 10f; //ドラッグ時のZ座標

//各座標データ
private Vector3 origin = new Vector3(0f, 0f, 0f); //原点
private const float rightSide = 720f; //右側
private const float leftSide = -720f; //左側
private const float zero = 0.0f; //原点やYZ軸用


/// <summary>
/// タッチパネル制御メソッド
/// </summary>

// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{
//タッチパネルの原点出し
if (transform.childCount == 0 && transform.localPosition != origin)
{
tweener.Kill(true);
transform.localPosition = origin;
}
}

//タップ時のオフセット取得
public void OnMouseDown()
{
axisY = transform.position.y;
this.offset = transform.position - Camera.main.ScreenToWorldPoint(Input.mousePosition);
}


//ドラッグ時の処理
public void OnMouseDrag()
{
if (onMouse == false)
{
transform.localPosition = origin;
sensorParent.SetActive(true);

return;
}

sensorParent.SetActive(false);

frameObj[0].transform.SetParent(this.transform, false);
frameObj[1].transform.SetParent(this.transform, false);
frameObj[2].transform.SetParent(this.transform, false);

Vector3 currentScreenPoint = Input.mousePosition;
Vector3 currentPosition = Camera.main.ScreenToWorldPoint(currentScreenPoint) + this.offset;
currentPosition.y = axisY;
currentPosition.z = axisZ;
transform.position = currentPosition;
}

//ドロップ時の処理
public void OnMouseUpAsButton()
{
sensorParent.SetActive(true);

if (transform.localPosition.x <= -200)
tweener = transform.DOLocalMove(new Vector3(leftSide, zero, zero), scrollSpeed);

else if (transform.localPosition.x >= 200)
tweener = transform.DOLocalMove(new Vector3(rightSide, zero, zero), scrollSpeed);

else
transform.DOLocalMove(new Vector3(zero, zero, zero), scrollSpeed);
}

//タッチパネル上にマウスや指があるか
public void OnMouseEnter()
{
onMouse = true;
}

public void OnMouseExit()
{
onMouse = false;
} 
}

カレンダーのフリックとして作りましたが、
ページの切り替えやイラスト図鑑なんかにも使えると思うので、
応用して頂ければ、幸いかと思います。

今回はこの辺りで失礼します。(^^)/~~デハデハ

この記事へのコメント