0°

吃豆子的字符串游戏机制作

简介

哈哈,它不是一个吃豆人,但它是沿着一个长LED条移动收集好东西,躲避坏东西的一个游戏。所以你明白了吗?!

一群中学生在周末和我一起学习和玩Arduino,NodeMCU和MQTT,当我听说Maker-Faire活动时,这是我们用我们拥有的东西制作一个很酷游戏的绝佳机会。到目前为止学到了

所以孩子们想出了收集的彩色宝石,我们称之为翡翠,黄金等,并引入了惊人的黄蜂来追逐你。游戏开始非常简单,但获得高分非常具有挑战性。你沿着灯带移动一段LED,向左推动以收集宝石并向右移动以击倒黄蜂,但是在确定你的分数时,时间是重要的,我将在后面的游戏机制中解释。

步骤一 材料清单

吃豆子的字符串游戏机制作

硬件:

  • Arduino Mega*1
  • NodeMCU*1
  • 操纵杆*1
  • LED灯条*1
  • 电源*1
  • 按钮*1
  • 亚克力盒子*1
  • 电线,电容器,连接器,电阻器
  • 长杆:用于架起LED灯条

软件:

  • Arduino IDE

我们使用Arduino Mega板来作为游戏的基础设施:操纵杆,LED灯条,闪烁灯,黄蜂,随机出现和消失,碰撞和游戏事件,得分。

NodeMCU将游戏与互联网连接起来。事件和分数发布到MQTT频道,因此感兴趣的应用可以接收和处理信息。

Android App收听MQTT频道,并随着游戏的进展显示得分板。当游戏结束时,它可以让你保存游戏得分并显示在游戏排行榜中。

MQTT频道和消息传递本质上是非常迅速的,因此排行榜也保存在云数据库中,因此每当Android应用程序启动时,它将获得截至目前的排行榜。

步骤二 原理说明

吃豆子的字符串游戏机制作

我们使用操纵杆来控制运动和其它动作,只需在LED灯条上下移动光段(5-LED蓝光段)。

光段到达闪闪发光的宝石时,向左推操纵杆以收集它并获得分数。请记住,该段有5个LED,标记为ABCDE。如果你用“C”收集了宝石,你获得100分,“B”或“D”,50分,“A”或“E”,你得到25分。所以游戏很容易让你有5-LED触摸宝石,但同时你需要有一个非常好的时机来使用“C”灯来最大化分数是很有挑战性的。

宝石停留在一个位置 – 它不会移动,但它停留在那里的时间有限 – 也就是说,它会在一段随机的时间后消失。所以你需要赶紧去那里。经过一段随机的消失时间后,宝石会在另一个位置弹出,而你再次到达那里的时间有限。

宝石很好。世界是公平的,任何好事都会变得糟糕。红色LED是黄蜂,它看起来像红宝石宝石,但它一直在追逐光段(Light Saber)。同样,它有一个随机的生命时间。你可以逃避它直到它消失(你得到0分),或者你可以在它触及你的那一刻(你得到一些积分)将它击倒,否则,它会触动你并且它会刺痛你 – 你会失去分数,你会因为冻结你一段时间而失去宝贵的游戏时间。

游戏以START按钮开始,你可以获得45秒收集宝石,淘汰或避免黄蜂获得最高分。当时间到了,游戏结束了,你将被放在排行榜上。

如果游戏闲置一段时间,它将进入自动游戏模式。它可能比你更好或更差。

综上所述:

1. 45秒的比赛时间,

2.宝石收集点:C = 100,BD = 50,AE = 25

3.黄蜂蜇伤:每次刺痛都是-24,根据黄蜂的生命时间,它会在你被冻结时刺痛你好几次。

任何人都可以轻松开始游戏 – 您可以将宝石旁边的5-LED光段对齐并向左推动操纵杆,对吧?同时,如果你试图得到一个高分,游戏是非常具有挑战性的,它需要非常好的时机并期望将中间C与宝石对齐,并且它需要你有足够的反应能够在它刺痛之前击倒黄蜂。

