Jogos em 3D no Instagram Javascript ou rota de salsicha



Após o primeiro artigo sobre programação de jogos em máscaras no Instagram, um cliente me pediu para criar um jogo no Instagram para sua pizzaria. Este jogo foi planejado para ser usado para fins de promoção de negócios. Claro, eu entendo que, a julgar pelo número de visualizações do primeiro artigo, o tema do Instagram não é particularmente interessante para a comunidade Habr. Aparentemente, esse serviço ainda é considerado algum tipo de entretenimento frívolo para as loiras, e um artigo sobre ele é adequado apenas como leitura de sexta-feira para um coquetel à noite. No entanto, hoje, apenas sexta-feira. Pegue um coquetel. E não se esqueça de convidar loiras. No entanto, é provável que no futuro a tecnologia atinja tais alturas que começaremos a jogar no Instagram no GTA-5. Ou 10.

Este artigo discutirá dois jogos ao mesmo tempo. Fiz um para pedir e depois o segundo para mim. A cada novo jogo, eu me deparava com novas tarefas, a busca de soluções para as quais me familiarizei com as novas nuances do desenvolvimento. Talvez essa história seja útil para outros desenvolvedores de elementos de realidade aumentada. Os jogos são escritos em Javascript puro, sem o uso de bibliotecas adicionais. Para exibir gráficos 3D, as ferramentas internas do Instagram são usadas.

Jogo 1. Pizza


Então, qual é a essência do primeiro jogo. Pizza girando. Os componentes voam de sua superfície - pedaços de linguiça, tomate e assim por diante. Dois jogadores devem pegar sua boca. O vencedor é aquele que, como você pode imaginar, vai pegar mais. Com o tempo, a velocidade de rotação aumenta. O mais engraçado neste jogo para mim foi programar as rotas de vôo desses elementos comestíveis. Imagine programar um vôo de salsicha. Não, definitivamente, nunca me diverti tanto com programação. Eu ri agora mesmo, escrevendo este artigo.

Quando o cliente viu o protótipo em funcionamento, ele me enviou o seguinte: “:))”. Houve um problema ao testar o resultado final. Consistia no fato de que os sujeitos não podiam jogar: estavam simplesmente rindo - foi muito divertido.

Deixe-me lembrá-lo, o desenvolvimento de máscaras e jogos é feito no Spark AR Studio. A partir daí, você pode enviar seu trabalho no Instagram para que todos possam ver. Vale ressaltar que as tecnologias da web obscurecem os limites entre sistemas operacionais para um desenvolvedor. Você escreve um código, que funciona no aplicativo Instagram para iOS e Android, sem modificações ou alterações. Obviamente, o pagamento por isso se torna uma velocidade de script fundamentalmente baixa.

O cronograma do jogo foi fornecido pelo cliente. Houve algumas dificuldades com os modelos 3D. Em particular, ao usar o mecanismo de animação interno Spark AR Studio, descobriu-se que, se o objeto em movimento é dimensionado, suas coordenadas são determinadas incorretamente no jogo. Mas esse efeito irritante não é observado se você não dimensionar completamente o objeto inteiro, mas cada uma de suas malhas separadamente. Eu tive que deixar a escala da pizza 1: 1 e prescrever um certo coeficiente para cada elemento que a produz. Também houve problemas com o formato FBX para o qual você precisa exportar modelos para um jogo do Instagram. O designer gráfico enviou modelos com texturas embutidas, que, além disso, foram colocadas ao longo de um caminho relativo. O editor 3D os viu, mas o Spark AR não. Eu tive que reembalar os modelos para que os arquivos de textura estivessem separados dos modelos e ao longo de um caminho com eles.

E outro pequeno problema que encontrei foi que o objeto api Saprk AR responsável por exibir o texto na tela se recusou a aceitar o valor como um valor - uma pontuação do jogo, por exemplo. Durante muito tempo, não entendi por que nada é exibido. O que estou fazendo errado? Aconteceu que você primeiro precisa converter os números em strings (.toString ()). Isso é desnecessário para outras línguas, mas atípico para javascript, que sempre fez isso por si só.

Animação simples


Uma das novidades para mim neste jogo foi programar a animação de objetos 3D. (No meu jogo anterior do Instagram “Tic-Tac-Toe”, não havia animação). O mecanismo de animação no Spark AR Studio é muito específico. São necessários alguns parâmetros de entrada e, em seguida, conecta reativamente a variável ao objeto no qual você deseja alterar alguma coisa. Por exemplo, isso alterará a coordenada y (startValue, endValue - seus valores iniciais e finais) de algum objeto 3D ao longo do tempo t:

var driverParameters = {
    durationMilliseconds: t,
    loopCount: Infinity,
    mirror: false
};
var driver = Animation.timeDriver(driverParameters);
var sampler = Animation.samplers.linear(startValue, endValue);
sceneObject.transform.y = Animation.animate(driver, sampler);
driver.start();

Para mover os ingredientes da pizza no espaço, decidi simplesmente executar três dessas animações em paralelo para cada coordenada. Foi o suficiente para indicar a coordenada inicial (startValue) e a coordenada final calculada pelo ângulo de rotação da pizza (endValue), de modo que ela estivesse em algum lugar distante, caso o jogador não pegasse essa “concha” com a boca. Se travado - então o movimento para. O evento de abertura da boca que eu já descrevi em um artigo anterior. Somente aqui está um jogo para dois e, portanto, já haverá duas faces e duas bocas:

FaceTracking.face(0).mouth.openness.monitor().subscribe(function(event) {
    if (event.newValue > 0.2) {
        ...
    };
});

FaceTracking.face(1).mouth.openness.monitor().subscribe(function(event) {
    if (event.newValue > 0.2) {
        ...
    };
});

O ponto principal deste jogo é pegar ingredientes voadores com a boca, ou seja, encontrar um objeto voador em uma determinada área pequena ao redor do centro da boca de uma pessoa. No início, esse cálculo não queria ser feito corretamente para mim, mas uma solução para o problema foi encontrada após a introdução de um objeto oculto vinculado às coordenadas da boca e funcionou. Por alguma razão, as coordenadas diretas da boca não retornaram. Aqui cameraTransform.applyTo é a redução das coordenadas dos pontos de face às coordenadas no mundo 3D (o método é retirado da documentação).

move: function() {
    //   (   )

    //    
    var object = Scene.root.find('pizzafly');
    var olast = {
        x: object.transform.x.pinLastValue(),
        y: object.transform.y.pinLastValue(),
        z: object.transform.z.pinLastValue()
    };

    //   ,       
    var objectHidden = Scene.root.find('nullObject');
    objectHidden.transform.x = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).x;
    objectHidden.transform.y = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).y;
    objectHidden.transform.z = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).z;

    //   
    var mouth = {
        x: objectHidden.transform.x.pinLastValue(),
        y: objectHidden.transform.y.pinLastValue(),
        z: objectHidden.transform.z.pinLastValue()
    };

    //    
    var d = {
        x: Math.abs(olast.x - mouth.x),
        y: Math.abs(olast.y - mouth.y),
        z: Math.abs(olast.z - mouth.z)
    };

    //  
    if ((d.x > 0.03) || (d.y > 0.03) || (d.z > 0.03)) {
        // 
        ...
    } else {
        // 
        ...
    };

},

Posteriormente, percebi que você deveria remover a verificação de profundidade (coordenada z), pois nesse jogo em particular é visualmente difícil estimar a profundidade. Ou seja, agora tornou-se possível pegar o ingrediente abrindo a boca a qualquer momento durante o voo. O principal é a combinação de x e y.


Finalmente, o último problema que encontrei ao escrever este jogo foi limitar o tamanho da versão final para 4 MB. Além disso, de acordo com a recomendação do Facebook, para que o jogo seja exibido no maior número possível de dispositivos móveis, é aconselhável caber até 2 MB. E os modeladores em 3D são pessoas criativas e querem criar modelos pesados ​​com uma grade densa e texturas enormes, completamente sem se importar conosco, programadores, ou melhor, com o desempenho final dos jogos. Também reduzi de alguma forma as texturas em tamanho e as comprimi em jpg (em vez de png), mas o próprio modelo de pizza teve que ser enviado para revisão (retopologia). Como resultado, no entanto, foi possível caber no volume de 2 MB com todos os modelos, texturas e um script. E o jogo continuou com moderação.

E depois de algum tempo, ela voltou com a redação de que "o efeito contém muito texto estático". Eu tive que remover os números da contagem regressiva e, em vez deles, definir a animação para a seta no cronômetro, que agora começou a contar o tempo em vez deles. Depois disso, o jogo foi aprovado.

