【Unity】2048ピース移動中のフリックキャンセル

2019/10/15追記・修正
2019/10/18追記②・削除


フリック入力の調整ができました。
かなり複雑なので、うまく説明できないかもしれませんが、
書いていきたいと思います。


ピースの移動中にフリック入力ができてしまう!
このスクリプトには致命的な欠陥です。

ピースのポジションを確認しながら移動計算を
行う為、移動中の座標を読み込むとエラーが発生します。

なのでフリック入力管理は必須事項でっす。


以前、ピースの移動判定、
Move=true なら入力をキャンセルしていましたが、
ピース毎にスピードが違い、先に停止したピースは、
入力を受け付けてしまう事になります。

ピース全体の動きを認識させるにはどうすればいいのでしょう…

少しコードの流れを見てみる事に…
2048㉞.jpg
フローと呼べるほどの物でもないんですが、
流れを図にしてみました。

コードだけを見ていると分かりにくいのですが、
図にすると見えてきそうでっす(^ё^) ♪♪


各ピースの共有部分は、GameManager側の移動検知~ピース生成までです。
フリック入力後、ピースが生成されるまでキャンセルを掛ければ対応できそうです。


まず、フリック入力を判断するようにしてみます。
PieceManager

private bool input ; //フリックの入力フラグ

void Update()
{
//フリックが入力中か判断する
if (input)
{
return;
}

//タッチの始点を取得
if (Input.GetKeyDown(KeyCode.Mouse0))
{
touchStartPos = new Vector3(Input.mousePosition.x,
Input.mousePosition.y,
Input.mousePosition.z);
//移動前のピース配置取得
GetAxis();
}
//タッチの終点を取得
if (Input.GetKeyUp(KeyCode.Mouse0))
{
touchEndPos = new Vector3(Input.mousePosition.x,
Input.mousePosition.y,
Input.mousePosition.z);

input = true;     ←ここで取得
GetDirection();
}
}

これでフリック入力を読み取れると思います。

inputをfalseにするタイミングは、当然ピース生成後なので、
フリック入力をした事をGameManagerに渡す必要があります。
GameManager

public static bool create=false; //ピース生成までのフラグ

//ピースの移動検知
public void Moving()
{
create = true;
}

フリック入力後、Moving()メソッドを呼び出し、
ピースの生成後、falseにすれば生成したか判断できそうです。

ピースからも見えるように、フラグの変数はstaticにしておきます。
PieceManager

void GetDirection()
{
float directionX = touchEndPos.x - touchStartPos.x;
float directionY = touchEndPos.y - touchStartPos.y;


if (Mathf.Abs(directionY) < Mathf.Abs(directionX))
{
if (30 < directionX)
{
//右向きにフリック
Direction = "right";
DestinationR();
}
else if (-30 > directionX)
{
//左向きにフリック
Direction = "left";
DestinationL();
}
}
else if (Mathf.Abs(directionX) < Mathf.Abs(directionY))
{
if (30 < directionY)
{
//上向きにフリック
Direction = "up";
DestinationU();
}
else if (-30 > directionY)
{
//下向きのフリック
Direction = "down";
DestinationD();
}
}
else
{
//タッチ判定なら
input = false;
return;
}
gameManager.Moving();   ←ここで呼び出す
}

GetDirection()メソッドの最後でMoving()を呼び出します。

タッチの場合、移動処理も行われず、
Moving()メソッドが呼び出されるとマズイので、
returnするのですが、inputをtrueのまま終わると操作できなくなるので、
input = falseを挟みます。

これで、フリック入力開始がGameManagerでも判断できるようになりました。
後は、ピース生成後にcreateをfalseにすればいいのですが、
少し問題があります。

流れを見てもらうと分かるのですが、衝突判定でもピース生成が行われます。

移動中のピースがあっても衝突判定でピースが生成されてしまうと、
そこで、フラグがfalseになってしまいます。

それでは意味が無いので流れ図を見ると…

移動後のピース生成呼び出しはコルーチン後、
必ずSelectPosition()を経由しています。

この中にフラグの管理を導入します。
GameManager

//空きマスからランダムに2カ所を選ぶ
public void SelectPosition()
{
List list = new List();
int num = 2;
int rot = 0;

for(int i = 0; i < 16; i++)
{
if (Flag[i] == false)
{
list.Add(i);
}
}

if (list.Count == 1)
{
num = 1;
}

while (rot < num)
{
int pos = list.GetAndRemoveAtRandom();
int x = pos % frame;
int y = pos / frame;

CreatePiece(x, y, 0);
rot++;
}

create = false;    ←ここで終了にする
}

衝突判定からは、直接CreatePiece()メソッドを呼ぶので
これでピース移動後の生成と切り分けができそうです。

ピース生成が終了したか判断できるようになったので、
フリック入力の復帰をするのですが…

簡単に
if (GameManager.create = false )
{
input = false ;
}
Updateの頭にしたい所なのですが…

これだとフリック入力しない間、呼ばれ続けてしまいます。
少し工夫する必要が…


そもそもピースの生成判断は、
各ピース移動完了後に必要になります。

なので、ピースの移動完了のフラグを用意して、
移動完了ならcreateを見に行くようにします。

流れ図からピースの移動処理後
必ずPieceCorrection()を経由するので、
PieceManager

private bool set; //ピース停止のフラグ

//ピース停止時の位置修正
void PieceCorrection()
{

float x = transform.localPosition.x;
float y = transform.localPosition.y;



foreach (int i in Position)
{
if (i - span <= x && i + span >= x)
{
x = i;
}
if (i - span <= y && i + span >= y)
{
y = i;
}
}

PosNo(x, y);
transform.localPosition = new Vector3(x, y, 0);
gameManager.FlagOn(LineX, LineY, PieceNo);
set = true;                 ←ここで取得
}

こんな感じで呼び出せば停止したと分かります。
これを元にメソッドを作ります。
PieceManager

//ピース移動後フリック入力の可否を判断する
void FlickProperty()
{
if (set)
{
if (GameManager.create == false)
{
input = false;
set = false;
}
}
}

こんな感じにしておけば、
呼び出される回数も減りそうです。

後は、Updateの頭で呼び出せば機能します。

void Update()
{
//ピース移動後のフリック判定
FlickProperty();          ←ここで呼び出す

//フリックが入力中か判断する
if (input)
{
return;
}

//タッチの始点を取得
if (Input.GetKeyDown(KeyCode.Mouse0))
{
touchStartPos = new Vector3(Input.mousePosition.x,
Input.mousePosition.y,
Input.mousePosition.z);
//移動前のピース配置取得
GetAxis();
}
//タッチの終点を取得
if (Input.GetKeyUp(KeyCode.Mouse0))
{
touchEndPos = new Vector3(Input.mousePosition.x,
Input.mousePosition.y,
Input.mousePosition.z);
if (this.gameObject.tag == "None")
{
this.gameObject.tag = "No" + PieceNo;
}
input = true;
GetDirection();
}
}

これで終わったと思ったのですが、
問題がありました( ̄~ ̄;) ウーン

・ピースが全て動かない状況では、createがfalseにならない
・衝突で出来たピースは動いてしまう

まず、生成ピースをフリックできないようにします。

private bool input = true; //フリックの入力フラグ
private bool set=true; //ピース停止のフラグ

これは、そんなに難しい事ではなくて、
生成時にピースが移動中なら条件を合わせるだけですね。

必ずUpdateの最初で判断されるので、
createがfalseなら二つのフラグもfalseにしてくれます。


さて、ピースが全て動かない状況ですが…

例えば

1、3、1、0
2、1、0、0
1、2、1、0
2、0、0、0

赤が生成されたピース

こんな状況で左フリックするとピースの移動は起きません。
当然、ピース生成も行われません。