步骤三 硬件搭建

吃豆子的字符串游戏机制作
吃豆子的字符串游戏机制作
吃豆子的字符串游戏机制作

如接线图所示,

Arduino Mega板上的引脚连接:

PIN 8 – 操纵杆

PIN 9 – 操纵杆向下

PIN 10 – 操纵杆向左

PIN 11 – 操纵杆右

GND – 操纵杆通用

PIN 7 – 开始按钮

PIN 5 – LED灯条

PIN 5 – LED Star

PIN 16 – RX

PIN 17 – TX

NodeMCU板上的引脚连接:

D5 – TX

D6 – RX

步骤四 开始编程

吃豆子的字符串游戏机制作

Arduino游戏代码:

#include <Adafruit_NeoPixel.h>
#include <SoftwareSerial.h>

#define PIN_LED 6
#define STRIP_LEN 180
#define STAR_LED 5
#define STAR_LEN  7

#define PIN_START 7
#define PIN_UP 8
#define PIN_DOWN 9
#define PIN_LEFT 10
#define PIN_RIGHT 11

// game state
#define GAME_STARTED 1
#define GAME_OVER 2
#define GAME_IDLE 0

const long IDLE_TIME_START = 10000; // if no activity for 10 secs, go in idle mode
const long GAME_DURATION = 30000;   // 30 seconds