Jogo 2. Sobre a borboleta (ButterFlap)



Agora vou falar sobre a criação de um segundo jogo. Não posso deixar de fazer jogos, esse é o meu hobby. Alguns dias antes de 8 de março, decidi criar um jogo no Instagram para este feriado. Algo sobre flores, borboletas e doces. Mas não conseguia pensar em qual poderia ser a essência do jogo. O pensamento girou em minha cabeça. Talvez a borboleta colete doces e os coloque em uma cesta? Ou talvez as borboletas apanhem doces no ar e os joguem, e o jogador precisará pegá-los com a boca? Em geral, eu golpeei por alguns dias e, não encontrando uma solução, percebi que, em 8 de março, meu jogo ainda não teria tempo para passar pela moderação, já que o último leva 3-4 dias. Eu já queria sair desse empreendimento, quando de repente a ideia surgiu por si só, de repente. Eu não amarrei mais o jogo no Dia da Mulher, agora era apenas um jogo.

Scroller. Da esquerda para a direita, a paisagem e vários obstáculos se movem. O jogador deve controlar uma borboleta que possa se mover livremente na tela e coletar diamantes pendurados no ar. Da direita para a esquerda, um pouco mais rápido que o resto da imagem, as nuvens estão se movendo, das quais a chuva está caindo. Você precisa se esconder da chuva sob as flores. Se uma borboleta cai na água, suas asas se molham e ela cai, este é o fim do jogo. Eu chamei o jogo simplesmente: ButterFlap.

Sim, tudo isso parece bom. Eu, diretamente, queria me jogar. Mas lembrei que não era um artista. No entanto, sou capaz de criar modelos 3D, e isso é mais fácil do que desenhar. Então, foi decidido, que houvesse um scroller 3D.

Artes gráficas


Encontrei modelos on-line isentos de royalties que precisava refinar. Ele puxou as texturas para aquelas que não tinham texturas, tendo previamente colocado o último em um atlas de textura: artefatos na forma de “escadas” são visíveis em modelos sem textura e a suavização pode ser definida para texturas no jogo, o que torna a aparência dos objetos mais apresentáveis ​​e não tão planos. Esses modelos que continham texturas também tinham suas falhas que precisavam ser corrigidas. Em primeiro lugar, o tamanho das texturas não era múltiplo de dois no poder. Ou seja, por exemplo, poderia ser igual a 1200x1200. Eu tive que comprimir para 1024x1024. Os processadores de vídeo podem dimensionar ou preencher texturas inadequadas com espaço vazio, ou seja, aquelas que não são 1024x1024, 512x512, 256x256 etc. De qualquer forma, essas ações são uma carga extra durante o trabalho do jogo e um consumo de memória sem sentido,portanto, é melhor preparar as imagens corretas primeiro manualmente. A situação também foi salva pelo fato de eu distribuir todas as texturas por atlas de textura; portanto, se, por exemplo, a textura original tinha tamanho de 400x200 pixels, eu poderia colocá-la no atlas 1024x1024, como é, ao lado de outras similares. Depois disso, é naturalmente necessário escalar também a varredura UV, mas isso é feito em alguns segundos. Ainda deparamos com variantes de modelos onde, por algum motivo, as texturas foram amarradas ao longo de um caminho absoluto, como "C: \ Work \ Vasya \ Map.jpg". Bem, não existe essa pasta no meu computador! Eu tive que especificar os caminhos para as texturas manualmente ... Mas por que você teve a idéia de que eu deveria manter todos os meus projetos no drive “C:”!? Ah, esses modeladores, artistas livres ... A propósito, pelos nomes das pastas nos caminhos deixados aleatoriamente, você pode inadvertidamente aprender mais sobre a identidade do modelador,por exemplo, o nome dele. Prazer em conhecê-lo!

