Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about Columns game, Actionscript 3, Flash, Game development and Users contributions.

It’s quite a long time I don’t write anything about a Tetris game, my last entry about it was Creation of a complete Flash Tetris game a couple of years ago, so I am happy to show you the complete source code of a Tetris-like game called Columns made by Walter Siracusa, but first play the game:

Move pieces with LEFT, RIGHT and DOWN arrows, and use UP to shift blocks.

The code is commented in french and was ported from an old AS2 script so you may have to adjust it a bit before you can use for your own projects, but all in all it’s a very interesting script which will allow you to create a complete game.

//COLUMNS par Walter Siracusa

const TAB_LG:uint=6; //Largeur de la table (en cases)
const TAB_HT:uint=13; //Hauteur

const BLK_LG:uint=32; //Largeur d'un bloc (en pixels)
const BLK_HT:uint=32; //Hauteur
const BLK_EMPTY:uint=1; //Constante de vide dans la table Tab (associée à l'image-clé 1 du clip blk_mc)
const BLK_LINE:uint=3; //Nb de blocs mininum pour valider un alignement

//Les états du jeu :
const STATE_MOVE:uint=1; //Descente déplacement au clavier des 3 blocs
const STATE_LINE:uint=2; //Détruit les blocs alignés
const STATE_FALL:uint=3; //Chute de blocs au dessus du vide

//--------------------------------

var Tab:Array=new Array();
var Tab_bool:Array=new Array();
var Tab_blk:Array=new Array();
var State:uint;
var Current1:uint,Current2:uint,Current3:uint;
var Next1:uint,Next2:uint,Next3:uint;
var Breaking:Boolean=false;
var Key_UP:Boolean; //Etat de la touche UP (false = relâché, true = enfoncée)
var Timer0_drop:uint;
var Score:uint=0;

var Blk_container=new Sprite();
var Bag:Bag_mc=new Bag_mc();
var Gameover:Gameover_mc=new Gameover_mc();

var blong_snd=new Blong_snd()
var disp_snd=new Disp_snd()
var zik_snd=new Zik_snd()
var zik_chn=zik_snd.play(0,1000)

stop();
initGame();

function initGame(){
	//Créer la table de base, une table booléene temporaire et une table de référence des blocs
	for(var i:uint=0;i<TAB_LG;i++){ 
		Tab[i]=new Array(TAB_HT);
		Tab_bool[i]=new Array(TAB_HT);
		Tab_blk[i]=new Array(TAB_HT);
	}
	
	//Construit la grille avec des occurences de blk_mc
	for(var j:uint=0;j<TAB_HT;j++)
		for(i=0;i<TAB_LG;i++){
			Tab[i][j]=BLK_EMPTY;
			var blk:Blk_mc=new Blk_mc();
			Tab_blk[i][j]=blk;
			blk.x=i*BLK_LG;
			blk.y=j*BLK_HT;
			Blk_container.addChild(blk);
		}
	addChild(Blk_container); //Contient une grille remplie de blocs
	
	//Ajoute une occurence de bag_mc
	addChild(Bag);
	Bag.vt=2;
	Next1=Math.floor(1+Math.random()*6)*10; //10(R),20(V),30(B),40(J),50(M),60(C)
	Next2=Math.floor(1+Math.random()*6)*10;
	Next3=Math.floor(1+Math.random()*6)*10;
	initBag();
	
	score_txt.text=String(Score);
	Timer0_drop=getTimer();
	Breaking=false;
	Key_UP=false;
	State=STATE_MOVE;
	
	stage.addEventListener(KeyboardEvent.KEY_DOWN,keyboardDown)
	stage.addEventListener(KeyboardEvent.KEY_UP,keyboardUp)
	addEventListener(Event.ENTER_FRAME,mainLoop);
}

function initBag(){
	Bag.y=-BLK_HT; //Démarre 1 case au dessus de la table
	Bag.x=0;
	
	Current1=Next1;
	Current2=Next2;
	Current3=Next3;

	Next1=Math.floor(1+Math.random()*6)*10; //10(R),20(V),30(B),40(J),50(M),60(C)
	Next2=Math.floor(1+Math.random()*6)*10;
	Next3=Math.floor(1+Math.random()*6)*10;

	next1_mc.gotoAndStop(Current1);
	next2_mc.gotoAndStop(Current2);
	next3_mc.gotoAndStop(Current3);
	
	setBag();
}

function setBag(){
	Bag.blk1_mc.gotoAndStop(Current1);
	Bag.blk2_mc.gotoAndStop(Current2);
	Bag.blk3_mc.gotoAndStop(Current3);
}

//--------------------------------

