As 3.0 - Movimentação Direcionada De Objetos - Programação De Jogos

noobinhu
em Outras Linguagens

noobinhu

Ex Administrador
avatar
Marquês
Marquês

INFOS

Grupo: MarquêsRegistrado: 01/03/06Posts: 1022Gênero: Masculino

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.

 

car_sprites_pack.png

 

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:

 

car.png

 

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

Henrique Moura

Ex-administrador
avatar
Herói
Herói

INFOS

Grupo: HeróiRegistrado: 17/02/08Posts: 2541

O tópico está maravilhoso, sem dúvidas!

Ponto de reputação adicionado.

 

Abraços.

KaKilo ~
 


Lom14fD.png