Criação de games – Campo Minado em AS3

Criação de games – Campo Minado em AS3

Olá pessoal,

Vamos criar mais um joguinho, que vai apresentar a programação de games com o AS3.

Pré-requisitos:

Conhecimento básico em ActionScript 2.0 e 3.0

Macromedia Flash CS3

Classe EventDispatcher
Classe DisplayObject

Objetivo:

O objetivo deste tutorial é que você, ao término dele, possa entender como funciona a programação orientada a objetos de games, criar seus próprios eventos nos jogos. Procurei selecionar um jogo simples no resultado final que todo mundo conhece (campo minado), mas que no momento do desenvolvimento vai explorar sua capacidade de resolver problemas bem relacionados a programação. Visto em nível de código este joguinho não é tão simples.

Conceitos:

Os conceitos que serão vistos aqui, são a utilização dos dispatchers para resolver alguns problemas, resolução de problemas algorítmicos simples, arganização dos objetos pelo AS3, duplicar movie clips com a ausência do duplicateMovieClip.

Conteúdo

Primeiro vamos preparar os movie clips principais, no fim deste tutorial tem o link dos arquivos fontes, nele eu coloquei as imagens que vamos utilizar. Crie um documento novo Adobe flash CS 3 com o nome mines.fla com o background da cor #CCCCCC . Depois vá em insert > new symbol ou CTRL + F8, o novo símbolo é do tipo movie clip e se chamará Mine. Neste novo movie clip vazio crie 11 Keyframes. Com exceção do primeiro Keyframe, todos os outros receberão um nome. A nome de cada frame a partir do 2° será a seguinte: _1, _2, _3, _4, _5, _6, _7, _8, die e clear. Dessa forma facilitará no momento da programação. Agora no primeiro keyframe você colocará a figura chamada square.jpg coloque nas coordenadas mostradas na figura.

posição do square
Figura 1 – Dimensões e a posição da imagem

Seguindo, clique no primeiro keyframe e aperte F9 para colocar o comando stop() no editor de script. Agora exatamente na mesma posição, mas no keyframe 10 que tem o nome die, coloque a figura bomb.jpg, ela será colocada na mesma posição e dimensões mostrada na figura 1, nos outros keyframes você vai apenas colocar um número no stage com a ferramenta text, seguindo a lógica número 1 no keyframe _1, 2 no jeyframe _2, todos posicionados no x=2.8 e y=0 até o keyframe _8, isso representa um determinado número de bombas que estão próximas de um quadrado (Regra do jogo), se você jogou campo minado alguma vez já observou os números que aparecem no local que clicou . Volte à cena principal, falta um botão novo jogo, que você irá achar em window > components ou aperte CTRL + F7. Escolha o Button na lista que aparecerá, arraste-o para o stage e altere seu label para novo jogo como na figura abaixo, e o nome de instância do botão é newGame_btn:

componente no stage
Figura 2 – Componente no Stage

E para finalizar esta parte vamos linkar o mc Mine que acabamos de criar com o Action Script 3.0, vá na library e clique com o botão direito no mc Mine e escolha linkage, aparecerá esta janela de diálogo, configure como está mostrado.

posição do square
Figura 3 – Linkando o mc criado com Action Script 3.0

Acabamos esta fase, agora vamos a codificação, primeiramente construiremos duas classes: a primeira é que dispara eventos, recomendo ver os tutoriais mencionados nos pré-requisitos para continuar este tutorial, neles você ira entender como funciona as classes que são dispatcher e as listeners, abaixo a classe dispatcher que chamei de DispacherObject:

package
{
import flash.events.EventDispatcher;
import flash.events.Event; public class DispatcherObject extends EventDispatcher
{
private var label:String;

static var EXPLOSION:String = ‘explosion’;

public function DispatcherObject(label:String)
{
this.label = label;
}

public override function toString():String
{
return “[ Dispatcher "+label+" ]“;
}

public function dispatch()
{
this.dispatchEvent(new Event(DispatcherObject.EXPLOSION));
}
};
};

A classe é bem simples, possui um atributo para debugar e um estático que representa o no evento a ser disparado, chamei esse evento de explosion. Possui um construtor, um método para debugar que sobrescreve o método toString() da classe pai por meio da palavra reservada override e por fim o método que dispara o evento em si. Abaixo segue a classe listener que ‘escuta’ a classe emissora de eventos que acabamos de ver:

