TP3: Super Mario Bros 1-1
Entrega: 24/10/2025 às 23:59h
Introdução
O Super Mario Bros (SMB), lançado pela Nintendo em 1985, foi um dos jogos mais populares da era dos consoles 8 bits. SMB é um jogo de plataforma onde o objetivo do jogador é se mover para a direita para chegar a um mastro de bandeira no final de cada nível. O jogador controla o Mario, protagonista da série. O irmão de Mario, Luigi, é controlado pelo segundo jogador no modo multijogador e assume o mesmo papel e funcionalidade do Mario. Na narrativa do jogo, o mundo é chamado de Reino do Cogumelo e o Mario está atravessando-o para salvar a Princesa Peach do antagonista Bowser. O video a seguir mostra um gameplay do jogo original:
Objetivo
O objetivo desse projeto é praticar a implementação de integração com editores externos, movimentação de câmera, detecção de colisão com AABBs e animações 2D. Para isso, você irá implementar as mecânicas básicas de correr, pular, e acertar inimigos no primeiro nível do SMB. Primeiro, você irá implementar um parser para carregar objetos do jogo a partir de arquivos csv exportados do editor de mapas [Tiled]. Como parte dessa tarefa, você irá implementar o componente AnimatorComponent, que inicialmente irá desenhar apenas sprites estáticos (não animados) na tela. Segundo, você irá implementar a movimentação de câmera, garantindo que o personagem sempre fique a frente da câmera. Terceiro, você irá implementar o componente AABBColliderComponent para detectar colisões entre caixas delimitadoras alinhadas com os eixos (AABBs). Quarto, você irá incrementar o componente AnimatorComponent para desenhar animações 2D utilizando sprite sheets gerados pela [FreeTexturePacker]. Por fim, você irá implementar os goombas, incluindo as mecânicas de matá-los quando o jogador pula em cima deles e de matar o jogador quando eles o acertam no chão. O vídeo a seguir mostra um gameplay da versão que você irá implementar:
Código-base
Aceite o projeto tp3-super-mario-bros-1-1 no GitHub classroom [nesse link]
Clone o seu novo repositório no seu computador:
# Substitua <GITHUB_USERNAME> pelo seu usuário do GitHub git clone https://github.com/ufmg-dcc192/tp3-super-mario-bros-1-1-<GITHUB_USERNAME>.gitAbra o projeto tp3-super-mario-bros-1-1 na CLion e, antes de começar a sua implementação, verique com cuidado as definições de métodos e atributos de cada classe:
AnimatorComponent
Componente para desenhar game objects com sprites estáticos ou animados (estende
DrawComponent).RectComponent
Componente para desenhar game objects em formato de retângulo.
AABBColliderComponent
Componente para detecção de colisão entre AABBs.
Block
Classe que estende
Actorpara representar um bloco do jogo.Goomba
Classe que estende
Actorpara representar o Goomba, inimigo que se movimenta horizontalmente de um lado para o outro.Mario
Classe que estende
Actorpara representar o Mário, que é controlado pelo jogador.Spawner
Classe que estende
Actorpara representar um “gatilho” (ou trigger) que cria um goomba quando o jogador está próximo.Texture
Classe auxiliar para carregamento de texturas da memória secundária
Observação: o código base desse projeto foi construído a partir do código do projeto anterior [TP2: Asteroids], portanto muitas das classes já foram introduzidas anteriormente.
Instruções
Parte 0: Preparação dos Assets
Antes de começar a programar, você irá preparar os assets (recursos) que serão carregados no início do jogo. Esses assets incluem os sprite sheets dos personagens e dos blocos da primeira fase do Super Mario Bros., além de um arquivo .csv com o tilemap dessa fase.
Sprite Sheet
Utilize o [FreeTexturePacker] para gerar os sprite sheets do mario, goomba e blocos. Desabilite a opção
Allow trim, utilize o formato de dadosjson (Array)e o métodoBestAreaFitpara exportar os sprite sheets. Copie os sprite sheets (imagens e dados) para os seus respectivos locais dentro do diretórioAssetsdo projeto. Por exemplo, copie o sprite sheet do Mario para o localAssets/Sprites/Mario.Tilemap
Utilize o [Tiled] para abrir o arquivo
Levels/Level1-1/level1-1.tmxe exportar seu conteúdo para um arquivoLevels/Level1-1/Level1-1.csv. Inspecione o csv gerado para compreender sua estrutura de tiles.
Parte 1: Carregando uma fase
Na primeira parte, você irá ler um arquivo .csv exportado pelo Tiled para carregar a primeira fase do jogo. Para poder visualizar os blocos na tela, você também terá que implementar o componente AnimatorComponent para desenhar apenas sprites estáticos por enquanto.
Texture.cpp
Implemente o método
Loadpara carregar uma textura com a SDL e enviá-la para a GPU com OpenGL. Veja os slides da aula 11 para referência. Inicialmente, você utilizará a funçãoIMG_Loadda bibliotecaSDL_imagepara carregar a textura da memória secundária. Armazene a altura e largura da textura carregada nos atributosmWidthemHeight, respectivamente. Em seguida, utilize as funções da OpenGL para enviar essa textura para a GPU. Quando for alocar a textura na GPU com a funçãoglGenTextures, guarde o ID da textura no atributomTextureID. Use esse valor para ligar a textura comglBindTexture. Quando for enviar a textura para a GPU comglTexImage2D, note que a altura e largura da textura já estão nos atributosmWidthemHeight. Como a arte dos sprites é toda feita em pixel art, utilize o valorGL_NEARESTpara os filtrosGL_TEXTURE_MIN_FILTEReGL_TEXTURE_MAG_FILTER, isso irá garantir que suas texturas terão alta definição quando aumentadas ou diminuídas.Implemente o método
SetActivepara ativar e ligar uma textura no pipeline da GPU. Veja os slides da aula 11 para referência.
Shader.cpp
- Implemente o método
SetIntegerUniformenviar um valor inteiro para uma variável uniforme do shader. Ele será usado para indicar a texture unit de um sampler2D. Veja os slides da aula 11 para referência.
- Implemente o método
Renderer.cpp
No final do construtor da classe, antes de ativar o shader
mBaseShader, chame o métodoSetIntegerUniformimplementado anteriormente para inicializar o uniformuTexturecom zero.Implemente o método
CreateSpriteVertspara criar os vértices e os indíces de um quadrado unitário no sistema de coordenadas normalizado da OpenGL (origem no centro). Esse quadraro será utilizado pelos shaders para desenhar os sprites animados. Por isso, além das posições dos vértices, você terá que adicionar os valores uv de cada um para fazer o mapameamento de texturo. Após criar os vértices e índices, instancia um novoVertexArraye o armazene no ponteiromSpriteVerts. Como todos os objetos do jogo são retângulos, é mais eficiente criar esse VertexArray como parte do renderer, pois podemos aproveitar esses mesmos vértices para desenhar todos os game objects. Note que o métodoCreateSpriteVertsé chamado durante a incialização do renderer.
VertexArray.cpp
- Implemente o construtor da classe para configurar os buffers de vértice e índice considerando agora que cada vértice tem, além de suas atributos de posição
(x,y), também possui atributos de mapeamentos de textura(u,v). Note no shader de vértice que os atributos de posição estão nalocation = 0e os textura nalocation = 1. Veja slides da aula 11 para referência.
- Implemente o construtor da classe para configurar os buffers de vértice e índice considerando agora que cada vértice tem, além de suas atributos de posição
Base.vert e Base.frag
No shader de vértice, passe a variável
inTexCoorddo shader de vértice para o de gragmento por meio da variávelfragTexCoord.Implemente a função main do shader de fragmento para combinar a cor sólida
uColorpassada como uniform com a textura do objetouTexture. Faça a amostragem da cor da textura com a funçãotexture()usando as coordenadas de texturauTexturee utilize a funçãomix()para interpolar entre a cor uniforme definida por uColor e a cor da textura, controlando a mistura pelo fatoruTextureFactor. Armazene o resultado final em outColor, determinando a cor exibida no fragmento renderizado. Essa interpolação de cores sólidas e de texturas nos permitirá usar o mesmo shader para desenhar geometricas básicas coloridas e sprites com texutras.
AnimatorComponent.cpp
Implemente o construtor da classe para carregar uma textura usando o método
GetTexturedo renderer. Note o métodoGetTexturejá está todo implementado e que ele carrega uma textura da memória secundária, armazenando-a em umacash, para que, caso você queira usar essa textura novamente, ela sera carregada mais rapidamente da memória principal.Implemente o o método
Drawpara desenhar um sprite estático com a funçãoRenderer::DrawTexturese o componente estiver marcado como visívelmIsVisible == true. Utilize a posição, tamanho e rotação do objeto dono do componente para desenhar a textura. Note que a funçãoRenderer::DrawTexturejá cria a model matrix com base nessas propriedades, então você não precisa passá-la do actor. Aliás, o méotodActor::GetModelMatrixfoi removido da class Actor, pois a criação das matrizes de modelo passou a ser responsabilidade do renderer.
Block.cpp
Implemente o construtor da classe para adicionar um componente
AnimatorComponent, passando uma string vazia no atributodataPath, já que os blocos não possuem animacão. No mário, o tamanho dos blocos são todos iguais32x32e, por isso, já foi criada uma variável estáticaGame::TILE_SIZEpara representar esse valor. Utilize esse variável para especificar as largura e altura do bloco. Para testar seu código até aqui, crie uma instância deBlockna funçãoInitializeActorscom uma textura da sua escolha.auto block = new Block(this, "../Assets/Sprites/Blocks/BlockA.png"); block->SetPosition(Vector2(100.0f, 100.0f));
Mario.cpp
Implemente o construtor da classe para adicionar um componente
AnimatorComponentno Mario. Por enquanto, utilize o sprite estáticoIdle.pngpara desenhar o personagem, não o sprite sheet. Para testar seu código até aqui, crie uma instância deMariona funçãoInitializeActorscom uma textura da sua escolha.mMario = new Mario(this); mMario->SetPosition(Vector2(200.0f, 100.0f));
Game.cpp
Implemente o método
LoadLevelpara ler o arquivo csv e instanciar uma matriz de inteirosLEVEL_HEIGHT X LEVEL_WIDTHrepresentando os IDs dos tiles. Teste o seu código chamando essa função emInitializeActor, carregando a fase do arquivolevel1-1.csv. Imprima a matriz na saída padrão comSDL_Loge compare a saída com o conteúdo do arquivo: eles devem ser os mesmos. Note que já foi incluída uma biblioteca CSV.h para te auxiliar no carregamento desse tipo de arquivo.Implemente o método
BuildLevelpara percorrer a matriz de tiles carregada no item anterior e instanciar game objects para o Mário, os canos e os blocos. Para saber qual ID de tile corresponde a qual textura, abra o arquivolevel1-1.tmxe inspecione os IDs de cada tile. Inicialize as posições dos objetos no mundo de acordo com as suas posições na matriz de tiles. Guarde a instância do mario no atributomMarioda classe game. Os blocos não precisam ser armazenados na classe.Adicione ao método
InitializeActorsa chamada para as funçõesLoadLeveleBuildLevel, nessa ordem, para que a fase seja carregada e instanciada no momento de inicialização do jogo.
Ao final dessa etapa, você deveria ver uma saída como a ilustrada na figura abaixo:

