Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Nesse artigo vou explicar uma prática muito utilizada em jogos, a movimentação do personagem, do adversário, ou de qualquer outro objeto. A movimentação em muitos jogos não ocorre somente na horizontal e vertical, mas também na diagonal. Neste exemplo, mostrarei um carro movimentando-se na horizontal/vertical, mas com outras técnicas conseguiríamos o efeito da diagonal também, porém não neste artigo.
É importante ter conhecimento de AS 3.0 e POO para entender o artigo.
No final do artigo existe um link para baixar o exemplo já feito, junto com as sources, e um link para verificar o resultado.
Antes de tudo, faça seu projeto (.fla), faça também uma classe (.as) dentro da package lib nomeada de Carro. Como o exemplo que segue:
/*
Artigo de movimentação de Objetos em AS 3.0
Anderson Ferminiano
www.andersonferminiano.com
*/
package lib {
/*
Classe carro
É responsável pelo objeto carro na tela e toda sua movimentação
*/
public class Carro {
}
}
Esta classe manipulará todo o objeto do carro no palco. Seu projeto agora então deve ter um .fla na raiz, uma pasta chamada lib na raiz, e dentro da pasta lib um arquivo da classe Carro (Carro.as).
Para utilizar esta classe no seu projeto utilize a palavra reservada import, como segue:
import lib.Carro;
Isto é, importamos o arquivo Carro.as que se encontra na pasta lib que por sua vez se encontra na raiz do projeto.
Ok, agora vamos entender a lógica da movimentação, já vi muitos jogos utilizarem 4 imagens pra exibir cada uma das direções (norte, sul...), mas eu prefiro utilizar uma imagem só e na programação dizer o seguinte: "Quando o carro estiver direcionado para o leste, exiba somente a parte da imagem que vai do ponto x:10 e y:20, até o ponto x:30 e y:50, agora quando estiver direcionado para o oeste, exiba somente a parte da imagem que vai do ponto x:60 e y:60 até x:100 e y:100". Certo, então por onde começamos? Eu trouxe aqui uma imagem de vários carros para o leitor escolher as 4 direções que quiser, e entender que não importa o sprite, ou a organização de cada posição, no final qualquer imagem conseguirá ser renderizada corretamente.
/applications/core/interface/imageproxy/imageproxy.php?img=http://www.andersonferminiano.com/games/car_artigo_movimentacao/images/car_sprites_pack.png&key=10c21530c4cd1b8fd34365185bde030aabf218a4ebf8aef5c5b4e5606c55ce34" alt="Imagem Postada" />
Agora recorte no seu programa de edição favorito, caso não queira recortar basta copiar a de exemplo, - eu costumo utilizar o Photoshop CS4 - como desejar, é recomendável recortar as 4 direções da mesma cor para no final ter mais sentido a movimentação, carros ainda não costumam ter acessórios de camaleões. Eu recortei o branco, e ficou assim:
/applications/core/interface/imageproxy/imageproxy.php?img=http://www.andersonferminiano.com/games/car_artigo_movimentacao/images/car.png&key=2b445cb4474c133bcddf2c875b3a073c614936fd6c42090af8758290b068b4be" alt="Imagem Postada" />
Crie um diretório chamado images na raiz do projeto, e dentro deste salve o recorte de seu carro como car.png.
Temos então nossa imagem recortada, voltamos para a classe Carro para definir alguns métodos e propriedades.
Defina 8 propriedades dentro da classe:
object, pointsTop, pointsBottom, carroSpr, x, y, dir, speed
Crie 4 getters/setters simples para as seguintes propriedades: x, y, dir, speed.
Importe 3 classes no package: MovieClip (flash.display.MovieClip) para manipulação do carro, Point (flash.geom.Point) e Rectangle (flash.geom.Rectangle) para manipulação de qual parte da imagem será exibida.
Sua classe deve ser algo assim:
/*
Artigo de movimentação de Objetos em AS 3.0
Anderson Ferminiano
www.andersonferminiano.com
*/
package lib {
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.display.MovieClip;
/*
Classe carro
É responsável pelo objeto carro na tela e toda sua movimentação
*/
public class Carro {
var object:MovieClip;
var pointsTop:Array;
var pointsBottom:Array;
var carroSpr;
var x;
var y;
var dir;
var speed;
// seta velocidade
public function setSpeed(spd){
this.speed = spd;
}
// retorna velocidade
public function getSpeed(){
return this.speed;
}
// seta posição x
public function setX(x){
this.x = x;
}
// seta posição y
public function setY(y){
this.y = y;
}
// retorna posição x
public function getX(){
return this.x;
}
// retorna posição y
public function getY(){
return this.y;
}
// seta direção
public function setDirection(dir){
this.dir = dir;
}
// retorna direção
public function getDirection(){
return this.dir;
}
}
}
Crie um construtor que inicie uma instância de MovieClip na propriedade object, 2 arrays em pointsTop e pointsBottom, e sete o valor 0 para x, y e speed.
Crie o método setSprite(carroSpr) que adicione o objeto carroSpr como filho da propriedade object do tipo MovieClip.
Crie um getter para a propriedade object.
Sua classe deve estar assim:
/*
Artigo de movimentação de Objetos em AS 3.0
Anderson Ferminiano
www.andersonferminiano.com
*/
package lib {
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.display.MovieClip;
/*
Classe carro
É responsável pelo objeto carro na tela e toda sua movimentação
*/
public class Carro {
var object:MovieClip;
var pointsTop:Array;
var pointsBottom:Array;
var carroSpr;
var x;
var y;
var dir;
var speed;
// inicia variáveis básicas de configurações
public function Carro(){
this.object = new MovieClip();
this.pointsTop = new Array();
this.pointsBottom = new Array();
this.x = 0;
this.y = 0;
this.speed = 0;
}
// seta sprite de todas posições do carro
public function setSprite(carroSpr){
this.object.addChild(carroSpr);
}
// seta velocidade
public function setSpeed(spd){
this.speed = spd;
}
// retorna velocidade
public function getSpeed(){
return this.speed;
}
// seta posição x
public function setX(x){
this.x = x;
}
// seta posição y
public function setY(y){
this.y = y;
}
// retorna posição x
public function getX(){
return this.x;
}
// retorna posição y
public function getY(){
return this.y;
}
// seta direção
public function setDirection(dir){
this.dir = dir;
}
// retorna direção
public function getDirection(){
return this.dir;
}
// retorna movieclip
public function getObject(){
return this.object;
}
}
}
Implemente o método update que será responsável por enviar todas propriedades para a propriedade object, e aí sim alterar realmente o que foi feito.
public function update(){
this.object.x = this.x;
this.object.y = this.y;
}
Certo, neste momento já é possível exibir a imagem que recortamos no palco somente implementando a classe Carro corretamente ao projeto. Volte ao arquivo .fla.
Neste momento ele deve estar assim:
/*
Artigo de movimentação de Objetos em AS 3.0
Anderson Ferminiano
www.andersonferminiano.com
*/
// importa arquivo .as dentro da pasta api que se localiza na raiz do projeto (.fla)
import lib.Carro;
Carregue a imagem que recortamos das 4 direções da seguinte maneira:
var carro:Carro = new Carro();
carro.setDirection(1);
// carrega imagem do carro (car.png)
var loader:Loader = new Loader();
loader.load(new URLRequest("images/car.png"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){
carro.setSprite(loader);
carro.update();
stage.addChild(carro.getObject());
});
Vamos lá, se você rodar o projeto agora, verá uma imagem com as 4 direções no palco, e agora como faremos pra exibir somente a direção correta?
Lembra daquelas 2 classes que implementamos na package? Point e Rectangle? É agora que elas entram!
Existe uma propriedade na classe MovieClip chamada scrollRect que pede um objeto do tipo Rectangle, que é definido das propriedades x, y, largura e altura. Esta propriedade scrollRect faz com que somente a área do Rectangle seja exibido no palco. Vamos lá então, voltamos para nossa classe.
Implemente o método setDirectionPoint na sua classe, ele será o responsável por cuidar da área que o rectangle terá:
public function setDirectionPoint(direction,pointTop,pointBottom){
this.pointsTop[direction] = pointTop;
this.pointsBottom[direction] = pointBottom;
}
Então agora temos que conseguir o x, y do canto superior esquerdo e o x, y do canto inferior direito de cada uma das 4 direções. No Photoshop CS4, basta abrir a imagem *.png, ir em Window -> Info ou apertar F8, e então ir com o mouse em cima de cada uma das pontas. Anote-as separadamente no bloco de notas.
Feito isto, eu obtive como resultado da imagem de exemplo o seguinte:
1 - Norte:
Superior Esquerdo - 99,67 | Inferior Direito - 158,127
2 - Leste:
Superior Esquerdo - 0,0 | Inferior Direito - 97,65
3 - Norte:
Superior Esquerdo - 98,0 | Inferior Direito - 159,65
4 - Leste:
Superior Esquerdo - 0,66 | Inferior Direito - 98,127
Por padrão, utilizo sempre os IDs em sentido horário para direção, 1 - norte, 2 - leste, etc...
Agora voltamos para nosso *.fla... e à partir do método setDirectionPoint iremos implementar esses pontos, sempre primeiro o id da direção, depois o ponto superior esquerdo, e depois o inferior direito. Seguindo isto cheguei em:
/*
Direções padrões (Sentido horário)
1: north - norte
2: east - leste
3: south - sul
4: west - oeste
*/
carro.setDirectionPoint(1, new Point(99,67), new Point(158,127));// norte
carro.setDirectionPoint(2, new Point(0,0), new Point(97,65)); // leste
carro.setDirectionPoint(3, new Point(98,0), new Point(159,65)); // sul
carro.setDirectionPoint(4, new Point(0,66), new Point(98,127)); // oeste
E meu código completo:
/*
Artigo de movimentação de Objetos em AS 3.0
Anderson Ferminiano
www.andersonferminiano.com
*/
// importa arquivo .as dentro da pasta api que se localiza na raiz do projeto (.fla)
import lib.Carro;
var carro:Carro = new Carro();
carro.setDirection(1);
/*
Direções padrões (Sentido horário)
1: north - norte
2: east - leste
3: south - sul
4: west - oeste
*/
carro.setDirectionPoint(1, new Point(99,67), new Point(158,127));// norte
carro.setDirectionPoint(2, new Point(0,0), new Point(97,65)); // leste
carro.setDirectionPoint(3, new Point(98,0), new Point(159,65)); // sul
carro.setDirectionPoint(4, new Point(0,66), new Point(98,127)); // oeste
// carrega imagem do carro (car.png)
var loader:Loader = new Loader();
loader.load(new URLRequest("images/car.png"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){
carro.setSprite(loader);
carro.update();
stage.addChild(carro.getObject());
});No método update, adicione mais uma linha de código:
this.object.scrollRect = new Rectangle(this.pointsTop[this.dir].x, this.pointsTop[this.dir].y, this.pointsBottom[this.dir].x-this.pointsTop[this.dir].x, this.pointsBottom[this.dir].y-this.pointsTop[this.dir].y);
O que esta linha faz? Primeiro, ela seta os primeiros parâmetros com a posição superior esquerda da atual direção (propriedade dir), depois ela calcula à partir de uma subtração simples do ponto inferior e o ponto superior, a largura e a altura do objeto.
Seu método deve estar assim:
// atualiza x, y, e direção (scrollRect)
public function update(){
this.object.x = this.x;
this.object.y = this.y;
// calcula à partir dos pontos de direção, o local que vai ser exibido da imagem
this.object.scrollRect = new Rectangle(this.pointsTop[this.dir].x, this.pointsTop[this.dir].y, this.pointsBottom[this.dir].x-this.pointsTop[this.dir].x, this.pointsBottom[this.dir].y-this.pointsTop[this.dir].y);
}
E a mágica está pronta! Agora se você fez tudo certo, seu carro aparecerá direcionado para o norte...
Ok, mas como vamos agora direcioná-lo pelo teclado?
Agora volte para o *.fla jovem gafanhoto, usaremos um array para guardar todas as teclas pressionadas e então um timer verificando as teclas pressionadas e executando sua respectiva ação. Por que isso? Porque se executarmos as ações direto no evento do teclado, teremos um delay que fará com que o carro fique lento em sua movimentação.
Utilizaremos então o seguinte código para adicionar e remover as teclas ativas do array.
// faz eventos do teclado, pra saber a direção que o carro vai
var keys:Array = new Array(); // array com todas keys que estão pressionadas
// quando usuario pressionar, adiciona no array como true
stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent){
keys[e.keyCode] = true;
});
// quando usuario soltar, adiciona no array como false
stage.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent){
keys[e.keyCode] = false;
});
E o gameLoop de 60 fps - 1s/60 - padrão.
// faz gameLoop de 1 fps (1000ms/60) pra manipular as keys pressionadas
var gameLoop:Timer = new Timer(1000/60);
gameLoop.addEventListener(TimerEvent.TIMER, function(e){
// setinha pro norte pressionada
if (keys[Keyboard.UP] == true) {
carro.setDirection(1);
}
// setinha pro sul pressionada
else if (keys[Keyboard.DOWN] == true) {
carro.setDirection(3);
}
// setinha pro oeste pressionada
else if (keys[Keyboard.LEFT] == true) {
carro.setDirection(4);
}
// setinha pro leste pressionada
else if (keys[Keyboard.RIGHT] == true) {
carro.setDirection(2);
}
// faz as alterações e dá update pra confirmar
carro.update();
});
// inicia timer
gameLoop.start();
Seu código do *.fla deve ser:
/*
Artigo de movimentação de Objetos em AS 3.0
Anderson Ferminiano
www.andersonferminiano.com
*/
// importa arquivo .as dentro da pasta api que se localiza na raiz do projeto (.fla)
import lib.Carro;
var carro:Carro = new Carro();
carro.setDirection(1);
/*
Direções padrões (Sentido horário)
1: north - norte
2: east - leste
3: south - sul
4: west - oeste
*/
carro.setDirectionPoint(1, new Point(99,67), new Point(158,127));// norte
carro.setDirectionPoint(2, new Point(0,0), new Point(97,65)); // leste
carro.setDirectionPoint(3, new Point(98,0), new Point(159,65)); // sul
carro.setDirectionPoint(4, new Point(0,66), new Point(98,127)); // oeste
// carrega imagem do carro (car.png)
var loader:Loader = new Loader();
loader.load(new URLRequest("images/car.png"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){
carro.setSprite(loader);
carro.update();
stage.addChild(carro.getObject());
});
// faz eventos do teclado, pra saber a direção que o carro vai
var keys:Array = new Array(); // array com todas keys que estão pressionadas
// quando usuario pressionar, adiciona no array como true
stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent){
keys[e.keyCode] = true;
});
// quando usuario soltar, adiciona no array como false
stage.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent){
keys[e.keyCode] = false;
});
// faz gameLoop de 1 fps (1000ms/60) pra manipular as keys pressionadas
var gameLoop:Timer = new Timer(1000/60);
gameLoop.addEventListener(TimerEvent.TIMER, function(e){
// setinha pro norte pressionada
if (keys[Keyboard.UP] == true) {
carro.setDirection(1);
}
// setinha pro sul pressionada
else if (keys[Keyboard.DOWN] == true) {
carro.setDirection(3);
}
// setinha pro oeste pressionada
else if (keys[Keyboard.LEFT] == true) {
carro.setDirection(4);
}
// setinha pro leste pressionada
else if (keys[Keyboard.RIGHT] == true) {
carro.setDirection(2);
}
// faz as alterações e dá update pra confirmar
carro.update();
});
// inicia timer
gameLoop.start();
Neste momento seu carro muda de direção, mas não se move, então vamos adicionar mais 1 método na classe para fazê-lo andar! O método walk.
// anda de acordo com direção atual da instância
public function walk(){
switch(this.getDirection()){
case 1:
this.setY(this.getY()-this.getSpeed());
break;
case 3:
this.setY(this.getY()+this.getSpeed());
break;
case 2:
this.setX(this.getX()+this.getSpeed());
break;
case 4:
this.setX(this.getX()-this.getSpeed());
break;
}
}
Agora no *.fla utilize o método walk logo após de cada setDirection, ficando assim:
gameLoop.addEventListener(TimerEvent.TIMER, function(e){
// setinha pro norte pressionada
if (keys[Keyboard.UP] == true) {
carro.setDirection(1);
carro.walk();
}
// setinha pro sul pressionada
else if (keys[Keyboard.DOWN] == true) {
carro.setDirection(3);
carro.walk();
}
// setinha pro oeste pressionada
else if (keys[Keyboard.LEFT] == true) {
carro.setDirection(4);
carro.walk();
}
// setinha pro leste pressionada
else if (keys[Keyboard.RIGHT] == true) {
carro.setDirection(2);
carro.walk();
}
// faz as alterações e dá update pra confirmar
carro.update();
});
Ok, mas de que adianta ele andar, se a propriedade speed dele está zerada? Vamos lá! Implemente a velocidade... e ficaremos assim:
/*
Artigo de movimentação de Objetos em AS 3.0
Anderson Ferminiano
www.andersonferminiano.com
*/
// importa arquivo .as dentro da pasta api que se localiza na raiz do projeto (.fla)
import lib.Carro;
var carro:Carro = new Carro();
carro.setDirection(1);
carro.setSpeed(5);
/*
Direções padrões (Sentido horário)
1: north - norte
2: east - leste
3: south - sul
4: west - oeste
*/
carro.setDirectionPoint(1, new Point(99,67), new Point(158,127));// norte
carro.setDirectionPoint(2, new Point(0,0), new Point(97,65)); // leste
carro.setDirectionPoint(3, new Point(98,0), new Point(159,65)); // sul
carro.setDirectionPoint(4, new Point(0,66), new Point(98,127)); // oeste
// carrega imagem do carro (car.png)
var loader:Loader = new Loader();
loader.load(new URLRequest("images/car.png"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){
carro.setSprite(loader);
carro.update();
stage.addChild(carro.getObject());
});
// faz eventos do teclado, pra saber a direção que o carro vai
var keys:Array = new Array(); // array com todas keys que estão pressionadas
// quando usuario pressionar, adiciona no array como true
stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent){
keys[e.keyCode] = true;
});
// quando usuario soltar, adiciona no array como false
stage.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent){
keys[e.keyCode] = false;
});
// faz gameLoop de 1 fps (1000ms/60) pra manipular as keys pressionadas
var gameLoop:Timer = new Timer(1000/60);
gameLoop.addEventListener(TimerEvent.TIMER, function(e){
// setinha pro norte pressionada
if (keys[Keyboard.UP] == true) {
carro.setDirection(1);
carro.walk();
}
// setinha pro sul pressionada
else if (keys[Keyboard.DOWN] == true) {
carro.setDirection(3);
carro.walk();
}
// setinha pro oeste pressionada
else if (keys[Keyboard.LEFT] == true) {
carro.setDirection(4);
carro.walk();
}
// setinha pro leste pressionada
else if (keys[Keyboard.RIGHT] == true) {
carro.setDirection(2);
carro.walk();
}
// faz as alterações e dá update pra confirmar
carro.update();
});
// inicia timer
gameLoop.start();
E sua classe Carro:
/*
Artigo de movimentação de Objetos em AS 3.0
Anderson Ferminiano
www.andersonferminiano.com
*/
package lib {
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.display.MovieClip;
/*
Classe carro
É responsável pelo objeto carro na tela e toda sua movimentação
*/
public class Carro {
var object:MovieClip;
var pointsTop:Array;
var pointsBottom:Array;
var carroSpr;
var x;
var y;
var dir;
var speed;
// inicia variáveis básicas de configurações
public function Carro(){
this.object = new MovieClip();
this.pointsTop = new Array();
this.pointsBottom = new Array();
this.x = 0;
this.y = 0;
this.speed = 0;
}
// seta velocidade
public function setSpeed(spd){
this.speed = spd;
}
// retorna velocidade
public function getSpeed(){
return this.speed;
}
// seta sprite de todas posições do carro
public function setSprite(carroSpr){
this.object.addChild(carroSpr);
}
// seta posição x
public function setX(x){
this.x = x;
}
// seta posição y
public function setY(y){
this.y = y;
}
// retorna posição x
public function getX(){
return this.x;
}
// retorna posição y
public function getY(){
return this.y;
}
// seta direção
public function setDirection(dir){
this.dir = dir;
}
// retorna direção
public function getDirection(){
return this.dir;
}
// anda de acordo com direção atual da instância
public function walk(){
switch(this.getDirection()){
case 1:
this.setY(this.getY()-this.getSpeed());
break;
case 3:
this.setY(this.getY()+this.getSpeed());
break;
case 2:
this.setX(this.getX()+this.getSpeed());
break;
case 4:
this.setX(this.getX()-this.getSpeed());
break;
}
}
// atualiza x, y, e direção (scrollRect)
public function update(){
this.object.x = this.x;
this.object.y = this.y;
// calcula à partir dos pontos de direção, o local que vai ser exibido da imagem
this.object.scrollRect = new Rectangle(this.pointsTop[this.dir].x, this.pointsTop[this.dir].y, this.pointsBottom[this.dir].x-this.pointsTop[this.dir].x, this.pointsBottom[this.dir].y-this.pointsTop[this.dir].y);
}
// seta pontos de direção, ponto no topo e ponto inferior
public function setDirectionPoint(direction,pointTop,pointBottom){
this.pointsTop[direction] = pointTop;
this.pointsBottom[direction] = pointBottom;
}
// retorna movieclip
public function getObject(){
return this.object;
}
}
}
Está pronto seu carrinho que se move na direção corretamente! Que tal fazer um joguinho de colisões de carro agora em?!
Link para visualização de resultado: http://www.andersonferminiano.com/games/car_artigo_movimentacao/artigomovimentacao.swf
Link para download do resultado e sources: http://www.andersonferminiano.com/games/car_artigo_movimentacao.rar
Fonte: http://www.andersonferminiano.com/post-31-As-30-Movimentacao-Direcionada-Objetos-Jogos
Carregando comentários...