terça-feira, 19 de novembro de 2019

Atividade 12 - MEF 2


MEF_Tarefa1
Utilizando a definição da Wikipédia, o conceito de Máquina de Estado Finita é concebido como uma máquina abstrata que deve estar em um de um número finito de estados. A máquina está em apenas um estado por vez, este estado é chamado de estado atual. Uma transição indica uma mudança de estado e é descrita por uma condição que precisa ser realizada para que a transição para outro estado ocorra.
No caso da tarefa pedida, a maquina de estado é utilizada para saber o estado atual do personagem e assim sincronizar a realização do movimento com a imagem que representa o estado do personagem, no caso se ele deve ir para a direita, o movimento deve ser feito para esse lado e a imagem deve mostrá-lo olhando para a direita.
A dificuldade que tive ao fazer essa código foi o fato de que, ao pressionar o botão, o personagem se movimentava várias casas ao mesmo tempo. Para solucionar isso usei um sistema de flag, onde, quando a tecla era pressionada, a variável de controle era verdadeira e a ação de andar só seria feita em um espaço por pressionar de botão.

Atividade 11 - Máquina de Estados Finita

O objetivo desta atividade é reimplementar a máquina de estados já criada, substituindo a variável keyPressed pela função keyPressed(), explicando as diferenças de uso, vantagens e desvantagens.

Primeiramente, é importante estabelecer as diferenças entre a variável booleana keyPressed e a própria função keyPressed(). No caso da variável, podemos inseri-la em qualquer função do nosso programa como um elemento pontual, tornando-o mais extenso por causa de verificação true ou false, porém mais prático se houverem poucas instâncias.

No entanto, ao optar pela função keyPressed(), podemos criar uma única variável booleana (verificador) no começo do código, e depois apenas coordenar as ações com o verificador, as ferramentas "keyCode" e "key" e a função keyReleased(). Dessa forma, podemos inserir várias ações sem prejudicar a legibilidade do código.

No código da Máquina de Estados criada, foram criadas armazenadores de imagem pelo PImage, e constantes "final int" para os Estados. Por padrão, o Estado irá ser "parado" e o tempo será 0. No "void setup" são carregadas as imagens.

No "void draw" são colocadas as funções criadas processaEstados(), mostraMario(estadoMario) e println(estadoMario, tempo), este último para indicar o tempo em que a ação está sendo realizada e o número do Estado (0 = parado, 1 = andando, 2= martelada).

No processaEstados() são usados testes booleanos para mudanças dos estados, no mostraMario exibem-se as imagens de acordo com o Estado atual e na função keyPressed() verifica-se as teclas pressionadas e muda-se o estadoMario até que se solte a tecla.

Atividade 10 - Tilesets

Para criar o mapa desejado, utilizei os dois tiles abaixo.



No programa primeiro iniciei as variáveis PImage, que guardam as informações das imagens escolhidas.

Depois fiz uma matriz que mapeia as imagens dos tiles de acordo com o número dado na função que será implementada posteriormente, no comando switch.


Então, fiz uma função que lê a matriz e carrega a imagem de acordo com a numeração dada pelo comando switch em cada posição. Caso seja 0, carrega a imagem da grama, se for 1, carrega a imagem da água. Para a imagem não ficar muito grande, fiz os espaçamentos horizontais entre as unidades serem de 60 pixels, e os verticais, de 30 pixels. O resultado pode ser verificado abaixo:


O conceito de Tesselação (do latim tessellae) é o recobrimento de superfícies bidimensionais (planos) com unidades básicas sendo polígonos congruentes ou não, de forma que não existam espaços entre eles e de modo que a superfície total seja igual à área recoberta.

Isso é exatamente o que ocorre com os cenários de um jogo 2D, há uma área fixa que é recoberta por unidades, os tiles, sem que haja nenhum espaço, para dar a sensação de continuidade e aprimorar a experiência do jogador.


Link do código: https://drive.google.com/open?id=0B3-iqp7LYuNJS2k2VFJUTHlNems

Atividade 9 - Pong

Nesta tarefa, devemos explicar e exemplificar a diferença entre o uso da variável de sistema keyPressed e a função keyPressed() com base no desenvolvimento de uma versão simplificada do jogo Pong, que considere a colisão do centro da bola com o bastão e com dois ou mais obstáculos.