package
{
import flash.events.IEventDispatcher;
import flash.events.Event; public class ListenerObject
{

private var label:String;

public function ListenerObject(label:String)
{
this.label = label;
}

public function addDispatcher(dispatcher:IEventDispatcher, functionHandler:Function)
{
dispatcher.addEventListener(DispatcherObject.EXPLOSION, functionHandler);
}

public function toString()
{
return “[ Listener "+label+" ]“;
}
};
};

Semelhante a classe acima também possui método e atributo para debugar, e um método que recebe dois parâmetros: um é o dispatcher que ela ficará ‘ouvindo’ e o outro parâmetro e a função que será chamada no momento do disparo do evento.

A próxima classe é a classe que representará cada quadrado do campo minado:

package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.events.MouseEvent;
import flash.events.Event; public class Square extends Sprite
{
/*indica se é bomba ou não*/
private var bomb:Boolean;
/*cópia do mc que representa a bomba*/
private var mc:MovieClip;

/*indica quantas bombas estão perto*/
private var proximity: Number;

/*listeners e dispatchers*/
private var listener:ListenerObject;

private var listener2:ListenerObject;

private var disp:DispatcherObject;

var disp2:DispatcherObject;

/*construtor, recebe o mc que representa a mina e um booleano
* que indica se é uma bomba
*/
function Square ( target_mc:MovieClip, bomb:Boolean )
{

this.mc = target_mc;

this.addChild ( target_mc );

this.bomb = bomb;

this.mc.buttonMode = true;

listener = new ListenerObject(‘CS3′);

listener2 = new ListenerObject(‘ADOBE’);

disp2 = new DispatcherObject(‘MINADO’);

doEvents();
};

/* seta quantas bombas estão proximas desse Square*/
function setProximity ( proximity:Number )
{
this.proximity = proximity;
};
/* retorna a proximidade*/
function getProximity ( )
{
return this.proximity;
};

/* retorna true ou false dizendo se é bomba*/
function isBomb()
{
return this.bomb;
};

/*seta dispachador de eventos que ‘ouvirá’ o clique em uma bomba*/
function setDispatcher ( d:DispatcherObject )
{
this.disp = d;

listener.addDispatcher( d, onExplosion );
};

/*seta quem irá disparar o armegedon*/
function setDispatcherArm ( d:DispatcherObject )
{
this.disp2 = d;

listener2.addDispatcher( d, explode );
};

/*adiciona evento de click do mouse*/
function doEvents()
{
this.mc.addEventListener( MouseEvent.CLICK, check );
};
/*remove evento de click do mouse*/
function undoEvents()
{
this.mc.removeEventListener( MouseEvent.CLICK, check );
};
/*verifica em que o usuário clicou e dispara os eventos necessários*/
function check (event:Event)
{

if(this.bomb)
this.disp.dispatch();
else
{
if(this.proximity == 0)
{
this.proximity = 1000;
this.disp2.dispatch();
this.mc.gotoAndStop(‘clear’);

}
else
this.mc.gotoAndStop(‘_’+this.proximity);

}

};

/*método de callback que será invocado quando clicar numa bomba*/
function onExplosion (event:Event)
{
this.mc.gotoAndStop(‘die’);

};

/*método de callback que será invocado quando houver um armagedon*/
function explode(event:Event)
{
if(this.proximity == 0)
{
this.proximity = 1000;
this.mc.gotoAndStop(‘clear’);
this.disp2.dispatch();
}
else if (this.proximity == 1000)
return;
else
this.mc.gotoAndStop(‘_’+this.proximity );
};
};
};

Agora vamos a classe controladora do jogo, optei por chamá-la de Game, ela possui como um dos métodos principais um para posicionar bombas aleatoriamente no campo (método prepare()), também método para computar a proximidade de um quadrado (Square) de uma bomba (método computeProximity()). E um para criar o efeito de explosão em cadeia que é visto no jogo de campo minado, ele ocorre quando você clica em uma posição que não está proximo de nunhuma bomba, este quadrado por sua vez explode todos os seus quadrados vizinhos num raio de um quadrado.

Se ele explodir um quadrado vizinho que também tem proximity igual a Zero, esse também realiza uma explosão em cadeia. Chamei este método de armagedon(). Podem abservar que todas as explosões são ativadas por eventos, e não preciso varrer o campo para ir explodindo as bombas, elas se explodem ‘sozinhas’. É como se eu fosse ligando um fio em cada uma e precisasse apertar apenas um botão para dispará-las, esse tipo de conceito ajuda em muitos tipos de jogos que existem diversos objetos para manipular. Veja a implementação abaixo:

