ぬるーむ

Unity初心者が誰もが知っているゲームの模倣をしています。個人的な備忘録ですが、入門書を読み終えたばかりの初心者の方は「こんなへなちょこでもいいのか!」「俺の方がうまく作れる」と作成意欲がわいたりするかもしれません。

Unityでぷよぷよを作ってみた 3


スポンサードリンク

ぷよの消去

毎フレーム、状態がVanishのとき以下の処理を行う。

  1. 隣接した同色ぷよを取得する(同色リスト)。
  2. 同色リストのぷよが4つ以上のものを消去可能リストに追加する。
  3. 消去可能リストの中のぷよを全て消去する。

隣接した同色ぷよの取得

迷路を深さ優先探索で解くかんじで、隣接する同色ぷよを取得する。

f:id:Nullsuke:20201020214754p:plain
迷路にみたてる

private readonly int[,] dirs = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
private readonly Puyo[,] field;
private readonly int width;
private readonly int height;

//隣接した同色ぷよを取得
private List<Puyo> PickSameColor(Puyo puyo)
{
    //同色リスト
    var same = new List<Puyo>();

    puyo.Checked = true;

    //ぷよの上下左右を調べる
    for (int i = 0; i < dirs.GetLength(0); i++)
    {
        int nx = puyo.IntX + dirs[i, 0];
        int ny = puyo.IntY + dirs[i, 1];

        if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue;

        var np = field[nx, ny];

        if (np == null || np.Checked || puyo.Color != np.Color) continue;
        
        var s = PickSameColor(np);

        same.AddRange(s);
    }

    same.Add(puyo);

    return same;
}

消去可能なぷよの取得

全てのぷよで以下の処理を行う。

  1. 隣接した同色ぷよを取得する(同色リスト)。
  2. 同色リストのぷよが4つ以上のものを消去可能リストに追加する。
    f:id:Nullsuke:20201021222522p:plain
    消去1
    f:id:Nullsuke:20201021222600p:plain
    消去2
    f:id:Nullsuke:20201021222649p:plain
    消去3
    f:id:Nullsuke:20201021222711p:plain
    消去4
    f:id:Nullsuke:20201021222727p:plain
    消去5
private readonly Puyo[,] field;
private readonly int width;
private readonly int height;

//消去できるぷよを取得
public List<Puyo> GetVanishablePuyoes(Puyo[,] field)
{
    //消去可能リスト
    var vanishable = new List<Puyo>();
    ClearCheckedFlag();

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            var p = field[x, y];

            if (p == null || p.Checked) continue;
            
            var same = PickSameColor(p);

            //同色リストの中身が4以上なら消去可能リストに追加
            if (same.Count >= 4)
            {
                vanishable.AddRange(same);
            }
        }
    }

    return vanishable;
}

//全てのCheckedフラグを消す
private void ClearCheckedFlag()
{
    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            var p = field[x, y];
            if (p != null) p.Checked = false;
        }
    }
}

ぷよの消去、連鎖

ぷよを消去した後、ぷよの落下→消去を繰り返すことで連鎖になる。

private static readonly Vanisher vanisher = new Vanisher(field);

public List<Puyo> GetVanishablePuyoes()
{
    return vanisher.GetVanishablePuyoes();
}
private static List<Puyo> droppablePuyoes = new List<Puyo>();
private enum State { Normal, FreeFall, Vanish, Wait }
private int chainCount;

private void Update()
{
    switch (state)
    {
        case State.Normal:
            //落下、移動、回転、固定などの処理
            break;

        case State.FreeFall:
            //落下可能なぷよを下にあるものから順に落下させる
            droppablePuyoes.OrderBy(p => p.IntY).ToList<Puyo>()
                   .ForEach(p => p.FreeFall());
            //落下できなくなったぷよは全てリストから消す
            droppablePuyoes.RemoveAll(p => !p.Droppable);

            if (droppablePuyoes.Count == 0)
            {
                //連鎖へ遷移
                state = State.Vanish;
            }

            break;

        case State.Vanish:
            //消去可能ぷよを取得
            var vanishable = field.GetVanishablePuyoes();

            if (vanishable.Count > 0)
            {
                chainCount++;

                //ぷよを消去
                vanishable.ForEach(p => p.Vanish());
                
                //落下可能ぷよを取得
                droppablePuyoes = field.GetDroppablePuyoes();

                if (droppablePuyoes.Count > 0) state = State.FreeFall;
                else
                {
                    //通常へ遷移
                    state = State.Normal;
                    //この組ぷよ自身の消去と新しい組ぷよの作成
                    OnFalled();
                }
            }
            else
            {
                //通常へ遷移
                state = State.Normal;
                ////この組ぷよ自身の消去と新しい組ぷよの作成
                OnFalled();
            }

            break;

        case State.Wait:
            break;
    }
}