Decidi lançar uma nova versão do meu antigo jogo de navegador, que por alguns anos tem sido bem-sucedido como um aplicativo nas redes sociais. Desta vez, decidi projetá-lo também como um aplicativo para Windows (7-8-10) e colocá-lo em várias lojas. Obviamente, no futuro você poderá fazer montagens para MacOS e Linux.
O código do jogo é escrito inteiramente em javascript puro. Para exibir gráficos 3D, a biblioteca three.js é usada como um link entre o script e o WebGL. No entanto, esse foi o caso na versão antiga do navegador. O mais importante neste projeto para mim foi o motivo, em paralelo com o jogo, de adicionar minha própria biblioteca, projetada para complementar o three.js com as ferramentas para um trabalho conveniente com objetos de cena, suas animações e muitos outros recursos. Eu então o abandonei por um longo tempo. É hora de voltar para ela.
Minha biblioteca contém ferramentas convenientes para adicionar e remover objetos da cena, alterar as propriedades de partes individuais de objetos (malhas), animação independente da taxa de quadros de objetos 3D, um sombreador de céu com uma textura estrelada do céu à noite e muito mais. Vou falar sobre alguns deles. Em relação ao céu, implementei sua criação com uma única função que utiliza vários parâmetros de entrada, inicializa o sombreador, carrega a textura da nuvem (se necessário) e inicia a atualização do céu com uma determinada iteração.
No entanto, tudo é um pouco mais complicado lá - para funções periódicas, mas raramente chamadas, outra construção realmente funciona, usando setInterval (), no qual os eventos podem ser executados em intervalos diferentes, e isso reduzirá tudo isso a um denominador comum e funcionará à direita eventos necessários na lista. Lá você também pode jogar o intervalo de atualização do céu. Mas o movimento dos objetos de jogos em 3D para maior suavidade já foi implementado por meio de requestAnimationFrame () ...
Então, como estamos falando do céu, começaremos com ele.
O céu
A adição do firmamento à cena é a seguinte.
Primeiro, você precisa adicionar a luz padrão three.js à cena com seus valores de brilho máximo (inicial). Toda a cena com seus objetos, luz e outros atributos, para não bagunçar o espaço global, será armazenada no espaço de nomes apscene.
Depois disso, você já pode executar a animação do céu com shaders, texturas (blackjack e ... bem, tudo bem) através de uma das minhas funções:
m3d.graph.skydom.initWorld(
Como resultado, um céu dinâmico aparece na cena com uma mudança suave na altura do sol e, consequentemente, uma mudança na hora do dia.
Não é necessário usar todos os tipos de iluminação no palco. E não é necessário alterar todos os parâmetros, dependendo da hora do dia. Porém, brincando com o brilho, você pode criar uma imagem bastante realista da mudança do dia e da noite. Você pode nomear os parâmetros como quiser, o principal é observar as chaves dos objetos dentro deles, conforme são especificadas no three.js.
Como fica, você pode ver o vídeo da cena demo:
Este é um jogo diferente. É só que o horizonte não está cheio de vários objetos e, portanto, o trabalho desse script é mais claramente visível. Mas no jogo sobre o qual a história está sendo discutida, exatamente a mesma abordagem é usada. A alta velocidade da passagem do tempo aqui é definida apenas para fins de demonstração e, portanto, o tempo flui, é claro, mais lentamente, com o mesmo passo da iteração atualizando o firmamento. Nesta demonstração, a propósito, um shader de água está envolvido, também com parâmetros variáveis, dependendo da altura do sol ... Mas ainda não o finalizei.
Desempenho
Tudo isso é muito pouco exigente em ferro. Trabalhando no navegador Chrome, ele carrega o Xeon E5440 no LGA775 (e com 4 GB de RAM) em 20% e o núcleo da placa gráfica GT730 em 45%. Mas isso é puramente devido à animação da água. Se falamos de um jogo em que não há água, mas há uma cidade, esta:
então, no momento em que o carro se move pela cidade - 45%, placa de vídeo 50%. Em princípio, com uma redução de alguns fps (até cerca de 30 quadros por segundo), funciona razoavelmente bem, mesmo em Pentium4 3GHz (1Gb de RAM) e em um tablet no Intel Atom 1.3GHz (2Gb de RAM).
Todo esse hardware é extremamente fraco e outros jogos semelhantes no WebGL e no HTML5, mesmo em 2D, diminuem a velocidade de Deus, até o ponto em que fica impossível reproduzi-los. Como eles dizem, escreva o jogo você mesmo, conforme necessário, e jogue.
Scene
A cena 3D em three.js é um objeto de cena e sua matriz derivada é, de fato, todos os modelos 3D carregados na cena. Para não registrar uma chamada do carregador de inicialização para cada modelo, decidi que toda a cena do jogo seria definida na forma de uma determinada configuração, com um grande locd associativo de matriz: {} (como dados de localização), que conteria todas as configurações - luzes, caminhos de texturas pré-carregadas e imagens para a interface, caminhos para todos os modelos que devem ser carregados no palco e muito mais. Em geral, esta é a configuração completa da cena. Ele é definido uma vez no arquivo js do jogo e alimentado no meu carregador de cenas.
E esse objeto locd: {}, em particular, contém os caminhos para os modelos 3D individuais que precisam ser carregados. O caminho zero é o caminho comum e, em seguida, os caminhos relativos para cada objeto, como:
['path/myObj', scale, y, x,z, r*Math.PI, 1, '', '', '', 1, ['','','',''], 'scene']
Entende-se que todos os modelos são exportados do editor 3D para o formato json, ou seja, possuem caminhos como path / myObj.json. Isso é seguido pela escala (como o editor pode ser salvo com uma escala que não é adequada para o jogo), a posição do objeto em altura (y), ao longo dos eixos (x) e (z) e, em seguida, o ângulo de rotação ® do modelo em (y), vários parâmetros opcionais e o nome da cena em que carregar o modelo - na cena principal (cena) ou no plano de fundo (cena).
Sim, foi necessário implementar isso não na forma de um simples, mas na forma de uma matriz associativa. Portanto, a ordem dos parâmetros é incompreensível, mesmo sem documentação, ou pelo menos sem o tipo de função que aceita esses parâmetros, você não entenderá. Penso que no futuro vou refazer essas linhas em matrizes associativas. Enquanto isso, fica assim:
landobj: [ ['gamePath/'], [ ['landscape/ground', 9.455, 0, 0,0, 0*Math.PI, 1, '', '', '', 1, ['','','',''], 'scene'], ['landscape/plants', 9.455, 0, 0,0, 0*Math.PI,1, '', '', '', 1, ['','','',''], 'scene'], ['landscape/buildings/house01', 2, 0, -420,420, -0.75*Math.PI, 1, '', '', '', 1, ['','','',''], 'scene'], ... ] ],
Esses modelos são carregados no palco e colocados nas coordenadas dadas aqui no espaço. Em princípio, todos os modelos podem ser carregados como um único objeto, ou seja, exportados do editor como uma cena inteira do jogo e carregados em coordenadas (0; 0; 0). Depois, haverá apenas uma linha: paisagem / solo - eu tenho ground.json - essa é a parte principal do mundo do jogo. Mas, neste caso, será difícil manipular objetos individuais da cena, pois você precisará primeiro olhar no console do navegador e lembrar qual dos filhos desse enorme terreno é. E entre em contato com eles por números. Portanto, os modelos de jogos em roaming são mais bem carregados com objetos separados. Em seguida, eles podem ser acessados pelo nome a partir da matriz associativa, que será criada automaticamente especialmente para esse fim.
A configuração completa do jogo pode ser, por exemplo, assim:
locd:{
Sim, é melhor refazer todos esses subarrays em arrays associativos, caso contrário, a ordem dos parâmetros neles não é clara ...
Modelos 3D

Outra coisa interessante. Carregando modelos. Minha biblioteca aceita modelos 3D com texturas e define automaticamente alguns parâmetros para seus elementos individuais (malhas), dependendo dos nomes. O fato é que, por exemplo, se o modelo estiver definido para projetar uma sombra, ele será convertido por cada malha incluída em sua composição. Nem sempre é necessário que o modelo inteiro projete uma sombra completamente ou adquira outras propriedades que afetem fortemente o desempenho. Portanto, se você ativar um determinado sinalizador sinalizando que é necessário considerar cada malha separadamente, ao carregar é possível determinar qual malha terá essa ou aquela propriedade e quais não. Bem, por exemplo, não há absolutamente nenhuma necessidade de a sombra ser projetada pelo teto horizontal plano da casa ou por muitos detalhes irrelevantes do modelo em um fundo grande. Mesmo assim, o player não poderá ver essas sombras e a energia do processador de vídeo será usada para processá-las.
Para fazer isso, no editor de gráficos (Blender, Max, etc.), você pode especificar imediatamente os nomes das malhas (no campo de nome do objeto) de acordo com uma determinada regra. Deve haver um sublinhado (_). Os caracteres de controle condicional devem ficar no lado esquerdo, por exemplo: d - lado duplo (a malha é bidirecional, caso contrário - unidirecional), c (sombra projetada) - lança uma sombra, r (recebe sombra) - leva sombras. Ou seja, por exemplo, o nome da malha de tubos na casa pode ser cr_tube. Muitas outras letras também são usadas. Por exemplo, "l" é um colisor, ou seja, a parede da casa, com o nome crl_wall01, não permitirá que o jogador passe por si mesma e também projeta e faz sombra. Não há necessidade de fazer coletores, como um telhado ou uma maçaneta da porta, e assim degradar o desempenho. Como você já entendeu, ao carregar um modelo, minha biblioteca analisa os nomes das malhas e fornece as propriedades correspondentes na cena. Mas, para isso, é necessário nomear corretamente todas as malhas antes de exportar o modelo do editor 3D. Isso economizará significativamente o desempenho.
Todos os sinalizadores de controle para malhas dentro de um objeto:
col_ ... é o colisor. Essa malha será exibida simplesmente como um colisor transparente e invisível. No editor, ele pode parecer com qualquer coisa, apenas sua forma é importante. Por exemplo, pode ser um paralelepípedo em torno de todo o modelo, se for necessário que o jogador não consiga passar por esse modelo (construção, pedra grande, etc.).
l_ ... é um objeto colidível. Dando a qualquer malha uma propriedade de colisão.
i_ ... - cruzamentos. A malha será adicionada à lista de interseções, que pode ser usada, por exemplo, para clicar nela, ou seja, para dar interatividade ao jogo.
j_ ... também interseções. O mesmo que acima, apenas uma versão mais recente - com um algoritmo aprimorado para encontrar interseções no jogo e menor consumo de recursos.
e_ ... - cruzamentos para as portas das casas (entrada / saída). Exclui interseção sobre outras malhas de objetos. É usado se for necessário nas casas em algum momento tornar apenas as portas interativas, excluindo todos os outros elementos interativos. Com imaginação, você pode criar esse e muitos outros usos.
c_ ... - projetar sombras. A malha lança uma sombra.
r_ ... - recebe sombras. Uma malha aceita sombras de todas as outras malhas que as lançam.
d_ ... - bilateral (dupla face). Visível em ambos os lados, a textura é sobreposta em ambos os lados.
t_ ... - transparente (transparente), se o objeto inteiro estiver definido como alphatest em three.js.
u_ ... - transparente (transparente), com densidade fixa (0,4), se o objeto inteiro não especificar o alphatest em three.js.
g_ ... - vidro. A transparência fixa está definida (0,2).
h_ ... - invisível (oculto). Para partes do objeto (malhas) que devem ser ocultadas ao adicionar o objeto à cena. Entrou na lista de ocultos.
v_ ... visível. Todos os objetos, exceto aqueles marcados com "h", já estão visíveis, mas com o sinalizador "v", eles são inseridos em uma lista separada, visível para ocultação adicional ou outras manipulações.
Como resultado, o nome da malha pode ser algo assim: crltj_box1 (projeta, aceita uma sombra, colisor, transparente, interativo). E outra malha como parte do mesmo modelo: cr_box2 (somente projeta e cria sombras). Naturalmente, os caracteres de controle podem ser definidos em qualquer ordem. Assim, no editor, você pode controlar a exibição futura de partes do objeto no jogo, ou melhor, algumas de suas propriedades, economizando, ao mesmo tempo, poder de computação.
A essência do jogo
O significado, de fato, do jogo sobre o qual a história é, é mover-se pelo perímetro do campo quadrado e comprar empresas. O campo é feito na forma de ruas em 3D. O modelo econômico do jogo é significativamente diferente daquele de seu tipo. No meu jogo, quando alguém inicia um negócio, seu lucro esperado cai. E vice-versa, quando você descobre algo, aumenta. Todos os cálculos de lucros e perdas são feitos na inspeção fiscal no campo Iniciar. Você também pode tomar um empréstimo em um banco, negociar valores mobiliários e fazer várias outras coisas. Melhorei o comportamento da IA em comparação com a versão antiga. Refez quase todos os modelos e texturas 3D do jogo e desempenho otimizado. Fez mais configurações e muito mais.
Animação
Para animar o movimento dos objetos do jogo, é usado o mecanismo mais simples, que com uma determinada taxa de quadros (limitada ao 60º) altera quaisquer parâmetros numéricos por um determinado intervalo de tempo, distribuindo valores intermediários ao manipulador. E no manipulador, por exemplo, o modelo é exibido.
Por exemplo. Precisamos mover o objeto obj no espaço da posição (10; 10; 50) para o ponto (100; 300; 60). Definimos 3 parâmetros, indicando seus valores iniciais e finais. A coordenada x varia de 10 a 100, y - de 10 a 300 e z - de 50 a 60. E tudo isso deve acontecer, digamos, em 4 segundos.
m3d.lib.anim.add( 'moveobj', 1, 4000, 'and', {userpar1:111, obj:my3DObject}, [ {lim1:10, lim2:100, sstart:10, sfin:100, t:0}, {lim1:10, lim2:300, sstart:10, sfin:300, t:0}, {lim1:50, lim2:60, sstart:50, sfin:60, t:0} ], function(){ myPeriodicFun(this); }, function(){ myFinishFun(this); } ); m3d.lib.anim.play();
A primeira linha de 5 parâmetros: moveobj - nome da animação (qualquer), 1 - número de fluxo (você pode animar objetos em paralelo em um número ilimitado de fluxos), 4000 - tempo de animação 4 segundos e - até um parâmetro não utilizado, que no futuro será responsável pela lógica de transição entre animações no mesmo fluxo, userpar é qualquer matriz associativa que será transmitida ao manipulador como um parâmetro, por exemplo, com um raio calculado, senos, cossenos e geralmente quaisquer valores calculados para essa animação, para não serem calculados em sobre o tempo de cada iteração. Ou com referência a um objeto 3D que, de fato, será animado.
Em seguida é uma matriz com parâmetros mutáveis. Nós achamos que o primeiro parâmetro é a mudança na coordenada x, o segundo é y, o terceiro é z. Escrevemos para cada um em lim1 e lim2, de qual e para qual tamanho ele será alterado. No sstart e no sfin, especificamos os mesmos valores. Aqui você pode especificar um início, por exemplo, de algum outro valor, então o parâmetro "rolará" em um círculo a partir dele, ignorando o lim2 e iniciando uma nova "revolução" com o lim1. Bem, por exemplo, isso é necessário se nossa animação estiver em loop entre alguns valores (lim1 e lim2), mas precisamos iniciá-la não desde o início (ou seja, não de lim1), mas de algum valor intermediário.
t: 0 apenas define que a animação para este parâmetro é executada 1 vez, de acordo com o tempo total (4000), como se estendido a ele. Se definirmos outro número, menor que o horário principal, esse parâmetro será repetido e repetido até o horário da animação principal (4000). É conveniente, por exemplo, definir a rotação do objeto quando o ângulo deve cruzar repetidamente a linha de 360 graus e redefinir para 0.
Em seguida, vêm dois retornos de chamada - aquele que será executado a cada iteração e o que será executado uma vez após a conclusão de toda a animação (ponto de saída).
O primeiro retorno de chamada myPeriodicFun (this), por exemplo, pode ser assim:
myPeriodicFun:function(self) { var state=self.par[0].state, state_old=self.par[0].state_old; var state2=self.par[1].state, state_old2=self.par[1].state_old; var state3=self.par[2].state, state_old3=self.par[2].state_old; if ((state!=state_old)||(state2!=state_old2)||(state3!=state_old3)) { var obj=self.userpar.obj; obj.position.x=state; obj.position.y=state2; obj.position.z=state3; ap.cameraFollowObj(obj); }; },
Ou seja, a cada iteração do movimento, um parâmetro (self) é lançado nessa função que contém os valores intermediários calculados para todos os parâmetros de animação: self.par [0] .state, self.par [1] .state, self.par [2] .state. Este é o nosso x, ye z no momento atual. Por exemplo, se a animação durar 4 segundos, após 2 segundos x será igual a (100-10) / 2 = 45. E o mesmo acontece com todas as coordenadas. Assim, em myPeriodicFun, simplesmente exibimos nosso objeto nessas coordenadas.
Se o navegador começar a ficar lento ou apenas rodar lentamente neste hardware, não será assustador: o tempo total de animação não será alterado, apenas a taxa de quadros cairá e a imagem se transformará em uma apresentação de slides.Por que verificar f ((state! = State_old) ..., isto é, se o novo valor calculado é igual ao antigo (state_old lembra o valor calculado na iteração anterior), bem, por exemplo, para que, se algum parâmetro for alterado menos de um, não redesenhe o objeto inteiro e não gaste a energia do sistema nele, e o mecanismo de animação produz números inteiros em state e state_old, que, digamos, podem ser interpretados como uma etapa igual a um pixel, e se o objeto não se mover em relação à posição anterior mesmo por 1 pixel, não há necessidade de redesenhar, pois A posição na tela não muda.Em geral, a animação é entendida como uma simples alteração em qualquer número de parâmetros em um determinado tempo, com a entrega de seus valores intermediários a uma função de retorno de chamada. Por exemplo, você pode adicionar outro quarto parâmetro, que será responsável pelo ângulo de rotação do objeto. E você geralmente pode agrupar os parâmetros de muitos objetos em uma animação se eles se moverem de alguma maneira uniforme. Você pode colocar animações em diferentes fluxos, para que sejam processadas em paralelo. Você pode adicionar (m3d.lib.anim.add ()) a um único thread uma sequência inteira de animações, e elas serão executadas uma após a outra. Além disso, cada thread terá sua própria sequência independente. O principal é que o sistema tem energia suficiente e tudo não se transforma em uma apresentação de slides.PS: Os fluxos aqui são realizados simplesmente por enumeração seqüencial a cada iteração de todas as animações paralelas e cálculo para cada uma delas de valores intermediários de todos os seus parâmetros. Ou seja, não há multithreading real em javascript.O mesmo "mecanismo" também pode ser usado para animar os elementos da interface, configurando-os para alterar 2 coordenadas no plano da tela e exibindo esses elementos nas funções de retorno de chamada de acordo com os valores intermediários obtidos. O que, de fato, é feito no meu jogo.Sombras dinâmicas
A exibição de sombras em toda a cena acabou sendo tão inútil que, quando ativadas, os qps caíram várias vezes. Isso não é bom. Vamos mostrar as sombras dos objetos em um determinado quadrado pequeno ao redor do player. Eu direi imediatamente que tal técnica aumenta significativamente a taxa de quadros.Nada complicado aqui. As sombras de three.js são projetadas dentro de uma certa câmera de sombra especificada pela caixa (shadowCameraLeft, shadowCameraRight, shadowCameraTop, shadowCameraBottom). Pode ser esticada para toda a cena ou pode ser feita de forma a seguir a câmera principal e as sombras serem projetadas apenas em torno dela. A única coisa que adicionei a este sistema é a etapa pela qual as sombras serão atualizadas. Não há absolutamente nenhuma necessidade de fazer essa atualização toda vez que o player se contrai, pois isso carrega o sistema com cálculos. Deixe-o superar uma certa distância mínima ao longo de qualquer um dos três eixos para que as sombras sejam atualizadas.Na minha biblioteca, ao inicializar o mundo 3D, um objeto contr é criado, que a qualquer momento contém as coordenadas da câmera. Também é possível definir o parâmetro do usuário contr.cameraForShadowStep, que contém a etapa da posição da câmera na qual a posição da câmera sombra é alterada.Se, digamos, o paralelepípedo da câmera sombra tiver dimensões 700x700x700, então contr.cameraForShadowStep poderá ser definido como, por exemplo, 20. E quando o jogador mover 20 em qualquer um dos eixos, a posição inicial será lembrada novamente e as sombras ao redor do jogador serão atualizadas. A escala do mundo 3D pode ser qualquer, dependendo da escala em que todos os modelos foram criados no editor 3D. E é provável que, em vez de 700x700x700 e 20, você precise usar 7000x7000x7000 e 200. Mas isso não muda a essência de forma alguma.A propósito, quando o sol se move no céu, as sombras são atualizadas independentemente desse sistema, pois a direção das sombras deve mudar para lá. Ou seja, eles serão atualizados mesmo se o jogador estiver parado sem movimento. Lá, a função de atualizar as sombras de acordo com o período de atualização do céu é estupidamente chamada.Point Light System
A presença de mais de uma dúzia de fontes pontuais de luz no palco é tão forte em qps quanto sombras dinâmicas. E até torna impossível tocar o velho "cânhamo". Além disso, não importa se essas fontes estão no campo de visão do jogador (o alcance do desenho do mundo pode ser definido) ou a uma distância considerável. Se eles estão estupidamente presentes no palco, tudo funciona lentamente. Portanto, eu forneci no menu de configurações do jogo, que pode ser chamado antes de carregar o mundo 3D, opções para o número dessas fontes (2, 4 ou 8) chamado "Light of Lanterns".
Assim, à noite, acender todas as luzes colocadas no palco ao mesmo tempo não funcionará. Acima, na descrição do esquema de inicialização mundial, apresentei matrizes de userPointLights e lightsPDynamicAr. No userPointLights, as coordenadas de todas as lâmpadas no palco são definidas em uma matriz. E lightsPDynamicAr contém as configurações de luz para todas as 8 instâncias. Dependendo da configuração do número de luzes, a biblioteca pegará a primeira delas e aumentará a cena no campo de visão do jogador.De fato, enquanto o jogador está em movimento, é feita uma busca por 2 a 8 lanternas mais próximas do jogador pela matriz de coordenadas das lanternas userPointLights. E fontes de luz pontuais se movem sob elas. Em outras palavras, 2-8 lâmpadas de iluminação seguem o jogador, cercando-o. Além disso, isso também é feito não com todos os quadros de fps, mas com uma determinada etapa. Não há absolutamente nenhuma necessidade de iniciar a função de busca 60 vezes por segundo, especialmente se o jogador não estiver em movimento - então deixe as luzes já encontradas ao seu redor acenderem.É assim que fica em movimento (Xeon E5440, GeForce GT730):Construção de distribuição
Como não uso nenhum ambiente de desenvolvimento sofisticado (exceto o bloco de notas avançado), escrevi um arquivo bat no qual o Google Closure Compiler é chamado para ofuscar o código de cada arquivo * .js. E o nwjc.exe do pacote nw.js é chamado para compilar js em binários (* .bin). Vou dar um exemplo para um dos arquivos:java -jar D: \ servidores de web \ Encerramento \ compiler.jar --js D: \ servidores de web \ proj \ m3d \ www \ jogo \ bus \ bus.js --js_output_file D: \ servidores de web \ proj \ nwProjects \ bus \ game \ bus \ bus.js
cd D: \ "Arquivos de programas" \ Web2Exe \ down \ nwjs-sdk-v0.35.5-win-ia32
D: \ "Arquivos de programas" \ Web2Exe \ down \ nwjs-sdk-v0.35.5-win -ia32 \ nwjc.exe D: \ servidores de web \ proj \ nwProjetos \ barramento \ jogo \ barramento \ bus.js D: \ servidores web \ proj \ nwProjetos \ barramento \ jogo \ barramento \ barramento.bin
do D: \ servidores \ proj \ nwProjetos \ bus \ jogo \ bus \ bus.js
Em seguida, uso o utilitário Web2Executable simples para criar um arquivo exe com assembly no Windows. Eu escolhi o nw.js versão 0.35.5, embora também estejam disponíveis versões mais recentes. Não notei nenhum efeito deles, exceto pelo aumento do tamanho da montagem.O utilitário é capaz de baixar a versão selecionada do nw.js para a própria pasta especificada. A saída é uma montagem. O arquivo executável de 35 megabytes contém, de fato, o próprio jogo. Tudo o resto é node-webkit. A pasta localidades contém arquivos, obviamente, com alguns recursos em diferentes idiomas. Excluí-os e deixei apenas aqueles relacionados ao inglês. A propósito, isso não impediu o lançamento da versão em russo do jogo (no jogo, o idioma alterna entre russo e inglês). Por que todos esses arquivos então, eu não sei. Mas sem o inglês, nada começa.A montagem inteira levou 167 MB.Depois, agrupei tudo em um arquivo de distribuição executável usando um dos utilitários gratuitos projetados para essa finalidade, e a saída foi de 70,2 MB Businessman3DSetup.exe.Postagem
Enviei a montagem para diferentes lojas de aplicativos. A maioria deles ainda está moderando meu jogo. No momento, apenas a coceira a publicou até agora. Eu aviso imediatamente, o jogo é pago, o preço é de US $ 3. Ainda não existem compras, mas ainda não participei de sua promoção. O GOG se recusou a publicar, citando o fato de que o jogo é bastante simples e de nicho. Eu, em princípio, concordo. A Epic Store, eu acho, fará o mesmo.A versão publicada do jogo é de usuário único, com bots no valor de 1 a 5. Idioma - russo e inglês. Pretendo terminar a versão da rede. Mas pensando bem - para liberá-lo na forma do mesmo aplicativo ou na forma de uma versão do navegador da Web disponível imediatamente no Windows, Linux, iOs e MacOs, e geralmente onde quer que o navegador suporte o WebGL. Na verdade, o webkit é um navegador e o jogo funciona bem nele, no Firefox, no Edge e até no IE11 no Windows, embora no último seja muito lento.Conclusões
Acho que ainda não estou pronto para colocar meu mecanismo para uso geral, pois ainda não está concluído. Vou escrever primeiro outro jogo, exatamente esse, no vídeo de demonstração, sobre navios, porque nesse jogo você pode "executar" o trabalho com o shader de água. Além disso, pretendo implementar um mecanismo de física simples lá. Sim, e você ainda precisa concluir todos os outros recursos, corrigir todas as falhas. E é melhor fazer isso em dois jogos do que em um, pois algumas nuances são possíveis. Enquanto isso, meu mecanismo, na minha opinião, ainda está muito afiado para um jogo.Além disso, não tenho certeza de que alguém precise de tudo isso. Se você olhar com sobriedade, ninguém escreve jogos em javascript puro. Mas gosto porque os jogos são fáceis e rápidos para o navegador. Eles carregam rapidamente, não exigem muita RAM e trabalham muito rapidamente em comparação com os concorrentes no html5, mesmo quando comparados ao 2D. Acho que vou lançar mais de um jogo de navegador (e não apenas) em todos esses desenvolvimentos.