Índice:

Jogo de plataforma controlado por Arduino com joystick e receptor IR: 3 etapas (com imagens)
Jogo de plataforma controlado por Arduino com joystick e receptor IR: 3 etapas (com imagens)

Vídeo: Jogo de plataforma controlado por Arduino com joystick e receptor IR: 3 etapas (com imagens)

Vídeo: Jogo de plataforma controlado por Arduino com joystick e receptor IR: 3 etapas (com imagens)
Vídeo: ROBÔ BLUETOOTH - Arduino - CONTROLADO PELO CELULAR ANDROID 2024, Julho
Anonim
Jogo de plataforma controlado por Arduino com joystick e receptor IR
Jogo de plataforma controlado por Arduino com joystick e receptor IR

Hoje, vamos usar um microcontrolador Arduino para controlar um jogo de plataforma simples baseado em C #. Estou usando o Arduino para obter a entrada de um módulo de joystick e enviar essa entrada para o aplicativo C # que escuta e decodifica a entrada por meio de uma conexão serial. Embora você não precise de nenhuma experiência anterior na construção de videogames para concluir o projeto, pode levar algum tempo para absorver algumas das coisas que acontecem no "loop do jogo", que discutiremos mais tarde.

Para concluir este projeto, você precisará de:

  • Comunidade Visual Studio
  • Um Arduino Uno (ou similar)
  • Um módulo controlador de joystick
  • Paciência

Se você está pronto para começar, continue!

Etapa 1: conecte o joystick e o LED IV

Conecte o Joystick e o LED IV
Conecte o Joystick e o LED IV
Conecte o Joystick e o LED IV
Conecte o Joystick e o LED IV

Aqui, a conexão é bastante simples. Incluí diagramas mostrando apenas o joystick conectado, bem como a configuração que estou usando, que inclui o joystick e um LED infravermelho para controlar o jogo com um controle remoto, que vem com muitos kits Arduino. Isso é opcional, mas parecia uma ideia legal poder fazer jogos sem fio.

Os pinos usados na configuração são:

  • A0 (analógico) <- Horizontal ou eixo X
  • A1 (analógico) <- Vertical ou eixo Y
  • Pino 2 <- entrada do joystick
  • Pino 2 <- entrada de LED infravermelho
  • VCC <- 5V
  • Chão
  • Ground # 2

Etapa 2: Crie um novo esboço

Crie um novo esboço
Crie um novo esboço

Começaremos criando nosso arquivo de esboço do Arduino. Isso pesquisa o joystick para alterações e envia essas alterações para o programa C # a cada vários milissegundos. Em um videogame real, verificaríamos a entrada de uma porta serial em um loop de jogo, mas comecei o jogo como um experimento, de modo que a taxa de quadros é, na verdade, baseada no número de eventos na porta serial. Na verdade, eu tinha começado o projeto no projeto irmão do Arduino, Processing, mas era muito, muito mais lento e não conseguia lidar com o número de caixas na tela.

Portanto, primeiro crie um novo Sketch no programa editor de código do Arduino. Vou mostrar meu código e explicar o que ele faz:

#include "IRremote.h"

// Variáveis IR int receiver = 3; // Pino de sinal do receptor IRrecv irrecv (receptor); // cria instância de 'irrecv' decode_results results; // cria instância de 'decode_results' // Joystick / variáveis de jogo int xPos = 507; int yPos = 507; byte joyXPin = A0; byte joyYPin = A1; byte joySwitch = 2; clickCounter de byte volátil = -1; int minMoveHigh = 530; int minMoveLow = 490; int currentSpeed = 550; // Padrão = uma velocidade média int speedIncrement = 25; // Quantidade para aumentar / diminuir a velocidade com Y input unsigned long current = 0; // Mantém o carimbo de data / hora atual int wait = 40; // ms para esperar entre as mensagens [Nota: espera inferior = taxa de quadros mais rápida] volatile bool buttonPressed = false; // Verifica se o botão está pressionado void setup () {Serial.begin (9600); pinMode (joySwitch, INPUT_PULLUP); attachInterrupt (0, salto, QUEDA); corrente = milis (); // Configure a hora atual // Configure o receptor infravermelho: irrecv.enableIRIn (); // Iniciar o receptor} // configurar void loop () {int xMovement = analogRead (joyXPin); int yPos = analogRead (joyYPin); // Manipula o movimento do Joystick X independentemente do tempo: if (xMovement> minMoveHigh || xMovement current + wait) {currentSpeed = yPos> minMoveLow && yPos <minMoveHigh // Se apenas movido um pouco …? currentSpeed //… apenas retorna a velocidade atual: getSpeed (yPos); // Só mude yPos se o joystick se mover significativamente // int distance =; Serial.print ((String) xPos + "," + (String) yPos + ',' + (String) currentSpeed + '\ n'); corrente = milis (); }} // loop int getSpeed (int yPos) {// Valores negativos indicam Joystick movido para cima if (yPos 1023? 1023: currentSpeed + speedIncrement;} else if (yPos> minMoveHigh) // Interpretado "Down" {// Proteger de indo abaixo de 0 retornar currentSpeed - speedIncrement <0? 0: currentSpeed - speedIncrement;}} // getSpeed void jump () {buttonPressed = true; // Indica que o botão foi pressionado.} // jump // Quando um botão é pressionado no remoto, trata da resposta adequada void translateIR (decode_results results) // executa a ação com base no código IR recebido {switch (results.value) {case 0xFF18E7: //Serial.println("2 "); currentSpeed + = speedIncrement * 2; break; case 0xFF10EF: //Serial.println("4 "); xPos = -900; break; case 0xFF38C7: //Serial.println("5"); jump (); break; case 0xFF5AA5: // Serial. println ("6"); xPos = 900; break; case 0xFF4AB5: //Serial.println("8 "); currentSpeed - = speedIncrement * 2; break; default: //Serial.println (" outro botão "); break;} // Fim da chave} // END translateIR

