[unity]アイコンをドラッグで移動、入れ替え(4)

画像に alt 属性が指定されていません。ファイル名: g.gif

「なに切る?」ツールを作る時に設計した UI のお話。完結編です。

他の記事

この記事は4番目の記事です。他の記事については以下のリンクをご覧ください。

  1. はじめに(概要)
  2. クラスの構造、実装ルール
  3. マウスで牌を移動、カーソル位置に戻す
  4. ドラッグした牌に合わせて、他の牌を動かす← この記事

サンプル環境

  • サンプル環境の牌は、記事とは別の画像になっています。

牌を入れ替える条件

牌を入れ替えるのに必要な条件は、牌がどの枠に近づいたのか(触れたのか)? がポイントです。
普通に考えれば「牌の持っているコライダーと、枠のもっているコライダーが接触すれば…」となりますが、なんとなくコライダーを使わずに実装してしまいました。

2点間の距離がn以下だったら…という計算式で凌いでいます。
2点間の距離の求め方を書いた記事はこちら。

コード

(PaiPosGroup.cs)
    Result getNearestPosNo(Vector3 loc)
    {
        float   distance  = INIT_DISTANCE;
        int     nearPosNo = -1;

        for (int i = 0; i < poss.Count; i++)
        {
            PaiPos pos = poss[i];

            float   x    = pos.transform.localPosition.x - loc.x;
            float   y    = pos.transform.localPosition.y - loc.y;
            float   d    = x*x + y*y;

            if (d < distance)
            {
                distance   = d;
                nearPosNo = i;
            }
        }

        return new Result() { PosNo = nearPosNo, Distance = distance };
    }

ドラッグ中の牌と、PaiPosGroup に登録された枠全て(この例だと 12 個)の距離を算出し、「一番近い枠番号」「その距離」を返しています。

距離を返しているのは、例えばこういう場合。

一番近い枠番号は8ですが、枠に接触していないので入れ替える必要がありません。
枠に接触するぐらいの距離に達している場合のみ、牌を入れ替える必要があります。

牌の入れ替えは1つずつではない

牌が横移動のみであれば、あるいは1つずつでよかったかもしれませんが、マウスドラッグで自由自在に動くので…。

この場合、4から8の間にある牌は全て左に1つずつ動かし、④を8に挿入します。


8から4の場合は全て右に1つずつ動かして…と逆方向になります。

コード

    void replaceDragPai()
    {
        if (resDrag.PosNo > resBegin.PosNo)
        {
            // 牌を入れ替える(左..ドラッグ元、右..ドラッグ先)
            for (int i = resBegin.PosNo; i < resDrag.PosNo; i++)
            {
                PaiPos w0 = GetEntity(i);
                PaiPos w1 = GetEntity(i+1);

                var model = w0.Model;
                w0.LinkModel(w1.Model);
                w0.MoveToPos();
                w1.LinkModel(model);
            }
        }
        else
        if (resDrag.PosNo < resBegin.PosNo)
        {
            // 牌を入れ替える(右..ドラッグ元、左..ドラッグ先)
            for (int i = resBegin.PosNo; i > resDrag.PosNo; i--)
            {
                PaiPos w0 = GetEntity(i);
                PaiPos w1 = GetEntity(i-1);

                var model = w0.Model;
                w0.LinkModel(w1.Model);
                w0.MoveToPos();
                w1.LinkModel(model);
            }
        }
    }


resBegin.PosNo から resDrag.PosNo に向けて牌を入れ替えています。
左からか、右からかによって入れ替える方向が違うので、場合分けしています。

2022年のプログラム作法では、こういう for 文の使い方、忌み嫌われそうです。ユルシテ。

LinkModel によって「枠に所属する牌を入れ替え」、MoveToPos によって「所属した枠まで自動的に牌を動かす(アニメーション開始)」となっています。
w1 は基本的に空欄想定なので、MoveToPos を実行していません。

MoveToPos は その3 の OnEndDrag で使ったものと同じです。

元は別処理でしたが、使いまわしが利く方法はないか、クラスの設計を見直しました。

OnDrag のみ判定する(それ以外では判定しない)

細かい仕様ですが、この牌入れ替えが必要なのは OnDrag している時のみです。
なにも場合分けせず「枠に近づいたら無条件で入れ替え」という単純化したロジックにすると、OnEndDrag で牌が戻っている時に入れ替えが起こる…など予測しづらい問題が起こるかもしれません。

完成

以上で牌を任意に入れ替える処理、完成です!

こんな風に無茶苦茶な速度で入れ替えても大丈夫。

設計が甘いと、こういう操作で大抵不具合が起こり、プログラマは想定外の操作だと仕様説を唱え、逆切れします。暖かく見守ってあげてください。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA