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;
// Audio
[SerializeField] AudioClip seHit;
[SerializeField] AudioClip seDelete;
// 次のブロック
BlockController nextBlock;
// 現在のブロック
BlockController currentBlock;
// フィールド全体のデータ
Transform[,] fieldTiles;
// 現在のスコア
int score;
// 現在の経過時間
float gameTimer;
// Audio
AudioSource audioSource;
// Start is called before the first frame update
void Start()
{
// 2次元配列を初期化
fieldTiles = new Transform[FieldWidth, FieldHeight];
// Audio
audioSource = GetComponent<AudioSource>();
// 初回のブロックを作成
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();
// SE
audioSource.PlayOneShot(seHit);
}
// ワールド座標をインデックス座標に変換
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()
{
// SE再生
bool isPlaySE = false;
// 上から調べる
for (int y = FieldHeight-1; y >= 0; y--)
{
// ラインが揃っているか
if (!IsDeleteLine(y)) continue;
// ライン削除
DeleteLine(y);
// ラインを下にずらす
FallLine(y);
// スコアを加算
score += ScoreLine;
textScore.text = "" + score;
isPlaySE = true;
}
// SE
if (isPlaySE)
{
audioSource.PlayOneShot(seDelete);
}
}
// リトライボタン
public void OnClickRetry()
{
SceneManager.LoadScene("TetrisPuzzleScene");
}
}