function mainLoop(evt:Event){
	if(!Breaking){ //Aucun bloc en cours de destruction (valeur donnée dans la timeline de clip blk_mc)
		switch(State){
			case STATE_MOVE: bagDown();break;
			case STATE_LINE: blockLine();break;
			case STATE_FALL: blockFall();
		}
	}
}

function keyboardDown(evt:KeyboardEvent){
	if(State==STATE_MOVE){
		var i_blk:int=Math.floor(Bag.x/BLK_LG);
		var j_blk:int=Math.floor(Bag.y/BLK_HT); //= -1 au départ

		//Touches enfoncées
		if(evt.keyCode==Keyboard.LEFT && Bag.x>0 && (Tab[i_blk-1][j_blk+1]==BLK_EMPTY || drop_mc.visible))
			Bag.x-=BLK_LG;
		else if(evt.keyCode==Keyboard.RIGHT && Bag.x<(TAB_LG-1)*BLK_LG && (Tab[i_blk+1][j_blk+1]==BLK_EMPTY || drop_mc.visible))
			Bag.x+=BLK_LG;
		else if(evt.keyCode==Keyboard.UP && !Key_UP){ //Rotation des blocs de bag
			Key_UP=true;
			var tmp:uint=Current3;
			Current3=Current2;
			Current2=Current1;
			Current1=tmp;
			setBag();
		}else if(evt.keyCode==Keyboard.DOWN) Bag.vt=8; //Vitesse de descente maximale de bag
	}
}

function keyboardUp(evt:KeyboardEvent){
	//Touches relâchées
	if(evt.keyCode==Keyboard.UP) Key_UP=false; //Autorise UP après chaque relâchement
	if(evt.keyCode==Keyboard.DOWN) Bag.vt=2; //Vitesse de descente par défaut de bag
}

function bagDown(){
	if(getTimer()>Timer0_drop+1500){ //Temps passé
		var i_blk:int=Math.floor(Bag.x/BLK_LG);
		var j_blk:int=Math.floor((Bag.y)/BLK_HT);

		//Ne touche pas le fond de la grille et pas de bloc dessous => Chute de bag de vt pixels
		if(Bag.y<(TAB_HT-1)*BLK_HT && (Tab[i_blk][j_blk+1]==BLK_EMPTY || drop_mc.visible)){
			next1_mc.gotoAndStop(Next1); //Affiche le bag suivant
			next2_mc.gotoAndStop(Next2);
			next3_mc.gotoAndStop(Next3);
			drop_mc.visible=false;
			Bag.y+=Bag.vt;
		}else{
			if(Bag.y==(-BLK_HT+Bag.vt)) gameOver(); //Colonne pleine => GAME OVER
			else{ //Touche le fond ou un bloc => Modifie les blocs de la grille en rapport avec ceux de bag 
				putBlk(i_blk,j_blk,Current1);
				putBlk(i_blk,j_blk-1,Current2);
				putBlk(i_blk,j_blk-2,Current3);
				initBag(); //Nouveau bag
				State=STATE_LINE; //3 blocs posés => De nouveaux alignements ? => Mode LINE
			}
		}
	}else{ //En attente du timer...
		drop_mc.visible=true;
		drop_mc.x=Bag.x;
	}
}

function putBlk(i_blk:int,j_blk:int,val:uint){
	if(j_blk>=0){ //N'écris pas hors de la table (cas ou bag déborde)
		Tab[i_blk][j_blk]=val;
		Tab_blk[i_blk][j_blk].gotoAndStop(val);
	}
}

