Tabla de contenido:

Juego de plataformas controlado por Arduino con joystick y receptor de infrarrojos: 3 pasos (con imágenes)
Juego de plataformas controlado por Arduino con joystick y receptor de infrarrojos: 3 pasos (con imágenes)

Video: Juego de plataformas controlado por Arduino con joystick y receptor de infrarrojos: 3 pasos (con imágenes)

Video: Juego de plataformas controlado por Arduino con joystick y receptor de infrarrojos: 3 pasos (con imágenes)
Video: ¿Quieres ir más lejos? Aprende a programar microcontroladores PIC 2024, Mes de julio
Anonim
Juego de plataformas controlado por Arduino con joystick y receptor de infrarrojos
Juego de plataformas controlado por Arduino con joystick y receptor de infrarrojos

Hoy, usaremos un microcontrolador Arduino para controlar un simple juego de plataformas basado en C #. Estoy usando Arduino para tomar la entrada de un módulo de joystick y enviar esa entrada a la aplicación C # que escucha y decodifica la entrada a través de una conexión en serie. Aunque no necesita ninguna experiencia previa en la creación de videojuegos para completar el proyecto, es posible que necesite algo de tiempo para absorber algunas de las cosas que suceden en el "bucle del juego", que discutiremos más adelante.

Para completar este proyecto, necesitará:

  • Comunidad de Visual Studio
  • Un Arduino Uno (o similar)
  • Un módulo de controlador de joystick
  • Paciencia

Si está listo para comenzar, ¡continúe!

Paso 1: conecte el joystick y el LED de infrarrojos

Conecte el joystick y el LED de infrarrojos
Conecte el joystick y el LED de infrarrojos
Conecte el joystick y el LED de infrarrojos
Conecte el joystick y el LED de infrarrojos

Aquí, la conexión es bastante simple. He incluido diagramas que muestran solo el joystick conectado, así como la configuración que estoy usando, que incluye el joystick más un LED infrarrojo para controlar el juego con un control remoto, que viene con muchos kits Arduino. Esto es opcional, pero parecía una buena idea poder hacer juegos inalámbricos.

Los pines utilizados en la configuración son:

  • A0 (analógico) <- Horizontal o eje X
  • A1 (analógico) <- Eje vertical o Y
  • Pin 2 <- Entrada del interruptor del joystick
  • Pin 2 <- Entrada de LED infrarrojos
  • VCC <- 5 V
  • Suelo
  • Tierra # 2

Paso 2: crea un nuevo boceto

Crear un nuevo boceto
Crear un nuevo boceto

Comenzaremos con la creación de nuestro archivo de boceto Arduino. Esto sondea el joystick en busca de cambios y envía esos cambios al programa C # cada varios milisegundos. En un videojuego real, verificaríamos el puerto serie en un bucle de juego para la entrada, pero comencé el juego como un experimento, por lo que la velocidad de fotogramas se basa en la cantidad de eventos en el puerto serie. De hecho, había comenzado el proyecto en el proyecto hermano de Arduino, Processing, pero resultó que era mucho, mucho más lento y no podía manejar la cantidad de cajas en la pantalla.

Entonces, primero cree un nuevo Sketch en el programa editor de código Arduino. Mostraré mi código y luego explicaré lo que hace:

#include "IRremote.h"