O mais difícil foi encontrar um modelo de borboleta adequado com animação. O Spark AR usa animação que pode ser exportada de qualquer editor 3D para o formato FBX. As asas de um par de modelos de borboleta que eu baixei eram de algum modo estranhamente espelhadas - uma asa foi modelada e a segunda foi refletida de alguma maneira que eu não entendi e, portanto, duas delas apareceram. Com essa abordagem de modelagem, como resultado, no jogo, uma das asas (copiada) não queria receber luz da fonte e sempre permaneceu fraca. Não corri o risco de mudar drasticamente o modelo, porque a animação teria voado. E na animação eu sou um noob ainda maior do que na modelagem 3D. Talvez o problema fosse outra coisa: tentei, por exemplo, expandir os normais, mas isso não ajudou. Em suma, os modelos 3D gratuitos são uma dor. Como resultado, depois de ter lavado a noite toda,por tentativa e erro, encontrei um modelo adequado que, depois de algum ajuste fino em um arquivo, começou a parecer satisfatório. Algo que eu não gostei, mas foram pequenas coisas: refiz a textura, mudei a geometria em alguns lugares e ajustei os parâmetros do material. Finalmente, a borboleta decolou. Viva. Mas agora estou exausta. Então eu decidi ir para a cama e continuar no dia seguinte. Sim, passei 7-8 dias na criação do jogo. Mas era um trabalho tão tranqüilo à noite, lendo documentação, artigos e encontrando respostas para perguntas.Então eu decidi ir para a cama e continuar no dia seguinte. Sim, passei 7-8 dias na criação do jogo. Mas era um trabalho tão tranqüilo à noite, lendo documentação, artigos e encontrando respostas para perguntas.Então eu decidi ir para a cama e continuar no dia seguinte. Sim, passei 7-8 dias na criação do jogo. Mas era um trabalho tão tranqüilo à noite, lendo documentação, artigos e encontrando respostas para perguntas.


Durante toda a noite, no dia seguinte, trabalhei em gráficos. A especificidade das máscaras de jogo para o Instagram, como já mencionei, é que é aconselhável não exceder o volume de 2 MB para o jogo inteiro (máximo de 4 MB). Eu não estava preocupado com o script: é improvável que seu tamanho exceda 50 Kb. Mas em modelos 3D de plantas teve que ser bastante conjurado. Por exemplo, a parte do girassol onde as sementes estão localizadas foi feita por geometria. Centenas de polígonos ... Substitua-o por um fragmento de uma esfera de algumas dúzias de triângulos e estique a textura baixada, de fato, desta parte. O número de folhas também pode ser reduzido. Fazemos grama no fundo da cena com um plano de dois triângulos com uma textura sobreposta com um canal alfa. Alcançamos o volume com sombras e copiando fragmentos da imagem dentro da própria textura.

Em geral, é desejável minimizar o número de texturas, bem como o tamanho em bytes. São as texturas que criam a maior parte do jogo. Estou acostumado a texturas onde a transparência é necessária, colocada em um atlas de textura - em png de 32 bits, e texturas que não usam o canal alfa, empacotadas em outro atlas, salvando-as em jpg. Jpg pode ser encolhido mais forte que png. Os elementos Grass e UI que precisam de transparência foram para o atlas png e tudo o mais para jpg.

O volume total. No total, eu tenho 4 tipos de plantas, uma pedra, uma borboleta, que o jogador controlará e uma nuvem. As texturas de todos esses modelos na forma compactada levaram 2 atlas 1024x1024 jpg e png com um volume total de 500 Kb. Os próprios modelos levaram cerca de 200 Kb. Além de um script. Sons - 31 Kb. Total: cerca de 1 Mb. Em verde, o tamanho da compilação é apenas exibido (é o que deve caber em 2 MB).


De repente, encontrei um problema completamente inesperado. Excluí vários modelos não utilizados, mas não através do Spark AR Studio, mas do sistema de arquivos. Posteriormente, ao montar a construção, descobri que eles desapareceram da cena do Spark AR Studio, mas ainda entraram na montagem. E limpar o projeto de recursos não utilizados é impossível. Os links para eles do "Resumo do ativo" não levam a lugar algum. Aparentemente, esse é um defeito no Spark AR Studio. Eu tive que recriar o projeto novamente, adicionando todos os recursos necessários desde o início.

Cena


Estou pensando há muito tempo em como implementar a rolagem de todos os objetos na tela. Das ferramentas para isso, existe apenas javascript e o mecanismo de animação interno mais simples do Spark AR Studio, que pode simplesmente alterar as coordenadas do objeto do valor inicial para a final em um determinado momento.

