Wild Witch Project - Um game que você nunca viu

Neste jogo você precisa ajudar uma bruxa adolescente a salvar seu mundo da Mortífera Bunda Assassina do Espaço Sideral

Tutorial Blender + Away3D + AS3 - O principio


Olá criançada! Hoje temos uma surpresa no pedaço.
Inaugurando oficialmente a "seção Tutoriais" do blog! Aehhh


Vamos à estrutura de nosso tutorial, vai ser assim, com personagens e cores destacando o tipo do texto, assim você pode ler pulando oque não interessa conforme seus conhecimentos.

-Os newbies que me sigam, além de mandar você procurar no Google, irei mostrar detalhes da forma mais simples possível.
Sou Cibele e não sei porque sobrou pra mim ser a heroína dessa p#%$*$a de game.


-Eu vou orienta-los quando o assunto estiver indo pro lado prático.
Sou Winderfall (não Pitfall TM) o grande Arqui-mago que des-de à decada de 80 vem levando paz e conhecimento ao mundo.


-Quando o pior acontecer eu estarei ao seu lado, se tiverem medo de minha digital-sabedoria pule para os parágrafos mais adiante.
Os poucos que me conhecem me conhecem como "O Sábio Nerd das Montanhas"
Este tutorial será dedicado a um publico mais amplo, pois não só programadores se interessam pela Away3D, mas também pessoas que não entendem nada de informática mas também querem usa-la.
Bom, sentem na cadeira, e preparem um café pois será bem extenso essa primeira jornada.


Passo a passo agora
Tenha seu modelo no Blender pronto.
O Away3D esta um pouco a frente do Papervision mas acho que nem tudo dele esta preparado para usar 100% dos recursos da versão 10 do player do Flash por questões de compatibilidade da linguagem nos player de 9 à 10. Isto em termos leigos significa que ele não aproveita ainda ao máximo os recursos que ele possui.

De qualquer forma iremos mostrar como chegar aqui.

Mesh
Como iremos montar um modelo para web devemos nos concentrar em uma modelagem económica. O quanto deve ser a economia é relativo ao tipo de utilização do 3D.


Siga esta lógica desenvolvida para manter um conforto ao usuário (quem não segue geralmente gera aqueles swf que dão errada má fama ao Flash)

Quantidade de polígonos
menos mais
elementos de decoração de página, banners e elementos secundáriosjogos, animaçoes principais, elementos principais (que o usuário irá focar a atenção) como cabeçalhos e informativos.

Nota: Isto também vale para qualquer projeto web independente da tecnologia usada, por mais que usem mecanismos de render diferentes.

Animação

Vamos usar o formato mais simples de arquivo para animação o MD2.

Este padrão de arquivo irá guardar sua sequência de frames como uma cópia da malha deformada à cada frame! Então você deve economizar nos frames pois cada um dobra a malha base.

Desmitificando: Não se preocupe em excesso, você não vai ganhar qualidade inserindo um número absurdo de frames, pois na hora de renderizar o Away3D irá fazer a devida interpolação deixando a animação suave mesmo que haja apenas 2 quadros nela.
12 frames para animar um andar ou correr é o bastante.
Se o processador do usuário aguentar 500fps ele cria os frames intermediarios em tempo de execução.
Faça o arranjo da animação economizando o máximo de quadros que der, lembre-se que você na verdade está apenas passando as poses chaves pro Away3D, o resto é com ele.

Crie um arquivo de texto chamado anim.txt que deve ter a seguinte estrutura: Obs.: NÃO digite no Word, você precisa de um arquivo de "texto puro",  use apenas o Bloco de Notas (eu recomendo o NotePad++).
# MD2 Frame Name List
stand 12
run 12
jump 6
attack 7
pain0 0
pain2 0
pain3 0
flip 0
salute 0
taunt 0
wave 0
point 0
crstnd 0
crwalk 0
crattack 0
crpain 0
crdeath 0
death0 0
death2 0
death3 0

A primeira linha é um cabeçalho indicando o tipo de arquivo, e não deve ser mudada. As próximas indicam o nome da animação e a quantidade de quadros nela. Ex.:

