目录
简介
哈哈,它不是一个吃豆人,但它是沿着一个长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