// IR variables int receptor = 3; // Pin de señal del receptor de infrarrojos IRrecv irrecv (receptor); // crea una instancia de los resultados de decode_results 'irrecv'; // crea una instancia de 'decode_results' // Joystick / variables de juego int xPos = 507; int yPos = 507; byte joyXPin = A0; byte joyYPin = A1; byte joySwitch = 2; clickCounter de bytes volátiles = -1; int minMoveHigh = 530; int minMoveLow = 490; int currentSpeed = 550; // Predeterminado = una velocidad promedio int speedIncrement = 25; // Cantidad para aumentar / disminuir la velocidad con la entrada Y unsigned long current = 0; // Mantiene la marca de tiempo actual int wait = 40; // ms para esperar entre mensajes [Nota: espera más baja = velocidad de fotogramas más rápida] volatile bool buttonPressed = false; // Calibre si se presiona el botón void setup () {Serial.begin (9600); pinMode (joySwitch, INPUT_PULLUP); attachInterrupt (0, salto, CAYENDO); actual = milis (); // Configurar la hora actual // Configurar el receptor de infrarrojos: irrecv.enableIRIn (); // Iniciar el receptor} // configurar void loop () {int xMovement = analogRead (joyXPin); int yPos = analogRead (joyYPin); // Maneja el movimiento del Joystick X independientemente del tiempo: if (xMovement> minMoveHigh || xMovement current + wait) {currentSpeed = yPos> minMoveLow && yPos <minMoveHigh // ¿Si solo se moviera un poco…? currentSpeed //… simplemente devuelve la velocidad actual: getSpeed (yPos); // Cambie yPos solo si el joystick se movió significativamente // int distance =; Serial.print ((String) xPos + "," + (String) yPos + ',' + (String) currentSpeed + '\ n'); actual = milis (); }} // loop int getSpeed (int yPos) {// Los valores negativos indican que el joystick se movió hacia arriba if (yPos 1023? 1023: currentSpeed + speedIncrement;} else if (yPos> minMoveHigh) // Interpretado "Abajo" {// Proteger de pasando por debajo de 0 return currentSpeed - speedIncrement <0? 0: currentSpeed - speedIncrement;}} // getSpeed void jump () {buttonPressed = true; // Indica que se presionó el botón.} // jump // Cuando se presiona un botón en el remoto, maneja la respuesta apropiada void translateIR (decode_results results) // toma acción en base al código IR recibido {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 (" otro botón "); break;} // Interruptor final} // END translateIR

Traté de crear el código para que se explicara por sí mismo, pero hay algunas cosas que vale la pena mencionar. Una cosa que traté de tener en cuenta fue en las siguientes líneas:

int minYMoveUp = 520;

int minYMoveDown = 500;

Cuando el programa se está ejecutando, la entrada analógica del joystick tiende a saltar, generalmente permaneciendo alrededor de 507. Para corregir esto, la entrada no cambia a menos que sea mayor que minYMoveUp o menor que minYMoveDown.

pinMode (joySwitch, INPUT_PULLUP);

attachInterrupt (0, salto, CAYENDO);

El método attachInterrupt () nos permite interrumpir el bucle normal en cualquier momento, para que podamos tomar entradas, como cuando se presiona el botón cuando se hace clic en el botón del joystick. Aquí, hemos adjuntado la interrupción en la línea anterior, usando el método pinMode (). Una nota importante aquí es que para adjuntar una interrupción en el Arduino Uno, debe usar el pin 2 o el 3. Otros modelos usan diferentes pines de interrupción, por lo que es posible que tenga que verificar qué pines usa su modelo en el sitio web de Arduino. El segundo parámetro es para el método de devolución de llamada, aquí llamado ISR o "Rutina de servicio de interrupción". No debe tomar ningún parámetro ni devolver nada.

Serial.print (…)

Esta es la línea que enviará nuestros datos al juego C #. Aquí, enviamos la lectura del eje X, la lectura del eje Y y una variable de velocidad al juego. Estas lecturas se pueden ampliar para incluir otras entradas y lecturas para hacer el juego más interesante, pero aquí, solo usaremos un par.

Si está listo para probar su código, cárguelo en Arduino y presione [Shift] + [Ctrl] + [M] para abrir el monitor serial y ver si está obteniendo algún resultado. Si está recibiendo datos del Arduino, estamos listos para pasar a la parte C # del código …

Paso 3: crear el proyecto de C #

Para mostrar nuestros gráficos, inicialmente comencé un proyecto en Processing, pero luego decidí que sería demasiado lento mostrar todos los objetos que necesitamos mostrar. Entonces, elegí usar C #, que resultó ser mucho más suave y más receptivo al manejar nuestra entrada.

Para la parte C # del proyecto, es mejor simplemente descargar el archivo.zip y extraerlo en su propia carpeta, luego modificarlo. Hay dos carpetas en el archivo zip. Para abrir el proyecto en Visual Studio, ingrese la carpeta RunnerGame_CSharp en el Explorador de Windows. Aquí, haga doble clic en el archivo.sln (solución) y VS cargará el proyecto.

Hay algunas clases diferentes que creé para el juego. No entraré en todos los detalles sobre cada clase, pero daré una descripción general de para qué sirven las clases principales.

La clase de caja

Creé la clase de caja para permitirle crear objetos rectangulares simples que se pueden dibujar en la pantalla en forma de Windows. La idea es crear una clase que se pueda ampliar utilizando otras clases que quieran dibujar algún tipo de gráfico. La palabra clave "virtual" se utiliza para que otras clases puedan anularlas (utilizando la palabra clave "anular"). De esa manera, podemos obtener el mismo comportamiento para la clase Player y la clase Platform cuando sea necesario, y también modificar los objetos como sea necesario.

