feat: 全量同步 254 个常用的 Arduino 扩展库文件
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
MD_MAX72XX::fontType_t _Fixed_5x3[] PROGMEM = {
|
||||
'F', 1, 32, 127, 5,
|
||||
2, 0, 0, // 32 - 'Space'
|
||||
1, 23, // 33 - '!'
|
||||
3, 3, 0, 3, // 34 - '"'
|
||||
3, 31, 10, 31, // 35 - '#'
|
||||
3, 22, 31, 13, // 36 - '$'
|
||||
3, 9, 4, 18, // 37 - '%'
|
||||
3, 10, 21, 26, // 38 - '&'
|
||||
1, 3, // 39
|
||||
2, 14, 17, // 40 - '('
|
||||
2, 17, 14, // 41 - ')'
|
||||
3, 10, 4, 10, // 42 - '*'
|
||||
3, 4, 14, 4, // 43 - '+'
|
||||
2, 16, 8, // 44 - ','
|
||||
3, 4, 4, 4, // 45 - '-'
|
||||
1, 16, // 46 - '.'
|
||||
3, 8, 4, 2, // 47 - '/'
|
||||
3, 31, 17, 31, // 48 - '0'
|
||||
2, 0, 31, // 49 - '1'
|
||||
3, 29, 21, 23, // 50 - '2'
|
||||
3, 17, 21, 31, // 51 - '3'
|
||||
3, 7, 4, 31, // 52 - '4'
|
||||
3, 23, 21, 29, // 53 - '5'
|
||||
3, 31, 21, 29, // 54 - '6'
|
||||
3, 1, 1, 31, // 55 - '7'
|
||||
3, 31, 21, 31, // 56 - '8'
|
||||
3, 23, 21, 31, // 57 - '9'
|
||||
1, 10, // 58 - ':'
|
||||
2, 16, 10, // 59 - ';'
|
||||
3, 4, 10, 17, // 60 - '<'
|
||||
3, 10, 10, 10, // 61 - '='
|
||||
3, 17, 10, 4, // 62 - '>'
|
||||
3, 1, 21, 3, // 63 - '?'
|
||||
3, 14, 21, 22, // 64 - '@'
|
||||
3, 30, 5, 30, // 65 - 'A'
|
||||
3, 31, 21, 10, // 66 - 'B'
|
||||
3, 14, 17, 17, // 67 - 'C'
|
||||
3, 31, 17, 14, // 68 - 'D'
|
||||
3, 31, 21, 17, // 69 - 'E'
|
||||
3, 31, 5, 1, // 70 - 'F'
|
||||
3, 14, 17, 29, // 71 - 'G'
|
||||
3, 31, 4, 31, // 72 - 'H'
|
||||
3, 17, 31, 17, // 73 - 'I'
|
||||
3, 8, 16, 15, // 74 - 'J'
|
||||
3, 31, 4, 27, // 75 - 'K'
|
||||
3, 31, 16, 16, // 76 - 'L'
|
||||
3, 31, 2, 31, // 77 - 'M'
|
||||
3, 31, 14, 31, // 78 - 'N'
|
||||
3, 14, 17, 14, // 79 - 'O'
|
||||
3, 31, 5, 2, // 80 - 'P'
|
||||
3, 14, 25, 30, // 81 - 'Q'
|
||||
3, 31, 5, 26, // 82 - 'R'
|
||||
3, 18, 21, 9, // 83 - 'S'
|
||||
3, 1, 31, 1, // 84 - 'T'
|
||||
3, 15, 16, 15, // 85 - 'U'
|
||||
3, 7, 24, 7, // 86 - 'V'
|
||||
3, 15, 28, 15, // 87 - 'W'
|
||||
3, 27, 4, 27, // 88 - 'X'
|
||||
3, 3, 28, 3, // 89 - 'Y'
|
||||
3, 25, 21, 19, // 90 - 'Z'
|
||||
2, 31, 17, // 91 - '['
|
||||
3, 2, 4, 8, // 92 - '\'
|
||||
2, 17, 31, // 93 - ']'
|
||||
3, 2, 1, 2, // 94 - '^'
|
||||
3, 16, 16, 16, // 95 - '_'
|
||||
2, 1, 2, // 96 - '`'
|
||||
3, 12, 18, 28, // 97 - 'a'
|
||||
3, 31, 18, 12, // 98 - 'b'
|
||||
3, 12, 18, 18, // 99 - 'c'
|
||||
3, 12, 18, 31, // 100 - 'd'
|
||||
3, 12, 26, 20, // 101 - 'e'
|
||||
3, 4, 31, 5, // 102 - 'f'
|
||||
3, 20, 26, 12, // 103 - 'g'
|
||||
3, 31, 2, 28, // 104 - 'h'
|
||||
1, 29, // 105 - 'i'
|
||||
2, 16, 13, // 106 - 'j'
|
||||
3, 31, 8, 20, // 107 - 'k'
|
||||
1, 31, // 108 - 'l'
|
||||
3, 30, 6, 30, // 109 - 'm'
|
||||
3, 30, 2, 28, // 110 - 'n'
|
||||
3, 12, 18, 12, // 111 - 'o'
|
||||
3, 30, 10, 4, // 112 - 'p'
|
||||
3, 4, 10, 30, // 113 - 'q'
|
||||
2, 30, 4, // 114 - 'r'
|
||||
3, 20, 30, 10, // 115 - 's'
|
||||
3, 4, 30, 4, // 116 - 't'
|
||||
3, 14, 16, 30, // 117 - 'u'
|
||||
3, 14, 16, 14, // 118 - 'v'
|
||||
3, 14, 24, 14, // 119 - 'w'
|
||||
3, 18, 12, 18, // 120 - 'x'
|
||||
3, 22, 24, 14, // 121 - 'y'
|
||||
3, 26, 30, 22, // 122 - 'z'
|
||||
3, 4, 27, 17, // 123 - '{'
|
||||
1, 27, // 124 - '|'
|
||||
3, 17, 27, 4, // 125 - '}'
|
||||
3, 6, 2, 3, // 126 - '~'
|
||||
3, 31, 31, 31, // 127 - 'Full Block'
|
||||
};
|
||||
@@ -0,0 +1,652 @@
|
||||
// Implements the game of Bricks using MD_MAXPanel library
|
||||
//
|
||||
// Hardware used
|
||||
// =============
|
||||
// LEFT_PIN - bat left switch, INPUT_PULLUP
|
||||
// RIGHT_PIN - bat right switch, INPUT_PULLUP
|
||||
// UP_PIN - unused
|
||||
// DOWN_PIN - unused
|
||||
// SELECT_PIN - unused
|
||||
// ENTER_PIN - unused
|
||||
// BEEPER_PIN - piezo speaker
|
||||
// CLK_PIN, DATA_PIN, CS_PIN - LED matrix display connections
|
||||
//
|
||||
// Libraries used
|
||||
// ==============
|
||||
// MD_MAX72XX available from https://github.com/MajicDesigns/MD_MAX72XX
|
||||
//
|
||||
// Rules of the Game
|
||||
// =================
|
||||
// Player must keep the ball in play by batting it away. Each 'brick' that
|
||||
// is hit scores points. Once all the bricks are cleared the game continues
|
||||
// with a new screen. Game end when the ball is let out of bounds.
|
||||
|
||||
#include <MD_MAXPanel.h>
|
||||
#include "Font5x3.h"
|
||||
#include "score.h"
|
||||
#include "sound.h"
|
||||
|
||||
// Turn on debug statements to the serial output
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
#define PRINT(s, x) { Serial.print(F(s)); Serial.print(x); }
|
||||
#define PRINTS(x) { Serial.print(F(x)); }
|
||||
#define PRINTD(x) { Serial.print(x, DEC); }
|
||||
#define PRINTXY(s,x,y) { Serial.print(F(s)); Serial.print(F("(")); Serial.print(x); Serial.print(F(",")); Serial.print(y); Serial.print(")"); }
|
||||
#define PRINTSTATE(s) { Serial.print("\n++>"); Serial.print(s); }
|
||||
|
||||
#else
|
||||
#define PRINT(s, x)
|
||||
#define PRINTS(x)
|
||||
#define PRINTD(x)
|
||||
#define PRINTXY(s,x,y)
|
||||
#define PRINTSTATE(s)
|
||||
|
||||
#endif
|
||||
|
||||
// Hardware pin definitions.
|
||||
// All momentary on switches are initialized INPUT_PULLUP
|
||||
const uint8_t UP_PIN = 2;
|
||||
const uint8_t RIGHT_PIN = 3;
|
||||
const uint8_t DOWN_PIN = 4;
|
||||
const uint8_t LEFT_PIN = 5;
|
||||
const uint8_t SELECT_PIN = 6;
|
||||
const uint8_t ENTER_PIN = 7;
|
||||
const uint8_t BEEPER_PIN = 9;
|
||||
|
||||
// Define the number of devices in the chain and the SPI hardware interface
|
||||
// NOTE: These pin numbers will probably not work with your hardware and may
|
||||
// need to be adapted
|
||||
const MD_MAX72XX::moduleType_t HARDWARE_TYPE = MD_MAX72XX::FC16_HW;
|
||||
const uint8_t X_DEVICES = 4;
|
||||
const uint8_t Y_DEVICES = 5;
|
||||
|
||||
const uint8_t CLK_PIN = 12; // or SCK
|
||||
const uint8_t DATA_PIN = 11; // or MOSI
|
||||
const uint8_t CS_PIN = 10; // or SS
|
||||
|
||||
// SPI hardware interface
|
||||
MD_MAXPanel mp = MD_MAXPanel(HARDWARE_TYPE, CS_PIN, X_DEVICES, Y_DEVICES);
|
||||
// Arbitrary pins
|
||||
// MD_MAXPanel mx = MD_MAXPanel(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, X_DEVICES, Y_DEVICES);
|
||||
|
||||
uint16_t FIELD_TOP, FIELD_RIGHT; // needs to be initialised in setup()
|
||||
const uint16_t FIELD_LEFT = 1;
|
||||
|
||||
const uint8_t BRICK_SIZE_DEFAULT = 3;
|
||||
const uint8_t BAT_SIZE_DEFAULT = 3; // must be an odd number
|
||||
const uint8_t BAT_EDGE_OFFSET = 1;
|
||||
|
||||
const char TITLE_TEXT[] = "BRICKS";
|
||||
const uint16_t SPLASH_DELAY =3000; // in milliseconds
|
||||
|
||||
const char GAME_TEXT[] = "GAME";
|
||||
const char OVER_TEXT[] = "OVER";
|
||||
const uint16_t GAME_OVER_DELAY = 3000; // in milliseconds
|
||||
|
||||
const uint8_t FONT_NUM_WIDTH = 3;
|
||||
const uint8_t MAX_LIVES = 9;
|
||||
const uint16_t MAX_SCORE = 999;
|
||||
|
||||
// A class to encapsulate the bricks bat
|
||||
// Bat runs at the bottom of the display to keep the ball in play
|
||||
class cBrickBat
|
||||
{
|
||||
private:
|
||||
uint16_t _x, _y; // the position of the center of the bat
|
||||
uint16_t _xmin, _xmax; // the max and min bat boundaries
|
||||
int8_t _vel; // the velocity of the bat (+1 for moving up, -1 moving down)
|
||||
uint8_t _size; // the size in pixels for the bat (odd number)
|
||||
uint8_t _pinLeft; // the pin for the up switch
|
||||
uint8_t _pinRight; // the pin for th down switch
|
||||
uint16_t _batDelay; // the delay between possible moves of the bat in milliseconds
|
||||
uint32_t _timeLastMove; // the millis() value for the last time we moved the bat
|
||||
|
||||
public:
|
||||
enum hitType_t { NO_HIT, CORNER_HIT, FLAT_HIT };
|
||||
|
||||
void begin(uint16_t x, uint16_t y, uint16_t xmin, uint16_t xmax, uint8_t size, uint8_t pinL, uint8_t pinR)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
_xmin = xmin;
|
||||
_xmax = xmax;
|
||||
_vel = 0;
|
||||
_size = size;
|
||||
_pinLeft = pinL;
|
||||
_pinRight = pinR;
|
||||
_batDelay = 40;
|
||||
pinMode(_pinLeft, INPUT_PULLUP);
|
||||
pinMode(_pinRight, INPUT_PULLUP);
|
||||
PRINTXY("\nbat @", _x, _y);
|
||||
PRINTXY(" limits", _xmin, _xmax);
|
||||
}
|
||||
|
||||
uint16_t getX(void) { return (_x); }
|
||||
uint16_t getY(void) { return (_y); }
|
||||
|
||||
int8_t getVelocity(void) { return(_vel); }
|
||||
void draw(void) { mp.drawHLine(_y, _x - (_size / 2), _x + (_size / 2), true); }
|
||||
void erase(void) { mp.drawHLine(_y, _x - (_size / 2), _x + (_size / 2), false); }
|
||||
bool anyKey(void) { return(digitalRead(_pinLeft) == LOW || digitalRead(_pinRight) == LOW); }
|
||||
|
||||
hitType_t hit(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
int16_t dx = x1 - x0;
|
||||
|
||||
// if we are not in the same y plane as the bat there can be no hit
|
||||
if (y1 != _y) return(NO_HIT);
|
||||
|
||||
// if the ball is at
|
||||
// - the left of the bat and travelling left to right, or
|
||||
// - the right of the bat and travelling right to left
|
||||
// then it is a corner hit
|
||||
if ((x1 == _x + (_size / 2) && dx < 0) || (x1 == _x - (_size / 2) && dx>0))
|
||||
return(CORNER_HIT);
|
||||
|
||||
// the ball is between the left and right bat boundaries inclusive
|
||||
// then it is a flat hit
|
||||
if (x1 <= _x + (_size / 2) && x1 >= _x - (_size / 2))
|
||||
return(FLAT_HIT);
|
||||
|
||||
// and in case we missed something, a default no hit
|
||||
return(NO_HIT);
|
||||
}
|
||||
|
||||
bool move(void)
|
||||
{
|
||||
bool moveLeft = (digitalRead(_pinLeft) == LOW);
|
||||
bool moveRight = (digitalRead(_pinRight) == LOW);
|
||||
|
||||
// if this is the time for a move?
|
||||
if (millis() - _timeLastMove <= _batDelay)
|
||||
return(false);
|
||||
_timeLastMove = millis();
|
||||
|
||||
mp.update(false);
|
||||
|
||||
if (moveRight)
|
||||
{
|
||||
PRINTS("\n-- BAT move Left");
|
||||
erase();
|
||||
_vel = 1;
|
||||
_x++;
|
||||
if (_x + (_size/2) > _xmax) _x--; // keep within top boundary
|
||||
draw();
|
||||
}
|
||||
else if (moveLeft)
|
||||
{
|
||||
PRINTS("\n-- BAT move right");
|
||||
erase();
|
||||
_vel = -1;
|
||||
_x--;
|
||||
if (_x - (_size/2) < _xmin) _x++; // keep within bottom boundary
|
||||
draw();
|
||||
}
|
||||
else
|
||||
_vel = 0;
|
||||
|
||||
mp.update(true);
|
||||
return((moveLeft || moveRight));
|
||||
}
|
||||
};
|
||||
|
||||
// A class to encapsulate the bricks ball
|
||||
// Ball bounces off the bat, edges and bricks
|
||||
class cBrickBall
|
||||
{
|
||||
private:
|
||||
uint16_t _x, _y; // the position of the center of the ball
|
||||
int8_t _dx, _dy; // the offsets for the x and y direction
|
||||
uint16_t _xmin, _ymin; // minimum bounds for the ball
|
||||
uint16_t _xmax, _ymax; // maximum bounds for the ball
|
||||
uint32_t _timeLastMove; // last time the ball was moved
|
||||
uint16_t _ballDelay; // the delay between ball moves in milliseconds
|
||||
bool _run; // ball is running when true
|
||||
|
||||
public:
|
||||
enum bounce_t { BOUNCE_NONE, BOUNCE_BACK, BOUNCE_TOP, BOUNCE_BOTTOM, BOUNCE_LEFT, BOUNCE_RIGHT };
|
||||
|
||||
void begin(uint16_t x, uint16_t y, uint16_t xmin, uint16_t ymin, uint16_t xmax, uint16_t ymax)
|
||||
{
|
||||
_dx = _dy = 1;
|
||||
_timeLastMove = 0;
|
||||
_ballDelay = 100;
|
||||
_xmin = xmin;
|
||||
_xmax = xmax;
|
||||
_ymin = ymin;
|
||||
_ymax = ymax;
|
||||
_run = false;
|
||||
reset(x, y);
|
||||
}
|
||||
|
||||
uint16_t getX(void) { return (_x); }
|
||||
uint16_t getY(void) { return (_y); }
|
||||
uint16_t getNextX(void) { return (_x + _dx); }
|
||||
uint16_t getNextY(void) { return (_y + _dy); }
|
||||
uint16_t getDelay(void) { return (_ballDelay); }
|
||||
void setDelay(uint16_t delay) { if (delay > 10) _ballDelay = delay; }
|
||||
|
||||
void start(void) { _run = true; }
|
||||
void stop(void) { _run = false; }
|
||||
void draw(void) { mp.setPoint(_x, _y, true); } // PRINTXY("\nball@", _x, _y); }
|
||||
void erase(void) { mp.setPoint(_x, _y, false); }
|
||||
void reset(uint16_t x, uint16_t y) { _x = x; _y = y; _dy = 1; }
|
||||
|
||||
bool move(void)
|
||||
{
|
||||
// if this is the time for a move?
|
||||
if (_run && millis() - _timeLastMove <= _ballDelay)
|
||||
return(false);
|
||||
_timeLastMove = millis();
|
||||
|
||||
// do the animation
|
||||
mp.update(false);
|
||||
erase();
|
||||
_x = _x + _dx;
|
||||
_y = _y + _dy;
|
||||
|
||||
// ensure it always stays in bounds
|
||||
if (_x < _xmin) { _x = _xmin; _dx = -_dx; }
|
||||
if (_x > _xmax) { _x = _xmax; _dx = -_dx; }
|
||||
if (_y < _ymin) { _y = _ymin; _dy = -_dy; }
|
||||
if (_y > _ymax) { _y = _ymax; _dy = -_dy; }
|
||||
|
||||
// now update
|
||||
draw();
|
||||
mp.update(true);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
void bounce(bounce_t b)
|
||||
{
|
||||
switch (b)
|
||||
{
|
||||
case BOUNCE_TOP:
|
||||
case BOUNCE_BOTTOM: _dy = -_dy; break;
|
||||
case BOUNCE_LEFT:
|
||||
case BOUNCE_RIGHT: _dx = -_dx; break;
|
||||
case BOUNCE_BACK: _dx = -_dx; _dy = -_dy; break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// A class to encapsulate the entire field of bricks
|
||||
// All the bricks are contained in this object.
|
||||
class cBrickField
|
||||
{
|
||||
private:
|
||||
struct brick_t
|
||||
{
|
||||
uint16_t x, y; // leftmost coordinate for this brick
|
||||
brick_t *next; // the next in the linked list
|
||||
};
|
||||
|
||||
uint8_t _size; // size of the bricks
|
||||
brick_t *_bricks; // start of the list
|
||||
brick_t *_deleted; // the deleted list
|
||||
|
||||
void draw(brick_t *pb) { mp.drawHLine(pb->y, pb->x, pb->x + _size-1, true); }
|
||||
void erase(brick_t *pb) { mp.drawHLine(pb->y, pb->x, pb->x + _size-1, false); }
|
||||
|
||||
void add(uint16_t x, uint16_t y)
|
||||
// add a brick to the start of the list
|
||||
{
|
||||
brick_t *pb;
|
||||
|
||||
if (_deleted == nullptr)
|
||||
pb = new brick_t;
|
||||
else
|
||||
{
|
||||
// take from the front of the deleted list
|
||||
pb = _deleted;
|
||||
_deleted = _deleted->next;
|
||||
}
|
||||
|
||||
pb->x = x;
|
||||
pb->y = y;
|
||||
pb->next = _bricks;
|
||||
_bricks = pb;
|
||||
|
||||
return(pb);
|
||||
}
|
||||
|
||||
void del(brick_t *pbPrev, brick_t *pb)
|
||||
// delete the specified brick from the list
|
||||
{
|
||||
if (pbPrev == nullptr)
|
||||
_bricks = pb->next;
|
||||
else
|
||||
pbPrev->next = pb->next;
|
||||
|
||||
// save to the front of the deleted list
|
||||
pb->next = _deleted;
|
||||
_deleted = pb;
|
||||
}
|
||||
|
||||
void dumpList(void)
|
||||
{
|
||||
PRINTS("\nDUMP List ===");
|
||||
for (brick_t *pb = _bricks; pb != nullptr; pb = pb->next)
|
||||
{
|
||||
PRINTXY("\n", pb->x, pb->y);
|
||||
draw(pb);
|
||||
}
|
||||
PRINTS("\n===");
|
||||
}
|
||||
|
||||
public:
|
||||
enum bounce_t { BOUNCE_NONE, BOUNCE_BACK, BOUNCE_UP, BOUNCE_DOWN };
|
||||
|
||||
void begin(uint16_t xmin, uint16_t ymin, uint16_t xmax, uint16_t ymax, uint8_t size)
|
||||
{
|
||||
// work out how many bricks we can put in the space
|
||||
const uint8_t gapX = 2;
|
||||
const uint8_t gapY = 3;
|
||||
uint8_t marginSide = 2;
|
||||
const uint8_t marginTop = gapY;
|
||||
const uint8_t numAcross = (xmax - xmin - marginSide - marginSide + size) / (gapX + size);
|
||||
const uint8_t numDown = (ymax - ymin - marginTop) / gapY;
|
||||
|
||||
// now adjust the side margin to center the display as much as possible
|
||||
marginSide = (1 + (xmax - xmin) - ((numAcross - 1)*gapX) - (numAcross*size))/2;
|
||||
|
||||
PRINT("\nBricks size=", size);
|
||||
PRINTXY(" field = ", xmin, ymin);
|
||||
PRINTXY(" ", xmax, ymax);
|
||||
PRINT(" -> Across=", numAcross);
|
||||
PRINT(" Down=", numDown);
|
||||
PRINT(" Adj margin=", marginSide);
|
||||
|
||||
_size = size;
|
||||
_bricks = _deleted = nullptr;
|
||||
|
||||
// create all the bricks
|
||||
uint16_t x = xmin + marginSide;
|
||||
for (uint8_t i = 0; i < numAcross; i++)
|
||||
{
|
||||
uint16_t y = ymax - marginTop;
|
||||
|
||||
PRINT("\nin column ", i);
|
||||
for (uint8_t j = 0; j < numDown; j++)
|
||||
{
|
||||
add(x, y);
|
||||
PRINTXY(" - ", x, y);
|
||||
y -= gapY;
|
||||
}
|
||||
x += (gapX + size);
|
||||
}
|
||||
|
||||
//dumpList(); // debug to verify the list is created properly
|
||||
}
|
||||
|
||||
bool emptyField(void) { return(_bricks == nullptr); }
|
||||
void drawField(void) { for (brick_t *pb = _bricks; pb != nullptr; pb = pb->next) draw(pb); }
|
||||
void eraseField(void) { while (_bricks != nullptr) { erase(_bricks); del(nullptr, _bricks); } }
|
||||
|
||||
bounce_t checkHits(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
|
||||
{
|
||||
brick_t *pb = _bricks;
|
||||
brick_t *pbPrev = nullptr;
|
||||
int16_t dx = x2 - x1;
|
||||
int16_t dy = y2 - y1;
|
||||
bounce_t b = BOUNCE_NONE;
|
||||
|
||||
while (pb != nullptr)
|
||||
{
|
||||
if (y2 == pb->y) // at the same height as this brick
|
||||
{
|
||||
// check one of the corners with sideways approach
|
||||
if ((x2 == pb->x && dx > 0) || (x2 == pb->x - _size - 1 && dx < 0))
|
||||
{
|
||||
PRINTS("\n-! BRICK hit on corner");
|
||||
b = BOUNCE_BACK;
|
||||
}
|
||||
// check the whole flat surface with any approach
|
||||
else if (x2 >= pb->x && x2 <= pb->x + _size - 1)
|
||||
{
|
||||
if (dy < 0)
|
||||
{
|
||||
b = BOUNCE_UP;
|
||||
PRINTS("\n-! BRICK hit on top");
|
||||
}
|
||||
else
|
||||
{
|
||||
b = BOUNCE_DOWN;
|
||||
PRINTS("\n-! BRICK hit on bottom");
|
||||
}
|
||||
}
|
||||
|
||||
// eliminate this brick
|
||||
if (b != BOUNCE_NONE)
|
||||
{
|
||||
PRINTXY("\n-! Ball @", x1, y1);
|
||||
PRINTXY(" next@", x2, y2);
|
||||
PRINTXY(" brick ", pb->x, pb->y);
|
||||
PRINTXY("-", pb->x+_size-1, pb->y);
|
||||
PRINTS(" - deleting");
|
||||
erase(pb);
|
||||
del(pbPrev, pb);
|
||||
break; // no point looping further - only one brick per hit
|
||||
}
|
||||
}
|
||||
// advance the pointers
|
||||
pbPrev = pb;
|
||||
pb = pb->next;
|
||||
}
|
||||
|
||||
return(b);
|
||||
}
|
||||
};
|
||||
|
||||
// main objects coordinated by the code logic
|
||||
cScore lives, score;
|
||||
cBrickBall ball;
|
||||
cBrickBat bat;
|
||||
cBrickField bricks;
|
||||
cSound sound;
|
||||
|
||||
void setupField(void)
|
||||
// Draw the playing field at the start of the game.
|
||||
{
|
||||
mp.clear();
|
||||
mp.drawHLine(FIELD_TOP, FIELD_LEFT, FIELD_RIGHT);
|
||||
mp.drawVLine(FIELD_LEFT, 0, FIELD_TOP);
|
||||
mp.drawVLine(FIELD_RIGHT, 0, FIELD_TOP);
|
||||
bat.draw();
|
||||
lives.draw();
|
||||
score.draw();
|
||||
ball.draw();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
#if DEBUG
|
||||
Serial.begin(57600);
|
||||
#endif
|
||||
PRINTS("\n[MD_MAXPanel_Bricks]");
|
||||
|
||||
mp.begin();
|
||||
mp.setFont(_Fixed_5x3);
|
||||
mp.setIntensity(4);
|
||||
mp.setRotation(MD_MAXPanel::ROT_90);
|
||||
|
||||
// one time initialization
|
||||
FIELD_TOP = mp.getYMax() - mp.getFontHeight() - 2;
|
||||
FIELD_RIGHT = mp.getXMax() - 1;
|
||||
|
||||
bat.begin((FIELD_RIGHT - FIELD_LEFT) / 2, BAT_EDGE_OFFSET, FIELD_LEFT + 1, FIELD_RIGHT - 1, BAT_SIZE_DEFAULT, LEFT_PIN, RIGHT_PIN);
|
||||
ball.begin(bat.getX(), bat.getY() + 1, FIELD_LEFT+1, 0, FIELD_RIGHT-1, FIELD_TOP-1);
|
||||
lives.begin(&mp, FIELD_LEFT + 1, FIELD_TOP + 1 + mp.getFontHeight(), MAX_LIVES);
|
||||
score.limit(MAX_SCORE); // set width() so we can used it below
|
||||
score.begin(&mp, FIELD_RIGHT - (score.width() * (FONT_NUM_WIDTH + mp.getCharSpacing())) + mp.getCharSpacing(), FIELD_TOP + 1 + mp.getFontHeight(), MAX_SCORE);
|
||||
sound.begin(BEEPER_PIN);
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
static enum { S_SPLASH, S_INIT, S_WAIT_START, S_POINT_PLAY, S_BALL_OUT, S_BRICKS_EMPTY, S_POINT_RESET, S_GAME_OVER } runState = S_SPLASH;
|
||||
|
||||
switch (runState)
|
||||
{
|
||||
case S_SPLASH: // show splash screen at start
|
||||
PRINTSTATE("SPLASH");
|
||||
{
|
||||
const uint16_t border = 2;
|
||||
|
||||
mp.clear();
|
||||
mp.drawRectangle(border, border, mp.getXMax() - border, mp.getYMax() - border);
|
||||
mp.drawLine(0, 0, border, border);
|
||||
mp.drawLine(0, mp.getYMax(), border, mp.getYMax()-border);
|
||||
mp.drawLine(mp.getXMax(), 0, mp.getXMax()-border, border);
|
||||
mp.drawLine(mp.getXMax(), mp.getYMax(), mp.getXMax()-border, mp.getYMax()-border);
|
||||
mp.drawText((mp.getXMax() - mp.getTextWidth(TITLE_TEXT)) / 2, (mp.getYMax() + mp.getFontHeight())/2 , TITLE_TEXT);
|
||||
sound.splash();
|
||||
delay(SPLASH_DELAY);
|
||||
|
||||
runState = S_INIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case S_INIT: // initialize for a new game
|
||||
PRINTSTATE("INIT");
|
||||
ball.reset(bat.getX(), bat.getY() + 1);
|
||||
lives.set(MAX_LIVES);
|
||||
score.reset();
|
||||
setupField();
|
||||
bricks.begin(FIELD_LEFT + 1, FIELD_TOP / 3, FIELD_RIGHT - 1, FIELD_TOP - 1, BRICK_SIZE_DEFAULT);
|
||||
bricks.drawField();
|
||||
|
||||
runState = S_WAIT_START;
|
||||
PRINTSTATE("WAIT_START");
|
||||
break;
|
||||
|
||||
case S_WAIT_START: // waiting for the start of a new game
|
||||
if (bat.anyKey())
|
||||
{
|
||||
PRINTS("\n-- Starting Game");
|
||||
sound.start();
|
||||
ball.start();
|
||||
runState = S_POINT_PLAY;
|
||||
PRINTSTATE("POINT_PLAY");
|
||||
}
|
||||
break;
|
||||
|
||||
case S_POINT_PLAY: // playing a point
|
||||
// handle the bat animation first
|
||||
bat.move();
|
||||
|
||||
// now move the ball and check what this means
|
||||
if (ball.move())
|
||||
{
|
||||
cBrickBat::hitType_t lastHit;
|
||||
|
||||
// check for top/bottom edge collisions
|
||||
if (ball.getX() <= FIELD_LEFT + 1)
|
||||
{
|
||||
PRINTS("\n-- COLLISION left edge");
|
||||
ball.bounce(cBrickBall::BOUNCE_LEFT);
|
||||
sound.bounce();
|
||||
}
|
||||
else if (ball.getX() >= FIELD_RIGHT - 1)
|
||||
{
|
||||
PRINTS("\n-- COLLISION right edge");
|
||||
ball.bounce(cBrickBall::BOUNCE_RIGHT);
|
||||
sound.bounce();
|
||||
}
|
||||
if (ball.getY() >= FIELD_TOP - 1)
|
||||
{
|
||||
PRINTS("\n-- COLLISION top edge");
|
||||
ball.bounce(cBrickBall::BOUNCE_TOP);
|
||||
sound.bounce();
|
||||
}
|
||||
|
||||
// check for bat collisions
|
||||
if ((lastHit = bat.hit(ball.getX(), ball.getY(), ball.getNextX(), ball.getNextY())) != cBrickBat::NO_HIT)
|
||||
{
|
||||
PRINTS("\n-- COLLISION bat");
|
||||
ball.bounce(lastHit == cBrickBat::CORNER_HIT ? cBrickBall::BOUNCE_BACK : cBrickBall::BOUNCE_BOTTOM);
|
||||
sound.hit();
|
||||
}
|
||||
|
||||
// check for out of bounds at the bottom
|
||||
if (ball.getY() < BAT_EDGE_OFFSET)
|
||||
{
|
||||
PRINTS("\n-- OUT!");
|
||||
runState = S_BALL_OUT;
|
||||
}
|
||||
|
||||
// check for any hits to bricks
|
||||
cBrickField::bounce_t b;
|
||||
if ((b = bricks.checkHits(ball.getX(), ball.getY(), ball.getNextX(), ball.getNextY())) != cBrickField::BOUNCE_NONE)
|
||||
{
|
||||
score.increment();
|
||||
sound.bounce();
|
||||
// check if this is the end of all the bricks
|
||||
if (bricks.emptyField())
|
||||
{
|
||||
PRINTS("\n== BRICKS empty");
|
||||
runState = S_BRICKS_EMPTY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise we need to bounce the ball in the right direction
|
||||
switch (b)
|
||||
{
|
||||
case cBrickField::BOUNCE_UP: ball.bounce(cBrickBall::BOUNCE_BOTTOM); break;
|
||||
case cBrickField::BOUNCE_DOWN: ball.bounce(cBrickBall::BOUNCE_TOP); break;
|
||||
case cBrickField::BOUNCE_BACK: ball.bounce(cBrickBall::BOUNCE_BACK); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case S_BRICKS_EMPTY: // handle the bricks being completed
|
||||
PRINTSTATE("BRICKS_EMPTY");
|
||||
ball.stop();
|
||||
ball.erase();
|
||||
lives.increment();
|
||||
ball.setDelay(ball.getDelay() - 5);
|
||||
sound.point();
|
||||
bricks.begin(FIELD_LEFT + 1, FIELD_TOP / 3, FIELD_RIGHT - 1, FIELD_TOP - 1, BRICK_SIZE_DEFAULT);
|
||||
bricks.drawField();
|
||||
runState = S_POINT_RESET;
|
||||
break;
|
||||
|
||||
case S_BALL_OUT: // handle the ball going out
|
||||
PRINTSTATE("BALL_OUT");
|
||||
ball.stop();
|
||||
ball.erase();
|
||||
lives.decrement();
|
||||
sound.point();
|
||||
if (lives.score() != 0)
|
||||
runState = S_POINT_RESET;
|
||||
else
|
||||
runState = S_GAME_OVER;
|
||||
break;
|
||||
|
||||
case S_POINT_RESET:
|
||||
PRINTSTATE("POINT_RESET");
|
||||
bat.draw();
|
||||
ball.reset(bat.getX(), bat.getY() + 1);
|
||||
ball.draw();
|
||||
delay(500);
|
||||
runState = S_WAIT_START;
|
||||
PRINTSTATE("WAIT_START");
|
||||
break;
|
||||
|
||||
case S_GAME_OVER:
|
||||
PRINTSTATE("GAME_OVER");
|
||||
bricks.eraseField();
|
||||
mp.drawText((mp.getXMax() - mp.getTextWidth(GAME_TEXT)) / 2, FIELD_TOP / 2 + mp.getFontHeight() + 1, GAME_TEXT);
|
||||
mp.drawText((mp.getXMax() - mp.getTextWidth(OVER_TEXT)) / 2, FIELD_TOP / 2 - 1, OVER_TEXT);
|
||||
|
||||
sound.over();
|
||||
delay(GAME_OVER_DELAY);
|
||||
runState = S_INIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <MD_MAXPanel.h>
|
||||
|
||||
// A class to encapsulate the score display
|
||||
class cScore
|
||||
{
|
||||
private:
|
||||
MD_MAXPanel *_mp; // the MAXPanel object for drawing the score
|
||||
uint16_t _score; // the score
|
||||
uint16_t _x, _y; // coordinate of top left for display
|
||||
uint8_t _width; // number of digits wide
|
||||
uint16_t _limit; // maximum value allowed
|
||||
|
||||
public:
|
||||
void begin(MD_MAXPanel *mp, uint16_t x, uint16_t y, uint16_t maxScore) { _mp = mp; _x = x, _y = y; limit(maxScore); reset(); }
|
||||
void reset(void) { erase(); _score = 0; draw(); }
|
||||
void set(uint16_t s) { if (s <= _limit) { erase(); _score = s; draw(); } }
|
||||
void increment(uint16_t inc = 1) { if (_score + inc <= _limit) { erase(); _score += inc; draw(); } }
|
||||
void decrement(uint16_t dec = 1) { if (_score >= dec) { erase(); _score -= dec; draw(); } }
|
||||
uint16_t score(void) { return(_score); }
|
||||
void erase(void) { draw(false); }
|
||||
uint16_t width(void) { return(_width); }
|
||||
|
||||
void limit(uint16_t m)
|
||||
{
|
||||
erase(); // width may change, so delete with the curret parameters
|
||||
|
||||
_limit = m;
|
||||
// work out how many digits this is
|
||||
_width = 0;
|
||||
do
|
||||
{
|
||||
_width++;
|
||||
m /= 10;
|
||||
} while (m != 0);
|
||||
}
|
||||
|
||||
void draw(bool state = true)
|
||||
{
|
||||
char sz[_width + 1];
|
||||
uint16_t s = _score;
|
||||
|
||||
// PRINT("\n-- SCORE: ", _score);
|
||||
sz[_width] = '\0';
|
||||
for (int i = _width - 1; i >= 0; --i)
|
||||
{
|
||||
sz[i] = (s % 10) + '0';
|
||||
s /= 10;
|
||||
}
|
||||
|
||||
_mp->drawText(_x, _y, sz, MD_MAXPanel::ROT_0, state);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
// A class to encapsulate primitive sound effects
|
||||
class cSound
|
||||
{
|
||||
private:
|
||||
const uint16_t EOD = 0; // End Of Data marker
|
||||
uint8_t _pinBeep; // the pin to use for beeping
|
||||
|
||||
// Sound data - frequency followed by duration in pairs.
|
||||
// Data ends in End Of Data marker EOD.
|
||||
const uint16_t soundSplash[1] PROGMEM = { EOD };
|
||||
const uint16_t soundHit[3] PROGMEM = { 1000, 50, EOD };
|
||||
const uint16_t soundBounce[3] PROGMEM = { 500, 50, EOD };
|
||||
const uint16_t soundPoint[3] PROGMEM = { 150, 150, EOD };
|
||||
const uint16_t soundStart[7] PROGMEM = { 250, 100, 500, 100, 1000, 100, EOD };
|
||||
const uint16_t soundOver[7] PROGMEM = { 1000, 100, 500, 100, 250, 100, EOD };
|
||||
|
||||
void playSound(const uint16_t *table)
|
||||
// Play sound table data. Data table must end in EOD marker.
|
||||
{
|
||||
uint8_t idx = 0;
|
||||
|
||||
//PRINTS("\nTone Data ");
|
||||
while (table[idx] != EOD)
|
||||
{
|
||||
uint16_t t = table[idx++];
|
||||
uint16_t d = table[idx++];
|
||||
|
||||
//PRINTXY("-", t, d);
|
||||
tone(_pinBeep, t);
|
||||
delay(d);
|
||||
}
|
||||
//PRINTS("-EOD");
|
||||
noTone(_pinBeep); // be quiet now!
|
||||
}
|
||||
|
||||
public:
|
||||
void begin(uint8_t pinBeep) { _pinBeep = pinBeep; }
|
||||
void splash(void) { playSound(soundSplash); }
|
||||
void start(void) { playSound(soundStart); }
|
||||
void hit(void) { playSound(soundHit); }
|
||||
void bounce(void) { playSound(soundBounce); }
|
||||
void point(void) { playSound(soundPoint); }
|
||||
void over(void) { playSound(soundOver); }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user