Tentei criar o código para ser autoexplicativo, mas existem algumas coisas que vale a pena mencionar. Uma coisa que tentei explicar foi nas seguintes linhas:

int minYMoveUp = 520;

int minYMoveDown = 500;

Quando o programa está em execução, a entrada analógica do joystick tende a pular, geralmente ficando em torno de 507. Para corrigir isso, a entrada não muda, a menos que seja maior que minYMoveUp ou menor que minYMoveDown.

pinMode (joySwitch, INPUT_PULLUP);

attachInterrupt (0, salto, QUEDA);

O método attachInterrupt () nos permite interromper o loop normal a qualquer momento, para que possamos receber informações, como o botão pressionado quando o botão do joystick é clicado. Aqui, anexamos a interrupção na linha anterior, usando o método pinMode (). Uma observação importante aqui é que, para conectar uma interrupção no Arduino Uno, você precisa usar o pino 2 ou 3. Outros modelos usam pinos de interrupção diferentes, então você pode ter que verificar quais pinos seu modelo usa no site do Arduino. O segundo parâmetro é para o método de retorno de chamada, aqui chamado de ISR ou "Rotina de serviço de interrupção". Não deve receber nenhum parâmetro ou retornar nada.

Serial.print (…)

Esta é a linha que enviará nossos dados para o jogo C #. Aqui, enviamos a leitura do eixo X, a leitura do eixo Y e uma variável de velocidade para o jogo. Essas leituras podem ser expandidas para incluir outras entradas e leituras para tornar o jogo mais interessante, mas aqui, usaremos apenas algumas.

Se você estiver pronto para testar seu código, carregue-o no Arduino e pressione [Shift] + [Ctrl] + [M] para abrir o monitor serial e ver se você está obtendo alguma saída. Se você estiver recebendo dados do Arduino, estamos prontos para avançar para a parte C # do código …

Etapa 3: Crie o projeto C #

Para exibir nossos gráficos, inicialmente comecei um projeto em Processing, mas depois decidi que seria muito lento mostrar todos os objetos que precisamos exibir. Portanto, optei por usar C #, que acabou sendo muito mais suave e mais responsivo ao lidar com nossas entradas.

Para a parte C # do projeto, é melhor simplesmente baixar o arquivo.zip e extraí-lo em sua própria pasta e, em seguida, modificá-lo. Existem duas pastas no arquivo zip. Para abrir o projeto no Visual Studio, insira a pasta RunnerGame_CSharp no Windows Explorer. Aqui, clique duas vezes no arquivo.sln (solução) e o VS carregará o projeto.

Existem algumas classes diferentes que criei para o jogo. Não vou entrar em todos os detalhes sobre cada aula, mas vou dar uma visão geral de para que servem as aulas principais.

The Box Class

Eu criei a classe box para permitir que você crie objetos retangulares simples que podem ser desenhados na tela em um formulário de janelas. A ideia é criar uma classe que pode ser estendida usando outras classes que podem querer desenhar algum tipo de gráfico. A palavra-chave "virtual" é usada para que outras classes possam substituí-las (usando a palavra-chave "substituir"). Dessa forma, podemos obter o mesmo comportamento para a classe Player e a classe Platform quando precisarmos, e também modificar os objetos como for necessário.

