具有1602显示屏的最简单的Arduino游戏-第2部分

第2部分从头到尾


我们将继续在arduino上进行游戏,然后将此游戏放入我为汽车所做的程序中,并基于我们的知识,我们将制作第二个有趣的游戏并为其制作合适的音乐。

要了解我们现在需要写的内容,我们需要为将要拥有的内容和形式制定一个计划。 我们有主角,他有两帧动画,上一课我们已经把它弄清楚了。 我没有开始改变他的跳跃精神,不是因为我太懒了,我只是不明白这一点。 接下来,我们应该指定一个寿命,连续制造心脏并不有趣,数字也很陈旧,让我们确定三个生命点并用例如电池电量标记它们。 为了使眼睛愉悦,一颗心将在此电池附近跳动。 在屏幕左侧设置的每回合点数(纯粹是数字形式),而敌人将拥有一个邪恶的仙人掌,这将使它开始。

我们决定了这些对象,然后绘制它们,立即绘制所有精灵并将它们写下来。





我们有工作的精灵,我们已经可以想象它在屏幕上的外观,在第一堂课之后,我们将它们写入二进制系统,记住零在哪里为空,单位在哪里,像素会燃烧。 继续:

"--------------------------------------------------------------------------" //   : 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,}; "--------------------------------------------------------------------------" 

现在我们有了精灵,是时候恢复所有这些了。 首先,让我们考虑一下我们需要哪些其他功能。 知道arduino不能完美运行,并且在某些地方甚至是反复无常的,我们开始尝试尽可能简化这种微控制器的寿命。 不要过载,同时要求全额回报。 因此,我们引入了这样的附加功能,这些功能将过着自己的生活并完成我们需要的一切:

-心脏动画
-英雄动画
-英雄伤害检查
-邪恶仙人掌的运动
-累积点数(目前,每秒+1,然后更改)
-更新屏幕(但这不准确,很可能我们将删除此功能并添加另一个功能,我不喜欢屏幕闪烁,我想要稳定)。 随后,我们通过删除英雄的先前位置来替换此功能,这将删除屏幕的实际闪烁,将小人调零将在邪恶脚本中,我认为最多为一两行。
-遥控器
-循环和设置设置

我们希望我们有一个心跳的动画。 将其置于单独的功能中,并迫使我们过着自己的独立生活,这将使我们更容易跟踪作品,并且将来也将更易于编辑,因为我们将所有,全部或几乎所有内容都放在一个位置。 该代码可以显示在loop()中并注释掉,但是我个人习惯将其分成单独的函数,您并不是在整个列表中寻找该代码,并且您知道一个单独的函数可以控制游戏的各个元素。

现在,我将编写代码片段,我们将在最后连接它们并获得完整的脚本,我现在将向您解释其本质和想法,然后我们将组装拼图并享受结果。
在开始编写代码之前,我将解释两点。

lcd.createChar // //这是访问屏幕并使用其中一个存储单元记录新字符的命令。 单元格的编号以及用逗号分隔的带信息的变量的名称都写在方括号中。

我们将使用一个四位数的数字变量通过一个数字变量来控制渲染,以使动画正确,如果我们想让心脏随处可见,那么普通的bool变量将适合我们。 但是我的想法是不同的,一个大的打击和两个短的打击,所以看起来会更有趣。

 "--------------------------------------------------------------------------" 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;} //     ,          } } } "--------------------------------------------------------------------------" 

再一次,我想将您的注意力集中在以下代码上:
lcd.createChar(x,y); 从(0 ... 7)数据分配给存储单元“ x”以显示在屏幕“ y”上

继续=)

现在,我们有一个代码,可以产生有趣的心跳效果,它没有任何用处,仅显示展示=)

此外,基于此我们将创建英雄的动画,有很多优点,我们深入研究本文,您将学到更多的我的思维方式,而您需要向我解释的内容也就越少,而您需要更多地专注于代码和构想。 我将尝试在脚本注释中解释更多信息,以减少在脚本外部的编写。
因此,让我们开始主角并为此创建另一个函数:

 "--------------------------------------------------------------------------" //             ().     =)  ,         . 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 timer () //   { if (currentMillis - DHTTimeRCheck >= DHTTimeR) //       { DHTTimeRCheck = currentMillis; //   Timer_z ++; //      lcd.setCursor(0, 0); //       lcd.print(Timer_z); //     } } --------------------------------------------------------------------------" 

仅此而已。 越远越容易。

现在,仍然需要解决我们的仙人掌问题。

他的任务很简单,从右到左一直走到尽头,并尝试触摸英雄以造成伤害。 有了损坏,一切都变得更加容易,一触即一击。 到目前为止,复杂性不断增加,我们不会这样做。 让仙人掌以0.5f(复杂度,这已经是您的作业=)的速度移动了,或者用俄语,半秒是一个步骤。

让我们看一下这段代码的外观:

 "--------------------------------------------------------------------------" 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; // -         } } } } "--------------------------------------------------------------------------" 

仍然有很多内容,在对每个部分进行试用之后,我已经可以布置完整的脚本,进行组装,配置并准备在您的设备上进行测试。

所以现在我们有了最重要的脚本之一,这是一个损坏检查脚本,并且包含没有生命的玩家。 脚本中没有超自然的东西。 因此,我们可以开始分解它了(顺便说一句,如果您小心的话,您会注意到,当我们创建心脏时,我们并没有在屏幕上显示它,因此,现在您将看到我将这部分代码放在哪里):

 "--------------------------------------------------------------------------" 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); //   } } "--------------------------------------------------------------------------" 

这段代码非常简单,其主要功能是检查并等到所有条件都满足后,才能停止游戏并告诉我们我们输了。

现在,最后一个功能是管理。 实际上,如果我们解析上面的代码,那么对于我们来说,这似乎比简单容易。 从第一课开始,我们取出了遥控器代码,我全部编写了代码,它们看起来像这样:

* 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


按钮_代码(注意!)(遥控器代码可能有所不同)

谁还没有阅读,我建议您阅读第一部分。

类似的东西就可以了,您可以轻松配置所需的一切。

现在,我们将基于我们已经知道的知识创建最简单的算法,然后我们的游戏就会栩栩如生。

 "--------------------------------------------------------------------------" 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(); //    } "--------------------------------------------------------------------------" 

以上编写代码的结论:
按钮2是跳跃
按钮4向左移动
按钮6向右移动
按钮0重置游戏并重新启动

现在,我们仍然需要配置设置和循环,一切都已经结束了。 我们创建了所有其他功能,剩下的就是粘合并添加所有库。 我想我们将查看通用代码中已经存在的变量和主要设置\循环设置,因此让我们开始吧,然后您需要ctrl + c&ctrl + v,就这样=)并朝着这个方向进一步独立开发(如果您当然全部)我喜欢

 "--------------------------------------------------------------------------" #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--; //     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; // -         } } } } 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); //   } } 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;} //     ,          } } } "--------------------------------------------------------------------------" 

通过连接。我们已经编写了代码,但是这里是如何收集电线以及在何处粘贴的方式,我没有向您解释。尽管我确定要阅读这篇文章的人中有80%以上已经知道这一点,但是对于我来说,如果我不提供最多的信息,这篇文章将是不完整的。



A5显示器1602-SCL
A4显示器1602-SDA
A0红外传感器

现在,我正在为一款基于arduino的汽车创建一个无线电钥匙,并在屏幕1602上显示汽车外部的湿度和温度传感器数据(由于我即将来到莫斯科,正在寻找新工作,所以我需要知道汽车外部的状况那里很冷),时钟,电池电压表,然后将游戏放在那里(我仍然有4个空闲的插针,但我仍然必须将它们推到那里),以便我可以戳此游戏或在交通信号灯处计划的第二个游戏就像赛车老手一样好从点只是其中CA 2000年去绕过障碍物和音乐穿上摇滚赛车的背景。 Ugh classic =),并且不使用外部数据存储即可将其最佳地粘贴到一个arduino中(纯粹是挑战),但是我已经使用了〜60%,如果游戏代码最多占用15%-20%,然后音乐...哦...它会有问题,它很重,需要优化,我已经开始收集音轨并设法将重量减轻了近10倍,但是我在制作音符和音调时犯了错误,现在我必须重新做一遍。最有可能的是,我将在此处编写完整版本,并附上我当前正在从事的项目的描述。我希望有人给我一些新的想法,如果一切正常,时间允许,我将继续写文章。有什么问题吗?在评论中写。我希望有人给我一些新的想法,如果一切正常,时间允许,我将继续写文章。有什么问题吗?在评论中写。我希望有人给我一些新的想法,如果一切正常,时间允许,我将继续写文章。有什么问题吗?在评论中写。

全部订阅,喜欢,关注新文章。

谢谢大家的关注,可可!

PS:我仍然无法通过视频拍摄游戏的性能,手机上的摄像头没电了,这非常令人难过。但我会提出一些建议,并在文章中添加一个稍后的视频。

文章的第一部分->

Source: https://habr.com/ru/post/zh-CN425367/


All Articles