Para isso, desenvolvi uma versão do jogo Pong, que inicia com uma tela que solicita ao usuário para apertar um botão para começar o jogo:

Tela inicial.

Criei uma variável booleana chamada tela, que quando verdadeira cria esta página. Quando o 'espaço' é pressionado, o booleano tela fica falso, iniciando o jogo:

void keyPressed() {
  if(keyCode == 32) {
    tela = false;
  }
  if(tela == false && keyCode == 82) {
    tela = true;
    
    // reset
    barraX = (width/2)-(larguraB/2);
    bolaX = 15;
    bolaY = 100;
  }
}

Foi usada a função keyPressed() para verificar se o botão correto foi pressionado e realizar a ação;

A diferença entre a função keyPressed() e a variável keyPressed, é que nesta última ela pode ser usada em um if para verificar se um botão foi pressionado, baseado em um keyCode.
Já na função é possível fazer isto de maneira automática, sem a necessidade de um if, pois ao criar a função já está se assumindo que um botão foi pressionado.
O usuário pode vencer ou perder. Na tela principal existem duas caixas em que a bola deve colidir. Caso a bola ultrapasse o "chão" é apresentada uma tela que informa que o usuário perdeu a partida, mas que pode reiniciá-la ao apertar o botão 'R'.

O usuário pode perder a partida caso a bola ultrapasse o limite inferior da tela.

Para calcular a distância entre a bola e as caixas foi usada esta função:

float distancia(float x1, float y1, float x2, float y2) {
  float b, c;
  b = abs(x1 - x2) * abs(x1 - x2);
  c = abs(y1 - y2) * abs(y1 - y2);
  float a = sqrt(b + c);
  return a;
}

Caso o usuário deixe a bola cair e perca a partida, esta tela é apresentada, solicitando que ele reinicie a partida.

Para organizar melhor o código, decidi implementar cada funcionalidade separadamente. No draw() elas são chamadas após o usuário iniciar o jogo:

if(tela) {
    background(29, 212, 216);
    fill(255);
    textSize(15);
    text("APERTE 'ESPAÇO' PARA COMEÇAR", 25, height/2);
    stroke(255);
    line(20, (height/2)+20, width-30, (height/2)+20);
    line(50, (height/2)+35, width-65, (height/2)+35);
  }
  else {
    background(255, 193, 7);
    
    colisaoBola();
    ellipse(bolaX, bolaY, raio, raio);
    
    colisaoBarra();
    rect(barraX, height-alturaB, larguraB, alturaB);
    
    colisaoSprites();
  }


Caso as duas caixas sejam eliminadas e a bola não caia no chão, o usuário vence a partida.

Colisão com a barra e as paredes implementadas.

 A colisão da bola foi implementada nesta função:

void colisaoBola() {
  bolaX += velX;
  bolaY += velY;
  
  // paredes
  if((bolaX > width-(raio/2)) || (bolaX < 0+(raio/2))) {
    velX = -velX;
  }
  if(bolaY < 0+(raio/2)) {
    velY = -velY;
  }
  
  // barra
  if((bolaX + raio/2 >= barraX) && (bolaX - raio/2 <= barraX + larguraB) && 
    (bolaY + raio/2 >= height - alturaB) && (bolaY + raio/2 <= height)) {
    velY = -velY;
  }
  
  // caso caia no chão
  if(bolaY > height) {
    background(255, 87, 34);
    textSize(19);
    fill(255);
    stroke(255);

    text("VOCÊ PERDEU!", width/2 - 70, height/2-25);
    line(25, height/2-7, width-28, height/2-7);
    text ("APERTE 'R' PARA REINICIAR", 25, height/2+25);
  }
}

Ao apertar o botão 'R' todos os valores iniciais são "resetados", ou seja, seus valores atuais são atualizados como iniciais. Assim, foi possível reiniciar o game.

Caso o jogador vença, aparecerá uma tela de vitória.

Link do Código:  https://drive.google.com/open?id=1S0aP7P3ImJCn0xRmsgp3qoSmpQTyVaGH