Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Anderson Ferminiano

AS 3.0 - Movimentação Direcionada de Objetos - Programação de Jogos

Recommended Posts

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.

 

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:

 

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());
});

Executamos e... ou é? Não mudou nada! É, nós só pegamos ainda os pontos inferiores e superiores de cada posição, mas não implantamos a propriedade scrollRect no object. Voltamos então pra classe Carro...

 

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

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.