From f1ff7edc00643d3e09a1d62110f5e1bdfca29dc4 Mon Sep 17 00:00:00 2001 From: Stephen Denne Date: Sat, 25 Jul 2020 16:43:53 +1200 Subject: [PATCH] Added PacMan example --- examples/PacMan/Graphics.h | 124 ++++++++ examples/PacMan/PacMan.ino | 555 ++++++++++++++++++++++++++++++++++++ examples/PacMan/PacManMap.h | 91 ++++++ library.properties | 2 +- 4 files changed, 771 insertions(+), 1 deletion(-) create mode 100644 examples/PacMan/Graphics.h create mode 100644 examples/PacMan/PacMan.ino create mode 100644 examples/PacMan/PacManMap.h diff --git a/examples/PacMan/Graphics.h b/examples/PacMan/Graphics.h new file mode 100644 index 0000000..47859b4 --- /dev/null +++ b/examples/PacMan/Graphics.h @@ -0,0 +1,124 @@ +#define spriteBlank 0 +#define spritePacmanRight1 1 +#define spritePacmanRight2 2 +#define spritePacmanUp1 3 +#define spritePacmanUp2 4 +#define spritePacmanDown1 5 +#define spritePacmanDown2 6 +#define spriteGhostRightHunting 7 +#define spriteGhostRightRunning 8 +#define spritePellet 9 +#define spriteSuperPellet 10 +#define spriteWallRight 11 +#define spriteWallAbove 12 +#define spriteWallLeft 13 +#define spriteWallBelow 14 + +#define spriteR 15 +#define spriteE 16 +#define spriteA 17 +#define spriteD 18 +#define spriteY 19 +#define spriteBang 20 + +#define spriteEntering 0x20 +#define spriteShifted 0x40 +#define spriteReversed 0x80 +#define spriteMask 0x1F + +const uint8_t sprites [] PROGMEM = { + // Empty passage + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0 + // Pacman facing right, mouth open + 0x3C,0x7E,0xFF,0xFB,0xEF,0xE7,0x62,0x20, // 1 + // Pacman facing right, mouth closed + 0x3C,0x7E,0xFF,0xFB,0xFF,0xFF,0x7E,0x3C, // 2 + // Pacman going up, mouth open + 0x3C,0x7E,0xEC,0xF8,0xF0,0xFF,0x7E,0x3C, // 3 + // Pacman going up, mouth closed + 0x3C,0x7E,0xEF,0xFF,0xFF,0xFF,0x7E,0x3C, // 4 + // Pacman going down, mouth open + 0x3C,0x7E,0xFF,0x0F,0x1F,0x37,0x7E,0x3C, // 5 + // Pacman going down, mouth closed + 0x3C,0x7E,0xFF,0xFF,0xFF,0xF7,0x7E,0x3C, // 6 + + // Ghost facing right, hunting + 0xFC,0x7E,0x73,0xF7,0x7F,0xF3,0x77,0xFE, // 7 + // Ghost facing right, running + 0xFC,0x42,0x41,0xC9,0x41,0xC9,0x41,0xFE, // 8 + // Pellet + 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00, // 9 + // Power-up pellet + 0x00,0x18,0x3C,0x7E,0x7E,0x3C,0x18,0x00, // 10 + + // Wall with wall to right + //0x00,0x18,0x24,0x42,0x42,0x66,0x7E,0x42, // 11 + 0x00,0x00,0x18,0x24,0x24,0x3C,0x24,0x24, // 11 + // Wall with wall above + //0x00,0x1F,0x26,0x42,0x42,0x26,0x1F,0x00, // 12 + 0x00,0x00,0x1F,0x24,0x24,0x1F,0x00,0x00, // 12 + // Wall with wall to left + //0x42,0x7E,0x66,0x42,0x42,0x24,0x18,0x00, // 13 + 0x24,0x24,0x3C,0x24,0x24,0x18,0x00,0x00, // 13 + // Wall with wall below + //0x00,0xF8,0x64,0x42,0x42,0x64,0xF8,0x00, // 14 + 0x00,0x00,0xF8,0x24,0x24,0xF8,0x00,0x00, // 14 + + // R + 0x00,0x7F,0x7F,0x11,0x11,0x39,0x6E,0x4E, + // E + 0x00,0x00,0x7F,0x7F,0x49,0x49,0x49,0x41, + // A + 0x00,0x7C,0x7E,0x13,0x11,0x13,0x7E,0x7C, + // D + 0x00,0x7F,0x7F,0x41,0x41,0x63,0x3E,0x1C, + // Y + 0x00,0x00,0x07,0x0F,0x78,0x78,0x0F,0x07, + // ! + 0x00,0x00,0x40,0x1C,0x0F,0x07,0x03,0x00, + + // 0 + 0x7C,0x82,0x7C,0x00, + // 1 + 0x84,0xFE,0x80,0x00, + // 2 + 0xC4,0xA2,0x1C,0x00, + // 3 + 0x44,0x92,0x6C,0x00, + // 4 + 0x3E,0x20,0xF8,0x00, + // 5 + 0x9E,0x92,0x62,0x00, + // 6 + 0x7C,0x92,0x60,0x00, + // 7 + 0x02,0xE2,0x1E,0x00, + // 8 + 0x6C,0x92,0x6C,0x00, + // 9 + 0x8C,0x92,0x7C,0x00, + +}; + +const uint8_t digitGlyphs [] PROGMEM = { + // 0 + 0x7C,0x82,0x7C, + // 1 + 0x84,0xFE,0x80, + // 2 + 0xC4,0xA2,0x1C, + // 3 + 0x44,0x92,0x6C, + // 4 + 0x3E,0x20,0xF8, + // 5 + 0x9E,0x92,0x62, + // 6 + 0x7C,0x92,0x60, + // 7 + 0x02,0xE2,0x1E, + // 8 + 0x6C,0x92,0x6C, + // 9 + 0x8C,0x92,0x7C, +}; diff --git a/examples/PacMan/PacMan.ino b/examples/PacMan/PacMan.ino new file mode 100644 index 0000000..4955f01 --- /dev/null +++ b/examples/PacMan/PacMan.ino @@ -0,0 +1,555 @@ +/* + * TinyNunchuk & Tiny4kOLED example of a scrolling PacMan maze + */ + +/* + * My next steps if there's ram left, are to add + * - ghost movement + * - super pellets switching ghost mode + * - collisions + * - display of score (currently holding C button pauses the game and shows the score) + */ + +// Choose your I2C implementation before including Tiny4kOLED.h or TinyNunchuk.h +// The default is selected is Wire.h + +// To use the Wire library: +//#include + +// To use the Adafruit's TinyWireM library: +//#include + +// To use the TinyI2C library from https://github.com/technoblogy/tiny-i2c +//#include + +#include + +// The blue OLED screen requires a long initialization on power on. +// The code to wait for it to be ready uses 20 bytes of program storage space +// If you are using a white OLED, this can be reclaimed by uncommenting +// the following line (before including Tiny4kOLED.h): +//#define TINY4KOLED_QUICK_BEGIN + +#include + +#include "PacManMap.h" +#include "Graphics.h" + +bool gameStarted; +uint32_t score; +uint8_t pacmanDirection; +uint8_t pacmanLocationX; +uint8_t pacmanLocationY; +uint8_t pacmanStep; +bool wallAbove; +bool wallBelow; +bool wallLeft; +bool wallRight; +int8_t blinkyLocationX; // Red Ghost +int8_t blinkyLocationY; +int8_t pinkyLocationX; // Pink Ghost +int8_t pinkyLocationY; +int8_t inkyLocationX; // Blue Ghost +int8_t inkyLocationY; +int8_t clydeLocationX; // Orange Ghost +int8_t clydeLocationY; + +const uint8_t movementX [] = { 0xFF, 0x01, 0x00, 0x00 }; +const uint8_t movementY [] = { 0x00, 0x00, 0xFF, 0x01 }; + +// In order to keep pacman in the middle of the screen, +// the 0 values for topMazeLine and leftMazeColumn +// need to be to outside the map +#define screenOffsetXTiles 8 +#define screenOffsetYTiles 4 + +uint8_t topMazeLine; +uint8_t leftMazeColumn; + +uint8_t tileBits [136] = {}; + +uint8_t horizontalPellets[7*4] = {}; +uint8_t verticalPellets[10*4] = {}; + +void setup() { + nunchuk.begin(); + oled.begin(128, 64, sizeof(tiny4koled_init_128x64br), tiny4koled_init_128x64br); + //oled.setComOutputDirection(0); + restartGame(); +} + +void loop() { + nunchuk.update(); + + if (!gameStarted) { + if (topMazeLine != (19 + screenOffsetYTiles) * 8) { + scrollUp(); + } else { + if (nunchuk.z) { + gameStarted = true; + } + } + return; + } + + while (nunchuk.c) { + // pause to show scores and lives, and allow restarting + showScore(); + if (nunchuk.z) { + restartGame(); + // wait for Z to be released + while (nunchuk.z) { + nunchuk.update(); + } + return; + } else { + // wait for C to be released + while (nunchuk.c) { + nunchuk.update(); + } + oled.setDisplayStartLine(topMazeLine % 64); + redrawMaze(128, 8, 0, 0); + } + } + + // Pacman can only change from moving left/right to up/down when step is zero + // Only need to check for wall collisions when step is zero + // Still need to change this as attempted diagonal movement should result in change of direction when possible + bool movingLeft = nunchuk.x < 64; + bool movingRight = nunchuk.x > 192; + bool movingUp = nunchuk.y > 192; + bool movingDown = nunchuk.y < 64; + if (pacmanStep == 0) { // Diagonal movement changes axis + if (((pacmanDirection == 0) || (pacmanDirection == 1))) { + // Check up/down before left/right + if (movingUp && !wallAbove) movePacmanDecreasingStep(2); + else if (movingDown && !wallBelow) movePacmanIncreasingStep(3); + else if ((movingLeft || ((pacmanDirection == 0) && !movingRight)) && !wallLeft) movePacmanDecreasingStep(0); + else if ((movingRight || ((pacmanDirection == 1) && !movingLeft)) && !wallRight) movePacmanIncreasingStep(1); + } else { + // Check left/right before up/down + if (movingLeft && !wallLeft) movePacmanDecreasingStep(0); + else if (movingRight && !wallRight) movePacmanIncreasingStep(1); + else if ((movingUp || ((pacmanDirection == 2) && !movingDown)) && !wallAbove) movePacmanDecreasingStep(2); + else if ((movingDown || ((pacmanDirection == 3) && !movingUp))&& !wallBelow) movePacmanIncreasingStep(3); + } + } else { // Can't change axis + switch (pacmanDirection) { + case 0: + if (movingRight) movePacmanIncreasingStep(1); + else movePacmanDecreasingStep(0); + break; + case 1: + if (movingLeft) movePacmanDecreasingStep(0); + else movePacmanIncreasingStep(1); + break; + case 2: + if (movingDown) movePacmanIncreasingStep(3); + else movePacmanDecreasingStep(2); + break; + default: // 3 + if (movingUp) movePacmanDecreasingStep(2); + else movePacmanIncreasingStep(3); + break; + } + } +} + +void restartGame() { + for (uint8_t p = 0; p < 7*4; p++) { + horizontalPellets[p] = pgm_read_byte(&horizontalPelletsInit[p]); + } + for (uint8_t p = 0; p < 10*4; p++) { + verticalPellets[p] = pgm_read_byte(&verticalPelletsInit[p]); + } + gameStarted = false; + score = 0L; + pacmanDirection = 0; + pacmanLocationX = 13; + pacmanLocationY = 23; + pacmanStep = 4; + blinkyLocationX = 13 * 8 + 4; // Red Ghost + blinkyLocationY = 11 * 8; + pinkyLocationX = 13 * 8 + 4; // Pink Ghost + pinkyLocationY = 14 * 8; + inkyLocationX = 11 * 8 + 4; // Blue Ghost + inkyLocationY = 14 * 8; + clydeLocationX = 15 * 8 + 4; // Orange Ghost + clydeLocationY = 14 * 8; + topMazeLine = screenOffsetYTiles * 8; + leftMazeColumn = screenOffsetXTiles * 8 + 6 * 8; + checkSurroundings(); + oled.off(); + redrawMaze(128, 8, 0, 0); + oled.setDisplayStartLine(topMazeLine % 64); + // We're sacrificing a line to allow smooth scrolling + oled.setDisplayOffset(0); + oled.setMultiplexRatio(63); + oled.on(); + delay(1000); +} + +void showScore() { + oled.clear(); + oled.setDisplayStartLine(0); + oled.setCursor(0, 0); + oled.startData(); + int32_t remainingScore = score; + int32_t scoreSize = 1000000L; + while (scoreSize > 0) { + uint8_t digit = 0; + while (remainingScore >= scoreSize) { + digit++; + remainingScore -= scoreSize; + } + sendDigit(digit); + scoreSize /= 10; + } + oled.endData(); +} + +void sendDigit(uint8_t digit) { + uint8_t digitGlyphsOffset = digit * 3; + for (uint8_t j = 0; j < 3; j++) { + oled.sendData(pgm_read_byte(&digitGlyphs[digitGlyphsOffset++])); + } + oled.sendData(0x00); + oled.sendData(0x00); +} + +void scrollUp() { + // 1. Hide the bottom line + // With reversed com output direction setting, + // one less row from the bottom of the screen is shown + //oled.setMultiplexRatio(63); + // so also shift the offset + //oled.setDisplayOffset(1); + // 2. Change which line is the top line + topMazeLine++; + oled.setDisplayStartLine(topMazeLine % 64); + // 3. Redraw the page that contains the new bottom line + // If that's not the top page, then just redraw the bottom page + if ((topMazeLine & 0x07) == 0) { + redrawMaze(128,1,0,7); + } else { + redrawMaze(128,1,0,0); + } + // 4. Change the display to start from the top line + //oled.setDisplayOffset(0); + //oled.setMultiplexRatio(64); +} + +void scrollDown() { + if (topMazeLine == 0) return; + + // 1. Hide the top line + // With reversed com output direction setting, + // one less row from the bottom of the screen is shown + oled.setMultiplexRatio(62); + // 2. Change which line is the top line + topMazeLine--; + // 3. Redraw the page that contains the top line + redrawMaze(128,1,0,0); + // 4. Change the display to start from the top line + oled.setDisplayStartLine(topMazeLine % 64); + oled.setMultiplexRatio(63); +} + +void scrollLeft() { + leftMazeColumn++; + oled.scrollContentLeft(0,7,0,127); + delay(20); + redrawMaze(1, 8, 127, 0); +} + +void scrollRight() { + if (leftMazeColumn == 0) return; + leftMazeColumn--; + oled.scrollContentRight(0,7,0,127); + delay(20); + redrawMaze(1, 8, 0, 0); +} + +void drawPacman() { + // Pacman starts in the middle, so redraw the middle + switch (pacmanDirection) { + case 0: + redrawMaze(9,1,60,4); + break; + case 1: + redrawMaze(9,1,59,4); + break; + case 2: + redrawMaze(8,3,60,3); + break; + default: // 3 + redrawMaze(8,3,60,3); + break; + } +} + +void movePacmanIncreasingStep(uint8_t newDirection) { + if (pacmanStep == 7) { + changePacmanLocation(newDirection); + pacmanStep = 0; + checkSurroundings(); + } else { + pacmanStep++; + } + pacmanDirection = newDirection; + if (newDirection == 1) { + if (pacmanLocationX == 27) { + pacmanLocationX = 0; + leftMazeColumn = (screenOffsetXTiles - 8 + pacmanLocationX) * 8 + 4; + redrawMaze(128, 8, 0, 0); + return; + } + scrollLeft(); + } else { + scrollUp(); + } + drawPacman(); +} + +void movePacmanDecreasingStep(uint8_t newDirection) { + if (pacmanStep == 0) { + changePacmanLocation(newDirection); + pacmanStep = 7; + } else { + pacmanStep--; + if (pacmanStep == 0) checkSurroundings(); + } + pacmanDirection = newDirection; + if (newDirection == 0) { + if ((pacmanLocationX == 0) && (pacmanStep == 0)) { + pacmanLocationX = 26; + leftMazeColumn = (screenOffsetXTiles - 8 + pacmanLocationX) * 8 + 4; + redrawMaze(128, 8, 0, 0); + return; + } + scrollRight(); + } else { + scrollDown(); + } + drawPacman(); +} + +void changePacmanLocation(uint8_t newDirection) { + pacmanLocationX += movementX[newDirection]; + pacmanLocationY += movementY[newDirection]; +} + +void redrawMaze(uint8_t w, uint8_t h, uint8_t sx, uint8_t page) { + uint8_t topMazeTile = topMazeLine >> 3; + uint8_t topBlankBits = topMazeLine & 0x07; + uint8_t bottomMazeTile = ((topMazeLine + 7) >> 3) + 7; // (topMazeLine + 63) >> 3 + uint8_t bottomBlankBits = 7 - ((topMazeLine + 7) & 0x07); + uint8_t xOffset = (leftMazeColumn + sx) & 0x07; + uint8_t limit = w + xOffset; + for (int8_t tileY = topMazeTile + page; tileY < topMazeTile + page + h; tileY++) { + for (uint8_t j = 0; j < 136; j++) { + tileBits[j] = 0; + } + uint8_t p = tileY & 0x07; + uint8_t mask = 0xFF; + if ((topBlankBits != 0) && (tileY == topMazeTile)) { + mask = 0xFF << topBlankBits; + } + // We might be blanking the last row prior to shifting displayOffset up + if ((bottomBlankBits != 0) && (tileY == bottomMazeTile)) { + mask = 0xFF >> bottomBlankBits; + } + drawPage(mask, tileY, w, sx); + if ((topBlankBits != 0) && (bottomBlankBits != 0) && (tileY == topMazeTile)) { + uint8_t bottomMask = 0xFF >> bottomBlankBits; + drawPage(bottomMask, bottomMazeTile, w, sx); + } + oled.setCursor(sx, p); + oled.startData(); + for (uint8_t j = xOffset; j < limit; j++) { + oled.sendData(tileBits[j]); + } + oled.endData(); + } +} + +void drawPage(uint8_t mask, uint8_t tileY, uint8_t w, uint8_t sx) { + uint8_t leftMazeTile = (leftMazeColumn + sx) >> 3; + uint8_t rightMazeTile = (leftMazeColumn + sx + w - 1) >> 3; + uint8_t c = 0; + for (int8_t tileX = leftMazeTile; tileX <= rightMazeTile; tileX++) { + if (isWall(tileX, tileY)) { + bool wallRight = isWall(tileX + 1, tileY); + bool wallAbove = isWall(tileX, tileY - 1); + bool wallLeft = isWall(tileX - 1, tileY); + bool wallBelow = isWall(tileX, tileY + 1); + for (uint8_t j = 0; j < 8; j++) { + uint8_t bits = 0; + if (wallRight) bits |= pgm_read_byte(&sprites[spriteWallRight * 8 + j]); + if (wallAbove) bits |= pgm_read_byte(&sprites[spriteWallAbove * 8 + j]); + if (wallLeft) bits |= pgm_read_byte(&sprites[spriteWallLeft * 8 + j]); + if (wallBelow) bits |= pgm_read_byte(&sprites[spriteWallBelow * 8 + j]); + tileBits[c++] |= bits & mask; + } + } else { + uint8_t spriteDetail = getSprite(tileX, tileY); + uint8_t sprite = spriteDetail & spriteMask; + uint8_t spriteOffset = sprite * 8; + uint8_t spriteLeavingShift = 0; + uint8_t spriteEnteringShift = 0; + if (spriteDetail & spriteShifted) { + if (sprite <= spritePacmanRight2) { + c += pacmanStep; + } else { + spriteLeavingShift = pacmanStep; + } + } + if (spriteDetail & spriteEntering) { + spriteEnteringShift = 8 - pacmanStep; + } + if (spriteDetail & spriteReversed) { + spriteOffset += 7; + for (uint8_t j = 0; j < 8; j++) { + tileBits[c++] |= ((pgm_read_byte(&sprites[spriteOffset--]) << spriteLeavingShift) >> spriteEnteringShift) & mask; + } + } else { + for (uint8_t j = 0; j < 8; j++) { + tileBits[c++] |= ((pgm_read_byte(&sprites[spriteOffset++]) << spriteLeavingShift) >> spriteEnteringShift) & mask; + } + } + if (spriteDetail & spriteShifted) { + if (sprite <= spritePacmanRight2) { + c += 8 - pacmanStep; + tileX++; + } + } + } + } +} + +bool isWall(int8_t x, int8_t y) { + if (x < 0 + screenOffsetXTiles) return false; + if (x > 27 + screenOffsetXTiles) return false; + if (y < 0 + screenOffsetYTiles) return false; + if (y > 30 + screenOffsetYTiles) return false; + x-= screenOffsetXTiles; + y-= screenOffsetYTiles; + + uint8_t mapOffset = (y >> 3) * 14; + if (x < 14) { + mapOffset += x; + } else { + mapOffset += (27 - x); + } + uint8_t bits = pgm_read_byte(&mirroredMap[mapOffset]); + return (bits >> (y & 0x7)) & 0x01; +} + +void checkSurroundings() { + wallAbove = isWall(pacmanLocationX + screenOffsetXTiles, pacmanLocationY - 1 + screenOffsetYTiles); + wallBelow = isWall(pacmanLocationX + screenOffsetXTiles, pacmanLocationY + 1 + screenOffsetYTiles); + wallLeft = isWall(pacmanLocationX - 1 + screenOffsetXTiles, pacmanLocationY + screenOffsetYTiles); + wallRight = isWall(pacmanLocationX + 1 + screenOffsetXTiles, pacmanLocationY + screenOffsetYTiles); + eatPellet(); +} + +void eatPellet() { + for (uint8_t vp = 0; vp < 10; vp++) { + if (verticalPelletRows[vp] == pacmanLocationX) { + uint8_t pelletOffset = vp*4 + (pacmanLocationY >> 3); + uint8_t current = verticalPellets[pelletOffset]; + uint8_t palletBit = pacmanLocationY & 0x07; + if (bitRead(current, palletBit)) { + bitClear(current, palletBit); + verticalPellets[pelletOffset] = current; + score += 10; + if (((pacmanLocationX == 1) || (pacmanLocationX == 26)) && ((pacmanLocationY == 3) || (pacmanLocationY == 23))) score += 40; + } + return; + } + } + for (uint8_t hp = 0; hp < 7; hp++) { + if (horizontalPelletRows[hp] == pacmanLocationY) { + uint8_t pelletOffset = hp*4 + (pacmanLocationX >> 3); + uint8_t current = horizontalPellets[pelletOffset]; + uint8_t palletBit = pacmanLocationX & 0x07; + if (bitRead(current, palletBit)) { + bitClear(current, palletBit); + horizontalPellets[pelletOffset] = current; + score += 10; + if (((pacmanLocationX == 1) || (pacmanLocationX == 26)) && ((pacmanLocationY == 3) || (pacmanLocationY == 23))) score += 40; + } + return; + } + } +} + +uint8_t getSprite(int8_t x, int8_t y) { + if ((x < 0 + screenOffsetXTiles) || (x > 27 + screenOffsetXTiles) || (y < 0 + screenOffsetYTiles) || (y > 30 + screenOffsetYTiles)) return spriteBlank; + x-= screenOffsetXTiles; + y-= screenOffsetYTiles; + if (x == pacmanLocationX) { + if (y == pacmanLocationY) { + // Need to consider animation + uint8_t pacmanSprite = spritePacmanRight1; + if (pacmanDirection == 0) { + pacmanSprite |= spriteReversed; + } else if (pacmanDirection == 2) { + pacmanSprite = spritePacmanUp1; + } else if (pacmanDirection == 3) { + pacmanSprite = spritePacmanDown1; + } + if (pacmanStep & 1) pacmanSprite++; + if (pacmanStep) pacmanSprite |= spriteShifted; + return pacmanSprite; + } else { + if ((pacmanStep != 0) && (y == pacmanLocationY + 1)) { + uint8_t pacmanSprite = spritePacmanUp1; + if (pacmanDirection == 3) { + pacmanSprite = spritePacmanDown1; + } + if (pacmanStep & 1) pacmanSprite++; + pacmanSprite |= spriteEntering; + return pacmanSprite; + } + } + } + if (x == (blinkyLocationX >> 3) && y == (blinkyLocationY >> 3)) { + // Need to consider animation + return spriteGhostRightHunting | spriteReversed; + } + if (x == (pinkyLocationX >> 3) && y == (pinkyLocationY >> 3)) { + // Need to consider direction & animation + return spriteGhostRightHunting | spriteReversed; + } + if (x == (inkyLocationX >> 3) && y == (inkyLocationY >> 3)) { + // Need to consider animation + return spriteGhostRightHunting | spriteReversed; + } + if (x == (clydeLocationX >> 3) && y == (clydeLocationY >> 3)) { + // Need to consider animation + return spriteGhostRightHunting | spriteReversed; + } + + // READY! + if (!gameStarted && y == 17 && x >= 11 && x <= 16) return (spriteR + x - 11); + + for (uint8_t vp = 0; vp < 10; vp++) { + if (verticalPelletRows[vp]==x) { + if ((verticalPellets[vp*4 + (y >> 3)] >> (y & 0x07)) & 0x01) { + if (((x == 1) || (x == 26)) && ((y == 3) || (y == 23))) return spriteSuperPellet; + return spritePellet; + } + return spriteBlank; + } + } + for (uint8_t hp = 0; hp < 7; hp++) { + if (horizontalPelletRows[hp]==y) { + if ((horizontalPellets[hp*4 + (x >> 3)] >> (x & 0x07)) & 0x01) { + return spritePellet; + } + return spriteBlank; + } + } + return spriteBlank; +} diff --git a/examples/PacMan/PacManMap.h b/examples/PacMan/PacManMap.h new file mode 100644 index 0000000..9dd4005 --- /dev/null +++ b/examples/PacMan/PacManMap.h @@ -0,0 +1,91 @@ +/* + +28 columns x 31 rows +at 1 bit per square - requires 28 x 4 bytes = 112 bytes +Could store a 112 byte background (wall/space) in flash (though the map is mirrored so only need to store one side of it) + +Pills could be stored as bits of the clear path to fill - takes 39 bytes but makes everything a lot more complicated +Need to store current state as well as initial state. +Maybe store the lines of pills? 7 horizontal x 26, 10 vertical x 22 (less efficient to put into bytes: 7 x 4 + 10 x 3 = 58) + or 7 horizontal x 16, 10 vertical x 29 (more efficient in bytes: 7 x 2 + 10 x 4 = 54) + +Initial state is probably more efficent as an algorithm: + If wall then not a pill + Else if first 9 or last 11 rows then a pill + Else if 7th or 22nd column then a pill + Else not a pill + +The original uses x positions 0x3B to 0x20 +and y positions $21 to $3F + +############################ 0 +#............##............# 1 +#.####.#####.##.#####.####.# 2 +#O####.#####.##.#####.####O# 3 +#.####.#####.##.#####.####.# 4 +#..........................# 5 +#.####.##.########.##.####.# 6 +#.####.##.########.##.####.# 7 +#......##....##....##......# 8 +######.##### ## #####.###### 9 + #.##### ## #####.# 10 + #.## [] ##.# 11 + #.## ### ### ##.# 12 +######.## # # ##.###### 13 + . #[][][]# . 14 +######.## # # ##.###### 15 + #.## ######## ##.# 16 + #.## READY! ##.# 17 + #.## ######## ##.# 18 +######.## ######## ##.###### 19 +#............##............# 20 +#.####.#####.##.#####.####.# 21 +#.####.#####.##.#####.####.# 22 +#O..##.......().......##..O# 23 +###.##.##.########.##.##.### 24 +###.##.##.########.##.##.### 25 +#......##....##....##......# 26 +#.##########.##.##########.# 27 +#.##########.##.##########.# 28 +#..........................# 29 +############################ 30 + ()() + +The map is mirrored, so we only need to store one side of it: +*/ + +const uint8_t mirroredMap [] PROGMEM = { + 0xFF,0x01,0xDD,0xDD,0xDD,0xDD,0x01,0xDD,0xDD,0x1D,0xDD,0xDD,0xC1,0xDF, + 0xA3,0xA2,0xA2,0xA2,0xA2,0xBE,0x00,0xBF,0xBF,0x06,0xF6,0x16,0x10,0x07, + 0xF8,0x08,0x68,0x68,0xE8,0xEF,0x00,0x6F,0x6F,0x60,0x6D,0x6D,0x0D,0x7D, + 0x7F,0x43,0x5B,0x58,0x5B,0x5B,0x58,0x5F,0x5F,0x58,0x5B,0x5B,0x43,0x5F +}; + +const uint8_t horizontalPelletRows [] = { + 1,5,8,20,23,26,29 +}; +const uint8_t horizontalPelletsInit [] PROGMEM = { + 0xB4,0x0D,0xDB,0x02, + 0xB4,0x6D,0xDB,0x02, + 0x34,0x0C,0xC3,0x02, + 0xB4,0x0D,0xDB,0x02, + 0x84,0x0D,0x1B,0x02, + 0x34,0x0C,0xC3,0x02, + 0xB4,0x6D,0xDB,0x02, +}; + +const uint8_t verticalPelletRows [] = { + 1,3,6,9,12,15,18,21,24,26 +}; +const uint8_t verticalPelletsInit [] PROGMEM = { + 0xFE,0x01,0xF0,0x3C, + 0x22,0x01,0x90,0x27, + 0xFE,0xFF,0xFF,0x27, + 0xE2,0x01,0x90,0x27, + 0x3E,0x01,0xF0,0x3C, + 0x3E,0x01,0xF0,0x3C, + 0xE2,0x01,0x90,0x27, + 0xFE,0xFF,0xFF,0x27, + 0x22,0x01,0x90,0x27, + 0xFE,0x01,0xF0,0x3C, +}; diff --git a/library.properties b/library.properties index 2d3daaf..300387a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TinyNunchuk -version=0.1.0 +version=1.0.0 author=Stephen Denne maintainer=Stephen Denne sentence=This is a library for an ATTiny85 to use a Wii Nunchuk.