しかしフリック入力をしているので、createがtrueになります。
ピースの生成が行われない為、ピースがフリーズします(ノ_<。)うっうっうっ


少し複雑になるのですが、修正をしていきます。

流れ図を見てもらうと、移動処理の際GameManagerに
情報を流しています。

ここでピースの動向も渡せるようにします。
PieceManager

//GameManagerへのアウトプット
void Disclosure(bool state)    ←bool型の引数を追加
{
if (state)
{
gameManager.FlagOff(LineX, LineY, PieceNo);
}

gameManager.PieceSignal(state);
}


//右方向の移動計算
void DestinationR()
{
bool which; //移動フラグ     ←追加したフラグ変数
int temp = 0; //左隣のピースNo
int pos = 0; //移動マス数

if (LineX<=2)
{
for (int i = 3; LineX<= i; i--)
{
if (AxisX[i] == 0)
{
pos++;
}
else if (AxisX[i] == temp)
{
pos++;
temp = 0;
}
else if (AxisX[i] == MAX_No)
{
temp = HidePiece;
}
else
{
temp = AxisX[i];
}
}
}

if (pos == 0)
{
which = false;        ←移動無しなら”false”
PieceCorrection();
}
else
{
Min = Position[0];
Max = Position[LineX + pos];
which = true;        ←移動なら”true”
Move = true;
}
Disclosure(which);       ←ここで呼び出し
}

GameManagerに情報を渡すDisclosure()メソッドにbool型の引数を用意、
移動計算内で移動するかしないかを取得してDisclosure()メソッドに渡します。

Disclosure()メソッドでは、
trueならFlagOff()でポジション消去後、ピース生成呼び出し。
falseならピース生成呼び出しのみ。
(移動計算は左右上下すべて修正します)

GameManager側は、動向が流れてくるので、

PieceSignal()メソッドを修正します。
GameManager

private List<bool>status = new List<bool>(); //ピースの動向を取得

//ピース移動の合図
public void PieceSignal(bool state)
{
status.Add(state);      ←ここでリストに格納

if (signal)
{
StartCoroutine("WaitCreatePiece");
signal = false;
}
}

リスト型変数で動向を取得します。
受け取った情報を整理する為、
GameManager

private bool piecereate; //ピースの生成フラグ

//移動ピースの有無を確認
void CreateJudge()
{
if (status.Contains(true))
{
piecereate = true;
}
status.Clear();
}

ピースの生成フラグを用意します。

if (status.Contains(true))でリスト内のtrueを探して、
有ればpiecereateをtrueにします。

これでピースの動向が判断できるようになったので、
コルーチンの中身を修正します。
GameManager

//ピース移動後から生成までのコルーチン
IEnumerator WaitCreatePiece()
{
yield return new WaitForSeconds(waittime);

//ピースの動向確認
CreateJudge();

//ピースの移動が有ればピース生成
if (piecereate == true)
{
SelectPosition();
}
else
{
create = false;
}
piecereate = false;
signal = true;
yield break;
}

あとは移動ピースが有ればピース生成すればOKかと

かなり複雑な処理になってしまいましたが、
これでピース移動中にフリックを受け付けなくなりました。

フリック判定の拡張クラスを作って、
判断できるようになれば、ここまで複雑にならないのでしょうが、
勉強不足で、これが精一杯でっす(ノ_<。)うっうっうっ

なにはともあれ、これで先に進めそうです(*^_^*)


追記
スクリプトの
//ピース移動後から生成までのコルーチン
IEnumerator WaitCreatePiece()の中で

if (piecereate == true)
{
SelectPosition();
}
piecereate = false;
create = false;
signal = true;
yield break;

と書いていましたが、
createの使い方が間違っていたので修正します。

本編の方を修正したので確認して下さい。
(_ _(--;(_ _(--; pekopeko


追記②
タッチの終点を取得する部分に実験中のメソッドが残っていました。
不必要なので削除しました。
(゜゜;)ゴメンネ・・・

この記事へのコメント