SoftwareSerial mySerial(16, 17); // RX, TX
Adafruit_NeoPixel theStrip = Adafruit_NeoPixel(STRIP_LEN, PIN_LED, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel theStar  = Adafruit_NeoPixel(STAR_LEN, STAR_LED, NEO_GRB + NEO_KHZ800);

uint32_t sabreColor = theStrip.Color(0,127,127);
uint32_t headColor = theStrip.Color(0,255,255);
uint32_t offColor = theStrip.Color(0,0,0);

uint32_t waspColor = theStrip.Color(255,0,0);
uint32_t emeraldColor = theStrip.Color(0,255,0);
uint32_t sapphireColor = theStrip.Color(0,0,255);
uint32_t goldColor = theStrip.Color(255,127,0);
uint32_t amethystColor = theStrip.Color(255,0,255);

class Jetter
{
  uint16_t head;      // the head position of light sabre
  uint32_t head_color;  
  uint32_t on_color;  // the color of light sabre
  uint32_t off_color; // turn off light

  public:
  Jetter(uint16_t head0, uint32_t on_color0, uint32_t head_color0) {
    head = head0;
    on_color = on_color0;
    head_color = head_color0;
  }

  void attachStrip(Adafruit_NeoPixel& strip) {
    off_color = strip.Color(0,0,0);

    jetTo(head, strip);
  }

  // This saber will show a bright head + the full length of dim saber
  void jetTo_original(uint16_t pos, Adafruit_NeoPixel&  strip) {
    uint16_t i=0;

    if(head == pos) {
      Serial.println("no change");
      Serial.println();
      return;
    }

    if(head < pos) {
      for(i=head; i>head-3; i--) {
        strip.setPixelColor(i, on_color);
      }
      
      // turn on color from head -> pos
      Serial.println("Go up");
      for(i=head; i<=pos; i++) {
        strip.setPixelColor(i, on_color);
        strip.show();
      }
    }
    else {
      // turn off light from head -> pos
      Serial.println("Go down");
      for(i=head; i>pos; i--) {
        strip.setPixelColor(i, off_color);
        strip.show();
      }      
    }

    for(i=pos; i > pos-3; i--) {
      strip.setPixelColor(i, head_color);
    }
    strip.show();

    head = pos;
  }

  // this saber will just show the bright head
  void jetTo(uint16_t pos, Adafruit_NeoPixel&  strip) {
    uint16_t i=0;

    if(head == pos) {
      Serial.println("no change");
      Serial.println();
      return;
    }

    if(head < pos) {
      // turn on color from head -> pos
      Serial.println("Go up");
      for(i=head; i<=pos; i++) {
        strip.setPixelColor(i+2, head_color);
        strip.setPixelColor(i+1, head_color);
        strip.setPixelColor(i, head_color);
        strip.setPixelColor(i-1, on_color);
        strip.setPixelColor(i-2, on_color);
        // turn off tail
        strip.setPixelColor(i-3, off_color);
        strip.show();
      }
    }
    else {
      // turn off light from head -> pos
      Serial.println("Go down");
      for(i=head; i>pos; i--) {
        strip.setPixelColor(i+2, on_color);
        strip.setPixelColor(i+1, on_color);
        strip.setPixelColor(i, head_color);
        strip.setPixelColor(i-1, head_color);
        strip.setPixelColor(i-2, head_color);
        // turn off tail        
        strip.setPixelColor(i+3, off_color);
        strip.show();
      }      
    }

    head = pos;
  }
};


class Joystick 
{
  int upPin;
  int downPin;
  int leftPin;
  int rightPin;
  int stickDir = 0;   // joystick move direction
  
  public:
  Joystick(int up, int down, int left, int right) {
    upPin = up;
    downPin = down;
    leftPin = left;
    rightPin = right;

    pinMode(upPin, INPUT_PULLUP);
    pinMode(downPin, INPUT_PULLUP);
    pinMode(leftPin, INPUT_PULLUP);
    pinMode(rightPin, INPUT_PULLUP);
  }

  int checkDir() {
    int readUp = digitalRead(upPin);
    int readDown = digitalRead(downPin);
    int readLeft = digitalRead(leftPin);
    int readRight = digitalRead(rightPin);

    if(readUp==1 && readDown==1 && readLeft==1 && readRight==1) {
      stickDir = 0;     // no move
    }
    else if(readUp==0 && readDown==1 && readLeft==1 && readRight==1) {
      stickDir = 1;     // up
    }
    else if(readUp==1 && readDown==0 && readLeft==1 && readRight==1) {
      stickDir = 2;     // down
    }
    else if(readUp==1 && readDown==1 && readLeft==0 && readRight==1) {
      stickDir = 3;     // left
    }
    else if(readUp==1 && readDown==1 && readLeft==1 && readRight==0) {
      stickDir = 4;     // right
    }

    return stickDir;
  }
};


class Sparkle {
  uint16_t pos;
  uint32_t color;
  long  duration;
  long  lightTime;
  long  darkTime;
  long  darkDuration;
  int   state = 0;   // 0 - dark, 1 - light
  int   stingCount;

  public:
  Sparkle(uint32_t c, long d) {
    color = c;
    duration = d;
    pos = random(1,STRIP_LEN);
    lightTime = millis();
    darkTime = millis();
    // start with dark
    darkDuration = random(500, 3000);
    state = 0;
    stingCount = 0;
  }


  // got a gem!
  void collectGem(uint16_t pos, Adafruit_NeoPixel&  strip, Adafruit_NeoPixel&  star) {
    uint32_t preColor = strip.getPixelColor(pos);

    // shoot the gem to the box
    for(int i=pos; i>0; i--) {
        strip.setPixelColor(i, preColor);
        preColor = strip.getPixelColor(i-1);
        strip.setPixelColor(i-1, color);
        strip.show();
        delay(1);              
    }

    // light up stars
    for(int j=0; j<STAR_LEN; j++) {
      star.setPixelColor(j,color);
    }
    star.show();
  }


  // stung by wasp
  void blinkHead(uint16_t head, Adafruit_NeoPixel& strip) {
    uint32_t color = waspColor;
    
    for(int k=0; k<10; k++) {
        if( k%2 == 0 ) {
          color = offColor;
        }
        else {
          color = waspColor;
        }
        
        strip.setPixelColor(head+2, color);
        strip.setPixelColor(head+1, color);
        strip.setPixelColor(head, color);
        strip.setPixelColor(head-1, color);
        strip.setPixelColor(head-2, color);
        strip.show();
        delay(150);
    }
  }
  
  // see if captured gem
  void detect(uint16_t head, Adafruit_NeoPixel&  strip, Adafruit_NeoPixel&  star) {
    if( state == 1 ) {
      int hitPoint = 0;
      if( abs(head-pos) == 0 ) {
        hitPoint = 100;
      }
      else if( abs(head-pos) == 1 ) {
        hitPoint = 50;
      }
      else if( abs(head-pos) == 2 ) {
        hitPoint = 25;
      }
      else {
        hitPoint = 0;
      }

      if( hitPoint > 0 ) {
        // report points - write it through serial port to NodeMCU
        mySerial.print("point=");
        mySerial.println(hitPoint);
        
        // now shoot this gem to box, light up stars
        Serial.println("collecting gem");
        collectGem(pos, strip, star);

        darkTime= millis();
        darkDuration = random(5000, 7000);
        state = 0;        
      }
    }
  }

  // For Wasp to move around
  void moveLight(uint16_t head, Adafruit_NeoPixel& strip) {
    if( state == 1 ) {
      strip.setPixelColor(pos, offColor);
      if( pos < head ) {
        pos++;
      }
      else {
        pos--;
      }

      // detect wasp sting
      if( abs(pos-head) < 3 ) {
        stingCount++;      
      }
      if( stingCount > 3 ) {
        // report sting
        mySerial.println("point=-40");

        // show light effect - blink head
        blinkHead(head, strip);
      }
    }
  }


  // turn on/off light randomly
  void updateLight(Adafruit_NeoPixel&  strip) {
    unsigned long currMills = millis();

    if( state == 1 ) {
      // light state
      if( currMills - lightTime < duration ) {
        // light up for duration
        strip.setPixelColor(pos, color);
        strip.show();
      }
      else {
        // make it dark
        Serial.println("going to dark");
        strip.setPixelColor(pos, offColor);
        strip.show();
  
        darkTime= millis();
        darkDuration = random(1500, 3500);
        state = 0;
      }
    }
    else if( state == 0 ) {
      // dark state
      if( currMills - darkTime < darkDuration) {
        strip.setPixelColor(pos, offColor);
        strip.show();      
      } 
      else {
        // now light it up
        Serial.println("going to light....");
        pos = random(1,STRIP_LEN);
        
        strip.setPixelColor(pos, color);
        strip.show();
  
        lightTime = millis();
        state = 1;
        // reset sting count
        stingCount = 0;

      }
    }
  }
};


class GameState {
  int state;
  long startTime;       // game start
  long endTime;         // game over time
  long idleTime;        // mark idle time start
  long lastActiveTime;  // last time the user moved joystick
  
  public:
  GameState() {
    // start with game-over mode
    state = GAME_OVER;
    startTime = millis();
    endTime = millis();
    lastActiveTime = millis();
  }

  void setActive() {
    lastActiveTime = millis();  
  }

  void start() {
    startTime = millis();
    state = GAME_STARTED;
    // report game started
    mySerial.println("game=started");
  }

  int check() {
    long currTime = millis();
    long duration = currTime - startTime;

    // game time up
    if( state == GAME_STARTED && duration > GAME_DURATION ) {
      state = GAME_OVER;
      endTime = currTime;
      
      // report game over
      mySerial.println("game=ended");
    }

    if( state == GAME_OVER && (currTime - endTime) > IDLE_TIME_START ) {
      state = GAME_IDLE;
      // report game idle - no need to let scoreboard know
      Serial.println("Game going idle mode"); 
    }

    return state;
  }
};


uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
   return theStrip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else if(WheelPos < 170) {
    WheelPos -= 85;
   return theStrip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  } else {
   WheelPos -= 170;
   return theStrip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<theStrip.numPixels(); i++) {
      theStrip.setPixelColor(i, Wheel((i+j) & 255));
    }
    theStrip.show();
    delay(wait);
  }
}

void clearStrip() {
  uint16_t i = 0;
  for(i=0; i<STRIP_LEN; i++) {
    theStrip.setPixelColor(i, offColor); 
  }
  theStrip.show();
}

void clearStar() {
  for(int i=0; i<STAR_LEN; i++) {
    theStar.setPixelColor(i, offColor);
  }
  theStar.show();
}


Jetter sabre(60, sabreColor, headColor);
Joystick stick(PIN_UP, PIN_DOWN, PIN_LEFT, PIN_RIGHT);
GameState game;

Sparkle gold(goldColor, 1500);  // what color, stay for how long
Sparkle amethyst(amethystColor, 2000);
Sparkle emerald(emeraldColor, 2500);
Sparkle sapphire(sapphireColor, 3000);
Sparkle wasp(waspColor, 3500);

// Left move to capture gem
void detectLeftMove(uint16_t head)
{
  // current head position
  amethyst.detect(head, theStrip, theStar);
  emerald.detect(head, theStrip, theStar);
  sapphire.detect(head, theStrip, theStar);
  gold.detect(head, theStrip, theStar);
}

// Right move to knock off wasp
void detectRightMove(uint16_t head)
{
  // current head position
  wasp.detect(head, theStrip, theStar);
}

uint16_t spot = 0;
void setup() {
  Serial.begin(19200);
  mySerial.begin(9600);
  
  theStrip.begin();
  theStrip.show();
  theStar.begin();
  theStar.show();
  theStar.setBrightness(32);

  // to start game
  pinMode(PIN_START, INPUT_PULLUP);
  
  sabre.attachStrip(theStrip);
  spot = random(1,180);
}