Sim, o dilema ainda surgiu antes: criar um nível inteiro do jogo, uma cena em um editor 3D, duplicando as plantas repetidas o número necessário de vezes e colocando-as nas posições corretas, para rolar a cena inteira no jogo ou carregar apenas uma cópia de cada 3D objeto e substitua-os na direção do jogador fora da tela. A resposta foi óbvia. Nossa escolha é a segunda opção. Caso contrário, é impossível caber em 2 MB: provavelmente, a cena de acordo com a primeira opção será pesada. Mas então você precisa de um layout de objetos. E eu, sem hesitação, decidi usar o editor 3D como um editor de níveis. Sim, acontece que eu fiz as duas coisas. Plantas para o jogo, eu salvei cada uma em uma cópia. E do editor eu precisava apenas das coordenadas. Depois de concluir o trabalho,Escrevi as coordenadas de todos os objetos e fiz uma matriz com os dados do jogo.

lv:[
    {n:'flower1', x:8.0, y:-6.5},
    {n:'cloud', x:10.0, y:0.1},
    {n:'rain', x:10.0, y:6.6},
    {n:'flower1', x:14, y:-2.5},
    {n:'diamond_red', x:20, y:2.0},
	...
],

E ainda - uma matriz associativa separada, de acordo com os tipos de objetos em que os tamanhos dos coletores e seus deslocamentos em relação aos centros dos objetos 3D são armazenados, bem como uma bandeira (col) que determina se o objeto é um obstáculo (caso contrário, o jogador passa por ele). Para alguns tipos de objetos, o sinalizador (destruir) é definido, que determina se o objeto deve ser oculto após a interação, bem como o parâmetro (v), que determina algum grau de interação, por exemplo, o número de pontos que um jogador ganha ou perde.

colld:{
    'flower1': {dx:0, dy:5, w:2.3, h:1.4, col:true},
    'diamond_green': {dx:0, dy:0, w:1, h:1, col:false, v:1, destroy:true},
    'diamond_red':{dx:0, dy:0, w:1, h:1, col:false, v:-2},

    ...
},

Um pouco de nuance. A grama na parte inferior da cena deve rolar continuamente. Portanto, você deve usar duas cópias desse objeto e substituí-las uma após a outra à medida que se move. As flores aparecerão em uma tela, no máximo em uma cópia cada.

Tendo em mente o problema de escala que encontrei ao desenvolver o primeiro jogo, redefini a escala de todos os modelos no editor 3D. Assim, nos modelos Saprk AR carregados imediatamente em tamanhos normais. É verdade que, de qualquer maneira, no script, não poderia existir sem o "número mágico", o coeficiente global, o código universal, que contém a essência do universo. Um universo virtual, é claro. E estou pronto para revelar esse número para você. Use, pessoal! Eu não me importo! Este número é 0,023423. Em resumo, apesar da redefinição de todas as escalas, um metro no editor 3D acabou sendo igual a esse número no Spark AR Studio. Muito provavelmente, eu não entendo completamente todos os meandros do trabalho com gráficos 3D, daí o coeficiente. As coordenadas (mas não o tamanho) de todos os objetos que foram exportados do editor são multiplicadas por ele, você adivinhou.Onde ajustar a escala da cena no Spark AR, não encontrei.

O próximo problema que encontrei foi a perda da ordem de classificação dos objetos ao exportar de um editor 3D. Objetos complexos que consistem em várias malhas podem aparecer imprevisivelmente na cena do jogo de tal maneira que, por exemplo, uma malha que estava atrás de outra pulou repentinamente para a frente. E, se você observar a ordem dos objetos após exportar para o Spark AR Studio, realmente mostra que essa malha é, por algum motivo, mais alta na lista, embora tenha sido mais baixa no editor. Resolvi esse problema dividindo a cena em camadas e salvando-as em arquivos diferentes.

Animação complexa


