Tuesday, September 1, 2009

Make Your Own Custom Minesweeper Game with Actionscript 3

We'll make a variant of the very popular Minesweeper. This will also give you a very clear idea on how to go about creating games in Flash.




1. First hit Ctrl+J to bring up the properties window and set BOTH the width and height of the stage to 300 and the Background to the color #339966;


2. Now we'll draw the required components. For each, hit Ctrl+F8 to bring up the New Symbol Window and select the Export For Actionscript in the Advanced section. Maintain the dimensions i mention if you do not want to tweek the script.
First the boxes. This will create a mesh background to the minefield. Give the name in the New Symbol Window as box_mc.Click Ok and select the Rectangle Tool and Change the Stroke Color to #A6A6A6 and No Fill Color. Draw a 24x24 sized square and select the Top and Left edges and change their color to Black. Now go to the Align Panel and Click on Align Left anf Align Top so that the top left corner coincides with the small cross on the stage. If you do this correctly it sould look like this

Now the Mines. Again in the New Symbol Window give the name as mine_mc and make sure Export For Actionscript is selected. Draw it anyway you want just make sure they are smaller than your boxes. My mines are of the size 20x20 and look like this

For the box covers give the name as cover_mc draw a 23x23 rectangle with Stroke Thickness 2 and fill color #0066CC and Stroke Colors #012B54 and #81C0FE Align the Top-Left corner,similar to the box_mc, to make it look like this

Name the flags flag_mc and they should, again, be smaller than the boxes.This one measures 15x18.

For the following three objects you need not check the Export for Actionscript Box.
The timer sould be named timer_mc. First Draw a 60x30 Rectangle with the color #540101 with an alpha of 75. That done select the Text Tool and draw Dynamic Text Field like so

Select the text field and go to the Properties Panel and give it the instance name of time_txt.

Now for the final two components, the game lose and win splash screens. Hit Ctrl+F8 again and name it lose_mc. Draw a 225x225 square of the color #91000 and alpha 25. Centre it and create Static Text fields to write "You Lose" or whatever you want to indicate that the player has lost.

Follow the Exact same procedure and create a game win splash screen and name it win_mc, set the color to #00CC33 and change the text to "You Win" or something similar.



3. Till Now your Main Stage should be empty. Add a total of 3 new layer. Rename the top layer Actions, the next one Win, the next one Lose and the last one Timer. Select the Timer Layer and select the first frame. Now open your Library Window and drag an instance of timer_mc on to the stage. Go to the properties panel. Enter its instance name of timer_mc and set its X and Y coordinates to 150 and 2 respectively. Similarly drag the lose_mc on to the first frame of the Lose Layer and set the instance name as "lose_mc" and both the coordinates as 150. win_mc should be dragged on to the Win layer. The instance should be named as win_mc. Your stage should look like this.

4. Now time for some major scripting! Remember we are creating a 9x9 game with 10 mines. So there will be 81 boxes and box covers and 10 flags.
The boxes are logically defined as an Array of length 81. The value gives the number of mines in the adjacent boxes

/*-------------------INITIATE GAME-----------------------------*/

/*define the top left corner the mineField*/
var X=37.5 ,Y=37.5;

/*hide the two game end splash screens*/
lose_mc.visible = false;
win_mc.visible = false;

/*global variables*/
var isLost=0;/*flags game lose scenario*/
var isPlaying=0;/*shows if the game is in progress*/
var timeCount=0;/*counts the number of seconds*/
var removedCount=0;/*counts the number of boxws uncovered*/

/*holds the positions of the 10 mines*/
var minePos:Array = new Array();

/*for every box counts the number of adjacent mines*/
var mineCount:Array = new Array();

/*this will time the game*/
var timer:Timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, onTimer);
function onTimer(e:TimerEvent) {
if(isPlaying==1)/*start timing only if the game is in progress*/
timeCount++;
timer_mc.time_txt.text = timeCount;
timer_mc.time_txt.selectable = false;
}

/*This is the format for the text in each box
indicating the number of adjacent mines*/

var format:TextFormat = new TextFormat();
format.bold = true;
format.color = 0x0000FF;
format.blockIndent = 5;
format.size = 22;



/*Now we define the starting function which in turn calls all other necessary functions*/
function createNewGame() {
/*set the initial mine counters*/
for( var i=0;i<81;i++)>/*attach the 10 mines*/
attachMines();

/*create the rest of the minefield*/
createMineField();

/*attach the 10 marker flags*/
attachFlags();

/*start the timer*/
timer.start();
}

/*Call the function so that all required components are attached to the stage.*/
/*//////////////*/
createNewGame();
/*//////////////*/




/*--------------ATTACH MINES--------------------- */
/*Randomly decides upon 10 places to put the mines.
Since the same number may be returned by the Math.random()
function we have the 2nd loop. What it does is calls itself
if it finds two same number in the array. Since this may happen
sparsely the recursive call does not incur a great cost.*/