package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent; public class Game extends MovieClip
{
/*armazena as bombas e suas informações*/
private var field:Array;

/*número de bombas*/
private var nBombs:Number;

/*dispachador de eventos*/
private static var dispatcher:DispatcherObject;

private var listener:ListenerObject;

/*construtor da classe Game
@param largura e altura do campo e número de bombas
*/
function Game ( width:Number, height:Number, nBombs:Number )
{
this.nBombs = nBombs;

field = new Array(width);
for (var i=0; i < field.length; i++)
field[i] = new Array(height);

dispatcher = new DispatcherObject(‘MX’);

this.listener = new ListenerObject(‘MX’);

this.listener.addDispatcher( Game.dispatcher, gameOver );
}

function gameOver(event:Event)
{
for (var i =0; i < field.length; i++)
{
for(var j=0; j < field[i].length; j++)
field[i][j].undoEvents();
}
}
/*Método responsável por mostrar um número aleatórrio
* entre min e max
*/
private function randRange(min:Number, max:Number)
{
return Math.floor(Math.random() * (max – min + 1)) + min;
}

/* prepara o ambiente do jogo*
* colocando bombas em locais aleatórios
*/
private function prepare()
{
var i = 0;
var iAux = 0;
var jAux = 0;

while ( i < this.nBombs )
{
iAux = randRange(0, field.length-1);
jAux = randRange(0, field[0].length-1);

if (!field[iAux][jAux])
{
field[iAux][jAux] = new Square ( new Mine(), true );
i++;
field[iAux][jAux].setDispatcher ( Game.dispatcher );
this.addChild(field[iAux][jAux]);
}

}

for (i =0; i < field.length; i++)
{
for (var j = 0; j < field[i].length; j++)
{
if ( !field[i][j] )
{
field[i][j] = new Square (new Mine(), false);
this.addChild(field[i][j]);

}
}
}

}

/*inicia o jogo, prepara depois mostra na tela*/
public function init()
{
prepare();
display();

}
/*Método responsável mostar na tela*/
private function display()
{
for (var i =0; i < field.length; i++)
{
for(var j=0; j < field[i].length; j++)
{
field[i][j].x = j*16;
field[i][j].y = i*16;
computeProximity(field, i , j);
}
}
/* Aqui é muito importante, pois é aqui que sabemos
* se o quadrado na posição [i ,j] tem proximidade igual a 0
* se tiver, fazemos todos os seus vizinhos escutar sua explosão
* e se explodirem também
*/
for ( i =0; i < field.length; i++)
{
for( j=0; j < field[i].length; j++)
armagedon(field, i , j);
}
}

/* Método responsável por computar a proximidade de cada Square de uma bomba
* o número que aparece no campo minado quando você clica numa casa sem bomba
*/
private function computeProximity( arr:Array, i:Number, j:Number)
{
var proximity:Number = 0;

if( i-1 >= 0 && arr[i-1][j].isBomb())
proximity++;
if (i+1 < arr.length && arr[i+1][j].isBomb())
proximity++;
if (j-1 >= 0 && arr[i][j-1].isBomb())
proximity++;
if (j+1 < arr[0].length && arr[i][j+1].isBomb())
proximity++;
if (i-1 >= 0 && j-1 >= 0 && arr[i-1][j-1].isBomb())
proximity++;
if (i+1 < arr.length && j+1 < arr[0].length && arr[i+1][j+1].isBomb())
proximity++;
if (i+1 < arr.length && j-1 >= 0 && arr[i+1][j-1].isBomb())
proximity++;
if (i-1 >= 0 && j+1 < arr[0].length && arr[i-1][j+1].isBomb())
proximity++;

arr[i][j].setProximity( proximity );
}

/* Este método é responsável por criar um espécie de explosão em cadeia
* Quando um quadrado não está próximo de nenhuma bomba ele explode todas
* as bombas vizinhas, e estas por sua vez se tiverem proximity = 0 também
* criam a mesma reação em cadeia =)
* @param arr => array de array (Matriz) analizada
* @param i e j indíces da matriz
*/
private function armagedon( arr:Array, i:Number, j:Number)
{
if(arr[i][j].getProximity() == 0)
{
if( i-1 >= 0 )
arr[i-1][j].setDispatcherArm ( arr[i][j].disp2 );

if (i+1 < arr.length )
arr[i+1][j].setDispatcherArm ( arr[i][j].disp2 );

if (j-1 >= 0 )
arr[i][j-1].setDispatcherArm ( arr[i][j].disp2 );

if (j+1 < arr[0].length )
arr[i][j+1].setDispatcherArm ( arr[i][j].disp2 );

if (i-1 >= 0 && j-1 >= 0 )
arr[i-1][j-1].setDispatcherArm ( arr[i][j].disp2 );

if (i+1 < arr.length && j+1 < arr[0].length )
arr[i+1][j+1].setDispatcherArm ( arr[i][j].disp2 );

if (i+1 < arr.length && j-1 >= 0 )
arr[i+1][j-1].setDispatcherArm ( arr[i][j].disp2 );

if (i-1 >= 0 && j+1 < arr[0].length )
arr[i-1][j+1].setDispatcherArm ( arr[i][j].disp2 );

}
}

};
};

Agora Vamos ao código que realmente irá instanciar a classe Game para fazer nosso jogo funcionar:

var game:Game;/* Novo jogo*/
function newGame( event:MouseEvent)
{

/* aqui removemos o jogo atual e inciamos um novo
* e deixamos o resto do trabalho com a ActionScript
* Virtual Machine, que é o compilador do ECMA script usado
* pelo flash e que possui um ótimo coletor de lixo
*/
if(event)
removeChild(game);
/* game de 25 linhas e 25 colunas, com 100 bombas*/
game = new Game(25, 25, 100);

var isk:DisplayObject = null;

game.init();

/*adiciona o game a lista do root
* posicionando no local adequado
*/
isk = addChild(game);
isk.x = 0;
isk.y = 48;

isk.width = 400;
isk.height = 400;
}

newGame(null);

/* aqui adicionamos o evento de clique do mouse no botão novo jogo*/
newGame_btn.addEventListener(MouseEvent.CLICK, newGame);

Veja que eu quando inicio um novo jogo e descubro que acabou de ser realizado um outro simplesmente crio um novo, mas e os objetos criados no jogo anterior?? Para onde vão? Para quem não sabe a máquina virtual do flash 9 foi reimplementada e possui um ótimo coletor de lixo, que se encarrega de limpar esses espaços deixados sem referência.

Considerações Finais

Este é o jogo, só rodar e ver o resultado, aconselho estudar as classes de manipulação de eventos elas ajudam bastante na hora de resolver um poblema. Usando esta idéia você poderá criar outros jogos, use sua criatividade. Não pule etapas, se você estiver começando é melhor começar a programar os jogos dos tutoriais iniciais de games. Tivemos que nos adptar a ausência do duplicateMovieClip, mas que foi suprida pela forma ainda mais fácil de linkar o nome do mc como classe e apenas instanciar com o operador new o mc que se deseja duplicar, no nosso caso o mc Mine.

Não tente criar jogos antes de saber programar você pode acabar se frustrando por não conseguir, nos tutoriais mostro apenas como utilizar o que você sabe de Action Script 3.0 em jogos. Aqui apresentei uma forma de criar o game. Você pode colocar novas funcionalidaes nele como timer, contador de acertos entre outros. Apenas fiz núcleo do jogo, as inovações ficam de desafio para vocês. Depois postem no fórum as suas inovações se vocês quiserem para que possamos avaliar e jogar um pouco.


Espero que tenham gostado. Até a próxima.

Autor: Márcio Silva – Colunista de Flash & ActionSctipt MXSTUDIO.

Download dos arquivos

Qualquer dúvida envie um e-mail para marciosilva@mxstudio.com.br ou acesse nosso fórum

www.marciosilva.net

Escrito por Marcio Silva on janeiro 18, 2008. Arquivado em Flash. Você pode seguir as respostas a esse artigo pelo RSS 2.0. Você pode deixar respostas para esse artigo

1 resposta a Criação de games – Campo Minado em AS3

  1. Opa, olá marcio, bele?

    seguinte cara, segui seu tutorial e talz, só que apareceu um problema que n to conseguindo resolver, procurei do que se tratava esse erro, mas de nada adianto, se vc puder me dar uma força, fico grato =)

    Aqui tah o erro:

    ReferenceError: Error #1069: Property disp2 not found on Square and there is no default value.
    at Game/::armagedon()
    at Game/::display()
    at Game/init()
    at Untitled_fla::MainTimeline/newGame()
    at Untitled_fla::MainTimeline/Untitled_fla::frame1()

    Vlw

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>