I am trying to make my own tetris clone. I have actually got very far into it without running into major problems, but I am now stuck and can't find the answer after searching.
Basically I have used Input.GetAxis("Vertical") so that when player is holding down on controls the piece drops rapidly (else it goes back to normal drop speed). It all works great but in my memory of Tetris after the block is frozen at the bottom, even when the player is holding down on controls it still doesn't make the new block move fast until they let go of the down key and then press it again.
I have some ideas on how to do it, but i think i might have to delete much of my code and to be honest, I'm scared of breaking it. I am kinda hoping there is some nice elegant solution that I just don't know about yet.
I am posting the code now in case you'd like to look through it all. Its all in one big messy file, but I actually felt maybe making more class files would be unnecessary (perhaps when I am finished I will attempt to encapsulate it all properly and section it into logical classes as another exercise for myself):
using UnityEngine;
using System.Collections.Generic;
public class GameBoard : MonoBehaviour
{
const ushort CELL_COUNT_X = 10;
const ushort CELL_COUNT_Y = 24;
const ushort CELL_SIZE = 1;
GameObject brick;
List<GameObject> bricks = new List<GameObject>();
private Sprite brickSprite;
private Sprite wallSprite;
GameObject[] debugWallBricks = new GameObject[CELL_COUNT_X * CELL_COUNT_Y];
TetrisPiece fallingPiece;
const ushort BLOCKTYPE_COUNT = 7;
const short DEFAULT_POSITION_X = CELL_COUNT_X / 2 - 2;
const short DEFAULT_POSITION_Y = CELL_COUNT_Y - 4;
//bool[,] frozenCells = new bool[CELL_COUNT_X, CELL_COUNT_Y]; // ill use this to store record of all fixed/settled blocks no longer falling
FrozenPiece[,] frozenCells = new FrozenPiece[CELL_COUNT_X, CELL_COUNT_Y];
List<GameObject> frozenCellBricks = new List<GameObject>();
float dropTimer = 0f;
float dropTimerBaseSpeed = 1f;
float dropTimerSuperSpeed = 15f;
float dropDelay = 0.7f;
float moveTimer = 0f;
float moveToDropSpeedFactor = 5f;
float moveDelay; // this value depends on what value the dropDelay is and is set in Update
float rotTimer = 0f;
float rotDelay = 0.15f;
private void Start()
{
brickSprite = Resources.Load<Sprite>("Tex/tetris_brick");
wallSprite = Resources.Load<Sprite>("Tex/border_rounded");
CreateDebugWall();
MakeNewFallingPiece();
for (int y = 0; y < frozenCells.GetLength(1); y++)
{
for (int x = 0; x < frozenCells.GetLength(0); x++)
{
FrozenPiece f = new FrozenPiece(Color.grey, false);
f.isFilled = false;
frozenCells[x,y] = f;
}
}
}
// i clear the visual board every frame and re-create a new brick, spriterenderer etc every frame (probably a shit way !) todo
void ClearBoard()
{
foreach (GameObject go in bricks)
Destroy(go);
foreach (GameObject go in frozenCellBricks)
Destroy(go);
}
void CreateDebugWall()
{
int i = 0;
for (int x = 0; x < CELL_COUNT_X; x++)
{
for (int y = 0; y < CELL_COUNT_Y; y++)
{
brick = new GameObject();
brick.name = "Debug_Wall_Brick";
brick.AddComponent<SpriteRenderer>();
brick.GetComponent<SpriteRenderer>().sprite = wallSprite;
brick.transform.position = new Vector2(x * CELL_SIZE, y * CELL_SIZE);
brick.GetComponent<SpriteRenderer>().color = Color.grey;
debugWallBricks[i] = brick;
}
}
}
void MakeNewFallingPiece()
{
TetrisPiece.BlockType blockType = (TetrisPiece.BlockType)Random.Range(0, BLOCKTYPE_COUNT);
dropTimer = 0f;
fallingPiece = new TetrisPiece(blockType, DEFAULT_POSITION_X, DEFAULT_POSITION_Y);
DrawFallingPiece();
}
void DropDown()
{
// check cell directly below a block:
for (int y = 0; y < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); y++)
{
for (int x = 0; x < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); x++)
{
if (fallingPiece.GetCurrentOrientation().orientations[y, x] == 1)
{
if (fallingPiece.pos_y + y == 0)
{
Debug.Log("HIT FLOOR!");
ConvertFallingPieceToFrozenBricks();
return;
}
if (frozenCells[fallingPiece.pos_x + x, fallingPiece.pos_y + y - 1].isFilled == true)
{
Debug.Log("HIT FROZEN CELLS!");
ConvertFallingPieceToFrozenBricks();
return;
}
}
}
}
fallingPiece.pos_y--;
}
void MoveLeft()
{
// check cell directly to left of a block:
for (int y = 0; y < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); y++)
{
for (int x = 0; x < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); x++)
{
if (fallingPiece.GetCurrentOrientation().orientations[y, x] == 1)
{
if (fallingPiece.pos_x + x == 0)
{
Debug.Log("hit side wall");
return;
}
if(frozenCells[fallingPiece.pos_x - 1 + x, fallingPiece.pos_y + y].isFilled == true)
{
Debug.Log("tried moving left but there was a frozen brick");
return;
}
}
}
}
fallingPiece.pos_x--;
}
void MoveRight()
{
// check cell directly to left of a block:
for (int y = 0; y < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); y++)
{
for (int x = 0; x < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); x++)
{
if (fallingPiece.GetCurrentOrientation().orientations[y, x] == 1)
{
if (fallingPiece.pos_x + x == CELL_COUNT_X - 1)
{
Debug.Log("hit side wall");
return;
}
if (frozenCells[fallingPiece.pos_x + 1 + x, fallingPiece.pos_y + y].isFilled == true)
{
Debug.Log("tried moving right but there was a frozen brick");
return;
}
}
}
}
fallingPiece.pos_x++;
}
void RotatePositive()
{
for (int y = 0; y < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); y++)
{
for (int x = 0; x < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); x++)
{
if (fallingPiece.GetNextOrientation().orientations[y, x] == 1)
{
if (fallingPiece.pos_x + x < 0)
{
Debug.Log("tried rotation but hit a wall");
return;
}
if (fallingPiece.pos_x + x >= CELL_COUNT_X)
{
Debug.Log("tried rotation but hit a wall");
return;
}
if (fallingPiece.pos_y + y == 0)
{
Debug.Log("tried rotation but hit floor");
return;
}
}
}
}
fallingPiece.GotoNextOrientation();
}
void ConvertFallingPieceToFrozenBricks()
{
for (int y = 0; y < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); y++)
{
for (int x = 0; x < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); x++)
{
if (fallingPiece.GetCurrentOrientation().orientations[y,x] == 1)
frozenCells[fallingPiece.pos_x + x, fallingPiece.pos_y + y] = new FrozenPiece(fallingPiece.color, true);
}
}
fallingPiece = null;
}
void DrawFallingPiece()
{
if (fallingPiece == null)
return;
for (int y = 0; y < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); y++)
{
for (int x = 0; x < fallingPiece.GetCurrentOrientation().orientations.GetLength(0); x++)
{
if (fallingPiece.GetCurrentOrientation().orientations[y, x] == 1)
{
brick = new GameObject();
brick.name = "TetrisPiece_" + fallingPiece.blockType;
brick.AddComponent<SpriteRenderer>();
brick.GetComponent<SpriteRenderer>().sprite = brickSprite;
brick.transform.position = new Vector2((fallingPiece.pos_x + x) * CELL_SIZE, (fallingPiece.pos_y + y) * CELL_SIZE);
brick.GetComponent<SpriteRenderer>().color = fallingPiece.color;
bricks.Add(brick);
}
}
}
}
void DrawFrozenBricks()
{
for (int x = 0; x < frozenCells.GetLength(0); x++)
{
for (int y = 0; y < frozenCells.GetLength(1); y++)
{
if (frozenCells[x, y].isFilled == true)
{
brick = new GameObject();
brick.name = "Frozen Cell Brick";
brick.AddComponent<SpriteRenderer>();
brick.GetComponent<SpriteRenderer>().sprite = brickSprite;
brick.transform.position = new Vector2(x * CELL_SIZE, y * CELL_SIZE);
brick.GetComponent<SpriteRenderer>().color = frozenCells[x,y].color;
frozenCellBricks.Add(brick);
}
}
}
}
void HandleMovementAndBlockCreation()
{
float input_x = Input.GetAxis("Horizontal");
float input_y = Input.GetAxis("Vertical");
bool rotateButton = Input.GetButtonDown("Rotate_Positive");
moveDelay = dropDelay / moveToDropSpeedFactor;
moveTimer += Time.deltaTime;
rotTimer += Time.deltaTime;
if (fallingPiece != null)
{
if (input_y < 0)
dropTimer += dropTimerSuperSpeed * Time.deltaTime;
else
dropTimer += dropTimerBaseSpeed * Time.deltaTime;
if (dropTimer >= dropDelay)
{
DropDown();
dropTimer = 0f;
}
else
{
if (moveTimer >= moveDelay)
{
moveTimer = 0f;
if (input_x < 0)
MoveLeft();
else if (input_x > 0)
MoveRight();
}
if (rotateButton)
RotatePositive();
}
}
else
{
MakeNewFallingPiece();
}
}
private void Update()
{
ClearBoard();
HandleMovementAndBlockCreation();
DrawFallingPiece();
DrawFrozenBricks();
}
}
public class TetrisPiece
{
public enum BlockType { A, B, C, D, E, F, G };
public BlockType blockType;
private Orientation[] possibleOrientations;
private int currentOrientationIndex = 0;
public short pos_x, pos_y;
public Color color;
public TetrisPiece(BlockType blockType, short PosX, short PosY)
{
pos_x = PosX;
pos_y = PosY;
this.blockType = blockType;
switch (blockType)
{
case BlockType.A:
color = Color.red;
possibleOrientations = new Orientation[2];
possibleOrientations[0] = new Orientation(new int[4, 4] { { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 } });
possibleOrientations[1] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } });
break;
case BlockType.B:
color = Color.blue;
possibleOrientations = new Orientation[1];
possibleOrientations[0] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 1, 1, 0 }, { 0, 1, 1, 0 }, { 0, 0, 0, 0 } });
break;
case BlockType.C:
color = Color.green;
possibleOrientations = new Orientation[4];
possibleOrientations[0] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 1, 1, 1, 0 }, { 0, 0, 0, 0 } });
possibleOrientations[1] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 0, 0 } });
possibleOrientations[2] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 1, 0 }, { 0, 1, 0, 0 } });
possibleOrientations[3] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 } });
break;
case BlockType.D:
color = Color.cyan;
possibleOrientations = new Orientation[2];
possibleOrientations[0] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 } });
possibleOrientations[1] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 1, 0 } });
break;
case BlockType.E:
color = ColorTools.Color255(128f, 41f, 11f); // brown
possibleOrientations = new Orientation[2];
possibleOrientations[0] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 1, 0 }, { 0, 0, 1, 0 } });
possibleOrientations[1] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 0, 1, 1 }, { 0, 1, 1, 0 }, { 0, 0, 0, 0 } });
break;
case BlockType.F:
color = ColorTools.Color255(255f, 255f, 0f); // yellow
possibleOrientations = new Orientation[4];
possibleOrientations[0] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 1, 1, 0 } });
possibleOrientations[1] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 1, 1, 1 }, { 0, 0, 0, 1 } });
possibleOrientations[2] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 } });
possibleOrientations[3] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 1, 1}, { 0, 0, 0, 0 } });
break;
case BlockType.G:
color = ColorTools.Color255(128f, 0f, 128f); // purple
possibleOrientations = new Orientation[4];
possibleOrientations[0] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 1, 0 } });
possibleOrientations[1] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 1, 1, 0 }, { 0, 0, 0, 0 } });
possibleOrientations[2] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 } });
possibleOrientations[3] = new Orientation(new int[4, 4] { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 1, 0 }, { 1, 0, 0, 0 } });
break;
}
}
public Orientation GetCurrentOrientation()
{
return possibleOrientations[currentOrientationIndex];
}
public Orientation GetNextOrientation()
{
Debug.Log("index = " + currentOrientationIndex);
if (possibleOrientations.Length == 1)
return possibleOrientations[0];
if (currentOrientationIndex == possibleOrientations.Length - 1)
return possibleOrientations[0];
else
return possibleOrientations[currentOrientationIndex + 1];
}
public void GotoNextOrientation()
{
currentOrientationIndex++;
if (currentOrientationIndex >= possibleOrientations.Length)
currentOrientationIndex = 0;
}
}
public struct Orientation
{
public int[,] orientations;
public Orientation(int[,] Orientations)
{
orientations = Orientations;
}
}
public class FrozenPiece
{
public Color color;
public bool isFilled = false;
public FrozenPiece(Color Color, bool IsFilled)
{
color = Color;
isFilled = IsFilled;
}
}
public static class ColorTools
{
public static Color Color255(float r, float g, float b)
{
return new Color(r / 255f, g / 255f, b / 255f);
}
public static Color Color255_Alpha(float r, float g, float b, float a)
{
return new Color(r / 255f, g / 255f, b / 255f, a / 255f);
}
}