1 June 2014

Hello,
By the Able_Elizalde request, I will start the tutorial about ,,How to create a simple tetris game".



So far I've never tried to make a tetris game. And this may not be the best way to do tetris style game.

Anyway, what we need to do:

  1. Generate board
  2. Create a shape
  3. Move shape down
  4. Collision between other blocks and edge
  5. User input
  6. Block rotation
  7. Check if there is full row, destroy and move blocks down
Before we start you can test final result.
Generate board

To generate a board, we will first need to set board width and height. Tetris board size is 10x20. But we will also add left and right edge of the board. Which is 1 + 10  + 1 = 12.
Same for the height 1 + 20 + 1 and for the block spawn +2  = 24.
In a new c# script we will define few variables we will need for this tetris game.





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

public class Tetris : MonoBehaviour {

//Board
public int[,] board;

//Block 
public Transform block;

//Spawn boolean
public bool spawn;

//Seconds before next block spawn
public float nextBlockSpawnTime = 0.5f;

//Block fall speed
public float blockFallSpeed = 0.5f;

//Game over level
public int gameOverHeight = 22; //20 board + 2 edge 

//Current spawned shapes
private List<Transform> shapes = new List<Transform>();

//Set true if  game over
private bool gameOver;

//Current rotation of an object
private int currentRot = 0;

//Current pivot of the shape
private GameObject pivot;

void Start(){
//Deafult board is 10x16

//1+10+1 - Side edge

//+2 - Space for spawning
//+1 - Top edge 
//20 - Height
//+1 - Down edge 

board = new int[12,24];//Set board width and height

Now we will need to generate a board as soon as the game start, so make sure to call GenBoard in a Start function.
We will generate a tetris board like this:

  1. Edge of the board, we will mark it in board 2D array, so we can later check where is edge
  2. Board, mark it in board 2D array as empty space



void GenBoard(){
    for(int x=0; x<board.GetLength(0);x++){
        for(int y=0; y<board.GetLength(1);y++){
            if(x<11 && x>0){
                if(y>0 && y<board.GetLength(1)-2){
                    //Board
                    board[x,y]=0;
                    GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    cube.transform.position = new Vector3(x,y,1);
                    Material material = new Material(Shader.Find("Diffuse"));
                    material.color = Color.grey;
                    cube.renderer.material = material;
                    cube.transform.parent = transform;
                }
                else if(y<board.GetLength(1)-2){
                    board[x,y]=1;
                    GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    cube.transform.position = new Vector3(x,y,0);
                    Material material = new Material(Shader.Find("Diffuse"));
                    material.color = Color.black;
                    cube.renderer.material = material;
                    cube.transform.parent = transform;
                    cube.collider.isTrigger = true;
                    
                }
            }
            else if((y<board.GetLength(1)-2)){
                //Left and right edge
                board[x,y]=1;
                GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                cube.transform.position = new Vector3(x,y,0);
                Material material = new Material(Shader.Find("Diffuse"));
                material.color = Color.black;
                cube.renderer.material = material;
                cube.transform.parent = transform;
            }
        }
    }
}

Create a shape

Now lets create all shapes that can be spawned and each shape will have pivot point(later we will rotate shape around that point). One shape will consist out of 4 blocks. So spawn shape function will return 4 blocks. You will also need to add a new tag ,,Block,,. Then later we can just simply find all objects with that tag.
void SpawnShape(){
    
    int shape = Random.Range(0,6);//Random shape
    int height = board.GetLength(1)-4;
    int xPos = board.GetLength(0)/2-1;
    
    //Create a new pivot
    
    pivot = new GameObject("RotateAround"); //Pivot of the shape
    
    
    //SShape
    if(shape==0){
        
        pivot.transform.position = new Vector3(xPos,height+1, 0);
        
        shapes.Add(GenBlock(new Vector3(xPos, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos-1, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+1,0)));
        shapes.Add(GenBlock(new Vector3(xPos+1, height+1,0)));
        
        
        Debug.Log("Spawned SShape");
    }
    //IShape
    else if(shape==1){
        
        pivot.transform.position = new Vector3(xPos+0.5f,height+1.5f, 0);
        
        shapes.Add(GenBlock(new Vector3(xPos, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+1,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+2,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+3,0)));
        
        Debug.Log("Spawned IShape");
    }
    //OShape
    else if(shape==2){
        
        pivot.transform.position = new Vector3(xPos+0.5f,height+0.5f, 0);
        
        shapes.Add(GenBlock(new Vector3(xPos, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos+1, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+1,0)));
        shapes.Add(GenBlock(new Vector3(xPos+1, height+1,0)));
        
        Debug.Log("Spawned OShape");
    }
    //JShape
    else if(shape==3){
        
        pivot.transform.position = new Vector3(xPos,height+2, 0);
        
        shapes.Add(GenBlock(new Vector3(xPos, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos+1, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+1,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+2,0)));
        
        Debug.Log("Spawned JShape");
    }
    
    //TShape
    else if(shape==4){
        
        pivot.transform.position = new Vector3(xPos,height, 0);
        
        shapes.Add(GenBlock(new Vector3(xPos, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos-1, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos+1, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+1,0)));
        
        Debug.Log("Spawned TShape");
    }
    
    //LShape
    else if(shape==5){
        
        pivot.transform.position = new Vector3(xPos,height+1, 0);
        
        shapes.Add(GenBlock(new Vector3(xPos, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos-1, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+1,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+2,0)));
        
        Debug.Log("Spawned LShape");
    }
    
    //ZShape
    else{
        
        pivot.transform.position = new Vector3(xPos,height+1, 0);
        
        shapes.Add(GenBlock(new Vector3(xPos, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos+1, height,0)));
        shapes.Add(GenBlock(new Vector3(xPos, height+1,0)));
        shapes.Add(GenBlock(new Vector3(xPos-1, height+1,0)));
        
        Debug.Log("Spawned ZShape");        
        
    }
    
    
    
}

//Create block at the position
Transform GenBlock(Vector3 pos){
    
    Transform obj = (Transform)Instantiate(block.transform, pos, Quaternion.identity) as Transform;
    obj.tag = "Block";
    
    return obj;
}

Move shape down and collision

To actually move a shape down we will first need to spawn a shape. So in a update function:
if(!spawn && !gameOver){//If nothing spawned, if game over = false, then spawn
    StartCoroutine("Wait");
    spawn = true;
    //Reset rotation
    currentRot = 0;
}
Then we will wait some time before shape is spawned.
//Wait time before next block spawn
IEnumerator Wait(){
    
    yield return new WaitForSeconds(nextBlockSpawnTime);
    SpawnShape();
}
Now if you run the code you should see a random spawned shape, and also board in the background.

Let's now move that shape down.
void moveDown(){
    //Spawned blocks positions
    if(shapes.Count!=4){
        return;
    }
    Vector3 a = shapes[0].transform.position;
    Vector3 b = shapes[1].transform.position; 
    Vector3 c = shapes[2].transform.position;
    Vector3 d = shapes[3].transform.position;
    
    if(CheckMove(a,b,c,d)==true){    // Will we hit anything if we move block down(true = we can move)
        //Move block down by 1
        a = new Vector3(Mathf.RoundToInt(a.x),Mathf.RoundToInt(a.y-1.0f),a.z);
        b = new Vector3(Mathf.RoundToInt(b.x),Mathf.RoundToInt(b.y-1.0f),b.z);
        c = new Vector3(Mathf.RoundToInt(c.x),Mathf.RoundToInt(c.y-1.0f),c.z);
        d = new Vector3(Mathf.RoundToInt(d.x),Mathf.RoundToInt(d.y-1.0f),d.z);
        
        pivot.transform.position = new Vector3(pivot.transform.position.x, pivot.transform.position.y-1, pivot.transform.position.z);
        
        shapes[0].transform.position = a;
        shapes[1].transform.position = b; 
        shapes[2].transform.position = c; 
        shapes[3].transform.position = d; 
        
    }
    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;
        
        shapes.Clear(); //Clear spawned blocks from array
        spawn = false; //Spawn a new block
        
        
    }
}


bool CheckMove(Vector3 a, Vector3 b, Vector3 c, Vector3 d){
    //Check, if we move a block down will it hit something
    if(board[Mathf.RoundToInt(a.x),Mathf.RoundToInt(a.y-1)]==1){
        return false;
    }
    if(board[Mathf.RoundToInt(b.x),Mathf.RoundToInt(b.y-1)]==1){
        return false;
    }
    if(board[Mathf.RoundToInt(c.x),Mathf.RoundToInt(c.y-1)]==1){
        return false;
    }
    if(board[Mathf.RoundToInt(d.x),Mathf.RoundToInt(d.y-1)]==1){
        return false;
    }
    
    return true;
    
}
But this is never called. So let's call it in Start function, just this time we will use InvokeRepeating.
        InvokeRepeating("moveDown",blockFallSpeed,blockFallSpeed); //move block down
Now the block will move down until it hits something, then it will stop marking current shape position as filled. Source code
Donate Button

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

  1. the dropbox link was expired... please..... reupload please...

    ReplyDelete
  2. Good job, I like yours.

    ReplyDelete

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