Parte 2: Movimentação de Câmera
Na segunda parte, você irá implementar o movimento do jogador e da câmera, para que ele possa navegar horizontalmente sem colisão ao longo da fase.
Base.vert
- Modifique o shader de vértice para subtrair a posição da câmera
uCameraPosda posição do vértice. Essa subtração deve ser feita no sistema de coordenadas do mundo, ou seja, multiplique o vértice pela model matriz antes de fazer a subtração.
- Modifique o shader de vértice para subtrair a posição da câmera
Game.cpp
- Implemente o médoto
UpdateCamerapara ajustar a posição da câmera conforme o personagem principal se desloca pelo cenário, mantendo-o visível e centralizado na tela. Garanta que ela não ultrapasse os limites esquerdo e direito do nível, resultando em um movimento suave e controlado da visualização. Note que classeGamepossui constantesLEVEL_WIDTH,LEVEL_HEIGHTeTILE_SIZEque podem te ajudar.
- Implemente o médoto
Mario.cpp
Adicione o componente
RigidBodyComponentno construtor do personagem para habilitar sua movimentação. Utilize a funçãoSetApplyGravitypara desabilitar a gravidade do personagem enquanto você estiver trabalhando na parte 2: isso irá facilitar o teste da câmera.Implemente o método
OnProcessInputpara mover o personagem horizontalemte por meio de aplicação de forçasApplyForcecom oRigibBodyComponent. Faça com que, ao pressionar a teclaD, o personagem se desloque para a direita e tenha sua escala horizontalmScale.xajustada para a direit1.0f; e, ao pressionar a teclaA, ele se mova para a esquerda com a escala invertida-1.0f, simulando o espelhamento do sprite. Atualize o estadomIsRunningpara indicar que o personagem está correndo enquanto uma dessas teclas estiver pressionada.Modifique o método
OnUpdatepara garantir que a posição horizontal do jogador esteja sempre à frente da posição horizontal da câmera.
Ao final dessa etapa, você deveria ver uma saída como no vídeo a seguir:
Parte 3: Detecção de Colisão com AABBs
Na terceira parte, você irá implementar o componente AABBColliderComponent para detectar colisões no jogo.
AABBColliderComponent.cpp
Implemente os métodos
GetMineGetMaxpara calcular os pontos de mínimo, máximo da AABB, respectivamente Lembre-se que a posição do actormOwner->GetPosition()representa o centro da AABB.Implemente método
Intersectpara verificar se duas AABBs têm interseçãoImplemente os método
GetMinVerticalOverlapeGetMinHorizontalOverlappara calcular as sobreposições verticais e horizontais entre duas AABBs, respectivamente. Utilize os valores de Min e Max das duas AABBs envolvidas. Veja slides da aula 8 para referência.Implemente os métodos
DetectHorizontalCollisioneDetectVertialCollisionpara separar uma AABB após uma colisão horizontal e vertical, respectivamente. Em ambos os casos, percorra todos os coliders ativos, ignorando a si mesmo e os desativados, e teste comIntersectse há interseção com cada um deles. Quando uma colisão for detectada, calcule a sobreposição mínima no eixo (GetMinVerticalOverlapouGetMinHorizontalOverlap) e resolva o deslocamento do corpo rígido para eliminar a interpenetração (ResolveVerticalCollisionsouResolveHorizontalCollisions). Em seguida, notifique o objeto dono sobre o evento de colisão (OnVerticalCollisionouOnHorizontalCollision) antes de retornar o valor da sobreposição detectada.Implemente os métodos
ResolveHorizontalCollisionseResolveVerticalCollisionspara resolver as colisões horizontais e verticais, respectivamente, entre os objetos do jogo. Em ambos os casos, você deve corrigir a posição do objeto após uma colisão. Desloque o objeto na direção oposta à sobreposição mínima detectada, removendo a interpenetração com o obstáculo, e anule a componente de sua velocidade (vertical ou horizontal) no corpo rígido para impedir que ele continue se movendo na direção da colisão. No métodoResolveVerticalCollisions, você também deve verificar se interpenetração vertical foi de cima para baixo (maior que zero) para, nesse caso, marcar que o actor está no chãomOwner->SetOnGround().
Block.cpp
1. Adicione o componente `AABBColliderComponent` ao construtor dos blocos para habilitar colisões do jogador com os blocos do nível. Na criação, marque o bloco como estático `isStatic = true` e como sendo da camada `ColliderLayer::Blocks`.Mario.cpp
Habilite a gravidade no personagem, removendo a chamada para a função
SetApplyGravityque você adicionou na etapa anteriorAdicione o componente
AABBColliderComponentno construtor do personagem para habilitar colisões do jogador com os blocos do nível. Por padrão, o componente assume que o objeto não é estático, então não é necessário marcá-lo como não estático. Configura camada do jogador comoColliderLayer::Player.Estenda o método
OnProcessInputpara implementar o pulo alterando a velocidade vertical do personagem instântaneamenteSetVelocity. Note que a classe Mario possui um atributomJumpSpeed, que configura a velocidade do pulo de forma similar ao jogo original. É importante destacar também que o jogador só pode pular quando estiver no chãomIsOnGround == true.
Ao final dessa parte, você deveria ser capaz de navegar na primeira fase com as mecânicas de correr e pular, porém sem animação, como no vídeo a segui (ative o modo debug do jogo):
Parte 4: Animações
Na quarta parte, você irá implementar o componente AnimatorComponent para animar os objetos do jogo.
Base.vert
Modifique o shader de vértice para, quando for passar as coordenadas uv para shader de fragmento via
fragTexCoord, ajuste os valores uvinTexCoordpara que cada vértice seja mapeado para a posição correta da textura dentro do sprite sheet. Para isso, basta multiplicar os valores uv eminTexCoordpelos componentes de escala deuTexRect.zwe somando o deslocamentouTexRect.xy. Não se esqueça de atribuir o resultado final àfragTexCoordpara que o fragment shader possa amostrar as cores do sprite correspondente.AnimatorComponent.cpp
Todos os quadros de um objeto estão armazenados no vetor
mSpriteSheetData. Cada elemento desse vetor é umVector4, representando as coordenadas e dimensões de um sprite no sprite sheet. Além disso, todas as animações estão armazenadas no mapamAnimations. Uma animação é identificada por um nome (string) e definida por um vetor de índices de quadros (armazenados emmSpriteSheetData). A nome da animação corrente é armazenado na variável membromAnimName.Adicione no final do construtor da classe uma chamada para o método
LoadSpriteSheetDataImplemente a função
Updatepara atualizar o quadro atual da animação de acordo com o tempo decorrido. Primeiro, verifique se a animação está pausadamIsPausedou se o vetor de animaçõesmAnimationsestá vazio. Em ambos os casos, você pode retornar da função pois não tem nada para atualizar. Se uma das condições anteriores forem falsas, devemos então atualizar o contador de animaçãomAnimTimer, avançando-o proporcionalmente à taxa de quadros por segundomAnimFPSe aodeltaTime, e reinicie o contador quando ele ultrapassar o número total de quadros da animação, garantindo que a animação seja reproduzida em loop contínuo enquanto não estiver pausada. Veja slides da aula 11 para referência.Modifique o método
Drawpara desenhar o sprite corrente da animação. Verifique se o componente está visível e, caso exista uma animação ativa, obtenha o índice do quadro atual a partir demAnimTimerpara acessar o retângulo de textura correspondente no sprite sheet. Caso não haja animação, utilize a textura completa. Determine se o sprite deve ser espelhado horizontalmente com base na escala do objeto e desenhe-o chamandorenderer->DrawTexture, informando posição, tamanho, rotação, textura, recorte e posição da câmera para exibir corretamente a animação na tela.Implemente a função
SetAnimationpara definir a animação atualmente ativa do componente. Atualize o nome da animação para o valor recebido e chameUpdate(0.0f)para garantir que o quadro exibido corresponda imediatamente ao início da nova animação, evitando que o personagem mostre um quadro incorreto ao trocar de estado.Implemente a função
AddAnimationpara registrar uma nova animação no componente. Associe o nome da animação ao vetor de índices dos quadros correspondentes no sprite sheet, armazenando essa relação no mapamAnimationspor meio deemplace. Isso permitirá que o componente acesse posteriormente a sequência correta de sprites ao reproduzir a animação com o nome especificado.
Mario.cpp
Modifique o construtor para que o personagem utilize o novo componente de desenho
AnimatorComponent, agora passando como parâmetros a textura do sprite sheet../Assets/Sprites/Mario/Mario.pnge seus metados"../Assets/Sprites/Mario/Mario.json. Crie as animações “dead”, “idle”, “jump” e “run” com o métodoAnimatorComponent::AddAnimation. Em seguida, configure a animação atual como “idle” usando o métodoAnimatorComponent::SetAnimatione o FPS das animações para10.0fusando o métodoAnimatorComponent::SetAnimFPS.Ao final desse item, você deveria ter o mesmo resultado da Parte 3, ou seja, o mário se movendo e colidindo mas sempre no estado idle.
Implemente a método
ManageAnimationspara selecionar a animação correta enquanto o jogador estiver vivo!mIsdead:- Se estiver no chão e correndo, a animação correta é
"run" - Se estiver no chão, mas não estiver correndo, a animação
"idle" - Se estiver não estiver no chão, a animação é
"jump"
- Se estiver no chão e correndo, a animação correta é
Ao final dessa parte, você deveria ver as animações de run, idle e jump tocando de acordo com o estado do jogador, como no vídeo a seguir:
Parte 5: Inimigos
Na quinta parte, você irá implementar os goombas e os spawners, que criam goombas quando o jogador está próximo.
Game.cpp
- Morifique o método
BuildLevelpara instanciar spawners de acordo com a matriz de tiles.
- Morifique o método
Goomba.cpp
Crie os componentes
RigidBodyComponent,AABBColliderComponent, eAnimatorComponentno construtor, de forma similar ao MarioImplemente a função
Killpara tratar o estado de morte do inimigo. Marque o Goomba comomIsDying = true, altere sua animação para"dead"para refletir visualmente o evento e desativeSetEnabled(false)tanto o rigid body quanto o colisor, impedindo novas interações físicas ou colisões enquanto o inimigo é removido ou executa sua animação final.Implemente a função
OnUpdatepara atualizar o estado do inimigo a cada quadro. Caso o Goomba esteja morrendo, reduza o temporizador de morte e destruamState = ActorState::Destroyo ator quando o tempo se esgotarmDyingTimer <= 0.0f. Além disso, verifique se o inimigo saiu da tela verticalmente — por exemplo, caindo além do limite inferior da janela — e, nesse caso, também defina seu estado como Destroy, removendo-o do jogo.Implemente o método
OnHorizontalCollisionpara definir o comportamento do inimigo ao colidir horizontalmente com outros objetos. Faça com que, ao colidir com blocos ou outros inimigos, o Goomba inverta sua direção de movimento conforme o sinal da sobreposição mínima, fazendo ele virar sempre que colidir. Quando a colisão ocorrer com o jogador, invoque o métodoKill()no objeto colidido para aplicar o efeito de dano ou morte ao personagem principal.
Spawner.cpp
- Implemente a função
OnUpdatepara controlar a geração de inimigos durante o jogo. Verifique se o jogador está a uma distância horizontal menor quemSpawnDistanceem relação ao spawner e, quando essa condição for atendida, crie um novo objeto Goomba na mesma posição do spawner. Após gerar o inimigo, defina o estado do spawner como Destroy, garantindo que ele seja removido e não produza múltiplos inimigos na mesma posição.
- Implemente a função
Mario.cpp
Modifique o método
OnUpdatepara fazer com que o Mario perca o estado de estar no chãomIsOnGround = falsesempre que sua velocidade vertical for diferente de zero, indicando que ele está pulando ou caindo. Verifique também se o Mario caiu abaixo da janela do jogo e, nesse caso, chame o método Kill() para que ele entre no estado de morte. Por fim, atualize as animações do personagem chamando ManageAnimations().Implemente o método
Killpara definir o comportamento do personagem ao morrer. Altere a animação atual para"dead"para representar visualmente a morte, marque o estado interno como morto e desative tanto o corpo rígido quanto o colisor, impedindo novas interações físicas ou colisões após o evento.Implemente o método
OnHorizontalCollisionpara tratar colisões horizontais do personagem com outros objetos. Verifique se o objeto colidido pertence à camada de inimigos e, nesse caso, chame o métodoKill()para executar a lógica de morte do Mario, interrompendo seu movimento e ativando a animação correspondente.Implemente a função
OnVerticalCollisionpara definir o comportamento do personagem ao colidir verticalmente com outros objetos. Quando a colisão ocorrer com um inimigo, faça com que o Mario o mate chamando o métodoKill()no inimigo e aplique uma nova velocidade vertical positiva ao corpo rígido do Mario, simulando o impulso de salto que ocorre ao pisar sobre o inimigo.
Essa etapa conclui as implementações básicas do jogo, que deveria ter um resultado como o mostrado no vídeo do início do roteiro.
Parte 6: Customização
Na sexta, e última etapa, você irá ajustar as variáveis do jogo para criar uma versão única do Super Mário Bros.
Desenhe o fundo do jogo com a textura
Background.pngCrie um nível novo usando o tiled e o carregue no jogo.
Implemente a movimentação dos blocos de tijolo (tipo B) quando o mario os acerta por baixo. Nesse caso, o bloco deve se mover para cima e para baixo, retornando exatamente no ponto que estava originalmente.
Implemente a geração (spawn) do cogumelo quando o jogador colidir com blocos de interrogação pré-definidos. Ou seja, nem todo bloco de interrogação deve gerar o cogumelo, apenas alguns pré-selecionados. O cogumelo deve se mover como no jogo original, sendo destruído quando sair dos limites horizontais e verticais da fase. Quando o jogador colidir com o cogumelo, o mario deve ficar grande e, quando estiver nesse estado e colidir com inimigos, ao invés de morrer, o mario deve ficar pequeno novamente, como no jogo orinal.
Submissão
Para submeter o seu trabalho, basta fazer o commit e o push das suas alterações no repositório que foi criado para você no GitHub classroom.
Observação: você pode realizar quantos commits quiser. Isso é inclusive recomendado para dividir o problema em partes menores. Além disso, as mensagens nos commits não precisam ser necessariamente “Submissão TP3”. Você pode também criar branches se quiser, mas apenas o último commit da branch main será avaliado.
git add .
git commit -m 'Submissão TP3'
git push
Barema
- Parte 1: Carregando uma fase (5%)
- Parte 2: Movimentação de Câmera (5%)
- Parte 3: Detecção de Colisão com AABBs (25%)
- Parte 4: Animações (25%)
- Parte 5: Inimigos (10%)
- Parte 6: Customização (30%)