Jogo usando gráficos matemáticos em vez de gráficos



Nesta captura de tela, você é apresentado a um jogo aparentemente comum com gráficos de pixel. No entanto, nem tudo é tão simples.

O relevo da terra em alguns lugares se assemelha a um sinusóide, e as balas são semelhantes a dois gráficos simétricos da raiz de x.

De fato, tudo o que você vê na tela, de uma maneira ou de outra, está relacionado à matemática, curvas matemáticas e gráficos.

Antecedentes


Certa vez, enquanto assistia ao vídeo do canal "Numberphile", encontrei um material de vídeo muito interessante chamado "A fórmula de tudo" .

Neste vídeo, foi apresentada a fórmula auto-referencial de Tapper , que, com um certo valor de k, recriou sua imagem no gráfico. Essa fórmula é assim:

 frac12< moddopiso( lpiso fracy17 piso217 lxpisox rpisomod( lpisoy piso,17),2) r$pis


Essa fórmula realmente me interessou, e eu tive uma ideia:

"Mas e se você criar um jogo no qual, em vez de texturas comuns armazenadas em vários arquivos nos formatos .png e .jpg, serão utilizados gráficos e curvas matemáticos?"

Essa ideia me pareceu bastante interessante e difícil de implementar.

As tarefas


Eu enfrentei as seguintes tarefas:

  • Crie o significado do jogo, jogabilidade
  • Derivar fórmulas, cujos gráficos serão as silhuetas de caracteres, marcadores, superfícies de que preciso
  • Implementar tudo isso no jogo

Jogabilidade e significado do jogo


A jogabilidade e o significado do jogo mudaram várias vezes durante o desenvolvimento deste jogo. Tudo isso porque houve algumas dificuldades, em particular, com a derivação de fórmulas para certas silhuetas. Mas, em geral, este jogo é sobre um disco voador solitário que voa pelos planetas, explorando-os e matando inimigos que atrapalham seu caminho.

Fórmulas e sua subsequente implementação no jogo


Eu combinei os dois parágrafos a seguir em um subtítulo, porque é impraticável "pular" entre uma fórmula e sua implementação.

Para criar o jogo, a linguagem de programação c ++ e a biblioteca SFML foram escolhidas para criar janelas e desenhar algo nelas.

Como estou na escola e só recentemente descobri o que é um sinusóide e como ele se parece, tive grandes problemas em derivar várias fórmulas. Na maioria das vezes, isso terminava com uma seleção simples.

No final do artigo, haverá um link para o GitHub, onde todo o código será publicado. No artigo, apenas pequenos pedaços de código serão fornecidos para não ser descartados.

Superfície do planeta


Para a superfície do planeta, deduzi a seguinte fórmula:

f(x)=|sin(x)|

Uma fórmula bastante simples, mas quando implementada, havia a necessidade de controlar a altura e o comprimento de um dado sinusóide. Além disso, para evitar a curvatura do relevo, x é multiplicado por π. Portanto, o código final fica assim:

int groundC = ceil(abs(sin(((i+1)*groundScale*M_PI)))*groundHeight); 

Textura do planeta no espaço






A textura do planeta consiste em um círculo e um padrão nele. O jogo tem 4 fórmulas para criar padrões e 12 texturas de planetas com um padrão diferente. Dependendo da "etapa" da fórmula, vários padrões são criados. Além disso, ao gerar um planeta, ele é definido de maneira pseudo - aleatória para colorir, tamanho e posição no espaço.

Balas




Imagem de uma bala do jogo. A bala está virada.

Para marcadores, uma fórmula muito simples foi escolhida:

 sqrtx

O enredo desta fórmula é espelhado na abcissa.

Protagonista


Então chegamos às fórmulas mais complexas.

Deduzi a fórmula do protagonista com grande dificuldade. É assim:

 sqrtx frac12,8+x10,9x9,3x0,3

Sim, uma fórmula muito torta e muito feia. Mas o principal não é a fórmula, o principal resultado.

Para alcançar o resultado, no começo eu só queria me mover ao longo do eixo x com um certo passo, anote as coordenadas y e, em seguida, conecte todos esses pontos, obtendo assim nossa chapa. Mas então, acidentalmente, dei um pequeno passo e todo o meu prato apareceu maravilhosamente, com exceção de dois pontos finais, que eventualmente se conectaram. Como resultado, a placa fica assim:



Em seguida, precisávamos da textura do protagonista no espaço. É assim:



Foi baseado em um círculo. A cabine principal é feita usando a seguinte fórmula:

(x7x) frac0,8x

O gráfico desta fórmula é espelhado ao longo do eixo das ordenadas.

É assim que esta fórmula se parece em c ++:

 int x = round(pow(pow(i, 7 - i), 0.8 / i)); 

Inimigos e seu criador



À direita da imagem, há um gerador azul, objetos vermelhos são inimigos.

Spauner é um planeta comum com um padrão incomum. Este padrão é um gráfico da fórmula:

sin(x)x0,8


Fórmula de textura inimiga:

(x3x) frac1x



Árvores


Admito que não consegui derivar ou selecionar uma fórmula para criar a silhueta das árvores. Mas, para não violar o conceito básico de todo o jogo e as regras para não usar nenhum arquivo no formato .png e .jpg, usei um truque. Eu usei fractais para criar árvores.


Exemplo de árvore fractal

Muito provavelmente você concordará que as árvores fractais parecem bastante chatas. Se você adicionar alguns elementos aleatórios, por exemplo, 2 ramificações podem não necessariamente crescer, mas também 3 ou 1, ou não. Além disso, não é possível fazer o mesmo ângulo de inclinação em qualquer lugar.