run1 10

O nome da animação é "run1" e tem 10 quadros

As animações devem ser inseridos na ordem que aparecem na timeline do Blender.

Vivo tendo problemas quando não uso um número de animações igual ao padrão, mas uma solução simples é "zerar" a duração: die 0 por exemplo. Mas o nome vocês podem mudar

Textura

Outro detalhe é a textura, os arquivos MD2 trabalham com texturas de 256x256 pixels, você poderá usar uma textura maior substituindo o arquivo que irá trabalhar no Blender pelo arquivo final de maior resolução como 512x512 por exemplo (cuidado, recomendo um jpeg de 50Kb no máximo).

Salve o arquivo
File / export / md2
No assistente troque o arquivo tris.md2 pelo nome que quer salvar na pasta desejada.

No campo "MD2 file to save" clique em Browse e escolha o local e nome do arquivo.

Na opção "Frame List file to load" escolha o arquivo anim.txt

Clique em Export. De OK para tudo que ele for falando.
Basicamente o script python irá modificar alguns aspectos da geometria, como trocar quadrados por triângulos e mudar os eixos do modelo para deixar compatível com o padrão md2. Por isso tenha salvo o arquivo antes!

Baixe aqui o modelo mau animado que montei super rápido pra esse tutoria.

Vamos à parte da plataforma Flash
Como todos sabem este blog é entusiasta de tecnologias abertas, então vamos à parte da Adobe que muitos não conhecem, uns não acreditaram e outros torcem o nariz: Adobe Open Source.


O Flash Develop é baba pra instalar.
O Flex é só extrair do zip (qualquer duvida para instalar é só procurar no Google)
Para que você não baixo a versão errada da Away3D aqui vai o link.

Vamos trabalhar com um pacote de programação, chamado Classe. Este pacote tanto pode ser apontado diretamente para o Flex como pode ser usado no Flash.


Vá no menu projetos do Flash Develop e crie um novo projeto de AS3.
Ele cria um arquivo chamado Main.as, este arquivo é a classe principal de nosso swf, e pode ser usada depois dentro do Flash, mas por hora vamos à ela, se não estiver aberta dê dois cliques no ícone do arquivo Main.as
O Flash Develop tem um template baseado nos melhores processos de produção. Isto quer dizer que ele irá gerar todo código que você precisa automaticamente.

Na estrutura de pastas que ele cria você vai ver uma pasta chamada "bin" (de binários) jogue o md2 e a textura jpg nela.


Agora basta implementar nosso código. Vamos a entender primeiro oque o FlashDevelop criou.

Ele criou o esqueleto básico de uma classe, que iremos usar para controlar nosso pequeno ambiente 3D. Uma classe apesar de ser reusável e realicavel não deve ser entendida como algum componente que possa ser encaixado, muitos pensam que classes são como plugins ou add-ons que você instala em seus programas, classes não tem nada a ver com issu.
Classes são modelos de dados e funções para manipular estes dados.

Quando o swf estiver rodando você nunca irá trabalhar ou ter contato com a classe Main (salvo algumas técnicas que iremos ver adiante), mas sim com o objeto root que será a instância, isto é a substânciação do modelo descrito na classe Main em algo chamado Objeto.
O objeto sim é encaixavel e manipulável.

O Objeto possui um endereço fisico na memória e pode ser achado e diferênciado, a classe não, por tanto ele pode manipular eventos como mouse, teclado, carregamento. etc. E também pode ser visível.

para maiores informações sobre classes vá para a Wikepédia.

Toda classe segue a seguinte estrutura:

Os imports são indicadores, indicadores pro compilador do Flex e não para você, que dizem quais e onde estão determinados componentes de uma biblioteca externa (código de terceiros como a Away3D ou arquivos SWC).
As propriedades são variáveis ou constantes, elas guardam valores que iremos usar na classe.
Os métodos são a parte funcional da classe.

O FlashDeveloper ja criou tudo issu ai pra você, então iremos nos concentrar onde devemos mudar.
Basicamente iremos mecher nas propriedades e metodos acrescentando coisas.


