- Back to Home »
- Tetris game tutorial - part 2
3 June 2014
User input
Now when we spawn a block we want to make user able to controll that shape(left, right, rotate, fall down faster).
But each time we want to move left or right or even to rotate block we will need to check will it hit something.
In update function:
void Update(){ if(spawn && shapes.Count == 4){ //If there is block //Get spawned blocks positions Vector3 a = shapes[0].transform.position; Vector3 b = shapes[1].transform.position; Vector3 d = shapes[2].transform.position; Vector3 c = shapes[3].transform.position; if(Input.GetKeyDown(KeyCode.LeftArrow)){//Move left if(CheckUserMove(a,b,c,d,true)){//Check if we can move it left a.x-=1; b.x-=1; c.x-=1; d.x-=1; pivot.transform.position = new Vector3(pivot.transform.position.x-1, pivot.transform.position.y, pivot.transform.position.z); shapes[0].transform.position = a; shapes[1].transform.position = b; shapes[2].transform.position = c; shapes[3].transform.position = d; } } if(Input.GetKeyDown(KeyCode.RightArrow)){//Move right if(CheckUserMove(a,b,c,d,false)){//Check if we can move it right a.x+=1; b.x+=1; c.x+=1; d.x+=1; pivot.transform.position = new Vector3(pivot.transform.position.x+1, pivot.transform.position.y, pivot.transform.position.z); shapes[0].transform.position = a; shapes[1].transform.position = b; shapes[2].transform.position = c; shapes[3].transform.position = d; } } if(Input.GetKey(KeyCode.DownArrow)){ //Move down fast moveDown(); }CheckUserMove function:
bool CheckUserMove(Vector3 a, Vector3 b, Vector3 c, Vector3 d, bool dir){ //Check, if we move a block left/right will it hit something if(dir){//Left if(board[Mathf.RoundToInt(a.x-1),Mathf.RoundToInt(a.y)]==1 || board[Mathf.RoundToInt(b.x-1),Mathf.RoundToInt(b.y)]==1 || board[Mathf.RoundToInt(c.x-1),Mathf.RoundToInt(c.y)]==1 || board[Mathf.RoundToInt(d.x-1),Mathf.RoundToInt(d.y)]==1){ return false; } } else{//Right if(board[Mathf.RoundToInt(a.x+1),Mathf.RoundToInt(a.y)]==1 || board[Mathf.RoundToInt(b.x+1),Mathf.RoundToInt(b.y)]==1 || board[Mathf.RoundToInt(c.x+1),Mathf.RoundToInt(c.y)]==1 || board[Mathf.RoundToInt(d.x+1),Mathf.RoundToInt(d.y)]==1){ return false; } } return true; }So when user press left arrow or right it will first check if CheckUserMove returns true, which means that we can move shape left or right. Also when user press down arrow it will call function moveDown so it goes faster...
Block rotation
The way I did block rotation is really simple but not the best way. Anyway in the update function we will check when user press the space and then we will rotate blocks around object which is called RotateAround(this object moves with shape and his position is defined for each block in SpawnShape function). Also we will check after we rotate shape does any block hit edge or other block and if so will just rotate it again in default position.
if(Input.GetKeyDown(KeyCode.Space)){ //Rotate Rotate(shapes[0].transform,shapes[1].transform,shapes[2].transform,shapes[3].transform); }
void Rotate(Transform a, Transform b, Transform c, Transform d){ //Set parent to pivot so we can rotate a.parent = pivot.transform; b.parent = pivot.transform; c.parent = pivot.transform; d.parent = pivot.transform; currentRot +=90;//Add rotation if(currentRot==360){ //Reset rotation currentRot = 0; } pivot.transform.localEulerAngles = new Vector3(0,0,currentRot); a.parent = null; b.parent = null; c.parent = null; d.parent = null; if(CheckRotate(a.position,b.position,c.position,d.position) == false){ //Set parent to pivot so we can rotate a.parent = pivot.transform; b.parent = pivot.transform; c.parent = pivot.transform; d.parent = pivot.transform; currentRot-=90; pivot.transform.localEulerAngles = new Vector3(0,0,currentRot); a.parent = null; b.parent = null; c.parent = null; d.parent = null; } } bool CheckRotate(Vector3 a, Vector3 b, Vector3 c, Vector3 d){ if(Mathf.RoundToInt(a.x)<board.GetLength(0)-1){//Check if block is in board if(board[Mathf.RoundToInt(a.x),Mathf.RoundToInt(a.y)]==1){ //If rotated block hit any other block or edge, after rotation return false; //Rotate in default position - previous } } else{//If the block is not in the board return false;//Do not rotate } if(Mathf.RoundToInt(b.x)<board.GetLength(0)-1){ if(board[Mathf.RoundToInt(b.x),Mathf.RoundToInt(b.y)]==1){ return false; } } else{ return false; } if(Mathf.RoundToInt(c.x)<board.GetLength(0)-1){ if(board[Mathf.RoundToInt(c.x),Mathf.RoundToInt(c.y)]==1){ return false; } } else{ return false; } if(Mathf.RoundToInt(d.x)<board.GetLength(0)-1){ if(board[Mathf.RoundToInt(d.x),Mathf.RoundToInt(d.y)]==1){ return false; } } else{ return false; } return true; //We can rotate }Now when you press space you should see shape rotate by 90 deegres.
Check if there is full row, destroy and move blocks down
This is the last thing we need to do. We will check for full row each time shape hit something(You can find it in moveDown function). For each row we will count how many blocks are at the height 1. If there is 10 blocks(which is full row) move blocks down and again check height 1, because there can be again full row. And if less than 10 blocks then check row above this.
And also if at the game over height is more than 1 block then just set gameOver boolean to true, and spawninig will stop.
//Check specific row for match void checkRow(int y){ GameObject[] blocks = GameObject.FindGameObjectsWithTag("Block"); //All blocks in the scene int count = 0; //Blocks found in a row for(int x=1; x<board.GetLength(0)-1; x++){//Go through each block on this height if(board[x,y]==1){//If there is any block at this position count++;//We found +1 block } } if(y==gameOverHeight && count>0){//If the current height is game over height, and there is more than 0 block, then game over Debug.LogWarning("Game over"); gameOver = true; } if(count==10){//The row is full //Start from bottom of the board(withouth edge and block spawn space) for(int cy=y; cy<board.GetLength(1)-3; cy++){ for(int cx=1; cx<board.GetLength(0)-1; cx++){ foreach(GameObject go in blocks){ int height = Mathf.RoundToInt(go.transform.position.y); int xPos = Mathf.RoundToInt(go.transform.position.x); if(xPos == cx && height == cy){ if(height == y){//The row we need to destroy board[xPos,height] = 0;//Set empty space Destroy(go.gameObject); } else if(height > y){ board[xPos,height] = 0;//Set old position to empty board[xPos,height-1] = 1;//Set new position go.transform.position = new Vector3(xPos, height-1, go.transform.position.z);//Move block down } } } } } checkRow(y); //We moved blocks down, check again this row } else if(y+1<board.GetLength(1)-3){ checkRow(y+1); //Check row above this } }We will also need to call this in moveDown function when the shape hit something.
else{ //We hit something. Stop and mark current shape location as filled in board, also destroy last pivot gameobject Destroy(pivot.gameObject); //Destroy pivot //Set ID in board board[Mathf.RoundToInt(a.x),Mathf.RoundToInt(a.y)]=1; board[Mathf.RoundToInt(b.x),Mathf.RoundToInt(b.y)]=1; board[Mathf.RoundToInt(c.x),Mathf.RoundToInt(c.y)]=1; board[Mathf.RoundToInt(d.x),Mathf.RoundToInt(d.y)]=1; //**************************************************** checkRow(1); //Check for any match checkRow(gameOverHeight); //Check for game over //**************************************************** shapes.Clear(); //Clear spawned blocks from array spawn = false; //Spawn a new block }That's it. You should be able to rotate, move shape left and right, speed down. And when the row is full it should clear that row also all blocks above will fall. Feel free to request any tutorial. I hope you enjoyed.
Full tetris script.
Source code
Muchísimas gracias por el tutorial!!!!!!
ReplyDeleteThank you. I'm glad you like it.
Deletehow and where did you type this in like in excel or what
DeleteHi im getting some null error from block var in gen block method, can u help me pls, thanks
ReplyDeleteMan this stuff is great. I'm learning quite a bit here. You should add a donation link.
ReplyDeleteThanks Oats :)
Deletethis is a c# sprite?
ReplyDeleteSo Im making Tetris game and right now I am struggling with the rotations because I dont know how to make it reasonable for the blocks to change their positions in the right way(It would be nice to have one Formula for every block not sure wether its possible or not)
ReplyDeleteIve 7 different blocks in 7 different classes and every class looks like this:
public class TBlock
{
Texture2D Block;
const int x = 4;
const int y = 4;
public bool[,] tblock()
{
Color Color;
bool[,] vorm;
vorm = new bool[x, y];
vorm[x, y] = false;
vorm[0, 1] = true;
vorm[1, 1] = true;
vorm[1, 0] = true;
vorm[2, 1] = true;
Color = Color.Indigo;
return vorm;
}
public void RotationLinks(InputHelper inputhelper)
{
bool[,] nieuw = new bool[x, y];
if (inputhelper.KeyPressed(Keys.Left))
{
}
}
}
The problem is I dont know what to write in the if code.
I was looking for solution and I found this one:
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
ret[i, j] = matrix[n - j - 1, i];
}
}
return ret;
I would never have thought this myself to write this so I was wondering wether there are other methods or not. Besides of that, is this working for every block?
Hi,
DeleteTutorial above does not need any rotation matrix. I don't think I'll be able to solve your problem, sorry.
This comment has been removed by the author.
ReplyDelete