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
Donate Button

{ 10 коментара... read them below or Comment }

  1. Muchísimas gracias por el tutorial!!!!!!

    ReplyDelete
    Replies
    1. Anonymous8/27/2014

      Thank you. I'm glad you like it.

      Delete
    2. how and where did you type this in like in excel or what

      Delete
  2. Hi im getting some null error from block var in gen block method, can u help me pls, thanks

    ReplyDelete
  3. Man this stuff is great. I'm learning quite a bit here. You should add a donation link.

    ReplyDelete
  4. this is a c# sprite?

    ReplyDelete
  5. So 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)

    Ive 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?

    ReplyDelete
    Replies
    1. Hi,
      Tutorial above does not need any rotation matrix. I don't think I'll be able to solve your problem, sorry.

      Delete
  6. This comment has been removed by the author.

    ReplyDelete

- Copyright © Unity plus - Skyblue - Powered by Blogger - Designed by Johanes Djogan -