- Back to Home »
- Match3 game
18 April 2015
Hey there,
Today, I'd like to show you how to create a match3 style game.
We will start of by setting up our scene as following:
- Set new Main Camera position to Vector3(5, 4.5f, -10f)
- Create two c# scripts (Board and block)
- Create new empty Game Object and assign Board script
Once you have that you can go ahead and create gems or blocks for this match3 game:
- Create new 3d object - Cube Set cube scale to Vector3(0.8f,0.8f,0.8f)
- Make a prefab and change material(this way you can have more different blocks)
- Do this for at least 4 blocks.
Board script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | using UnityEngine; using System.Collections; using System.Collections.Generic; public class Board : MonoBehaviour { //Blocks used in game public GameObject[] Blocks; //Size of the board public int Width = 10; public int Height = 10; //2D grid public Block[,] Grid; //Blocks falling public bool Falling; void Start(){ //Initialize grid Grid = new Block[Width,Height*2]; } void Update(){ Falling = BlocksFalling(); //If blocks are not falling then we can spawn :] if(!Falling) Spawn(); //Make them fall down each frame Fall(); } void Spawn(){ //For each collumn for(int x=0; x<Width; x++){ int count = 0;// Count how many blocks we spawned //Go from bottom up for(int y=0; y<Height; y++){ //If we found empty block and there is empty position above playable board if(!Grid[x,y] && !Grid[x,Height+count]){ //Spawn it above the board CreateBlock(x,Height+count); //We spawned one more block count++; } } } } void Fall(){ //For each collumn for(int x=0; x<Width; x++){ int count = 0; //counter //Start from bottom and go up for(int y=0; y<Height*2; y++){ Block block = Grid[x,y];//Get block at this position //If the block exist if(block){ //If this block ID equals 1, that means that this block is currently getting destroyed if(block.ID == -1) y = Height*2;//Skip this collumn else if(block.NY != count){//If the new block position is not equal to current position....Then move it down Grid[x,y] = null;//Old position is now empty block.NY = count;//Set new block height block.CheckForMatch = true;//This block moved, so we check for match Grid[x,count] = block;//New position now equals this block } else if(block.CheckForMatch && !Falling){//If no blocks are falling then we can check for match if we need to Solve(Match (block)); //We call Solve method passing List that we found in match method block.CheckForMatch = false;//Do not check anymore } //We found one more block count++; } } } } void CreateBlock(int x, int y){ //Random ID int id = Random.Range(0,Blocks.Length); //Create block GameObject obj = Instantiate(Blocks[id], new Vector3(x,y), Quaternion.identity) as GameObject; //Block component Block block = obj.AddComponent<Block>(); //Set values Grid[x,y] = block; //We spawned block here block.ID = id; //Block have this ID block.X = x; block.Y = y; block.NY = y; block.board = this; //Just to make things clean obj.transform.parent = transform; } //This will return true if the blocks are falling and false if not bool BlocksFalling(){ for(int x=0; x<Width; x++){ for(int y=0; y<Height; y++){ if(Grid[x,y] && !Grid[x,y].IsReady()){ return true; } } } return false; } //Match3 will return List of Blocks that has been found against check block List<Block> Match(Block check){ List<Block> ToReturn = new List<Block>(); //Blocks to return List<Block> Matches = new List<Block>();//List of blocks bool linked = false; //If we found our check block //HORIZONTAL CHECK for(int x=0; x<Width; x++){//Left to right if(x==(int)check.X)//If we found our check block on this position then link matches linked = true; if(Grid[x,(int)check.Y] && Grid[x,(int)check.Y].IsReady() && Grid[x,(int)check.Y].ID == check.ID)//If this block is same as check block(id) Matches.Add(Grid[x,(int)check.Y]);//Add this block in the list else if(linked)//The block doesn't exist or ID is not equal or Its Falling....Then if linked we will stop there and we will check whatever we have in list break; else //Nothing of this is true, then just clear list and keep checking Matches.Clear(); } //We have list and blocks in it but we will return it only if we found 3 or more blocks if(Matches.Count>=3) ToReturn.AddRange(Matches); //Rese Matches.Clear(); linked = false; //VERTICAL CHECK //Same as before for(int y=0; y<Height; y++){ if(y==(int)check.Y) linked = true; if(Grid[(int)check.X, y] && Grid[(int)check.X, y].IsReady() && Grid[(int)check.X, y].ID == check.ID) Matches.Add(Grid[(int)check.X, y]); else if(linked) break; else Matches.Clear(); } if(Matches.Count>=3) ToReturn.AddRange(Matches); return ToReturn; } //Solve List of blocks void Solve(List<Block> Matches){ int count = Matches.Count; //How many blocks we have //We will destroy every block in this list, no matter how many we found...You can do stuff based on count foreach(Block block in Matches){ block.StartCoroutine("Destroy");//Start destroy } } } |
Block script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | using UnityEngine; using System.Collections; public class Block : MonoBehaviour { [HideInInspector]public int ID; //ID of this block //Position of this block in the board [HideInInspector]public float X; [HideInInspector]public float Y; [HideInInspector]public float NY; // New block height [HideInInspector]public Board board; [HideInInspector]public bool CheckForMatch = false; //If we need to check for match void Update(){ //If the new height is set then it will be lower than curent height if(NY<Y){//It should fall down //Move it down Y-=0.1f; //Update block position transform.position = new Vector3(X,Y); } else{//Its not falling, and it should not Y=NY; //Round Y transform.position = new Vector3(X,Y); //Round position } } //This will destroy block when called public IEnumerator Destroy(){ ID = -1; //We update block ID so it doesn't get matched or moved float time = 0; //Change block scale by time while(time<=1){ transform.localScale = Vector3.Lerp(new Vector3(0.8f,0.8f,0.8f), Vector3.zero, time); time += Time.deltaTime/2; yield return null; } //Update grid, set empty board.Grid[(int)X, (int)Y] = null; //At the end destroy that block gameobject Destroy(gameObject); } // Determines whether this Block is ready - falling down public bool IsReady(){ return Y==NY; } } |
board.Fall(): we go from bottom up to top. Once we found solid block we update that block height to equal current number of solid blocks found. Now we increase that value by one. You can go ahead and draw this method and see how it works. Here is my drawing :)
If you need anything else explained feel free to ask.
Thank's for reading , also I'd love to hear some feedback from you :]
SOURCE CODE
Source code
do you have full c# sprite of this game?
ReplyDeleteThe project is very interesting and useful.
ReplyDeleteI like it.
I wonder if the Horizontal + Vertical can be add to a same match list
So I insert 3 lines code to verify:
void CreateBlock (int x, int y)
{
//Random ID
int id = Random.Range (0, Blocks.Length);
//Create block
//Manual set ID
if(Time.timeSinceLevelLoad < 4)
if(x== 2 ||y%10 == 1)
id = 1;
//print ("<<>>" + id);
it will create a cross of id 1, and only horizontal matches found.
This really wasn't that useful, especially as a tutorial for new programmers
ReplyDeleteAlongside the code you should add in step by step guides on how to create the game, not just write the scripts.
You are right. I'll change it in the future. Thank's!
Delete