Como dito iremos operar na área vermelha do diagrama acima.


Vamos adicionar suporte ao Away3D.
Baixe a Away3D e descompacte.
Depois vá em Project/Properties va na aba Classpaths e adicione a pasta que esta no nível abaixo da away3d (geralmente away3d.num.da.versão). Você tera uma imagem assim:
 Preparar os elementos
Agora à Away3D funciona apartir de um componente principal, um objeto da classe View3D. Vamos criar nosso objeto de forma que ele seja utilizado durante todo o tempo de vida do objeto da classe Main (no caso todo o tempo):
Na linha 12 digite: private var view:View3D;

Vai notar que enquanto digitao Flash Develop abre um assistente para você, quando aparecer a classe View3D você pode clicar em Tab que ele completa o código para você.

Repare também que além disso o Flash Develop também inseriu uma linha logo no cabeçalho: import away3d.containers.View3D;

Esta linha vai indicar ao compilador que ele deve vincular esta classe ao nosso produto final.


Estes textos destacados são comentários, elementos que inserimos no código, com o objetivo de nos orientar e também orientar a IDE para que ela futuramente nos auxilie. Mais informação aqui.

Um comentário de código nada mais é que uma informação simples e rápida que você ira OBRIGATORIAMENTE colocar em tudo. Lembre-se ninguém e nem você querem parar para entender um monte de código quando precisar dele, querem apenas usar o código sem perder tempo. Esse é o objetivo das classes e o motivo das IDEs agilizarem a programação. E nem você irá se lembrar oque escreveu apenas lendo o nome da função e ninguém terá tempo de fazer isso quando estiver no aperto.

Agora iremos criar alguns métodos.
Separamos os metodos por função: "criação da cena", "carregar objetos", "rodar animação", "iniciar o swf". Assim organizamos nossa classe, deixando ela mais fácil de trabalahar.
Métodos nada mais são que "funções" com pequenos trechos de código, servem para evitar de que tenhamos que digitar o mesmo código mais de uma vez num programa e também para organiza-lo em uma estrutura compreencivel. Lembre-se, uma função sempre deve fazer uma "função" por vez, independente de q tenha 2 ou 200 linhas de código nela.
Abaixo do function init

Agora um pouco de código para fazer acontecer:
Iremos realmente criar os objetos usando o metodo setupAway3, olhe os comentáios com atenção.

private function setupAway3D():void
{
      view = new View3D(); //cria o container 3D
      stage.addEventListener( Event.RESIZE, resizeWindow ); //adiciona monitor para quando redimencionarem a janela
      addChild( view ); //insere view no palco para o povo poder ver
      resizeWindow( null ); //chama resizeWindow para forçar view a centralizar na tela
        view.camera.position = new Number3D( -500,500,500 );
}

A AS3 aceita atribuições múltiplas, vira e meche escrevo algo como: addChild( view = new View3D() );
Menos organizado, mais simples ainda para se entender.

Há linguagens que permitem mais atribuições múltiplas em níveis exagerados como Ruby: preco = 2.0 and real=1.0 and dolar = 1.87 and ... mais variáveis, entre outros artifícios para por varias coisas numa mesma linha, mas devemos ter cuidado com a legibilidade SEMPRE.

Haverá um momento que você terá que sacrificar organização e legibilidade por desempenho. Mas isso mais pra frente.

Repare também que chamamos resizeWindow com um null, isto porque não usamos a variável passada como evento dentro da função, então podemos forçar a função passando NADA que não gerará erros.

private function resizeWindow(e:Event):void
{
            view.x = stage.stageWidth / 2;
            view.y = stage.stageHeight / 2;
}

Agora a mão na massa, finalmente montar o cenário 3D

private function create3DScene():void

{
            //cria um material para atribuir à malha do chão
            var chaoMat:BitmapFileMaterial = new BitmapFileMaterial( "chao1.jpg" );
           
            //criamos um objeto genérico, que iremos usar para passar valores
            //à away 3D

            var objetoTemp:Object = new Object();
            objetoTemp.material = chaoMat;
            objetoTemp.width = 512;
            objetoTemp.height = 512;
            objetoTemp.rotationX = 0;
            objetoTemp.rotationY = 180;
            objetoTemp.segmentsW = 8;
            objetoTemp.segmentsH = 8;
            //cria o chão e adiciona ele à cena
            chao = new Plane( objetoTemp );
            //maneira  simples e rápida de escrever isto:
            //chao = new Plane( { material:chaoMat, width:256, height:256, rotationX: 0, rotationY:180 } );
           
            view.scene.addChild(chao); //a cena ja esta inclusa no objeto view (view.scene)
           
            view.camera.lookAt( chao.position ); //faz a camera olhar para o chão 
}
Repare que o material chaoMat, ele pede um arquivo chamado chao1.jpg que deve estar no mesmo diretório que o .swf e o .md2. Apenas clique na imagem abaixo  e mande salvar.



Repare no objetoTemp que é um objeto genérico, este objeto genérico pode receber qualquer propriedade que queremos. E podemos usa-lo para passar propriedades ao objeto Away3D.

Essas propriedades são definidas de acordo com a classe do Objeto que você esta criando, para saber quais propriedades podemos usar basta ir na documentação e ver as "propriedades públicas" da classe.

Classes não parecem mais fáceis agora?

Esse é um modelo usado em várias engines. Você pode passar valores atravéz de objetos criados na hora, ou passar objetos existentes nas classes como por exemplo:
view.camera.lookAt( chao,position );


Neste comando o objeto view possui um objeto camera que possui a função/metodo lookAt. lookAt pede um objeto como referência para onde apontar, mas o objeto tem que ser do tipo Number3D, o objeto chao possui um objeto position que é da classe Number3D.


Bom, esta é a hierarquia que temos agora. Observe os nomes dos objetos e a classe a que pertencem. Nesse diagrama o método lookAt aparece descrito com a classe Number3D entre parênteses, por isso na hora de apontar-lhe o alvo usamos: chao.position

E este método animLoop:
private function animLoop( e:Event ):void
       //renderiza view
       view.render();
}

Ele é um método que sera disparado a cada evento ENTER_FRAME (addEventListener( Event.ENTER_FRAME, animLoop ); ).
O uso de eventos é fundamental nos sistemas informatizados atuais, pois nem sempre as coisas vão ocorrer sequêncialmente.

No caso o ENTER_FRAME ocorre sempre que o player termina de processar os eventos do usuário, fazer uma coleta de lixo entre outras coisas, garantindo a sincronização entre os elementos do filme.

Ele tem apenas um comando (por enquanto) que chama o método render() do objeto view. Este método dispara o desenho de toda a cena 3D.


Bom você agora ainda se pergunta como isso tudo funciona? Há engines como a torque com seus métodos pre-definidos, mas na AS3 o único método iniciado automaticamente é o "contrutor da classe", o método Main() no nosso caso, que chama aquele outro método init():void.

TODO, código em programação é executado em seqência, mesmo que os eventos que o disparam sejam fora de ordem o código é sempre em linha reta. Em outros tutoriais veremos a ocorrência de código sendo executado mutuamente (o chamado paralelismo).


Lembram-se que na função init havia um comentário escrito "entry point", pois é, abaixo dele você irá chamar nossos métodos de inicialização!

private function init(e:Event = null):void
{
   removeEventListener(Event.ADDED_TO_STAGE, init);
   // entry point
   setupAway3D();
   create3DScene();
   addEventListener( Event.ENTER_FRAME, animLoop );
}

Neste momento seu código deve estar como este: http://pastebin.org/345497
E essa imagem deve estar aparecendo na tela XD


Nosso ator
Vamos carregar o ator agora, logo abaixo do chao (linha 96) colocamos:
Em componentes de cena acrescentamos:


private var ator:Loader3D; //objeto carregador
private var atorMesh:Mesh; //objeto com a malha


Agora na função create3DScene mandamos carregar o ator:
    //criamos o ator
    ator = Md2.load( "guerreiro.md2",
          { name:"ator", scaling:.18, loadersize:50,
             z:35, y:0, x:0, rotationY: -90 }
);
    //adiciona evento de carga ao ator
    ator.addOnSuccess( atorCarregado );
    view.scene.addChild( ator );

Nota 1: se o Flash Develop ainda não colocou o import ao Md2 adicione import away3d.loaders.Md2; nas linhas de import acima.
Nota 2: Lembra que usamos um objeto genérico para passar propriedades para o chão?
O trecho em destaque acima  faz a mesma coisa.

new Object()  é igual à {}

Não obrigatório mas bem prático: iremos adicionar um flag para indicar que o ator foi carregado e esta pronto.

//variaveis de controle
private var actorDone:Boolean = false;

Reparou lá em cima que adicionamos um evento de carga ao ator? Este é simplesmente um metodo na classe que irá funcionar quando ele estiver pronto na memoria.
private function atorCarregado(e:Event):void
{
     //obtem o objeto que cotrola a malha
     atorMesh = ator.handle as Mesh;
     //roda animação basica
     atorMesh.play( new AnimationSequence( "stand", true, true, 6 ) );
     //ativa ator
     actorDone = true;
     //sensor de teclas
     stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownListener );
     stage.addEventListener( KeyboardEvent.KEY_UP, keyUpListener );
}


A arquitetura da Away3D não oferece recurso para rodar animação pela classe Loader3D, mas essa classe oferece acesso à malha (classe Mesh) e essa sim tem o suporte a animação. Por isso a linha:
atorMesh = ator.handle as Mesh;
Em seguida rodamos a animação, para isso precisamos criar um objeto sequenciador e atrela-lo à nosso objeto mesh:
atorMesh.play( new AnimationSequence( "stand", true, true, 6 ) );

Estes parametros indicam:
stand = nome da animação (lembra da lista?)
true = suavizar a animação?
true = loop, sim

6 = A quantidade de frames por segundo


Nota: stand é uma palavra, quer dizer "parado". Uma palavra é uma sequência de caracteres e em informática chamamos uma sequência de caracteres de String.

Para que o compilador diferencie uma String do resto do código simplesmente a passamos com aspas, simples (') ou duplas (").

Depois adicionamos o monitoramento aos eventos de teclado.
Usamos o objeto stage como base para monitorar os eventos de teclado, este objeto é simplesmente TODO o seu swf portanto não haverá perigo de não monitorarmos a tecla devido ao foco em algum outro controle.


Movendo o personagem com o teclado
Insira estes imports no topo pra facilitar:
import flash.ui.Keyboard;
import flash.events.KeyboardEvent;

Vamos lembrar que esta é "uma" implementação. Adicionar interação pode ser feita de 1000 maneiras.
Focando em interação para jogos iremos usar o clássico modelo de controle direcional por teclas, de uma forma que apresente realismo.

Vamos às variáveis controladoras, iremos "tipar" elas assim:
private var moveFrente:Number = 0;
private var moveAngle:Number = 0;
private var kUp:Boolean = false;
private var kDown:Boolean = false;
private var kLeft:Boolean = false;
private var kRight:Boolean = false;

O "k" acima é pra abreviar "key" (tecla).

moveFrente e moveAngle são variáveis que podem variar de 1 à -1. É simples, ator tem os métodos moveForward e moveBackward mas se usarmos moveForward com um valor negativo ele anda para traz como se estivesse-mos usando moveBackward, então podemos usar apenas uma variável para controlar a velocidade.

private function movimento():void
{
    if ( kUp )
         moveFrente = 1;
    else if (kDown)
         moveFrente = -1;
    else
         moveFrente = 0;
    atorMesh.moveForward( moveFrente );
}
       
private function keyDownListener(ke:KeyboardEvent):void
{
    switch( ke.keyCode )
    {
        case Keyboard.UP:
             kUp = true;
             break;
        case Keyboard.DOWN:
             kDown = true;
             break;
    }
}

private function keyUpListener(ke:KeyboardEvent):void
{
    switch( ke.keyCode )
    {
         case Keyboard.UP:
              kUp = false;
              break;
         case Keyboard.DOWN:
              kDown = false;
              break;
    }
}

Em animLoop insira isso antes do view.render();
if (actorDone)
   movimento();


Este código extra em animLoop verifica se a variavel controladora (flag) indicando que o ator foi carregado é true e chama o método movimento() da nossa classe.

Em movimento nós trocamos o valor de moveFrente de -1 ou 1 conforme o estados das teclas pressionadas informadas por kUp e kDown e 0 (zero) se nenhuma das duas for pressionada.
E aplicamos o valor a moveForward.

Em keyDownListener e keyUpListener usamos um switch, essa estrutura vai ser bem eficiente mais para frente pois difere bastante do if, para setar os valores das variáveis de controle.

Estes métodos quando chamados como listeners (auditores) por padrão recebem um parametro do tipo de evento disparado. No caso um KeyboardEvent que como podem ver na documentação da classe tem todas as informações que precisamos.


Olhando ela assim aos pedaços a linguagem não parece ser tão "bicho de sete cabeças" né!?

Você esta lembrando de ao menos colar os exemplos e compilar? Se não nao esta aprendendo nada.


Ficou obvio para você que rotacionar o ator é pelo mesmo principio, né?
Um Ctrl+C e Ctrl+V no trecho de codigo de controle e uma mudança de variável apenas, a lógica é a mesma, so muda a variável e método:

Em movimento():
    //rotação
    if ( kLeft )
        moveAngle = -4;
    else if (kRight)
        moveAngle = 4;
    else
       moveAngle = 0;

                
    atorMesh.moveForward( moveFrente );
    atorMesh.rotationY += moveAngle;


Não temos exatamente um método rotate, mas sim a propriedade rotationX/Y/Z assim usamos um operador composto += para somar o valor de moveAngle direto no valor atual no eixo Y do ator.

Em seguida o switch (vou mostrar o código apenas de keyDownListener, depois é obvio que devem copiar o trecho em keuUpListener e mudar para false):

case Keyboard.LEFT:
       kLeft = true;
       break;
case Keyboard.RIGHT:
       kRight = true;
       break;


Alguem ja se perguntou porque estou usando moveForward ao invés de simplesmente setar X e Y como agente faz no 2D clássico?
Isto porque queremos que o ator se mova na direção que está sua "frente", e sua frente pode mudar de ângulo.
Como você deve saber quando você rotaciona um objeto no flash, como no desenho abaixo, e movimenta na tela, ele se move inclinado nos eixos X e Y e isso não é o efeito que desejamos.
A Away3D para facilitar nossa vida trabalha com métodos de manipulação de objetos no espaço que nos livra de pensar coisas como este diagrama.
moveForward, moveBackward, pitch, yam, roll
Estes métodos fazem uso de calculos bem complexos, que se vocês forem se aprofundar mais em Computação Gráfica irão se impressionar com a matemática que roda por de traz do monitor o tempo todo.

Nota: Os matemáticos levaram mais de 500 anos para desenvolver as formulas de projeção baseadas em matrizes, que é exatamente oque faz funcionar TUDO oque você está vendo de gráficos no seu computador. Veja bem, 500 anos é tempo pra cassete! Então não se desespere se não aprender nada de um dia pro outro, tenha paciência.




Notaram também que as propriedades/variáveis estão distribuidas pela classe?

Separei as de controle próximas aos métodos de controle, isto porque nossa classe é muito grande e assim facilita a leitura. Em projetos maiores usa-se na verdade um número maior de classes, justamente para facilitar o desenvolvimento e manutenção.


Bom, você até agora ainda se pergunta, porquê a animação não representa os movimentos?
Primeiro criamos uma variavel para indicar a animação corrente:

private var currentAnim:String = "stand";

Este não é o melhor jeito, é pegar do próprio objeto AnimationSequence. Mas é o jeito mais rápido no momento.

Depois o método:
private function trocaAnimacao( ani:String, loop:Boolean = true ):void
{
   //primeiro verifica se a animação corrente não é
   //a que esta sendo chamada, para não reiniciala
   if (ani != currentAnim)
  {
     //playa
     atorMesh.play( new AnimationSequence( ani, true, loop, 6 ) );
     //troca o valor da animação corrente
     currentAnim = ani;
  }
}

Agora adicionamos ao keyDownListener:
case Keyboard.UP:
       kUp = true;
       trocaAnimacao("run");
       break;
case Keyboard.DOWN:
        kDown = true;
        trocaAnimacao("run");
        break;

e ao keyUpListener:
case Keyboard.UP:
        kUp = false;
        trocaAnimacao("stand");
        break;
case Keyboard.DOWN:
        kDown = false;
        trocaAnimacao("stand");
        break;
Este método recebe o nome da animação  a ser exibida, e um opcional valor indicando se ela deve rodar em loop.
Se a animação corrente for diferente da que esta sendo passada ele troca a animação corrente.

Isto serve para evitar que a animação se reinicie atoa. Por exemplo ele ficar rodando o primeiro frame enquanto a tecla de andar estiver pressionada ou ficar pulando sem parar se você mandar pular.

Bom este é o código até agora: http://pastebin.org/354075

Simples, não?
Apesar de parecer complexo pelo tamanho que o código toma, tudo que fizemos foi usar instruções simples dot tipo: rode a animação de nome tal, suavemente e com 6 fps.


Devem ter notado outra coisa quando olharam o http://pastebin.org/354075: o código ficou totalmente organizado.
Em varios casos definimos as propriedades próximas aos métodos que as usam, separando a classe por setores dando mais legibilidade (isso não é obrigatório).

Muita coisa é flexível. No entanto uma coisa é fundamentalmente indispensável: indentar os blocos de código.
Ou seja, usar a tecla tab para alinhar os trechos de instruções dentro dos blocos { }. Observe que até a IDE faz isso por você. Sem issu pode ter certeza, você não vai entender NADA depois.

Reparou que no metodo trocaAnimacao nós fizemos loop:Boolean = true.
Isso diz à AS3 que o parâmetro loop é por default verdadeiro. Tanto que ao menos que tenhamos que passa-lo como false não precisamos indica-lo no código acima.

O toque de classe da programação: Refinamento

Agora, você vai encontrar um dos motivos que o trouxe aqui, a justificativa de saber programar o mínimo possível ser tão importante, quando poderíamos fazer apenas o "feijão com arroz". Não vamos nos contentar com o que temos e vamos tornar as coisas mais detalhadas. Vamos adicionar algumas forças fisicas (sem falar de fisica e de forma simples ).

O trouque aqui é parecer real e não ser real. Vamos as principais mudanças:
/**
 * estados de teclas de ação
 */
private var moveFrente:Number = 0; //movimentação atual
private var moveAngle:Number = 0; //angulação atual
private var moveFrenteT:Number = 0; //movimentação desejada
private var moveAngleT:Number = 0; //angulação desejada
private var upMSpeed:Number = 1; //aumento de velocidade
private const MSPEED:Number = 3; //velocidade linear
private const ASPEED:Number = 10; //velocidade angular
private const UPMSPEED:Number = 3.0; //aumento de velocidade


Agora estamos usando variáveis do tipo Number ao invés de Boolean para o controle. Isto nos permite variações de valores, no mundo real quando algo se move ou para, nunca é bruscamente, mas gradativamente. Então criamos um sistema de variáveis para simular essa aceleração e desaceleração.


Funciona assim:

variavel velocidadeAtual = valor real, que o objeto se encontra, positivo ou negativo.

variavel velocidadeDesejada = valor, que o objeto irá atingir, positiva ou negativa.

variavel diferenca = diferenca entre as velocidades atual e desejada

variavel aceleracao = um valor qualquer menor doque 1 e maior doque 0 positivos.

Ai oque fazemos é pegar o valor desejado e ver qual a diferença entre o valor atual da velocidade.

Se pegarmos essa diferença e aplicarmos ao valor atual, lógicamente teriamos a velocidade desejada de imediato.

Como oque queremos é simular a aceleração que ocorre na realidade, não podemos fazer isto. Então ao invés de aplicar a diferença de uma só vez aplicamos a variavel aceleracao acima descrita.

diferenca = velocidadeDesejada - velocidadeAtual (ex.: 20-10 =  10 )

diferenca = diferenca * aceleracao (ex.: 10 * 0.25 = 2.5 )

velocidadeAtual = velocidadeAtual  + diferenca (ex.: 10 + 2.5 = 12.5 )

Supondo que a aceleracao seja 2/3, iremos num primeiro frame aplicar 2/3 da diferênça da distância, no próximo frame a diferença é recalculada e será aplicado 2/3 disso. Agora já viu que a o passo dado irá sempre diminuir? Em outras palavras, quanto mais próximo do valor desejado mais devagar o movimento ocorre porque o 2/3 da distância será calculado infinitamente.

Será assim até o "ZERO lógico", que é quando o Flash não poderá lidar com o número de tão minúsculo que ficou sua fração e simplesmente assumira que é ZERO.

Em outras palavras esta é a equação:

A curva gerada por esse calculo é similar ao encontrado em softwares de animação.

Note que também começamos a usar const ao invéz de var, pra quem parou pra analizar a palavra, ja percebeu que const é usado para definir valores constantes, que não irão mudar. Constantes são acessadas mais rápidamente e são mais econômicas que váriaveis. Repare também que as escrevemos unicamente em caixa-alta (maiúsculas), por padrão.


Esta é a equação acima traduzida em AS3, já com alguns recursos de aprimoramento de performance.

private function movimento():void
{
   moveFrente += (moveFrenteT - moveFrente) * .2;
   moveAngle += (moveAngleT - moveAngle) * .5;

   atorMesh.moveForward( moveFrente * upMSpeed );
   atorMesh.rotationY += moveAngle * upMSpeed;
}

Dica: Sempre opte por usar multiplicação ao invés de divisão, isto deixa o código mais rápido Ex.: x*0.2  ao invés de x/5

Tembem repararam no uso de upMSpeed? upMSpeed é uma variável que setamos quando CTRL ou SHIFT estão pressionadas e aumenta a velocidade fazendo o char correr.


Dica: Quer usar esta classe dentro do Flash e não no FlashDeveloper?

Salve seu FLA na pasta, clique no seu palco (ou clique em ESC para não selecionar NADA no Flash) e na barra de propriedades tem um campo para se digitar a Base-Class do seu filme. Basta digitar Main ai e pronto.

Emfim este deve ser seu código: http://pastebin.org/355341

E agora você tem tudo que queria saber!
Brinque com os valores, e nos próximos tutorial iremos aprender a "pular"! E iremos fazer um trenzinho andar num trilho (paths 3d).
Agora, chegamos mesmo ao fim deste ultra longo tutorial inicial.
Nos veremos em outros.

E se quiserem compartilhar com um link ou um twitter agradecemos  ^_^

Link encurtado deste tutorial: http://vqv.me/04X

Notas finais
Bom, deu trabalho. Beeem mais do que eu esperava, mas eu ja sabia disso de outros tutoriais que montei. Este durou mais de uma semana de montagem!

Eu já havia cogitado pelos relatórios do Analitycs sobre oque os usuários vêem buscar neste blog. Mas já recebi meu segundo e-mail pedindo informações sobre as tecnologias em uso aqui.

Os trechos extras explicando detalhes também se aplicam a usuários sem conhecimento de lógica de programação, e iniciantes. Pois o público que procura pelo 3D em Flash é muito variado.

Foi uma barra montar o tutorial, só o CSS demorei umas 2 horas acertando e ainda não tá 100% mas a ideia de usar as imagens de fundo para cada chamada é extremamente necessária para dividir a leitura entre "dicas e básico", "mãos à obra" e "Massetes e detalhes mais complexos".
Depois os links externos e as re-lidas!!!
Os designers vão amar ter mais um para rechaçar pelo uso excessivo de cores na página.