Obviamente, era possível fazer um pseudo- máquina comum, baseado em marcações de computador, mas a seguinte idéia me pareceu mais interessante:

"E se cada árvore receber um número específico (semente) a partir do qual serão calculados números pseudo-aleatórios que afetam os parâmetros da árvore?"

Felizmente, o c ++ possui uma biblioteca de pseudo -andandos separada.

Como resultado, as árvores geradas ficam assim:



À esquerda está uma árvore com semente 13 e à direita - 22

E o código que gera essas árvores é:

 Branch Branch::createNewBranch(Branch cur, Tree* parent, float angleMultiplier, int level) { Vector2f sp(cur.startPoint.x, cur.startPoint.y); float randomAngle = ((*parent).getRand() * 15) - 5; float t = cur.thickness * 0.75; float l = cur.length * 0.67; float a = cur.angle + 30*angleMultiplier + randomAngle; sp.y -= (cos((cur.angle-180)*3.1415926 / 180) * cur.length); sp.x += (sin((cur.angle-180)*3.1415926 / 180) * cur.length); Branch gen(sp, t, l, a, level); if (level > 0) { int count = 100 * (*parent).getRand(); if (count >= 25 && count < 80) { //     ,      && count < 80,        ,  . , -          20%  10%,  ,    count<90 (*parent).addBranch(gen.createNewBranch(gen, parent, 1, level - 1)); (*parent).addBranch(gen.createNewBranch(gen, parent, -1, level - 1)); } if (count >= 80) { //    ,    count >= 90 if (count % 2 == 0) { (*parent).addBranch(gen.createNewBranch(gen, parent, -1, level - 1)); } else { (*parent).addBranch(gen.createNewBranch(gen, parent, 1, level - 1)); } } } return gen; } 

Nota Sim, eu sei que codifiquei algumas variáveis, mas, por favor, não me culpe por isso. Eu pensei que não fazia sentido criar variáveis ​​constantes separadas, o que, em princípio, afeta apenas a chance de criar um novo ramo.

Um pouco mais de código


Acima, forneci código apenas para gerar texturas. Nesta legenda, descreverei o código do jogo em si. Todo o código está no GitHub, o link para o projeto está em conclusão.

Jogador


O player possui dois métodos diferentes de atualização - spaceUpdate e planetUpdate. Assim, o spaceUpdate atualiza o player quando está no espaço, planetUpdate - quando está no planeta. No planeta, a aceleração e a velocidade do jogador são calculadas. Dependendo da aceleração horizontal, o ângulo de inclinação da placa muda de 30 graus para -30. Aproximando-se das barreiras, a velocidade do jogador diminui. Tais barreiras existem para o eixo x (0; mapSize.x) e para o eixo y. Para o eixo y, as coisas são um pouco mais complicadas. Existe uma altura mínima, calculada da seguinte forma: a altura mínima da terra é medida, adicionada à altura da onda senoidal e a altura das árvores. A altura das árvores é calculada de uma maneira muito simples - o comprimento inicial do galho multiplicado pelo número de ciclos realizados durante a geração da árvore. Não há borda superior - voando para fora do mapa, o jogador muda para spaceUpdate e o espaço é desenhado.

O SpaceUpdate funciona da seguinte maneira: a aceleração e a velocidade do jogador são calculadas. Em seguida, o ângulo de rotação do jogador é calculado. O ângulo é calculado da seguinte forma: se a aceleração é zero, o ângulo é calculado em relação à velocidade do jogador, se não, em relação à aceleração. Além disso, no espaço, o jogador tem a possibilidade de fotografar. As filmagens ocorrem da seguinte maneira: uma bala é criada com uma rotação como a de um jogador e adicionada à lista. Ao atualizar um jogador no espaço, cada marcador nesta lista também é atualizado. Ao renderizar um jogador, balas também são sorteadas. Além disso, no espaço, as coisas são um pouco mais complicadas com barreiras. O espaço é dividido em setores, em cada setor, 4 planetas, no total - 1 000 000 de planetas e 25 000 setores. Cada setor tem um ID exclusivo. Se o restante ao dividir por 500 for igual a 0 - existe uma barreira esquerda, se o restante de 499 estiver correto, se ao dividir por 500 o resultado for 0 - a barreira superior estará presente, se 499 for a superior. Se não houver barreiras, ao voar para fora do quadro, o player se moverá para o setor correspondente.

Espaço


Eu já descrevi a maior parte, mas ainda há algumas coisas. Em cada um dos setores do espaço, existem 4 planetas. Quando um jogador pressiona a tecla E, se estiver a um raio de distância deste planeta, o jogador se move para o planeta.

Inimigos


A IA dos inimigos é muito estúpida - se houver um jogador no raio de sua visibilidade, eles simplesmente tendem a colidir com ela, e há um pequeno erro, portanto, o caminho deles é uma curva. Se não houver jogador no raio de sua visibilidade, eles serão enviados ao seu criador.

Spawner


Em cada setor do espaço, há 1 criador. Os Spauners podem ter tamanhos diferentes. Tamanho afeta a visibilidade do jogador. Se o jogador estiver na sua zona de visibilidade, o criador cria inimigos a cada 5 segundos, mas o número de inimigos não pode exceder 10.

Conclusão


Depois de passar cerca de uma semana, criei um jogo que não usa nenhum arquivo .png ou .jpg.

Link para o projeto no GitHub

Para quem tem preguiça de baixar o projeto e executar o jogo, um pequeno vídeo sobre a jogabilidade:

Source: https://habr.com/ru/post/pt426377/


All Articles