function blockLine(){
	var val:uint,nb_blk;
	Breaking=false;
	
	for(var j:int=0;j<TAB_HT;j++) //Initialise la table des booléens
		for(var i:int=0;i<TAB_LG;i++) Tab_bool[i][j]=false;
	
	//Alignements horizontaux
	for(j=0;j<TAB_HT;j++){
		for(var i1:int=0;i1<TAB_LG;i1++){
			if(Tab[i1][j]!=BLK_EMPTY){
				val=Tab[i1][j];
				nb_blk=0;
				for(var i2:int=i1;i2<TAB_LG;i2++){
					if(Tab[i2][j]==val) nb_blk++;
					else{
						i2--; //On ne compte pas le bloc qui est différent
						break;
					}
				}
								
				if(nb_blk>=BLK_LINE){ //Alignement entre i1 et i2
					Breaking=true;
					if(i2==TAB_LG) i2--;
					for(i=i1;i<=i2;i++) Tab_bool[i][j]=true;
					Score+=(nb_blk*nb_blk);
				}
				i1=i2;
			}
		}
	}
		
	//Alignements verticaux
	for(i=0;i<TAB_LG;i++){
		for(var j1:int=0;j1<TAB_HT;j1++){
			if(Tab[i][j1]!=BLK_EMPTY){
				val=Tab[i][j1];
				nb_blk=0;
				for(var j2:int=j1;j2<TAB_HT;j2++){
					if(Tab[i][j2]==val) nb_blk++;
					else{
						j2--;
						break;
					}
				}
				
				if(nb_blk>=BLK_LINE){ //Alignement entre j1 et j2
					Breaking=true;
					if(j2==TAB_HT) j2--; //On ne compte pas le bloc qui est différent
					for(j=j1;j<=j2;j++) Tab_bool[i][j]=true;
					Score+=(nb_blk*nb_blk);
				}
				j1=j2;
			}
		}
	}
	
	//Alignements diagonales (Gauche-Droite)
	for(j=0;j<TAB_HT-(BLK_LINE-1);j++){
		for(i=0;i<TAB_LG-(BLK_LINE-1);i++){
			i2=i;
			j2=j;
			val=Tab[i2][j2];
			if(val!=BLK_EMPTY){
				nb_blk=0;
				while(i2<TAB_LG && j2<TAB_HT && Tab[i2][j2]==val){ 
					nb_blk++;
					i2++;
					j2++;
				}
				
				if(nb_blk>=BLK_LINE){ //Alignement entre (i1;j1) et (i2;j2)
					Breaking=true;
					i2--;
					j2--;
					i1=i;
					j1=j;
					while(j1<=j2) Tab_bool[i1++][j1++]=true;
					Score+=(nb_blk*nb_blk)*2;
				}
			}
		}
	}
	
	//Alignements diagonales (Droite-Gauche)
	for(j=0;j<TAB_HT-(BLK_LINE-1);j++){
		for(i=TAB_LG-1;i>=(BLK_LINE-1);i--){
			i2=i;
			j2=j;
			val=Tab[i2][j2];
			if(val!=BLK_EMPTY){
				nb_blk=0;
				while(i2>=0 && j2<TAB_HT && Tab[i2][j2]==val){ 
					nb_blk++;
					i2--;
					j2++;
				}
				
				if(nb_blk>=BLK_LINE){ //Alignement entre (i2;j2) et (i1;j1)
					Breaking=true;
					i2++; //i2>=i1
					j2--;
					i1=i;
					j1=j;
					while(j1<=j2) Tab_bool[i1--][j1++]=true;
					Score+=(nb_blk*nb_blk)*2;
				}
			}
		}
	}
	
	//Parcours la table des booléens pour détruire les blocs alignés
	if(Breaking){ //Des blocs sont à détruire
		for(j=0;j<TAB_HT;j++)
			for(i=0;i<TAB_LG;i++)
				if(Tab_bool[i][j]){ 
					Tab[i][j]=BLK_EMPTY;
					Tab_blk[i][j].play();
				}
		
		score_txt.text=String(Score);
		State=STATE_FALL; //La destruction entraine peut-être des chutes de blocs => STATE_FALL
		//Son
		disp_snd.play();
	}else{ 
		State=STATE_MOVE; //Pas ou plus de bloc détruit => pas de chute de bloc => STATE_MOVE
		Timer0_drop=getTimer();
	}
}

function blockFall(){
	var fall:Boolean=false;
	
	//Parcours la table de bas en haut (jusqu'à la ligne 1)
	for(var j:int=TAB_HT-1;j>0;j--)
		for(var i:int=0;i<TAB_LG;i++)
			if(Tab[i][j]==BLK_EMPTY && Tab[i][j-1]!=BLK_EMPTY){ //Un bloc au dessus du vide
				fall=true;
				//Copie le bloc d'une case vers le bas
				var val:uint=Tab[i][j-1]; //Valeur du bloc
				Tab[i][j]=val;
				Tab_blk[i][j].gotoAndStop(val);
				//Supprime l'ancienne position du bloc
				Tab[i][j-1]=BLK_EMPTY;
				Tab_blk[i][j-1].gotoAndStop(BLK_EMPTY);

				//Son
				blong_snd.play();
			}
	
	if(!fall) State=STATE_LINE; //Pas ou plus de chute => De nouveaux alignements => Mode LINE
}

function gameOver(){
		removeEventListener(Event.ENTER_FRAME,mainLoop);
		stage.removeEventListener(KeyboardEvent.KEY_DOWN,keyboardDown)
		stage.removeEventListener(KeyboardEvent.KEY_UP,keyboardUp)
		
		addChild(Gameover);
		Gameover.play();
		Gameover.x=107;
		Gameover.y=91;
		zik_chn.stop();
}

And of course here is the source code provided by Walter.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.