Por mais três noites, eu brinquei com programação. Se eu usasse o mecanismo Spark AR Studio padrão para animar as asas de uma borboleta, a tarefa de mover o fundo não era tão simples. Ainda não entendi como anexar não apenas um parâmetro variável às iterações do ciclo de movimento, mas uma função de retorno de chamada completa. De qualquer forma, não consegui, os parâmetros de animação atuais não quiseram ser transferidos para lá. E essa função é simplesmente necessária, porque, no primeiro jogo (com pizza), eu verifiquei a colisão no caso de abrir a boca assinando-a, então não havia esse evento aqui. E era apenas necessário verificar colisões com objetos ambientais à medida que o personagem se move, de acordo com suas coordenadas atuais. E para isso, as coordenadas devem ser comparadas a cada iteração. E então eu pensei. Afinal, eu já escrevi jogos em javascript.E por que não usar seu mecanismo de animação que escrevi anteriormente para esses jogos? Seu princípio de operação é aproximadamente o mesmo: em um determinado momento, o parâmetro (ou parâmetros) muda entre os valores iniciais e finais. E o valor atual é passado como parâmetro - você não acredita - na função de retorno de chamada fornecida, na qual, por exemplo, você pode definir um objeto 3D na cena com coordenadas iguais a esses valores atuais ou verificar colisões. Obrigado, cap. Eu tive que adaptar um pouco meu mecanismo ao “ecossistema” local: remover referências ao objeto de janela de lá, já que ele não está aqui, e outras pequenas coisas.E o valor atual é passado como parâmetro - você não acredita - na função de retorno de chamada fornecida, na qual, por exemplo, você pode definir um objeto 3D na cena com coordenadas iguais a esses valores atuais ou verificar colisões. Obrigado, cap. Eu tive que adaptar um pouco meu mecanismo ao “ecossistema” local: remover referências ao objeto de janela de lá, já que ele não está aqui, e outras pequenas coisas.E o valor atual é passado como parâmetro - você não acredita - na função de retorno de chamada fornecida, na qual, por exemplo, você pode definir um objeto 3D na cena com coordenadas iguais a esses valores atuais ou verificar colisões. Obrigado, cap. Eu tive que adaptar um pouco meu mecanismo ao “ecossistema” local: remover referências ao objeto de janela de lá, já que ele não está aqui, e outras pequenas coisas.

Sim, ainda - sobre rolar a paisagem e os objetos do ambiente. Decidi colocar o mundo inteiro em um NullObject, ou seja, em um objeto 3D vazio, em um contêiner e movê-lo usando apenas um parâmetro para animação - sua coordenada x. Dentro do contêiner, todos os modelos têm as mesmas coordenadas como se estivessem do lado de fora, só que agora o sistema de referência está vinculado a esse objeto vazio. Rochas e flores serão repetidas (além disso, em diferentes alturas do solo, de acordo com o diagrama de níveis), para que você possa reutilizar esses objetos enquanto se move, definindo-os na posição horizontal e vertical desejada dentro do "contêiner". Eu escrevi um sistema de busca de objetos que caem no quadro (no deslocamento atual do contêiner), que define o objeto que deixa o quadro em uma nova posição ainda mais, se ele aparecer lá. Você pode ver,como funciona no exemplo de três objetos. (Haverá mais deles no jogo, então você não verá mais esse efeito de "reorganização" dos objetos do ambiente.)



A função para atualizar as coordenadas dos objetos é assim:

oCoordSet: function(v) {
    //  :    
    //   
    var camx = -ap.oWorld.transform.x.pinLastValue();
    //  
    var x1 = camx - ap.game.scro.w2;
    var x2 = camx + ap.game.scro.w2;
    //   
    for (var i = 0; i < ap.d.lv.length; i++) {
        //    
        if ((ap.d.lv[i].x >= x1) & (ap.d.lv[i].x <= x2)) {
            //   
            ap.d.lv[i].o = Scene.root.find(ap.d.lv[i].n);
            //  -  
            ap.d.lv[i].o.transform.y = ap.d.lv[i].y;
            if ((ap.d.lv[i].n == 'cloud') || (ap.d.lv[i].n == 'rain')) {
                //    ,
                //  
                //   2.3,
                //     
                ap.d.lv[i].o.transform.x = ap.d.lv[i].x - (x2 - ap.d.lv[i].x) * 2.3 + 0.2;
            } else {
                //    ,
                //      
                ap.d.lv[i].o.transform.x = ap.d.lv[i].x;
            };
        };
    };
    //        
    //     
    if (camx > ap.game.grassPosLast) {
        ap.game.grassPosLast += ap.game.grassd;
        ap.game.grassi = 1 - ap.game.grassi;
        ap[ap.game.grassNm[ap.game.grassi]].transform.x = ap.game.grassPosLast;
    };
},

O personagem principal


O personagem principal do jogo é uma borboleta inafundável, que corajosamente voa para frente, superando obstáculos. Decidi fazer o gerenciamento definindo um marcador ou cursor (ponto de luz), virando e inclinando a cabeça. E na direção deste ponto, uma borboleta voará lentamente (na verdade, não é um lutador, nem pode se teletransportar). Por exemplo, se você se inscrever no evento de inclinação da cabeça, poderá implementar o controle vertical como este:

FaceTracking.face(0).cameraTransform.rotationX.monitor().subscribe(function(event) {
    var v = event.newValue;
    //  
    var scrH2 = ap.game.scr.h2;
    //
    var p = -v * 0.5;
    if (p < -scrH2) {
        p = -scrH2;
    } else if (p > scrH2) {
        p = scrH2;
    };
    //  
    var d = 0.006;
    //  
    var cur = ap.oPers.transform.y.pinLastValue();
    if (p < cur) {
        cur -= d;
        if (cur < p) {cur = p;};
    } else {
        cur += d;
        if (cur > p) {cur = p;};
    };
    //    ()
    ap.oPointer1.transform.y = p;
    //   ,
    // ,    
    ap.game.pers.dy + = cur - ap.game.pers.y;
});

Da mesma forma para a horizontal. Somente lá é necessário assinar o evento não de inclinação, mas de rotação da cabeça (rotaçãoY) e, em vez da altura da tela, considere sua largura.

Colisores


Tudo isso é maravilhoso, o mundo está se movendo e o personagem do jogo pode se mover livremente pela tela. Mas agora você precisa de um manipulador de colisão, caso contrário, nenhum jogo funcionará. Existem três eventos no jogo, segundo os quais a posição do personagem pode mudar. É a rotação e inclinação da cabeça, bem como o movimento do mundo, no qual a coordenada horizontal do jogador (x) aumenta automaticamente.

Como não sei como as iterações do manipulador de faces funcionam no Spark AR - sejam elas chamadas com uma certa frequência ou definidas com a freqüência de clock máxima possível, e no meu mecanismo de animação eu posso controlar esse parâmetro, decidi que definiria colisões em minha função o movimento do mundo, chamado em uma frequência definida por mim (60 quadros por segundo). Nos eventos de processamento de face, apenas "acumularemos" movimento.

O princípio será assim. Nos eventos de inclinação e rotação da cabeça, um deslocamento ao longo dos eixos x e y é acumulado. Além disso, na função de rolar o mundo, um deslocamento horizontal também é adicionado ao "cofrinho". E então é verificado, se o deslocamento acumulado for adicionado às coordenadas originais, haverá uma colisão com qualquer um dos objetos no mundo. Caso contrário, as coordenadas originais do jogador mais o deslocamento são feitas pelas coordenadas atuais. E então atualizamos os de origem para os atuais (redefinir) e redefinir as compensações. Nesse caso, reverta as coordenadas para o original. Além disso, precisamos determinar em qual eixo a colisão seria, pois as duas coordenadas não podem ser revertidas. Caso contrário, o jogador simplesmente "gruda" em um ponto no espaço e não pode mais se mover para lugar nenhum. É necessário dar-lhe liberdade de movimento ao longo do eixo ao longo do qual a colisão não ocorre.Então você pode dar a ele a chance de voar em torno de um obstáculo.

setPersPos: function(camx, camdx) {
    //   
    //       (camx,camdx)

    //     
    var persx = ap.game.pers.x;
    var persy = ap.game.pers.y;
    var dx = ap.game.pers.dx;
    var dy = ap.game.pers.dy;

    // ,     
    var col = ap.collisionDetect(
        {x: persx, y: persy, dx: dx, dy: dy},
        {x: camx, dx: camdx, y: 0}
    );

    if (col.f == true) {
        // 

        if (col.colx == true) {
            //   
            //    ,
            //   
            //(    ,    )
            ap.game.pers.x = col.x;
        } else {
            //    ,
            //   
            ap.game.pers.x = persx + dx;
        };

        if (col.coly == true) {
            // -  
            ap.game.pers.y = col.y;
        } else {
            ap.game.pers.y = persy + dy;
        };

    } else {
        //  ,   
        ap.game.pers.x = persx + dx;
        ap.game.pers.y = persy + dy;
    };

    // 
    ap.game.pers.dx = 0;
    ap.game.pers.dy = 0;

    //     
    ap.oPers.transform.x = ap.game.pers.x;
    ap.oPers.transform.y = ap.game.pers.y;
},

A própria função de detecção de colisão:

