PARTE # 2 de principio a fin
Continuamos haciendo un juego en arduino y luego lo incluimos en el programa que estoy haciendo para el automóvil y, según nuestro conocimiento, crearemos un segundo juego por diversión y haremos la música adecuada para él.
Para entender lo que necesitamos escribir ahora, necesitamos hacer un plan para lo que tendremos y de qué forma. Tenemos el personaje principal, tiene dos cuadros de animación, y ya lo vimos en la última lección. No comencé a cambiar el sprite de salto para él, no porque fuera demasiado vago, simplemente todavía no veo el punto en esto. Luego, deberíamos tener una designación de vida, simplemente hacer corazones seguidos no es interesante, y los números también son triviales, hagamos tres puntos de vida y márquelos con, por ejemplo, el nivel de la batería. Y para la alegría de la vista, un corazón latirá cerca de esta batería. El número de puntos por ronda, establecido a la izquierda de la pantalla, puramente digital y el enemigo tendremos un cactus malvado, para empezar lo hará.
Decidimos los objetos y los dibujamos, inmediatamente todos los sprites y los escribimos.









Tenemos sprites en funcionamiento y ya podemos imaginar cómo se verá en la pantalla, después de la primera lección los escribiremos en el sistema binario, recordemos dónde está el cero y dónde está la unidad, nuestro píxel se quema. Proceder:
"--------------------------------------------------------------------------" // : byte Player_1[SYMBOL_HEIGHT] = {B01110,B01110,B00100,B01110,B10101,B00100,B01110,B01010,}; // : byte Player_2[SYMBOL_HEIGHT] = {B00000,B01110,B01110,B00100,B11111,B00100,B01110,B01010,}; // : byte Enemy_1[SYMBOL_HEIGHT] = {B00010,B00110,B10111,B10110,B11111,B00110,B00110,B11111,}; // : byte Heart_L[SYMBOL_HEIGHT] = {B00000,B01010,B11111,B11111,B11111,B01110,B00100,B00000,}; // : byte Heart_R[SYMBOL_HEIGHT] = {B00000,B00000,B01010,B11111,B01110,B00100,B00000,B00000,}; // : byte Battery1[SYMBOL_HEIGHT] = {B01110,B11111,B11111,B11111,B11111,B11111,B11111,B11111,}; // : byte Battery2[SYMBOL_HEIGHT] = {B01110,B10001,B10011,B10111,B11111,B11111,B11111,B11111,}; // : byte Battery3[SYMBOL_HEIGHT] = {B01110,B10001,B10001,B10001,B10011,B10111,B11111,B11111,}; // : byte Battery4[SYMBOL_HEIGHT] = {B01110,B10001,B10001,B10001,B10001,B10001,B10001,B11111,}; "--------------------------------------------------------------------------"
Ahora tenemos sprites, y es hora de revivirlo todo. Primero, pensemos qué características adicionales necesitamos. Sabiendo que Arduino no funciona perfectamente y en algunos lugares es incluso muy caprichoso, comenzamos a tratar de simplificar la vida de este microcontrolador tanto como sea posible. No sobrecargue y al mismo tiempo exija el retorno total. Entonces, presentamos funciones adicionales que vivirán sus propias vidas y harán todo lo que necesitemos:
- animación del corazón
- animación de héroe
- control de daños del héroe
- movimiento del malvado cactus
- acumulación de puntos (por ahora, cada segundo +1, luego cambiar)
- actualizar la pantalla (pero esto no es exacto, lo más probable es que eliminemos esta función y agreguemos otra, no me gustó que la pantalla parpadeara, quiero estabilidad). Posteriormente, reemplazamos esta función con la eliminación de la ubicación anterior del héroe, esto eliminará el parpadeo real de la pantalla y la puesta a cero del villano estará dentro del guión malvado, creo que habrá un máximo de una o dos líneas.
- control remoto
- configuración de bucle y configuración
Queremos que tengamos una animación del latido del corazón. Al llevarlo a una función separada y obligarnos a vivir nuestra propia vida separada, será más fácil para nosotros rastrear el trabajo y en el futuro será más fácil de editar, ya que tenemos todo, bueno, o casi todo en un solo lugar. Este código podría mostrarse en loop () y comentarse, pero yo solía dividirlo en funciones separadas, no está buscando el código en toda la lista y sabe que una función separada controla elementos individuales de nuestro juego.
Ahora, escribiré piezas de código, las conectaremos al final y obtendremos un script completo, ahora te explicaré la esencia y la idea, y luego armaremos el rompecabezas y disfrutaremos del resultado.
Antes de comenzar el código, explicaré un par de puntos.
lcd.createChar // este es un comando para acceder a la pantalla y usar una de las celdas de memoria para grabar nuevos caracteres. El número de la celda y, separados por una coma, el nombre de la variable con información, se escriben entre paréntesis.
Controlaremos el renderizado a través de una variable digital, usando cuatro dígitos, para que la animación sea correcta, si quisiéramos hacer que el corazón simplemente latiera aquí y allá, entonces la variable bool ordinaria sería adecuada para nosotros. Pero mi idea es diferente, un gran golpe y dos cortos, por lo que se verá más interesante.
"--------------------------------------------------------------------------" void HeartHit () // { if (HeartControl == 0 || HeartControl == 2){lcd.createChar(1, Heart_L);} // , if (HeartControl == 1 || HeartControl == 3){lcd.createChar(1, Heart_R);} // , if (currentMillis - HeartHitBigCheck >= HeartHitBig) { // if (currentMillis - HeartHitLightCheck >= HeartHitLight) { // HeartHitLightCheck = currentMillis; // if (HeartControl<3){HeartControl++;} // , else {HeartControl = 0; HeartHitBigCheck = currentMillis;} // , } } } "--------------------------------------------------------------------------"
Una vez más, quiero centrar su atención en este código:
lcd.createChar (x, y); asignación a la celda de memoria "x" de (0 ... 7) datos para mostrar en la pantalla "y"
Adelante =)
Ahora, tenemos un código que crea el efecto de un latido interesante, no hace nada útil, solo muestra =)
Además, en base a esto, crearemos una animación de nuestro héroe, hay grandes ventajas, cuanto más profundicemos en este artículo, más aprenderá mi estilo de pensamiento y, para mí, menos necesitará explicar y centrarse más en el código y construir la idea. Intentaré explicar más en los comentarios del guión para escribir menos fuera del guión.
Entonces, comencemos con el personaje principal y creemos otra función para esto:
"--------------------------------------------------------------------------" // (). =) , . void PlAn () // { If (JumpB == true && GGpozY == 0){ // ( ) = . if (currentMillis - JumpUPCheck >= JumpUP) { // 0.8f JumpB = false; GGclear (); GGpozY = 1; // , . = ; (); = ; } } if (AnimPlayer == 1){lcd.createChar(0, Player_1);} // , if (AnimPlayer == 2){lcd.createChar(0, Player_2);} // , if (AnimPlayer < 2) // , , { lcd.setCursor(GGpozX, GGpozY); // lcd.write(0); // } if (currentMillis - AnimatedTimeCheck >= AnimatedTime) { // AnimatedTimeCheck = currentMillis; // if (AnimPlayer == 2){AnimPlayer = 1;} // else{AnimPlayer = 2;} // , . } } void GGclear () // { lcd.setCursor(GGpozX, GGpozY); // lcd.print(" "); // } "--------------------------------------------------------------------------"
Ahora, el temporizador, um, o más bien nuestros puntos que se otorgarán, simplemente escribamos un temporizador y supongamos que estos son puntos.
"--------------------------------------------------------------------------" void timer () // { if (currentMillis - DHTTimeRCheck >= DHTTimeR) // { DHTTimeRCheck = currentMillis; // Timer_z ++; // lcd.setCursor(0, 0); // lcd.print(Timer_z); // } } --------------------------------------------------------------------------"
Eso es todo Cuanto más lejos, más fácil.
Ahora, queda por resolver nuestro cactus.
Su tarea es simple, aparecer, ir de derecha a izquierda e intentar tocar al héroe para causar daño. Con el daño todo es más fácil, un toque, un golpe. Hasta ahora, aumentando la complejidad, no lo haremos. Deje que el cactus se mueva a una velocidad de 0.5f (complejidad, esta será su tarea =)) bien, o en ruso, medio segundo es un paso.
Echemos un vistazo a cómo se verá este fragmento de código:
"--------------------------------------------------------------------------" void enemy_go () // { if (Emeny_check_1 == 0) // , , , { Emeny_control = random (100); // , , , , , . if (Emeny_control == 1) { // = 1 100 . Emeny_check_1 = 1; // , , bool , , hitON = false; // } } if (Emeny_check_1 == 1) // , { if (currentMillis - TimeBlinkCheck >= TimeBlink) // 0.5f { TimeBlinkCheck = currentMillis; // lcd.createChar(2, Enemy_1); // 2 lcd.setCursor(E1pozX, E1pozY); // 1 lcd.print(" "); // E1pozX--; // lcd.setCursor(E1pozX, E1pozY); // 2 lcd.write(2); // if (E1pozX <= 0) // { lcd.setCursor(0,1); // lcd.print(" "); // Emeny_control = 0; // Emeny_check_1 = 0; // E1pozX = 16; // - \/ \/ \/ E1pozY = 1; // - } } } } "--------------------------------------------------------------------------"
Queda bastante tiempo y después de la prueba de cada pieza, ya puedo diseñar el script completo ensamblado, configurado y listo para la prueba en sus dispositivos.
Así que ahora tenemos uno de los scripts más importantes en línea, este es un script de verificación de daños y la inclusión de un jugador en ausencia de vida. No hay nada sobrenatural en el guión. Por lo tanto, podemos comenzar a desarmarlo (por cierto, si fueras cuidadoso, notarías que cuando creamos el corazón, no lo mostramos en la pantalla, así que ahora verás dónde pongo esta parte del código):
"--------------------------------------------------------------------------" void check_hit_gg_1 () // { if (E1pozX == GGpozX && E1pozY == GGpozY && hitON == false){ // Y , , LifeCheck -= 1; // hitON = true; // if (LifeCheck <= 0){ // AnimPlayer = 50; // loop () Emeny_check_1 = 50; // lcd.clear(); // lcd.setCursor(3, 0); // lcd.print("GAME OVER"); // } } else { // ! … lcd.setCursor(13, 0); // … lcd.write(1); // lcd.setCursor(14, 0); lcd.print("="); // lcd.setCursor(15, 0); lcd.write(3); // } } "--------------------------------------------------------------------------"
Este código es muy simple, su función principal es verificar y esperar hasta que se cumplan todas las condiciones para detener el juego y decirnos que hemos perdido.
Ahora la última función es la gestión. De hecho, si analizamos el código anterior, nos parecerá más fácil que simple. Desde la primera lección, sacamos los códigos de control remoto, los escribí todos, se ven así:
* CH- 0xFFA25D
* CH 0xFF629D
* CH+ 0xFFE21D
* << 0xFF22DD
* >> 0xFF02FD
* >|| 0xFFC23D
* - 0xFFE01F
* + 0xFFA857
* EQ 0xFF906F
* 0 0xFF6897
* 100+ 0xFF9867
* 200+ 0xFFB04F
* 1 0xFF30CF
* 2 0xFF18E7
* 3 0xFF7A85
* 4 0xFF10EF
* 5 0xFF38C7
* 6 0xFF5AA5
* 7 0xFF42BD
* 8 0xFF4AB5
* 9 0xFF52AD
Botón _ código (¡atención!) (Los códigos del control remoto pueden variar)
Quien no ha leído, le aconsejo que lea la primera parte.
Algo similar, entonces lo tendrá y podrá configurar fácilmente todo lo que necesite.
Ahora crearemos el algoritmo más simple, basado en lo que ya sabemos y nuestro juego cobrará vida.
"--------------------------------------------------------------------------" void IRCheck () // { if ( irrecv.decode( &results )) // , { if (results.value == 0xFF18E7 && GGpozY == 1){ // « 2 » «» GGclear (); // GGpozY = 0; // 2 ( ) JumpB = true; // JumpUPCheck = currentMillis; // } // 2 if (results.value == 0xFF10EF && GGpozX >= 0){ // GGclear (); // GGpozX -= 1; // } // 4 if (results.value == 0xFF5AA5 && GGpozX <= 15){ // GGclear (); // GGpozX += 1; // } // 6 if (results.value == 0xFF6897){ // 0 // , … lcd.clear(); // AnimPlayer = 1; // LifeCheck = 3; // Timer_z = 0; // GGpozX = 8; // \/ \/ \/ GGpozY = 1; // Emeny_check_1 = 0; // E1pozX = 16; // \/ \/ \/ E1pozY = 1; // . } irrecv.resume(); // } "--------------------------------------------------------------------------"
Conclusión del código escrito anterior:
el botón 2 es un salto
el botón 4 es un paso a la izquierda
el botón 6 es un paso a la derecha
el botón 0 restablece el juego y lo reinicia
Ahora, nos queda configurar la configuración y el bucle, todo ya está llegando a su fin. Hemos creado todas las funciones adicionales y solo tenemos que pegarlo todo y agregar bibliotecas. Creo que analizaremos las variables y la configuración principal \ configuración de bucle que ya están en el código general, así que comencemos, y luego se requiere que ctrl + c & ctrl + v y eso es todo =) y un mayor desarrollo independiente en esta dirección, si por supuesto Me gustó
"--------------------------------------------------------------------------" #include <IRremote.h> // #include <Wire.h> // i2P #include <LiquidCrystal_I2C.h> // 1602 LiquidCrystal_I2C lcd(0x3F,16,2); // int AnimPlayer = 1; // int GGpozX = 8; // int GGpozY = 1; // int Emeny_check_1 = 0; // int Emeny_control = 0; // int E1pozX = 16; // int E1pozY = 1; // int HeartControl = 0; // int LifeCheck = 3; // long Timer_z = 0; // long AnimatedTime = 300; // long AnimatedTimeCheck = 0; // long HeartHitBig = 800; // long HeartHitLight = 250; // long HeartHitBigCheck = 0; // long HeartHitLightCheck = 0; // long BatteyBlink = 200; // 1 long BatteyBlinkCheck = 0; // long JumpUP = 800; // long JumpUPCheck = 0; // long DHTTimeR = 1000; // long DHTTimeRCheck = 0; // long TimeBlink = 500; // long TimeBlinkCheck = 0; // long currentMillis = 0; // bool JumpB = false; // bool BatteryB = false; // bool hitON = false; // decode_results results; // IRrecv irrecv(A0); // enum { SYMBOL_HEIGHT = 8 }; byte Player_1[SYMBOL_HEIGHT] = {B01110,B01110,B00100,B01110,B10101,B00100,B01110,B01010,}; byte Player_2[SYMBOL_HEIGHT] = {B00000,B01110,B01110,B00100,B11111,B00100,B01110,B01010,}; byte Enemy_1[SYMBOL_HEIGHT] = {B00010,B00110,B10111,B10110,B11111,B00110,B00110,B11111,}; byte Heart_L[SYMBOL_HEIGHT] = {B00000,B01010,B11111,B11111,B11111,B01110,B00100,B00000,}; byte Heart_R[SYMBOL_HEIGHT] = {B00000,B00000,B01010,B11111,B01110,B00100,B00000,B00000,}; byte Battery1[SYMBOL_HEIGHT] = {B01110,B11111,B11111,B11111,B11111,B11111,B11111,B11111,}; byte Battery2[SYMBOL_HEIGHT] = {B01110,B10001,B10011,B10111,B11111,B11111,B11111,B11111,}; byte Battery3[SYMBOL_HEIGHT] = {B01110,B10001,B10001,B10001,B10011,B10111,B11111,B11111,}; byte Battery4[SYMBOL_HEIGHT] = {B01110,B10001,B10001,B10001,B10001,B10001,B10001,B11111,}; void setup() { Serial.begin(9600); // irrecv.enableIRIn(); // lcd.init(); // Wire.begin(); // lcd.backlight();// } void loop() { currentMillis = millis(); // IRCheck (); // if (AnimPlayer < 3){ // , , if (LifeCheck == 3) {lcd.createChar(3, Battery1);} // if (LifeCheck == 2) {lcd.createChar(3, Battery2);} // if (LifeCheck == 1) {// , 1 if (BatteryB == false){lcd.createChar(3, Battery3);} // if (BatteryB == true){lcd.createChar(3, Battery4);} // if (currentMillis - BatteyBlinkCheck >= BatteyBlink) {BatteyBlinkCheck = currentMillis; // if (BatteryB == false) {BatteryB = true;} else {BatteryB = false;}} // } timer(); // check_hit_gg_1 (); // PlAn(); // HeartHit (); // enemy_go(); // } } void IRCheck () // { if ( irrecv.decode( &results )) // , { if (results.value == 0xFF18E7 && GGpozY == 1){ // « 2 » «» GGclear (); // GGpozY = 0; // 2 ( ) JumpB = true; // JumpUPCheck = currentMillis; // } // 2 if (results.value == 0xFF10EF && GGpozX >= 0){ // GGclear (); // GGpozX -= 1; // } // 4 if (results.value == 0xFF5AA5 && GGpozX <= 15){ // GGclear (); // GGpozX += 1; // } // 6 if (results.value == 0xFF6897){ // 0 // , … lcd.clear(); // AnimPlayer = 1; // LifeCheck = 3; // Timer_z = 0; // GGpozX = 8; // \/ \/ \/ GGpozY = 1; // Emeny_check_1 = 0; // E1pozX = 16; // \/ \/ \/ E1pozY = 1; // . } irrecv.resume(); // } } void timer () // { if (currentMillis - DHTTimeRCheck >= DHTTimeR) // { DHTTimeRCheck = currentMillis; // Timer_z ++; // lcd.setCursor(0, 0); // lcd.print(Timer_z); // } } // (). =) , . void PlAn () // { if (JumpB == true && GGpozY == 0){ // ( ) = . if (currentMillis - JumpUPCheck >= JumpUP) { // 0.8f JumpB = false; GGclear (); GGpozY = 1; // , . = ; (); = ; } } if (AnimPlayer == 1){lcd.createChar(0, Player_1);} // , if (AnimPlayer == 2){lcd.createChar(0, Player_2);} // , if (AnimPlayer < 2) // , , { lcd.setCursor(GGpozX, GGpozY); // lcd.write(0); // } if (currentMillis - AnimatedTimeCheck >= AnimatedTime) { // AnimatedTimeCheck = currentMillis; // if (AnimPlayer == 2){AnimPlayer = 1;} // else{AnimPlayer = 2;} // , . } } void GGclear () // { lcd.setCursor(GGpozX, GGpozY); // lcd.print(" "); // } void enemy_go () // { if (Emeny_check_1 == 0) // , , , { Emeny_control = random (100); // , , , , , . if (Emeny_control == 1) { // = 1 100 . Emeny_check_1 = 1; // , , bool , , hitON = false; // } } if (Emeny_check_1 == 1) // , { if (currentMillis - TimeBlinkCheck >= TimeBlink) // 0.5f { TimeBlinkCheck = currentMillis; // lcd.createChar(2, Enemy_1); // 2 lcd.setCursor(E1pozX, E1pozY); // 1 lcd.print(" "); // E1pozX
Al conectar Creamos el código, pero así es cómo recolectar los cables y qué y dónde pegar, no te expliqué esto. Aunque estoy tan seguro de que más del 80% de los que deciden leer este artículo ya lo saben, pero en cuanto a mí, el artículo no estará completo si no proporciono la información máxima.
Pantalla A5 1602 - Pantalla SCLA4 1602 -Sensor IR SDA A0Ahora estoy trabajando en la creación de una clave de radio para un automóvil basado en Arduino, y en la pantalla 1602 quiero mostrar los datos del sensor de humedad y temperatura fuera del automóvil (dado que pronto estaré en Moscú, buscando un nuevo trabajo, necesitaré saber qué está sucediendo fuera del automóvil hace mucho frío allí), un reloj, un voltímetro de batería y poner juegos allí (todavía tengo 4 pines libres, pero todavía tengo que empujarlos allí) para poder meter este juego o el segundo que planeo en un semáforo hacer como correr viejo buen prisma de bolsillo 2000 ca desde el punto donde se acaba de dar la vuelta al obstáculo y la música para poner en un fondo del Rock n Roll Racing. Ugh classic =) y pegarlo todo de manera óptima en un arduino sin usar almacenamiento de datos externo (solo desafío), pero ya usé ~ 60% y si el código del juego toma un máximo de 15% - 20%,entonces la música ... oh ... habrá problemas con esto, pesa mucho, necesita ser optimizado, ya comencé a recopilar la pista y logré reducir el peso casi 10 veces, pero cometí errores al crear notas y tonos y ahora tengo que hacerlo todo de nuevo. Lo más probable es que escriba aquí la versión completa con una descripción del proyecto en el que estoy trabajando ahora. Espero que alguien me haya dado algo nuevo en qué pensar, si todo está bien y es tiempo, continuaré escribiendo artículos. Alguna pregunta? Escribe en los comentarios.Espero que alguien me haya dado algo nuevo en qué pensar, si todo está bien y es tiempo, continuaré escribiendo artículos. Alguna pregunta? Escribe en los comentarios.Espero que alguien me haya dado algo nuevo en qué pensar, si todo está bien y es tiempo, continuaré escribiendo artículos. Alguna pregunta? Escribe en los comentarios.Todos, suscríbete, sigue los nuevos artículos.¡Gracias a todos por su atención, chao cacao!PD: Todavía no puedo grabar el rendimiento del juego en video, la cámara del teléfono murió y es muy triste. Pero se me ocurrirá algo y agregaré un video posterior al artículo.La primera parte del artículo ->