SamL
(usa XUbuntu)
Enviado em 06/10/2016 - 21:07h
Ruanhenry escreveu:
Isso aé em cima tem alguma lógica para usar tiling ?
if ( SIM ){
Legal, mas e agora ?
} else {
Então como eu faço em trem ! ( Códigozin de preferência :D )
}
Vou tentar responder uma pergunta por vez, mas só vou poder fazer um exemplo amanhã, já que isso é bem grande e é mais teoria que prática.
* Como desenhar um tile na tela ?
Para desenhar qualquer tile na tela você precisará de pelo menos um retangulo fonte e outro de destino, só que só isso não basta, você precisa também definir qual o tamanho do tile, que como sendo um quadrado em jogos antigos, o nome dele fica sendo tilesize, em quase todos tutoriais por ai a fora será assim. Sendo tilesize o tamanho do tile, é preferível que seu valor seja em potência de 2, tipo, 16,32,64,128, etc, que indica quantos pixels vale o tile, pra esse tópico vou escolher o valor de 32 pixels que é um valor comum a muitos jogos.
Então, para desenhar o mapa você terá de percorrer cara célula e verificar que tile é aquele, todos os tiles do mesmo mapa têm o mesmo tilesize. E para desenhá-los você precisa definir a posição de destino do retangulo de destino, para o eixo Y, como sendo o numero da linha atual vezes tilesize, e para o eixo X, faça numero da coluna atual vezes tilesize, tome isso como um SDL_Rect chamado destino, sendo assim o destino.w e destino.h serão iguais a tilesize.
Veja o Código:
SDL_Rect destino;
for ( int i = 0; i <= 15; i ++ ){
for ( int j = 0; j <= 20; j++ )
if (mapa[i][j] == 1){
destino.x = j * tilesize;
destino.y = i * tilesize;
destino.w = tilesize;
destino.h = tilesize;
....agora aqui vem o código de desenhar na tela e jogar em 'destino'
}
}
Só que veja que o código acima não faz muita coisa e nem desenha um mapa como o super mario bros e afins. Pra isso, é preciso ter uma imagem (SDL_Texture). Com ela além de você definir o destino como feito acima, também precisa definir o retangulo fonte do tile. O retangulo fonte é como o retangulo que você usa para recortar uma imagem numa coordenada dentro da imagem/textura e então colar ela num ponto na tela em destino, esse retangulo fonte é de acordo com o tile que vc quer desenhar, então se por exemplo é pra desenhar o tile com valor '1', você precisa já ter em mãos o retangulo fonte daquele tile que está na imagem/textura dos tiles. Vamos ao código:
SDL_Rect destino, fonte;
for ( int i = 0; i <= 15; i ++ ){
for ( int j = 0; j <= 20; j++ )
if (mapa[i][j] == 1){
//código antigo
destino.x = j * tilesize;
destino.y = i * tilesize;
destino.w = tilesize;
destino.h = tilesize;
//código novo
//veja que aqui são as coordenadas da imagem do tile na textura de tiles
fonte = (SDL_Rect){20,40,tilesize,tilesize};
//fiz numa linha só pra facilitar, mas até aqui você está familiarizado com esse tipo de coisa
//.. desenha-se normalmente o tile na tela
SDL_RenderCopy(renderer,texturaDeTiles, &fonte, &destino);
}
}
Veja que não precisa de N rects para cada célula, o que precisa é de apenas um destino e para cada tile diferente, precisa de um retangulo fonte. E para todos os tiles que são vistos pelo jogador, precisa de uma imagem única para os tiles. Observe também que cada tile tem o mesmo tamanho na imagem de tiles, mas isso não é obrigatório
* Como fazer um grupo de tiles se transformar em uma mapa completo ( nada de extraordinário ) ?
Eu não entendi, pode ser mais claro nisso?
* Como detectar colisão com esses tiles ?
Não é uma pergunta nada fácil de responder, devido ter bastante teoria envolvida, fora isso não há um jeito "certo" de fazer, pois cada um faz de um jeito diferente. Mas vou tentar explicar como funciona, espere até amanhã quando eu tiver um exemplo pronto, é que não faço agora por questão de preguiça mesmo rsrsrs Se quiser olhar como eu mexo com isso, veja esse link de um jogo incompleto que fiz com um amigo:
https://sourceforge.net/projects/dangeroustux/
Veja a pasta src/chora_engine, é minha engine que uso para fazer jogos. Dê uma olhada na pasta src/chora_engine/tilemap, é lá que estão as funções de desenhar mapa, mas não a de colisão. Se quiser olhar a função de colisão com tiles, veja o src/player.cpp e .hpp, lá onde tem collision_hor para colisão horizontal e collision_ver para colisão vertical com os tiles.
ATUALIZAÇÃO...
Só corrigindo uma coisa que vi agora, você faz i <= 15 e j <= 20, quando na verdade deveria ser i < 15 e j < 20, pois de 0 até 19 são 20 numeros e as linhas e colunas começam a contar do zero, se deixar como estava vai acessar uma área de memória inválida.
* Como detectar colisão com esses tiles ?
Vou simplificar aqui como eu faço para detectar e processar uma colisão com tiles:
//compile com g++ testeColisao testeColisao.cpp -lm -lSDL2
#include <vector>
#include <cmath>
#include <SDL2/SDL.h>
//estrutura que representa um vetor matematico, usado para posicionar objetos
struct Vect
{
float x, y;
Vect (float _x=0, float _y=0)
{
x = _x;
y = _y;
}
};
struct Entidade
{
Vect posicao;
Vect vel; // velocidade da entidade/jogador/objeto do jogo
SDL_Rect retangulo;
std::vector<int> solidos;//aqui fica os tiles sólidos
Entidade ( int w, int h )
{
retangulo.x = retangulo.y = 0;
retangulo.w = w;
retangulo.h = h;
//somente um tile sólido e é o tile '1'
solidos.push_back(1);
}
// verifica se na posição 'p' do 'mapa' é um tile sólido
// retorna true caso sim e false caso não
bool ehSolido ( int mapa[15][20], int tilesize, Vect p )
{
//coordenadas de 'p' no 'mapa'
int x, y;
//converte de posição no espaço p.x para posição no mapa
x = int(p.x)/tilesize;
//converte de posição no espaço p.y para posição no mapa
y = int(p.y)/tilesize;
// só lembrando que a posição no mapa começa em zero
// se for negativo está fora do mapa
if (x < 0)
return false;
if (y < 0)
return false;
// se for maior que o mapa está fora do mapa
if (x >= 20)
return false;
if (y >= 15)
return false;
// se não está dentro do mapa,
// então basta verificar se o tile na posição x,y
// é um tile sólido, que está no vetor de tiles sólidos
for (unsigned i = 0; i < solidos.size(); i++)
if (mapa[y][x] == solidos.at(i))
return true;
// se não achou nenhum sólido na posição x,y...
return false;
}
// detecta colisão horizontal com o mapa
bool colisaoHorizontal ( int mapa[15][20], int tilesize )
{
//se não está movendo então não colide
if (vel.x == 0)
return false;
//se está movendo para direita
//para colisão à direita do jogador e à esquerda do tile
if (vel.x > 0)
{
Vect p;
//aqui eu não usarei o x e y do retangulo, mas apenas a posicao
//pega o canto direito superior do retangulo
p.x = posicao.x + retangulo.w;
p.y = posicao.y;
//observe que este é um ponto de colisão com o tile
//poderia ter vários outros pontos de colisão como um std::vector
//e então você verificaria colisão em todos eles
//mas por facilidade vou usar os 4 cantos do retangulo como
//pontos de colisão com os tiles
//Então verifica se é sólido (se colidiu)
if (ehSolido(mapa,tilesize,p))
{
//processa a colisão movendo o retangulo para fora do tile em 'p'
// só precisa mover ele na direção contrária à da velocidade
// então aqui move ele para esquerda
int x = (int(p.x)/tilesize)*tilesize;
//é assim, converte para posição no mapa int(p.x)/tilesize e depois
//reconverte para posição no mundo do jogo, só que numa posição onde tem um tile
//logo abaixo define a nova posição
posicao.x = p.x - retangulo.w + (x - int(p.x));
//subtrai retangulo.w de p.x pois senão fizer isso o jogador fica dentro do tile
//agora aqui vem o segredo, diminuir 1 de posicao.x e convertê-lo
posicao.x = floor(posicao.x) - 1;
//esse -1 evita que a entidade fique dentro do tile sólido e não posssa
//deslizar por ele, tipo caminhar sobre ele, e não ficar dentro do tile
//agora faz a velocidade x ser 0 para parar o movimento do jogador
vel.x = 0;
return true;
}
//senão continua verificando no lado direito do retangulo
//pega o canto direito inferior do retangulo
p.x = posicao.x + retangulo.w;
p.y = posicao.y + retangulo.h;
//Então verifica se é sólido (se colidiu)
if (ehSolido(mapa,tilesize,p))
{
// faz o mesmo de antes
int x = (int(p.x)/tilesize)*tilesize;
posicao.x = p.x - retangulo.w + (x - int(p.x));
posicao.x = floor(posicao.x) - 1;
vel.x = 0;
return true;
}
}
// para colisão à esquerda do jogador e a direita do tile
else
{
Vect p;
//pega o canto esquerdo superior
p.x = posicao.x;
p.y = posicao.y;
//Então verifica se é sólido (se colidiu)
if (ehSolido(mapa,tilesize,p))
{
// agora aqui o processo muda um pouco
// mesmo assim merece atenção, segue
int x = (int(p.x)/tilesize + 1)*tilesize;
//observe que aqui tem um +1 entre os parenteses
//ele serve para pegar o tile mais a direita da posicao.x
//pois em p.x está um tile sólido
//aqui vem a reposição do jogador, observe que removi apenas o retangulo.w
posicao.x = p.x + (x - int(p.x));
posicao.x = floor(posicao.x);
//zera a velocidade no eixo X
vel.x = 0;
return true;
}
//pega o canto esquerdo inferior
p.x = posicao.x;
p.y = posicao.y + retangulo.h;
//Então verifica se é sólido (se colidiu)
if (ehSolido(mapa,tilesize,p))
{
// agora aqui o processo do de cima
int x = (int(p.x)/tilesize + 1)*tilesize;
posicao.x = p.x + (x - int(p.x));
posicao.x = floor(posicao.x);
vel.x = 0;
return true;
}
}
return false;
}
bool colisaoVertical ( int mapa[15][20], int tilesize )
{
if (vel.y == 0)
return false;
// para colisão em baixo do jogador e em cima do tile
if (vel.y > 0)
{
Vect p;
//pega o canto esquerdo inferior
p.x = posicao.x;
p.y = posicao.y + retangulo.h;
//verifica se é sólido
if (ehSolido(mapa,tilesize,p))
{
//mesmo processo do colisaoHorizontal na parte vel.x > 0
//a diferença é que aqui é aplicado nno eixo vertical (Y)
int y = (int(p.y)/tilesize)*tilesize;
posicao.y = p.y - retangulo.h + (y - int(p.y));
posicao.y = floor(posicao.y) - 1;
vel.y = 0;
return true;
}
//pega o canto direito inferior
p.x = posicao.x + retangulo.w;
p.y = posicao.y + retangulo.h;
//verifica se é sólido
if (ehSolido(mapa,tilesize,p))
{
//mesmo processo acima
int y = (int(p.y)/tilesize)*tilesize;
posicao.y = p.y - retangulo.h + (y - int(p.y));
posicao.y = floor(posicao.y) - 1;
vel.y = 0;
return true;
}
}
// para colisão em cima do jogador e embaixo do tile
else
{
Vect p;
//pega o canto esquerdo superior
p.x = posicao.x;
p.y = posicao.y;
if (ehSolido(mapa,tilesize,p))
{
//mesmo processo do colsiaoHorizontal só que no eixo Y
int y = (int(p.y)/tilesize + 1)*tilesize;
posicao.y = p.y + (y - int(p.y));
posicao.y = floor(posicao.y);
vel.y = 0;
return true;
}
//pega o canto esquerdo superior
p.x = posicao.x + retangulo.w;
p.y = posicao.y;
if (ehSolido(mapa,tilesize,p))
{
//mesmo processo do colsiaoHorizontal só que no eixo Y
int y = (int(p.y)/tilesize + 1)*tilesize;
posicao.y = p.y + (y - int(p.y));
posicao.y = floor(posicao.y);
vel.y = 0;
return true;
}
}
return false;
}
void desenha ( SDL_Renderer * renderer, int r, int g, int b, int a=255 )
{
retangulo.x = int(posicao.x);
retangulo.y = int(posicao.y);
SDL_SetRenderDrawColor(renderer, r,g,b,a);
SDL_RenderFillRect(renderer, &retangulo);
}
};
int main ( int argc, char **argv )
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Event event;
SDL_Window * window = SDL_CreateWindow("Exemplo",0,0,640,480, SDL_WINDOW_SHOWN);
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
int tilesize = 32;
int mapa[15][20] =
{
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,1,1,0,
0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,
0,0,0,1,0,1,1,1,1,0,0,0,1,1,0,0,0,0,1,0,
0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,
1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
//A altura e largura do joagdor não pode ser maior que tilesize
//pois senão ele vai atravessar alguns tiles
//mas você pode adicionar mais alguns pontos de colisão a cada tilesize pixels
//na largura e altura
Entidade jogador(15,31);
int done = 0;
bool right = false, left = false, up = false, down = false;
while(!done)
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
done = 1;
if (event.type == SDL_KEYUP)
{
switch (event.key.keysym.sym)
{
case SDLK_RIGHT:
right = false;
break;
case SDLK_LEFT:
left = false;
break;
case SDLK_DOWN:
down = false;
break;
case SDLK_UP:
up = false;
break;
}
}
if (event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_RIGHT:
right = true;
break;
case SDLK_LEFT:
left = true;
break;
case SDLK_DOWN:
down = true;
break;
case SDLK_UP:
up = true;
break;
}
}
}
//aqui o jogador move 5 pixels para esquerda ou direita, cima ou baixo
//ele não pode mover com velocidade maior que tilesize
//pois se fizer isso vai atravesar as paredes de tiles
if (right)
jogador.vel.x = 5;
else if (left)
jogador.vel.x = -5;
else
jogador.vel.x = 0;//faz parar em X
//primeiro move...
jogador.posicao.x += jogador.vel.x;
//depois colide horizontalmente
jogador.colisaoHorizontal(mapa,tilesize);
if (down)
jogador.vel.y = 5;
else if (up)
jogador.vel.y = -5;
else
jogador.vel.y = 0;
//primeiro move...
jogador.posicao.y += jogador.vel.y;
//depois colide verticalmente
jogador.colisaoVertical(mapa,tilesize);
//Observe que para a colisão de fato funcionar tem que mover
//o joagdor em um eixo por vez. Se mover nos dois eixos antes
//de colidir então dará erro, tente isso se quiser ver como é.
SDL_SetRenderDrawColor(renderer,128,128,128,255);
SDL_RenderClear(renderer);
for (int i = 0; i < 15; i++)
for (int j = 0; j < 20; j++)
if (mapa[i][j] == 1)
{
SDL_SetRenderDrawColor(renderer,0,255,0,255);
SDL_Rect tile = {j*tilesize,i*tilesize,tilesize,tilesize};
SDL_RenderFillRect(renderer, &tile);
}
jogador.desenha(renderer, 255,255,0,255);
SDL_RenderPresent(renderer);
SDL_Delay(60);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
return 0;
}