collisionDetect: function(opers, ow) {
    // , opers -   , ow -  

    var res = {f: false, colx: false, coly: false, x: 0, y: 0};

    var ocoll, x, y, w, h, persx, persy, persx0, persy0, od, colx1, coly1, colx2, coly2;
    var collw = false, collh = false;

    //  
    //(  ,  "" )
    persx0 = opers.x + ow.x - ow.dx;
    persy0 = opers.y + ow.y;

    //       
    persx = persx0 + opers.dx;
    persy = persy0 + opers.dy;

    //  
    for (var i = 0; i < ap.d.lv.length; i++) {
        od = ap.d.lv[i]; //obj data

        //    (   ),
        //     
        //        
        if (typeof ap.d.colld[od.n] !== "undefined") {

            //       
            ocoll = ap.d.colld[od.n];
            colx1 = od.x + ocoll.x1;
            colx2 = od.x + ocoll.x2;
            coly1 = od.y + ocoll.y1;
            coly2 = od.y + ocoll.y2;

            if ((persx < colx1) || (persx > colx2) || (persy < coly1) || (persy > coly2)) {} else {
                //   
                res.f = true;

                //        ,
                //,    
                if ((persx0 < colx1) || (persx0 > colx2)) {
                    collw = true;
                };
                //        ,
                //,    
                if ((persy0 < coly1) || (persy0 > coly2)) {
                    collh = true;
                };

            };
        };

    };

    //   

    //  ,     ,
    //  
    if (collw == true) {
        res.colx = true;
        res.x = persx0 - ow.x;
    } else {
        res.x = opers.x;
    };

    // -  
    if (collh == true) {
        res.coly = true;
        res.y = persy0 + ow.y;
    } else {
        res.y = opers.y;
    };

    return res;
},

Chuva


Usei o mecanismo de partículas padrão Spark AR Studio para animar a chuva. Não há nada de especial aqui. Nós adicionamos um objeto Emtter à cena, não esquecendo de perguntar a ele Emissor -> Espaço -> Local (em vez de Mundo). Isso é para garantir que as gotas não fiquem dinamicamente atrás das nuvens durante o movimento, mas sempre caiam diretamente. Este método é mais conveniente para facilitar a determinação do momento em que a borboleta cai na chuva - não é necessário fazer uma correção de altura. Para as próprias gotas, preparei a textura apropriada. Bem, é claro, o objeto chuva se moverá junto com o objeto nuvem. Em seguida, adicionei uma condição de ocorrência de nuvem ao código de manipulação de colisões. E, se não houver obstáculo acima do jogador neste momento, a borboleta cai e o jogo termina.

Moderação


Para moderação bem-sucedida, o vídeo obrigatório do jogo não deve conter texto estático que não esteja vinculado a objetos. Caso contrário, o robô interrompe instantaneamente a moderação. No entanto, depois disso, uma pessoa verifica o jogo por vários dias. E ele não gostou da abundância de texto antes do jogo, assim como no final. Eu tive que reduzir a quantidade de texto. Depois disso, o jogo foi aprovado.

Sumário


Não que meu jogo tenha sido particularmente emocionante. Ela provavelmente não tem dinâmica e mecânica adicional. Vou lançar uma atualização. Mas percebi que fazer jogos no Instagram é interessante. Este é um tipo de desafio. Eu realmente gostei do processo de programação e resolução de todos os tipos de tarefas sob minimalismo em termos de ferramentas e quantidade de memória alocada. Lembro que alguém disse que 640 Kb é suficiente para todos. Agora tente montar um jogo 3D de 2 MB. Eu, talvez, não vou argumentar que eles são suficientes para todos ... Mas tente!


Para concluir, gostaria de reunir em uma lista todos os momentos não óbvios que encontrei. Talvez isso seja útil para alguém como um truque ao criar jogos para o Instagram.

  • Escala. Ajuste a escala de todos os modelos 3D no editor 3D para que ele fique imediatamente 1: 1 no palco do jogo.
  • . . , .
  • . FBX, . -. , .
  • . JPG , , , -, JPG, PNG.
  • . , , . Spark AR , . , , , 3D-.
  • 3D , : , . . .
  • , , . javascript .
  • No contexto do Spark AR, não há objeto de janela e todos os tipos de objetos específicos do navegador, como webkitRequestAnimationFrame, mozRequestAnimationFrame e assim por diante. Este é um destino ao programar em animação javascript.

All Articles