No se preocupe demasiado por todas las propiedades y atraiga llamadas. Escribí esta clase para poder extenderla a cualquier juego o programa de gráficos que quisiera hacer en el futuro. Si simplemente necesita dibujar un rectángulo sobre la marcha, no tiene que escribir una clase grande como esta. La documentación de C # tiene buenos ejemplos de cómo hacer esto.

Sin embargo, expondré parte de la lógica de mi clase "Box":

public virtual bool IsCollidedX (Box otherObject) {…}

Aquí verificamos si hay colisiones con objetos en la dirección X, porque el jugador solo necesita verificar si hay colisiones en la dirección Y (arriba y abajo) si está alineado con él en la pantalla.

public virtual bool IsCollidedY (Box otherObject) {…}

Cuando estamos encima o debajo de otro objeto del juego, buscamos colisiones en Y.

public virtual bool IsCollided (Box otherObject) {…}

Esto combina colisiones X e Y, devolviendo si algún objeto choca con este.

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

Usando el método anterior, pasamos cualquier objeto gráfico y lo usamos mientras el programa se está ejecutando. Creamos los rectángulos que puedan necesitar ser dibujados. Sin embargo, esto podría usarse para una variedad de animaciones. Para nuestros propósitos, los rectángulos funcionarán bien tanto para las plataformas como para el jugador.

La clase de personaje

La clase Character amplía mi clase Box, por lo que tenemos ciertas físicas listas para usar. Creé el método "CheckForCollisions" para verificar rápidamente todas las plataformas que hemos creado para detectar una colisión. El método "Jump" establece la velocidad ascendente del jugador en la variable JumpSpeed, que luego se modifica cuadro por cuadro en la clase MainWindow.

Las colisiones se manejan de manera ligeramente diferente aquí que en la clase Box. Decidí en este juego que si saltamos hacia arriba, podemos saltar a través de una plataforma, pero atrapará a nuestro jugador en el camino hacia abajo si choca con ella.

La clase de plataforma

En este juego, solo uso el constructor de esta clase que toma una coordenada X como entrada, calculando todas las ubicaciones X de las plataformas en la clase MainWindow. Cada plataforma está configurada en una coordenada Y aleatoria desde la mitad de la pantalla hasta 3/4 de la altura de la pantalla. La altura, el ancho y el color también se generan aleatoriamente.

La clase MainWindow

Aquí es donde ponemos toda la lógica que se utilizará mientras se ejecuta el juego. Primero, en el constructor, imprimimos todos los puertos COM disponibles para el programa.

foreach (puerto de cadena en SerialPort. GetPortNames ())

Console. WriteLine ("PUERTOS DISPONIBLES:" + puerto);

Elegimos en cuál aceptaremos las comunicaciones, según el puerto que su Arduino ya esté usando:

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

Preste mucha atención al comando: SerialPort. GetPortNames () [2]. El [2] significa qué puerto serie utilizar. Por ejemplo, si el programa imprimiera "COM1, COM2, COM3", estaríamos escuchando en COM3 porque la numeración comienza en 0 en la matriz.

También en el constructor, creamos todas las plataformas con espaciado semi-aleatorio y ubicación en la dirección Y en la pantalla. Todas las plataformas se agregan a un objeto List, que en C # es simplemente una forma muy fácil de usar y eficiente de administrar una estructura de datos similar a una matriz. Luego creamos el Player, que es nuestro objeto Character, establecemos la puntuación en 0 y establecemos GameOver en falso.

DataReceived vacío estático privado (remitente del objeto, SerialDataReceivedEventArgs e)

Este es el método al que se llama cuando se reciben datos en el puerto serie. Aquí es donde aplicamos toda nuestra física, decidimos si mostrar el juego terminado, mover las plataformas, etc. Si alguna vez has creado un juego, generalmente tienes lo que se llama un "bucle de juego", que se llama cada vez que el marco refresca. En este juego, el método DataReceived actúa como el bucle del juego, solo manipula la física a medida que se reciben datos del controlador. Podría haber funcionado mejor configurar un temporizador en la ventana principal y actualizar los objetos en función de los datos recibidos, pero como se trata de un proyecto de Arduino, quería hacer un juego que realmente se ejecutara en función de los datos que provenían de él..

En conclusión, esta configuración proporciona una buena base para expandir el juego y convertirlo en algo utilizable. Aunque la física no es del todo perfecta, funciona lo suficientemente bien para nuestros propósitos, que es usar el Arduino para algo que a todos les gusta: ¡jugar!

Recomendado: