Anderson Ferminiano 2 Denunciar post Postado Fevereiro 15, 2010 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. 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: 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
Michel Araújo 0 Denunciar post Postado Fevereiro 15, 2010 belo artigo, parabéns Anderson ^_^ Compartilhar este post Link para o post Compartilhar em outros sites
Matheus Brito 12 Denunciar post Postado Fevereiro 21, 2010 Show de Bola. Parabéns Compartilhar este post Link para o post Compartilhar em outros sites