【Unity】ピースのポジションを配列で管理する

2019/10/2追記
2019/10/9追記2 配列から2次元配列変換



ここまで、ピースの移動処理を作ってきたのですが、
前回挙げた問題点は、克服できておりません。

ただ、予期せぬ衝突判定と所定範囲外にピースが出るについては
処理速度などで変わるので、どうしようもない部分です。

なので、このままで行こうと思います。

動作中のフリック入力については、
動いている間、入力を受け付けないくらいしか
思いついてないですね~ ( ̄~ ̄;) ウーン

この部分は、今後の課題として置いておきます。


前回のCollisionで一つ修正する部分ができたので触れておきます。

二つのピースが衝突する際、Collisionが呼び出されるのですが、
先行しているピースは、既に停止しており位置調整も終わってます。

先行ピースのCollisionが働くと再度、位置調整を行う事になるので、
無駄となります。

なので、Collision内容に修正を加えました。

追加した内容は、

Updateの最初に
if(Move){Return;}
これは、ピースが移動中ならフリックの入力を受け付け
ないようにするためです。

OnCollisionEnter2Dの最初に
if(Move==false){Return;}
停止中のCollisionは受け付けないようにしました。


これだけです。(;^o^) \(ToT )あんたほんとにそれでいいの

フリック入力については、気休めにしかなりませんが、
Collisionについては、問題ないかと思います。



少しタイトルから逸れたので、本題のピースの座標管理でっす。

パズルにはピースの座標を管理しておく必要があるかと思います。
以前、スライドパズルでも配列変数に保管しておりました。

今回は、ピースが停止した時のポジションを
読み取りたいと思います。


ピースの座標は、transformを取得すればいいのですが、
枠を設定すると、どの枠にピースがあるか管理する必要が出てきます。

座標から枠番を取得するには、座標の範囲を指定して範囲内なら
trueにするといった感じなのですが…

ifやswitchで範囲を指定する事もできますが、
コードが長く、書くだけでも大変です。

そこで、座標からポジションを割り出して配列に格納する方法を考えてみます。

今回の枠は、4×4の形になっています。
Imageにするとこんな感じになるかと
2048⑪.jpg
横0縦0なら左上
横3縦3なら右下

これは、そのまま二次元配列に当てはめる事ができます。
では、座標からどのようにポジションを割り出すか考えてみます。

ピースの停止処理で座標を取得しているので、
その座標からポジションを割り出してみます。

座標の範囲は、-150から150 スパンは100です。

X座標の場合は、左から0番になっているので
-150なら0番を当てはめる必要があります。

なので、-150を計算で0にするには150を足してやればいいのです。

??? ですよね。

枠のポジション座標は、-150、-50,50,150です。
それぞれの数字に最大値である150を足してやると

0、100、200、300となります。
列番号が見えてきましたね~ (V)o\o(V)ふぉふぉふぉ

あとはスパンである100で割ってやると列番号に変わります。

これって数字の不思議なんですが、
0を中心にスパンを揃えて枠組みすると上の計算が成り立ちます。

例えば6枠スパン100にした場合、
枠の外側が-300から300
最小ポジションは、-300からスパンの半分50内側の-250になります。

-250、-150、-50、50、150、250がポジションの座標になるかと思います。

それぞれに最大値の250を足すと
0、100、200、300、400、500となりスパン100で割ると列番号に変わります。


5枠スパン150にしてみます。
枠の外側が-375から375
最小ポジションは-375からスパンの半分75内側の-300になります。

-300、-150、0、150、300がポジションの座標になるかと思います。

それぞれに最大値の300を足すと
0、150、300、450、600となりスパン150で割ると
0,1,2,3,4となります。

式に直すと
(現X座標+最大ポジション座標)/スパン
こんな感じです。


続いて、Y座標なのですが
この計算をすると上から順に3、2、1、0となってしまいます。

これでは、ポジションが逆転するのでマズイです。
ポジションを逆転させる必要があるので絶対値を使います。

まず、計算した数値から最大ポジションの数3を引きます。
するとそれぞれ、0、-1、-2、-3となります。

この数値を絶対値に直せば列番号にかわります。

絶対値にする場合は、Mathf.Absです。

式に直すと
Mathf.Abs((現Y座標+最大ポジション座標)/スパン-最大ポジション数)


前回のスクリプトに導入してみます。

private int[] Position = { -150, -50, 50, 150 }; //座標変数

private int LineX ; //ポジション縦列変数
private int LineY ; //ポジション横列変数


//ピース停止時の位置修正
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);
}


//ピースのポジションを取得する
void PosNo(float x, float y)
{
LineX = (Mathf.FloorToInt(x) + 150) / 100;
LineY = Mathf.Abs((Mathf.FloorToInt(y) + 150) / 100 - 3);
}

移動終了時の位置調整で座標を固定するので、
そこで、ポジションも取得できるようにしてみました。

座標はfloatで取得しているので、int型に代入する時は変換する必要があります。
また、小数点以下を切り捨てたいので、Mathf.FloorToIntを使ってます。

これでポジションが確定するので、配列を用意して、そのままポジションを
入力すれば保存する事ができます。

ポジション管理はGameManagerを作成して、そこに配列を用意すれば
問題ないかと思います。

現段階で考えているのが、
GameManager

private bool[,] Flag=new bool [4,4];

Flag[LineY,LineX]=true;

ざっくりしてますが、
これでピースが停止した時に、どのポジションにあるか分かると思います。

ピースの生成時に空きマスの確認ができるようになるので、
結構重要になりそうです。

パズルゲームのポジション管理には必須なので、
応用して頂けると幸いです。


最後に、ポジションを二次元配列で取得する方法を書きましたが、
二次元から一次元(ただの配列)に変換する事もできます。

縦数+(横数×枠数)

縦0、横0、枠数4×4 なら 0+(0×4)です。

縦1、横3、枠数4×4の場合、
1+(3×4)=13となります。

2048⑫.jpg

スライドパズルの時に配列は苦労しました。
もっと早く、この方法が分かっていれば簡単にできたかもしれないです。

やはりまだまだ勉強不足のようです。
精進あるのみでっす ┐('~`;)┌



追記
スクリプトにPosition変数を書いてなかったので追加しました。




追記2
2048を作成していて、一次から二次への変換が必要になった為、
変換の方法を追記します。

一次のポジションを枠数で割った値がy(横)
余りがx(縦)の座標となります。

2048⑫.jpg
一次ポジションが0の場合
枠数は4なので

0/4=0 余り0
y0 x0が二次ポジションとなります。

一次ポジションが1の場合、
1/4=0 余り1
y0 x1が二次ポジションとなります。

剰余の演算子は%でっす。

y=一次ポジション/枠数
x=一次ポジション%枠数

これで変換できます。

剰余については、Google先生にお尋ね下さいませ
┐(´~`;)┌ かんべんシテネ

この記事へのコメント