Não se preocupe muito com todas as propriedades e chamadas de desenho. Escrevi esta classe para poder estendê-la a qualquer jogo ou programa gráfico que queira fazer no futuro. Se você precisa simplesmente desenhar um retângulo na hora, não precisa escrever uma grande classe como esta. A documentação do C # tem bons exemplos de como fazer isso.

No entanto, vou apresentar um pouco da lógica da minha classe "Box":

public virtual bool IsCollidedX (Box otherObject) {…}

Aqui, verificamos se há colisões com objetos na direção X, porque o jogador só precisa verificar se há colisões na direção Y (para cima e para baixo) se ele estiver alinhado com ela na tela.

public virtual bool IsCollidedY (Box otherObject) {…}

Quando estamos sobre ou sob outro objeto do jogo, verificamos se há colisões Y.

public virtual bool IsCollided (Box otherObject) {…}

Isso combina colisões X e Y, retornando se algum objeto colidiu com este.

public virtual void OnPaint (Gráficos) {…}

Usando o método acima, passamos qualquer objeto gráfico e o usamos enquanto o programa está sendo executado. Criamos quaisquer retângulos que precisem ser desenhados. Isso pode ser usado para uma variedade de animações, no entanto. Para nossos propósitos, retângulos servem tanto para as plataformas quanto para o jogador.

A classe de personagem

A classe Character estende minha classe Box, então temos certas físicas prontas para usar. Criei o método "CheckForCollisions" para verificar rapidamente todas as plataformas que criamos para uma colisão. O método "Jump" define a velocidade ascendente do jogador para a variável JumpSpeed, que é então modificada quadro a quadro na classe MainWindow.

As colisões são tratadas de forma um pouco diferente aqui do que na classe Box. Decidi neste jogo que se pularmos para cima, podemos pular através de uma plataforma, mas ela pegará nosso jogador na descida se colidir com ela.

A classe de plataforma

Neste jogo, utilizo apenas o construtor desta classe que recebe uma coordenada X como entrada, calculando todas as localizações X das plataformas na classe MainWindow. Cada plataforma é configurada em uma coordenada Y aleatória de 1/2 da tela a 3/4 da altura da tela. A altura, largura e cor também são geradas aleatoriamente.

A classe MainWindow

É aqui que colocamos toda a lógica a ser usada enquanto o jogo está rodando. Primeiro, no construtor, imprimimos todas as portas COM disponíveis para o programa.

foreach (porta de string em SerialPort. GetPortNames ())

Console. WriteLine ("PORTAS DISPONÍVEIS:" + porta);

Nós escolhemos em qual iremos aceitar comunicações, de acordo com qual porta seu Arduino já está usando:

SerialPort = novo SerialPort (SerialPort. GetPortNames () [2], 9600, Parity. None, 8, StopBits. One);

Preste muita atenção ao comando: SerialPort. GetPortNames () [2]. O [2] significa qual porta serial usar. Por exemplo, se o programa imprimiu "COM1, COM2, COM3", estaríamos ouvindo em COM3 porque a numeração começa em 0 na matriz.

Também no construtor, criamos todas as plataformas com espaçamento semi-aleatório e posicionamento na direção Y na tela. Todas as plataformas são adicionadas a um objeto List, que em C # é simplesmente uma maneira muito amigável e eficiente de gerenciar uma estrutura de dados semelhante a um array. Em seguida, criamos o Player, que é nosso objeto Character, definimos a pontuação como 0 e definimos GameOver como false.

private static void DataReceived (object sender, SerialDataReceivedEventArgs e)

Este é o método que é chamado quando os dados são recebidos na porta serial. É aqui que aplicamos toda a nossa física, decidimos se exibir o jogo, mover as plataformas, etc. Se você já construiu um jogo, geralmente tem o que é chamado de "loop de jogo", que é chamado cada vez que o quadro atualiza. Neste jogo, o método DataReceived atua como o loop do jogo, apenas manipulando a física à medida que os dados são recebidos do controlador. Pode ter funcionado melhor configurar um Timer na janela principal e atualizar os objetos com base nos dados recebidos, mas como este é um projeto Arduino, eu queria fazer um jogo que realmente rodasse com base nos dados vindos dele.

Em conclusão, esta configuração oferece uma boa base para expandir o jogo para algo utilizável. Embora a física não seja perfeita, ela funciona bem para nossos propósitos, que é usar o Arduino para algo que todos gostam: jogar!

Recomendado: