Complete Flash AS3 “Columns” game with source code

Read all posts about "" game

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.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

214 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers