Movimento Personalizado de Fast Loops - Parte 1

Parte 1: Movimento de Oito Direções

Introdução:
Nesta aula, vamos falar sobre uma forma especial de se fazer um Movimento Personalizado: utilizando Fast Loops. Apesar de ser uma das últimas aulas do nível intermediário, não significa realmente que seja difícil. Não, não é. Mas as fórmulas usadas (sim, vamos usar fórmulas) são um pouco chatas de se entender, e você precisará captar a essência dela. O que isso significa? Significa que você precisa realmente entender para o quê ela serve, a fim de poder usá-la novamente depois, sem ter de recorrer à anotações.

Até agora, já fizemos isso com fórmulas simples (X("Active)+2 etc). Já trabalhamos bastante expressões no MMF2. Não acho que vocês terão problemas para fazer esse movimento.

O método foi desenvolvido por David Newton, e disponibilizado no site oficial da ClickTeam. Deixo aqui meus sinceros agradecimentos a ele. Agradeço também ao Hazael, que me falou sobre esse tipo movimento.

Começaremos com um modelo em 8 direções, a fim de entender a base dele. Então, partiremos, na aula seguinte, para a movimentação em plataforma propriamente dita. O que não significa que você não possa utilizá-lo para um jogo de RPG.

Preparando o Terreno:
Durante esta aula, iremos precisar dos seguintes objetos: um Active Object totalmente preenchido por uma única cor (de qualquer tamanho, mas vamos adotar, para essa aula, 32x32), o qual chamaremos de MC, e atuará como uma Máscara de Colisão (ou Collision Mask, explicarei daqui a pouco); alguns obstáculos, posicionados aleatoriamente no Frame, para testar as colisões.


Frame com alguns obstáculos e um Active Object totalmente preenchido por uma única cor.

Vá nas propriedades do MC (o Active totalmente preenchido com uma única cor), e vá para a aba Values. Aqui, iremos nomear os dois primeiros Alterable Values, por questão de organização. Clique duas vezes no botão New, embaixo de Alterable Values. Dê um duplo clique sobre Alterable Value A, e renomeie para MovX, depois, dando OK. Faça o mesmo com o Alterable Value B, mas, dessa vez, renomeie para MovY.


Variáveis nomeadas.

A) Máscara de Colisão:
Ainda há pouco, falei para vocês sobre uma tal Máscara de Colisão, certo? Bem, uma Máscara de Colisão é um objeto (Active, no caso de MMF2) responsável por detectar onde o personagem à ela relativo deve colidir.

O que isso significa? Significa que não programaremos o movimento do personagem em si, e sim, de um Active totalmente preenchido por uma única cor. Daí, usaremos um outro objeto para as animações do personagem, programaremos as animações e faremos com que sempre fique no mesmo lugar da Máscara, ao mesmo tempo que a deixamos invisível. Ou seja, o personagem controlado será formado por dois objetos: a Máscara de Colisão e as Animações.

Com isso, concluímos que, quando formos fazer um jogo pra valer, a Máscara de Colisão deve cobrir a área em que o personagem deve colidir.

Dois exemplos de Máscaras de Colisão. Perceba que no exemplo da direita, a Máscara de Colisão do Zero não o cobre totalmente.

Tenha em mente que o tamanho das animações mudam; o da Máscara de Colisão, não. Sendo assim, pode ser que, se você tomar como base a animação principal (personagem parado), pode ser que, em algumas animações, talvez pareça que ele "atravesse" um pouco os obstáculos. E, dependendo da forma com que você posiciona as animações, isso também pode acontecer.

Por isso, fique ligado no HotSpot. Lembre-se que o posicionamento leva em consideração o HotSpot; ou seja, ao colocar um Active 2 sempre se posicionando na posição 0 em relação ao X, e 0 em relação ao Y, ambos em relação a um Active 1, isso significa que o lugar onde está o HotSpot do Active 2 ficará em cima do lugar onde está o HotSpot do Active 1. Ou seja, se você colocar o Hotspot da Máscara de Colisão na extremidade inferior central, deverá colocar o HotSpot das animações no pé do personagem, sempre. A não ser que você queria que o personagem fique enterrado no chão até o peitoral. Ou flutue, se colocar muito embaixo.

Particularmente, acho que isso dá um efeito legal no jogo (obviamente, apenas um pouco, não faço com que o personagem fique enterrado até o peitoral como acabei de falar), então, muitas vezes, faço intencionalmente.

Basicamente, a Máscara de Colisão no Movimento Personalizado de Fast Loops é o que seriam os Detectores de Colisão no Movimento Personalizado Tradicional.

Programando o Movimento:
Chegou a hora! Vamos programá-lo com aceleração mesmo. No ponto onde estamos, isso não será problema. Considerarei que todos aqui tenham uma versão antiga do MMF2. Entretanto, que não chegue a ser arcaica; pelo menos tenha Fast Loops. Apesar de não ter certeza, ouvi dizer que em versões muito antigas, o MMF2 não tinha Fast Loops embutido nele. Acredito que a build 248 do programa sirva.

O nosso movimento é dividido tanto na movimentação horizonta, quanto na vertical, em duas partes: Manipulação de Variáveis e Loops de Movimentação. Acho que isso é tudo. Agora, vamos aos eventos!

A) Movimento Horizontal:
Aa) Manipulação de Variáveis:

Como o movimento é horizontal, iremos definir valores para o eixo X. O eixo X será definido através da variável MovX. Logo, aqui, manipularemos essa variável. Façamos, então, a aceleração:

Repeat while ("Player 1") Moved right
X Repeat while ("Player 1") Moved left
MovX of ("MC") < 3
Set MovX to MovX("MC")+0.25

Repeat while ("Player 1") Moved left
X Repeat while ("Player 1") Moved right
MovX of ("MC") > -3
Set MovX to MovX("MC")-0.25

Aceleração. No ponto onde estamos, você já deve saber interpretar tal evento corretamente. Se não souber, é bom dar uma lida de novo nas aulas passadas. Mas vou dar uma colher de chá, e explicar cada evento.

No primeiro, se o jogador 1 estiver pressionando para a direita, não estiver pressionando para a esquerda (a fim de evitar conflitos ao apertar as duas teclas simultâneamente) e a variável for menor que 3, adicionaremos 0.25 à variável, fazendo com que ela vá atingindo o valor da velocidade do nosso movimento gradualmente. O valor 0.25 é a aceleração (variação de velocidade, ou seja, quanto a velocidade irá variar no decorrer do tempo). Isso significa que, quanto menor o valor, mais o móvel vai demorar para atingir a velocidade 3. Você pode retardar essa meta também adicionando uma condição Every, e especificando uma certa quantidade de centésimos de segundo. Nesse caso, você estará apenas fazendo com que a velocidade varie de forma mais lenta. Mas o resultado será o mesmo.

No segundo evento, a mesma coisa, só que para a esquerda. Perceba que, aqui, subtraíremos 0.25 da variável se o jogador estiver pressionando para a esquerda, e o valor dela for MAIOR que -3. Eixo X negativo = movimento para a esquerda, como já vimos inúmeras vezes.

Aliás, lembra que eu disse que iria tomar medidas para que os com a versão mais antiga de MMF2 não fossem prejudicados? Pois é. Vocês devem ter notado (se não notaram, ou são muito desatentos, ou realmente não interpretaram a formula corretamente, e acredito que, nesse segundo caso, não estejam preparados para ver esse movimento) que eu adicionei e subtraí os valores da variável com a ação Set. Mais uma vez, me disseram que em versões mais antigas do MMF2, não existiam as funções Add e Subtract. Mas para quem tem o MMF2 com essas funções, podem usar, não há diferença alguma.

Agora, vamos para a desaceleração:

X Repeat while ("Jogador 1") Moved right
X Repeat while ("Jogador 1") Moved left
MovX of ("MC") > 0
Set MovX to MovX("MC")-0.25

X Repeat while ("Jogador 1") Moved right
X Repeat while ("Jogador 1") Moved left
MovX of ("MC") < 0
Set MovX to MovX("MC")+0.25

Existe outra forma, mais rápida, de se fazer desaceleração, mas por ser mais complexa (usa uma fórmula que não é difícil, mas é chata pra entender) e obrigar o programador a utilizar o mesmo valor para a desaceleração dos dois lados (o que é o normal, mas talvez, por circunstâncias especiais, o jogo precise que o personagem desacelere com velocidades diferentes para cada lado), eu preferi utilizar a forma padrão. Quem quiser dar uma olhada, adicionei dentro de um grupo desativado no exemplo. A ideia é bem simples, vocês vão entender só de olhar.

Mas voltando ao assunto... no primeiro caso, se o jogador não estiver pressionando para a direita, nem para a esquerda, e a variável estiver maior que 0 (ou seja, andando para a direita), iremos diminuir 0.25 da variável gradualmente, fazendo com que ela volte a ser 0, e o móvel fique em repouso.

No segundo caso, é o oposto: se o jogador não estiver pressionando para a direita, nem para a esquerda, e a variável estiver MENOR que 0 (ou seja, andando para a esquerda), iremos ADICIONAR 0.25 da variável gradualmente, fazendo com que ela volte a ser 0, e, então, o móvel fique em repouso.

Ab) Loops de Movimentação:
Terminamos de manipular as variáveis do eixo X. Agora, é que a coisa começa a esquentar. Faremos o personagem movimentar-se através de loops, de acordo com o valor da variável. Vamos fazer os eventos, e eu explico.

MovX of ("MC") <> 0
Start loop "MovX" Abs(MovX("MC"))

Aqui, iniciaremos um loop, chamado MovX (o nome do loop deve ficar entre aspas no MMF2), e ele será repetido na mesma quantidade do módulo da variável. A função Abs() faz com que o programa utilize o módulo do valor expresso entre os parênteses (no caso, a variável). Isso porque um número de repetições não pode ser negativo. xD

Por que iniciamos um loop? Você entenderá agora. Faça o seguinte evento:

On loop "MovX"
(MC) Set X position to X( "MC" )+(MovX( "MC" )/Abs(MovX( "MC" )))

Ou seja, enquanto o loop estiver sendo executado, a posição X da Máscara de Colisão será mudada para o valor expresso pela fórmula. Antes de eu explicar o que ela faz, tentem interpretá-la sozinhos. Então, prossiga a leitura. Essa fórmula faz com que ele adicione +1 ou -1 à posição X atual da Máscara de Colisão, dependendo do valor atual da variável. Veja bem, ele divide o valor da variável pelo seu módulo. Assim obtemos 1, de acordo com o sinal da variável.
Exemplos:
• Se a variável for 2: (+2)/(+2) = +1.
• Se a variável for -2: (-2)/(+2) = -1.
Dessa forma, ele descobre se a variável deve fazer o personagem andar para a direita ou para a esquerda, e o movimenta de acordo com ela.

O movimento em si já está pronto. Você pode testar seu projeto, e ver que a Máscara de Colisão já se move na horizontal. Mas ela ainda não colide... vamos, então, para o evento das colisões:

On loop "MovX"
("MC") is overlapping a backdrop
(MC) Set X position to X( "MC" )+((MovX( "MC" )/Abs(MovX( "MC" )))*(-1))
Set MovX to 0
Stop loop "MovX"

Interpretemos o evento, então. Se enquanto o loop MovX estiver sendo executado, a Máscara de Colisão estiver sobrepondo um obstáculo, a posição dela será definida para o valor expresso pela fórmula, a variável será definida para 0, e o loop será interrompido. Para os mais atentos, a fórmula é extremamente parecida com a do evento anterior. Ela expressa nada mais, nada menos, do que o oposto da divisão entre o valor da variável e seu módulo. Usem a cabeça... se essa divisão expressa o valor que deve ser adicionado à posição X para que o corpo se mova no sentido correto, o que seu oposto expressa...? Isso mesmo, o sentido oposto! Ou seja, enquanto o móvel estiver tentando se mover contra um obstáculo, ele irá anular o evento anterior, entrando em repouso.

Por exemplo, considere que a variável fosse +3. Sabemos que conforme o loop vai se repetindo (no caso, 3 vezes, pois o módulo de +3 é 3), vai sendo adicionado +1 à posição do móvel (pois programamos para que a posição fosse adicionada de acordo com o sinal da variável, e, nesse caso, a variável é positiva). Enquanto este móvel estiver colidindo com um obstáculo, ele irá adicionar à posição o oposto do que programamos anteriormente. Ou seja, -1. Agora, calculem: (+1)+(-1) => 1-1 = 0. O corpo entra em repouso.

Estas três ações juntas são responsáveis pela colisão. A principal é a de mudar a posição para o valor expresso pela fórmula, mas definir a variável para 0 e interromper o loop, evita que o personagem "trave" ao colidir.

Teste seu projeto agora. Se você fez tudo certo, ele deve estar colidindo.

B) Movimento Vertical:
É a mesma lógica dos movimentos horizontais. Mas lembre-se que, aqui, a trajetória é orientada para baixo, diferentemente de um Plano Cartesiano matemático normal. Ou seja, o eixo Y é invertido: valores positivos fazem o corpo ir para baixo, e, valores negativos, ir para cima.

Trabalharemos, agora, com o eixo Y; consequentemente, com a variável MovY.

Ba) Manipulação de Variáveis:
Vamos fazer a aceleração:

Repeat while ("Jogador 1") Moved down
X Repeat while ("Jogador 1") Moved top
MovY of ("MC") < 3
Set MovY to MovY("MC")+0.25

Repeat while ("Jogador 1") Moved top
X Repeat while ("Jogador 1") Moved down
MovY of ("MC") > -3
Set MovY to MovY("MC")-0.25

Não vou me demorar muito aqui. Mas perceba que no primeiro, para descer, verificamos se a variável é menor que 3, e somamos, pois como eu já disse, a trajetória é orientada para baixo. E no segundo, verificamos se a variável é maior que -3, e diminuímos.

Desaceleração:

X Repeat while ("Jogador 1") Moved top
X Repeat while ("Jogador 1") Moved down
("MC") MovY > 0
Set MovY to MovY("MC")-0.25

X Repeat while ("Jogador 1") Moved top
X Repeat while ("Jogador 1") Moved down
("MC") MovY < 0
Set MovY to MovY("MC")+0.25

Na desaceleração, fazemos com que a variável volte gradualmente a 0, de acordo com o sentido da trajetória (para baixo se a variável for positiva e para cima se for negativa), caso o jogador não esteja pressionando nem para cima nem para baixo.

Bb) Loops de Movimentação:
Vamos fazer os loops:

MovY of ("MC") <> 0
Start loop "MovY" Abs(MovY("MC"))

Se a variável for diferente de 0, o loop se iniciará, repetindo-se na mesma quantidade do módulo da variável.

Agora, movimentação:

On loop "MovY"
Set Y position to Y( "MC" )+(MovY( "MC" )/Abs(MovY( "MC" )))

Enquanto o loop estiver sendo executado, ele irá mover a Máscara de Colisão de acordo com o sentido proposto pela variável MovY.

Por fim, colisões do Movimento Vertical:

On loop "MovY"
("MC") is overlapping a backdrop
Set Y position to Y( "MC" )+((MovY( "MC" )/Abs(MovY( "MC" )))*(-1))
Set MovY to 0
Stop loop "MovY"

--//--

E... pronto! Fizemos um Movimento Personalizado de Fast Loops, de oito direções! O movimento é perfeito; pelo menos, eu não encontrei bug algum. Com as minhas explicações colossais, e até desnecessárias, o tutorial ficou bem extenso. Mas, na verdade, esse tipo de movimento é mais curto que o tradicional. E, depois que você pega o jeito, fica bem fácil; e bem mais rápido. Na próxima aula, veremos o movimento de plataforma, que é ainda mais rápido que este.

Caso tenha alguma dúvida, ou não conseguiu fazer algo corretamente, dê uma olhada neste exemplo.

Bom aprendizado. ;)
Flw.