Pitch: Estou fazendo um tower defense bem simples e só quero ir compartilhando por aqui
Introdução
Olá, começo dizendo que não domino esse assunto, apenas me interesso e gosto da ideia de criar coisas, logo, quero fazer um mini jogo de tower defense. Nada muito complexo, nada muito grande, nada muito bonito, somente algo funcional e que caso futuramente eu queira, possa voltar ao projeto e evoluir como eu quiser.
Vou usar a biblioteca raylib para fazer esse projeto, tomei essa decisão por conta de alguns fatores.
- É uma lib até que bem popular então existe um suporte bacana em cima dela
- A doc dela eu acho muito boa com o cheatsheet
- Os exemplos que a doc possui são de primeira e mostra o potencial dessa lib
- De quebra ainda devo aprender um pouco mais sobre a linguagem C que é um objetivo pessoal
Como vai funcionar?
Pretendo avançar no projeto por post (isso é óbvio), quero dizer que quando eu for trabalhar nesse projeto estarei escrevendo o post para me organizar nos objetivos da minha sessão atual, caso eu bata na parede e não tenha mais tempo para continuar o projeto na mesma sessão, eu vou encerrar a minha sessão mas o concluirei o post da forma que está.
Tentando explicar de outra forma, se na sessão eu tiver 4 metas e 1 hora para concluir elas, mas eu concluir apenas 2, eu irei manter as 4 metas no post já que estou usando ele como uma forma de organizar as minhas metas da sessão (tenho usado o README.md). O meu objetivo com isso é conseguir me organizar no meu projeto, compartilhar as minhas experiências e caindo nas graças de quem me lê conseguir aprender com pessoas mais experientes que eu através dos comentários.
Planejamento geral
Tenho uma ideia embaçada do que eu quero no jogo então vou tentar clarear aqui, mas não vou colocar todas as informações de uma vez aqui, já que provavelmente as coisas vão mudar conforme o andamento desse projeto. Então vou colocar umas mecânicas gerais aqui e que é necessário para que o jogo tenha um mínimo de sentido
- Ter um cena simples
- Conseguir posicionar unidades no mapa
- Unidades atacam inimigos automáticamente
- Ter unidades diferentes
- Todas as unidades devem ter habilidades únicas
- As unidades devem seguir padrões diferentes, por exemplo:
- Atacar o mais próximo
- Atacar o que tem menos vida
- Atacar o mais distante
- Inimigos com pathfinding
- Uma "tower" para defense
Isso é o que espero no final do projeto, os planos podem mudar até lá mas por agora são nesses objetivos que vou focar.
Planejamento para essa sessão
Não sei até onde vou conseguir fazer, mas para começar, quero focar em criar as unidades, então o plano inicial é o seguinte:
- Criar uma
structchamadaUnit - Desenhar a
Unitna tela - Criar um alvo
- Fazer a
Unitconseguir olhar para o alvo- Rodar a
Unitno próprio eixo para dar a sensação de que está olhando para o alvo
- Rodar a
A princípio acho que consigo fazer essas coisas de forma bem tranquila, tenho mexido com raylib nas últimas semanas e já lidei com algumas dessas coisas, embora eu sinta que ainda não fixei todo esse conteúdo. Além disso, tem um detalhe que será novidade para mim e que não especifiquei nas tasks anteriormente, mas de qualquer forma vou lidar com isso mais para frente, porque agora vou efetivamente começar a programar.
[Alguns minutos depois rs]
Bom, o código está assim atualmente
#include "raylib.h"
#include "raymath.h"
#include <math.h>
typedef struct Unit {
Vector2 pos;
Vector2 origin;
Rectangle body;
Color color;
int width;
int height;
float angle;
} Unit;
typedef struct Target {
Vector2 pos;
Color color;
int width; // radius
int speedX;
int speedY;
} Target;
int main(void) {
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "Tower Defense");
SetTargetFPS(60);
Unit unit = {0};
Target target = {0};
const int size = 50;
unit.pos = (Vector2){ screenWidth/2-size/2, screenHeight/2-size/2 };
unit.origin = (Vector2){ size/2, size/2 };
unit.color = RED;
unit.width = size;
unit.height = size;
unit.body = (Rectangle){
.x = unit.pos.x,
.y = unit.pos.y,
.width = unit.width,
.height = unit.height
};
target.pos = (Vector2){ unit.pos.x-size*2+size/2, unit.pos.y-size*2+size/2 };
target.color = BLUE;
target.width = size/2;
target.speedX = 10;
target.speedY = 10;
while (!WindowShouldClose()) {
const float dt = GetFrameTime();
// Calculate movement of target
target.pos.x += target.speedX;
target.pos.y += target.speedY;
if (target.pos.x >= screenWidth-target.width/2 || target.pos.x <= 0+target.width/2) target.speedX *= -1;
if (target.pos.y >= screenHeight-target.width/2 || target.pos.y <= 0+target.width/2) target.speedY *= -1;
// Calculate Unit angle
Vector2 unitDir = Vector2Subtract(target.pos, unit.pos);
float unitAngle = atan2f(unitDir.y, unitDir.x) * RAD2DEG;
unit.angle = unitAngle;
BeginDrawing();
ClearBackground(RAYWHITE);
DrawRectanglePro(unit.body, unit.origin, unit.angle, unit.color);
DrawCircle(target.pos.x, target.pos.y, target.width, target.color);
EndDrawing();
}
CloseWindow();
return 0;
}
Com esse arquivo consegui fazer tudo o que eu tinha planejado anteriormente.
- Criei a
Unite estou exibindo na tela - Criei um alvo e estou exibindo na tela
- Fiz a
Unitolhar para o alvo.
Além disso, decidi fazer com que o alvo se movesse para que dê para ver a Unit olhando para ele. Enfim, a lista de tarefas está assim agora
- Criar uma
structchamadaUnit - Desenhar a
Unitna tela - Criar um alvo
- Fazer a
Unitconseguir olhar para o alvo- Rodar a
Unitno próprio eixo para dar a sensação de que está olhando para o alvo
- Rodar a
A parte mais chata de fazer sem dúvida foi calcular o ângulo (acho que eu devia ter prestado mais atenção nas aulas do colégio).
Tentei fazer de cabeça mas não consegui e como estava ficando sem tempo, decidi pegar o código de um outro projeto meu que já possui a fórmula certinha para isso, só precisei dar uma limpadinha no código porque a fórmula estava mais complexa já que ela também calculava um "delay" (Lerp para quem conhece ou se interessou e quer pesquisar sobre), então precisei remover parte do código para simplificar o nosso projeto no momento atual.
Mas caso você queira a fonte de onde eu encontrei essa fórmula, eu vi nesse site, que por coincidência ele já mostra a fórmula pronta para o raylib.
Observações
O primeiro contra na minha opinião é que está tudo em um único arquivo. Certamente essa não é a forma correta de conduzir, mas já que até aqui não fiz nada demais, apenas dei um bootstrap (para usar um jargão da área), durante o resto do processo vou resolver isso (não como um profissional, mas vou tentar pelo menos kkkk).
Eu criei a Unit como um struct, isso funciona para o que tenho do jogo até agora, mas como falei no planejamento geral, quero que as Units possuam habilidades diferentes e pessoalmente não sei como eu faria isso já que pelo que pesquisei C não possui tipos genéricos como em C# ou Java, então acredito que terei que apelar para a minha arma secreta classes, classes. Criando uma classe da Unit ao invés da struct acredito que devo conseguir dar habilidades diferentes para diferentes unidades.
Outra coisa estranha no código é ter Vector2 e Rectangle dentro da struct de Unit já que Rectangle já possui a pos dentro dele, espero conseguir resolver isso com class. O único motivo para eu fazer isso é porque a função DrawRectanglePro exige como parâmetro um Rectangle então para eu não criar um novo 60 vezes por segundo, decidi colocar dentro de Unit mesmo, mas pretendo resolver isso no futuro com class.
Uma última observação é o dt que foi declarado e não é usado, isso aconteceu porque eu estava usando ele na fórmula da ângulo, mas como falei fiz umas alterações para facilitar para esse projeto e acabou que esqueci de tirar a declaração do código, por enquanto vai ficar assim porque o pai não é de ferro. kkkkk
Conclusão
Era basicamente isso que eu queria compartilhar, como já falei, não sou especialista no assunto, só gosto da ideia de criar coisas, por agora quero criar esse tower defense para eu mesmo jogar de vez em quando. Enfim, se você observou alguma coisa que pode ser melhorada, se você tiver alguma sugestão ou qualquer coisa que possa contribuir, por favor, me avisa na seção de comentários, dependendo pode ser até um desafio interessante kkkkk, mas sério, estou aberto a sugestões.
P.S.
Aceito todo tipo de sugestão e melhoria, inclusive no post em si que muito provavelmente cometi crimes acima contra a nossa lingua portuguesa (mals aí), mas também aceito sugestões no markdown de como posso fazer para organizar melhor caso vocês achem que ficou desorganizado ou difícil de entender, enfim, qualquer tipo de sugestão mesmo.
P.S. 2
Eu não tinha mais o link gamedevmath.com e não lembrava onde tinha encontrado ele, então tive que procurar novamente para colocar como referência no post, enquanto procurava acabei encontrando esse outro site que ainda não usei mas parece ser bem útil, então vou colocar aqui para referências futuras