void loop() {

  int state = game.check();
  Serial.print("Game state:");
  Serial.println( state );
  

  if( digitalRead(PIN_START) == LOW && state != GAME_STARTED) {
    Serial.println( "Starting game........");
    game.start();
    clearStrip();
    spot = 90;
    state = game.check();
  }
  
  int dir = stick.checkDir();
  switch(dir) {
    case 1: // up
      spot = spot + 2;  // move up 3 lights
      break;
    case 2: //down
      spot = spot - 2;
      break;
    case 3: // gem
      detectLeftMove(spot);
      break;
    case 4: // wasp
      detectRightMove(spot);
      break;
    default:
      // no movement, do nothing
      break;
  }
  
  if( dir > 0 && dir < 5) {
    game.setActive();
  }

  if( state == GAME_IDLE) {
    Serial.println("TBD: playing GAME_IDLE effect");
    // simulation of game
    detectLeftMove(spot);
    delay(50);
    spot = spot + random(-5, 3);
  }

  // protect strip light spot
  if(spot < 0 ) {
    spot = 5;
  }
  if(spot > STRIP_LEN) {
    spot = STRIP_LEN-5;
  }

  if( state == GAME_STARTED || state == GAME_IDLE) {
    sabre.jetTo(spot, theStrip);
    
    amethyst.updateLight(theStrip);
    emerald.updateLight(theStrip);
    sapphire.updateLight(theStrip);
    gold.updateLight(theStrip);
    
    wasp.moveLight(spot, theStrip);
    wasp.updateLight(theStrip);
  }
  else if( state == GAME_OVER) {
    Serial.println("TBD: playing GAME_OVER effect");
    rainbow(1);
    
    clearStar();
  }

  delay(10);
}

NodeMCU通信代码:

当前内容已被隐藏,您需要登录才能查看

Android记分板和排行榜代码:

当前内容已被隐藏,您需要登录才能查看

MQTT频道:我们使用CloudMQTT服务创建频道并在NodeMCU和Android应用中使用它。

Apex数据存储:用于保存排行榜分数的数据库服务。您可以将游戏保存在其他云数据存储中。

步骤五 最终演示

吃豆子的字符串游戏机制作
吃豆子的字符串游戏机制作
吃豆子的字符串游戏机制作

该游戏于2016年11月17日在Oracle Maker Faire首次公开播放。这是一个突然的惊喜,人们一次又一次地玩游戏很开心。这很上瘾!

这是假日季节与家人和朋友一起玩的完美游戏!

原作者:RaymondX

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
Lua
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论