function findMinePos() {
/*randomly find 10 mine positions*/
for(var i=0;i<10;i++)>int(Math.random()*80);

/*to prevent the same number being chosen twice*/
for(i=0;i<10;i++)>for(var j=i+1;j<10;j++)> if(minePos[i]==minePos[j])
findMinePos();
}
}
}

/*Now that 10 distinct locations has been chosen,
this function actually attaches the mines to these.
If you have used a different dimension for the mines
and/or boxes than i have you may have to tweek the
values in the script for proper alignment. */


function attachMines() {
findMinePos();
for(var i=0;i<10;i++)> var mine = new mine_mc();
mine.x=X+(minePos[i]%9)*25+12.5;
mine.y=Y+int (minePos[i]/9)*25+12.5;
updateMineCount(minePos[i]);
addChild(mine);
}
}

Now we have to understand since the boxes are defined as a 81 lentgh array, for any box X what the indeces for the adjacent boxes are--

for a box on the Left edge the indeces would be like 0,9,18,27...X.. i.e. X%9 = 0 and their adjacent boxes are

and for one on the Right edge the indeces are 8,17,26,...X,... i.e X%9 = 8 and their adjacent boxes are


/*this funcion takes the position of a mine
and updates all its adjacent boxes */

function updateMineCount(pos:Number) {
/*set the value of the position with the mine*/
mineCount[pos]=9;

if(pos+9>0&&pos+9<81)>if(pos-9>=0&&pos-9<81)>/*for all boxes NOT on the right edge*/
if(pos%9!=8)
{
if(pos+9+1>0&&pos+9+1<81)> if(pos+1>0&&pos+1<81)> if(pos-9+1>=0&&pos-9+1<81)>/*for all boxes NOT on the left edge*/
if(pos%9!=0)
{
if(pos-9-1>=0&&pos-9-1<81)> if(pos-1>=0&&pos-1<81)> if(pos+9-1>0&&pos+9-1<81)>/*-----------------CREATE REST OF THE MINEFIELD-------------*/

function createMineField() {
for(var i=0;i<81;i++)>/*....Attach Boxes..........*/
var b_mc=new box_mc();
b_mc.x=X+(i%9)*25;
b_mc.y=Y+int(i/9)*25;
b_mc.name="b"+i+"_mc";
addChild(b_mc);
/*.........................*/
/*.......Attach Text Fields.............*/
if(mineCount[i]!=0&&mineCount[i]<9)>var t_txt:TextField=new TextField();
t_txt.selectable = false;
t_txt.x=X+(i%9)*25;
t_txt.y=Y+int(i/9)*25;
t_txt.text = mineCount[i];
t_txt.setTextFormat(format);
addChild(t_txt);
}
/*.........................................*/
/*.............Attach Box Covers............*/
var cover=new cover_mc();
cover.x=X+.5+(i%9)*25;
cover.y=Y+.5+int(i/9)*25;
cover.name = "c"+i;
cover.addEventListener(MouseEvent.CLICK,clickHandler);
addChild(cover);
/*............................................*/
}

}
/*------------MineCover Click Handler-------------------*/
function clickHandler(e:MouseEvent) {
isPlaying=1;
var Name = e.currentTarget.name.toString();
var clickedBut=int(Name.substr(1,2));

/*if clicked on a mine invoke procedure lose*/
for(var i=0;i<10;i++)> if(minePos[i]==clickedBut)
lost();
}

/*if clicked on a box with no adjacent mines remove
all adjcent empty boxes*/

if(mineCount[clickedBut]==0)
{
getAdjacentEmptyFields(clickedBut);
begin=0;
end=0;
}

/*for all other boxes remove the cover and reveal
the number of adjacent mines*/

else if(mineCount[clickedBut]<9)>removeChild(getChildByName(e.currentTarget.name));
updateRemovedCount();
}
}

/*------Remove all adjacent empty boxes-------------------*/
/*this function uses a queue and two functions enqueue and dequeue
to work on the queue.When a box with no adjacent mines is clicked
this function searches through all its adjacent boxes and removes the
covers if it does not hide a mine and if an empty box is encountered
it is put in the queue and later taken up for processing. In this way
if you click on an empty box an area is cleared up;
*/


var queue:Array=new Array ;

/*beginning of the array*/
var begin=0;

/*end of the array*/
var end=0;

/*takes a number and inserts into the end of the queue*/
function enqueue(num:Number) {
queue[end]=num;
end++;
}

/*retrieves the number in front of the queue*/
function dequeue():Number {
return(queue[begin++]);
}


function getAdjacentEmptyFields(pos:Number) {
var Name:String = new String();
Name = "c"+pos;

/*remove cover*/
if(getChildByName(Name))
{
removeChild(getChildByName(Name));
updateRemovedCount();
}

/*flag cover removal of an empty box
in the array*/

mineCount[pos]=99;

/*..................Process Adjacent Boxes....................*/
processAdjacentBoxes(pos+9);
processAdjacentBoxes(pos-9);

if(pos%9!=8)
{
processAdjacentBoxes(pos+9+1);
processAdjacentBoxes(pos+1);
processAdjacentBoxes(pos-9+1);
}
if(pos%9!=0)
{
processAdjacentBoxes(pos-9-1);
processAdjacentBoxes(pos-1);
processAdjacentBoxes(pos+9-1);
}
/*......................................................*/

/*return if queue is empty*/
if(begin>=end&&begin!=0)
return;

/*process the position at the start of the queue*/
getAdjacentEmptyFields(dequeue());
}

/*process the position passed as argument*/
function processAdjacentBoxes(pos:Number) {
var Name:String = new String();

if(pos>0&&pos<81)>/*if empty box put it in the queue and
flag removal of cover of the empty box*/

if(mineCount[pos]==0)
{
mineCount[pos]=99;
enqueue(pos);
}

/*else if a box with text remove the cover only*/
else if(mineCount[pos]<9) name="c">if(getChildByName(Name))
{
removeChild(getChildByName(Name));
updateRemovedCount();
}
}
}
}


/*-----------------------FLAGS----------------------------------*/

/*The flags will be for marking purpose only and their position have
no bearing on the result whatsoever you have to remove all unmined
box covers to win*/


function attachFlags() {
for(var i=0;i<10;i++)>var flag=new flag_mc();
flag.x=X+i*24;
flag.y=Y+227;
flag.addEventListener(MouseEvent.MOUSE_DOWN,dragFlag);
flag.addEventListener(MouseEvent.MOUSE_UP,stopDragFlag);
addChild(flag);
}
}
function dragFlag(e:MouseEvent) {
e.currentTarget.startDrag();
}

function stopDragFlag(e:MouseEvent) {
e.currentTarget.stopDrag();
}
/*-----------------------------------------------------------------*/

/**********************Results**************************************/

/*This function is called every time a box cover is removed
if all 71 (81-10) unmined boxes are uncovered the game is won
*/

function updateRemovedCount() {
removedCount++;
if(removedCount==71&&isLost==0)
{/*game has been won*/
win_mc.visible=true;
isPlaying=0;
/*bring the splash screen to the top*/
setChildIndex(win_mc,numChildren-1)
}
}

function lost() {

var Name:String = new String();

/*remove all the covers of mined boxes to reveal their positions*/
for(var i=0;i<10;i++) name = "c"> if(getChildByName(Name))
removeChild(getChildByName(Name));
}
isLost=1;
isPlaying=0;
lose_mc.visible=true;
/*bring the splash screen to the top*/
setChildIndex(lose_mc,numChildren-1);
}
/*---------------------------------------------------------------------*/

Thats it!!
Hit Ctrl+Enter to test your movie. Happy gaming.


25 comments:

  1. Very easy to follow and use . Thank you :)

    ReplyDelete
  2. is the code to this online anywhere? the actionscript seems to have errors.

    ReplyDelete
  3. Code has like 20 errors, fixed them, still doesn't work.

    ReplyDelete
  4. sorry i'm a n00b but can you please explain some of the algorithms involved in placing, and selecting somewhere, to place the mines.

    ReplyDelete
  5. the code has errors because the blog editor mistakes all < and > signs to be html tags and removes everything in between. leave your email address so I can forward the code to you

    ReplyDelete
    Replies
    1. please send correct code to info@amandamoondesigns.com please

      Delete
    2. me too please
      safrastyandavidquantum1997@gmail.com

      Delete
    3. Please, send me a code srb.srbin@yahoo.com

      Delete
    4. pls
      starcrafttwoterran@gmail.com

      Delete
  6. i have the same problem is possible then plz provided the code for me also

    ReplyDelete
  7. can you please send me the code :((
    i need it badly.
    My email is:
    grace_reyes029@yahoo.com
    thank you.

    ReplyDelete
  8. mr. can you send this source code to me? i couldnt even do it correctly. and this game does not has level,right? thanks.. :) msofiyah@yahoo.com

    ReplyDelete
  9. gamertje99@HOTMAIL.COM can you mail me please thnx

    ReplyDelete
  10. Could you send me the code? I'd love to play. Thanks!
    etf.coding@gmail.com

    ReplyDelete
  11. Could you send me the code? Thanks!
    wayne11st1@yahoo.com.hk

    ReplyDelete
  12. can you send me the code please?
    nepaguy001@yahoo.com

    ReplyDelete
  13. Thanks for the tutorial. However, I need to see the code. Can you send this to robinfoley@hotmail.com?

    ReplyDelete
  14. Thanks very much, could you send me the code at dakotakgardner@gmail.com

    ReplyDelete
  15. May I ask for a mail too? hadryel10@gmail.com
    Thank you. :)

    ReplyDelete
  16. I would like the updated code as well please. (clobkid529 at yahoo dot com)

    ReplyDelete
  17. could I have the proper code
    my email address is bailey.cook@sd273.com

    ReplyDelete