Complete Flash AS3 “Columns” game with source code
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.