65.#8

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BlockController : MonoBehaviour
{
    // 落下タイマー
    float fallTimerMax = 0.5f;
    float fallTimer;

    // シーンディレクター
    [SerializeField] TetrisPuzzleSceneDirector gameSceneDirector;

    // 初期化
    public void Init(TetrisPuzzleSceneDirector director, float timerMax)
    {
        gameSceneDirector = director;
        fallTimerMax = timerMax;
    }

    // Start is called before the first frame update
    void Start()
    {
        fallTimer = fallTimerMax;
    }

    // Update is called once per frame
    void Update()
    {
        // 落下タイマー消化
        fallTimer -= Time.deltaTime;

        // 移動する方向
        Vector3 movePosition = Vector3.zero;

        // 左移動
        if(Input.GetKeyUp(KeyCode.LeftArrow))
        {
            movePosition = Vector3.left;
        }
        // 右移動
        else if (Input.GetKeyUp(KeyCode.RightArrow))
        {
            movePosition = Vector3.right;
        }
        // 下移動
        else if (Input.GetKeyUp(KeyCode.DownArrow))
        {
            movePosition = Vector3.down;
        }
        // 回転
        else if (Input.GetKeyUp(KeyCode.UpArrow))
        {
            transform.Rotate(new Vector3(0, 0, 1), 90);
            if(!gameSceneDirector.IsMovable(transform))
            {
                transform.Rotate(new Vector3(0, 0, 1), -90);
            }
        }

        // 時間経過で下移動
        if(fallTimer < 0)
        {
            movePosition = Vector3.down;
            fallTimer = fallTimerMax;
        }

        // 移動
        transform.position += movePosition;

        // 移動できなかった場合
        if(!gameSceneDirector.IsMovable(transform))
        {
            // 元に戻す
            transform.position -= movePosition;

            // 下に移動できなかった場合は終了
            if(movePosition == Vector3.down)
            {
                enabled = false;
            }
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class TetrisPuzzleSceneDirector : MonoBehaviour
{
    // フィールドサイズ
    const int FieldWidth = 10;
    const int FieldHeight = 20;
    // 1ラインのスコア
    const int ScoreLine = 100;

    // ブロックのプレハブ
    [SerializeField] List<BlockController> prefabBlocks;
    // 初回の落下速度
    [SerializeField] float startFallTimerMax;
    // UI
    [SerializeField] Text textScore;
    [SerializeField] GameObject panelResult;

    // 次のブロック
    BlockController nextBlock;
    // 現在のブロック
    BlockController currentBlock;
    // フィールド全体のデータ
    Transform[,] fieldTiles;

    // 現在のスコア
    int score;
    // 現在の経過時間
    float gameTimer;

    // Start is called before the first frame update
    void Start()
    {
        // 2次元配列を初期化
        fieldTiles = new Transform[FieldWidth, FieldHeight];

        // 初回のブロックを作成
        SetupNextBlock();
        // フィールドへ
        SpawnBlock();

        // スコア表示
        score = 0;
        textScore.text = "" + score;

        // UI初期設定
        panelResult.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        // 時間経過
        gameTimer += Time.deltaTime;

        // 動いている時はなにもしない
        if (currentBlock.enabled) return;

        // 動きが止まったらデータセット
        foreach (Transform item in currentBlock.transform)
        {
            Vector2Int index = GetIndexPosition(item.position);
            fieldTiles[index.x, index.y] = item;

            // ゲームオーバー
            if(index.y > FieldHeight - 2)
            {
                panelResult.SetActive(true);
                enabled = false;
            }
        }

        // 揃ったラインを削除
        DeleteLines();

        // 次のブロックをフィールドへ
        SpawnBlock();
    }

    // ワールド座標をインデックス座標に変換
    Vector2Int GetIndexPosition(Vector3 position)
    {
        Vector2Int index = new Vector2Int();

        index.x = Mathf.RoundToInt(position.x - 0.5f) + FieldWidth / 2;
        index.y = Mathf.RoundToInt(position.y - 0.5f) + FieldHeight / 2;

        return index;
    }

    // 移動可能か
    public bool IsMovable(Transform blockTransform)
    {
        foreach (Transform item in blockTransform)
        {
            Vector2Int index = GetIndexPosition(item.position);

            // 左右と下
            if(index.x < 0 || FieldWidth-1 < index.x || index.y < 0)
            {
                return false;
            }

            // ブロックがある
            if(GetFieldTile(index))
            {
                return false;
            }
        }

        return true;
    }

    // 次のブロックを作成
    void SetupNextBlock()
    {
        // prefabBlocksからランダムで作成
        int rnd = Random.Range(0, prefabBlocks.Count);

        // 表示位置
        Vector3 setupPosition = new Vector3(2.5f, 11f, 0);

        // ブロック生成
        BlockController prefab = prefabBlocks[rnd];
        nextBlock = Instantiate(prefab, setupPosition, Quaternion.identity);

        // 時間経過によって落下速度を早くする
        float fallTime = startFallTimerMax;
        if(gameTimer > 15)
        {
            fallTime = startFallTimerMax * 0.1f;
        }
        else if (gameTimer > 10)
        {
            fallTime = startFallTimerMax * 0.4f;
        }
        else if (gameTimer > 5)
        {
            fallTime = startFallTimerMax * 0.7f;
        }

        // 初期化
        nextBlock.Init(this, fallTime);

        // 動かないようにしておく
        nextBlock.enabled = false;
    }

    // ブロックをフィールドへ
    void SpawnBlock()
    {
        // 出現位置
        Vector3 spawnPosition = new Vector3(0.5f, 8.5f, 0);

        // ブロックをセット
        currentBlock = nextBlock;
        currentBlock.transform.position = spawnPosition;

        // 動かす
        currentBlock.enabled = true;

        // 次のブロックをセット
        SetupNextBlock();
    }

    // フィールドのブロックを返す
    Transform GetFieldTile(Vector2Int index)
    {
        if(index.x < 0 || FieldWidth -1 < index.x ||
            index.y < 0 || FieldHeight - 1 < index.y )
        {
            return null;
        }

        return fieldTiles[index.x, index.y];
    }

    // 削除可能かどうか
    bool IsDeleteLine(int y)
    {
        for (int x = 0; x < FieldWidth; x++)
        {
            if (!fieldTiles[x, y]) return false;
        }

        return true;
    }

    // ラインを削除
    void DeleteLine(int y)
    {
        for (int x = 0; x < FieldWidth; x++)
        {
            Destroy(fieldTiles[x, y].gameObject);
        }
    }

    // ラインを下げる
    void FallLine(int startY)
    {
        // 指定されたラインから上の全て
        for (int y = startY + 1; y < FieldHeight; y++)
        {
            for (int x = 0; x < FieldWidth; x++)
            {
                if (!fieldTiles[x, y]) continue;

                // ワールド座標更新
                fieldTiles[x, y].position += Vector3.down;
                // 内部データ更新
                fieldTiles[x, y - 1] = fieldTiles[x, y];
                fieldTiles[x, y] = null;
            }
        }
    }

    // ライン削除
    void DeleteLines()
    {
        // 上から調べる
        for (int y = FieldHeight-1; y >= 0; y--)
        {
            // ラインが揃っているか
            if (!IsDeleteLine(y)) continue;

            // ライン削除
            DeleteLine(y);

            // ラインを下にずらす
            FallLine(y);

            // スコアを加算
            score += ScoreLine;
            textScore.text = "" + score;
        }
    }

    // リトライボタン
    public void OnClickRetry()
    {
        SceneManager.LoadScene("TetrisPuzzleScene");
    }
}
テトリス

前の記事

64.#7
テトリス

次の記事

66.#9