Platform game written for the GHI/OutrageousCircuits RETRO game device. Navigate the caves collecting all the pickups and avoiding the creatures and haunted mine carts that patrol the caves. Oh and remember to watch out for the poisonous plants... This game demonstrates the ability to have multiple animated sprites where the sprites can overlap the background environment. See how the player moves past the fence and climbs the wall in the 3rd screen.

Dependencies:   mbed

Revision:
0:2ee0812e2615
Child:
1:ecf7bbccddc1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Nov 29 06:40:50 2014 +0000
@@ -0,0 +1,663 @@
+#include "GameEngine.h"
+#include "SpriteSheet.h"
+#include "Player.h"
+#include "font_IBM.h"
+
+// Block images
+static const ImageFrame emptyBlock(bmp, 0, 0, 8, 8);
+static const ImageFrame brickBlock(bmp, 0, 32, 8, 8);
+static const ImageFrame meshFenceTopBlock(bmp, 8, 32, 8, 8);
+static const ImageFrame meshFenceBlock(bmp, 16, 32, 8, 8);
+static const ImageFrame platformBlock(bmp, 24, 32, 8, 8);
+static const ImageFrame brickTrimBlock(bmp, 32, 32, 8, 8);
+
+// Sprite images
+static const ImageFrame playerWalk1(bmp, 0, 0, 16, 16);
+static const ImageFrame playerWalk2(bmp, 16, 0, 16, 16);
+static const ImageFrame playerWalk3(bmp, 32, 0, 16, 16);
+static const ImageFrame playerWalk4(bmp, 48, 0, 16, 16);
+
+static const ImageFrame angryBird1(bmp, 0, 16, 16, 16);
+static const ImageFrame angryBird2(bmp, 16, 16, 16, 16);
+static const ImageFrame angryBird3(bmp, 32, 16, 16, 16);
+static const ImageFrame angryBird4(bmp, 48, 16, 16, 16);
+
+// Blocks
+const Block blocks[] =
+{
+    Block(&emptyBlock, Block::Background, 0, 0),        // 0 - Empty block
+    Block(&brickBlock, Block::Solid, 2, 0),             // 1 - Brick - Red on black
+    Block(&meshFenceTopBlock, Block::Background, 1, 0), // 2 - Mesh fence top - Blue on black
+    Block(&meshFenceBlock, Block::Background, 1, 0),    // 3 - Mesh fence - Blue on black
+    Block(&platformBlock, Block::Platform, 5, 0),       // 4 - Platform - Cyan on black
+    Block(&brickTrimBlock, Block::Background, 2, 0),    // 5 - Brick trim - Red on black
+}; 
+
+// Sprite animation sequences
+const ImageFrame *playerWalking[] = { &playerWalk1, &playerWalk2, &playerWalk3, &playerWalk4, NULL };
+const ImageFrame *angryBird[] = { &angryBird1, &angryBird2, &angryBird3, &angryBird4, NULL };
+
+// Sprites
+Sprite sprites[] =
+{
+    Sprite(playerWalking, 7),   // 0 - Player walking
+    Sprite(angryBird, 6)        // 1 - Angry bird
+};
+
+static const uint8_t map[] = {
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+    1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+    1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+    1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+    1,4,4,0,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,1,
+    1,0,0,0,5,1,1,1,0,0,0,1,1,2,2,1,1,0,0,1,
+    1,4,4,0,0,0,5,1,0,0,0,1,1,3,3,1,1,0,0,1,
+    1,0,0,0,0,0,0,0,0,0,0,5,1,1,1,1,5,0,0,1,
+    1,4,4,0,0,0,0,0,0,0,0,0,5,5,5,5,0,0,0,1,
+    1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+    1,1,1,1,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+    1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,5,1,1,1,1,
+    1,1,1,1,4,4,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
+    1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+};
+
+class PatrollingEnemy : public GameObject
+{
+    public:
+        PatrollingEnemy(Game &game) :
+            GameObject(game),
+            _movingUp(false)
+        {
+            setSpriteId(1);            
+        }
+        
+        virtual void update()
+        {
+            if (_movingUp)
+            {
+                _movingUp = moveUp();
+            }
+            else
+            {
+                _movingUp = !moveDown();
+            }
+            animate();
+        }
+        
+    private:
+        bool _movingUp;
+};
+
+class MyGame : public Game
+{
+public:
+    MyGame() :
+        _player(*this),
+        _enemy1(*this)
+    {
+        _player.setStartPosition(100, 96);
+        _enemy1.setStartPosition(104, 16);
+        setMap(map, 20, 16, blocks, sprites);
+    }
+    
+private:    
+    Player _player;
+    PatrollingEnemy _enemy1;
+    
+protected:
+    virtual void update(float elapsedTime)
+    {
+        _player.update();  
+        _enemy1.update();
+        wait_ms(32);
+    }
+    
+    virtual void draw(float elapsedTime)
+    {
+        _player.draw();
+        _enemy1.draw();
+    }
+};
+
+int main()
+{
+    DigitalOut led1(P0_9);
+    led1 = 1; 
+    
+    MyGame game;        
+    game.run();
+}
+/*
+void draw(Block &block, int x, int y)
+{
+    uint8_t fc = block.getForegroundColor();
+    uint8_t fch = (fc << 4) & 0xf0;
+    uint8_t fcl = fc & 0x0f;
+    
+    uint8_t bc = block.getBackgroundColor();
+    uint8_t bch = (bc << 4) & 0xf0; 
+    uint8_t bcl = bc & 0x0f;
+    
+    uint8_t *bitmap = _bmp8x8.getBitmapData();
+    int offset = 0;
+    for (int iy = 0; iy < 8; ++iy)
+    {
+        uint8_t b = *block.getBits(iy);
+        bitmap[offset] = b & 0x80 ? ((bitmap[offset] & 0x0f) | fch) : ((bitmap[offset] & 0x0f) | bch); 
+        bitmap[offset] = b & 0x40 ? ((bitmap[offset] & 0xf0) | fcl) : ((bitmap[offset] & 0xf0) | bcl);
+        ++offset;
+        
+        bitmap[offset] = b & 0x20 ? ((bitmap[offset] & 0x0f) | fch) : ((bitmap[offset] & 0x0f) | bch); 
+        bitmap[offset] = b & 0x10 ? ((bitmap[offset] & 0xf0) | fcl) : ((bitmap[offset] & 0xf0) | bcl);
+        ++offset;
+        
+        bitmap[offset] = b & 0x08 ? ((bitmap[offset] & 0x0f) | fch) : ((bitmap[offset] & 0x0f) | bch); 
+        bitmap[offset] = b & 0x04 ? ((bitmap[offset] & 0xf0) | fcl) : ((bitmap[offset] & 0xf0) | bcl);
+        ++offset;
+        
+        bitmap[offset] = b & 0x02 ? ((bitmap[offset] & 0x0f) | fch) : ((bitmap[offset] & 0x0f) | bch); 
+        bitmap[offset] = b & 0x01 ? ((bitmap[offset] & 0xf0) | fcl) : ((bitmap[offset] & 0xf0) | bcl);
+        ++offset;
+    }
+    lcd.drawBitmap(x, y, _bmp8x8, 0, 0, 8, 8);
+}
+
+void compose(Block &block, int x, int y)
+{   
+    uint8_t *bitmap = _composedBitmap.getBitmapData();
+    int offsetRow = (y * _composedBitmap.getStride()) + (x >> 1);
+    
+    uint8_t fc = block.getForegroundColor();    
+    uint8_t fch = (fc << 4) & 0xf0;
+    uint8_t fcl = fc & 0x0f;
+    
+    uint8_t bc = block.getBackgroundColor();
+    uint8_t bch = (bc << 4) & 0xf0; 
+    uint8_t bcl = bc & 0x0f;
+    
+    for (int iy = 0; iy < 8; ++iy, offsetRow += _composedBitmap.getStride())
+    {
+        int offset = offsetRow;
+        uint8_t b = *block.getBits(iy);
+        bool highNibble = ((x & 0x01) == 0);
+        for(int c = 0; c < 8; ++c, b <<= 1)
+        {
+            if (b & 0x80)
+            {                                                
+                if (highNibble) bitmap[offset] =  ((bitmap[offset] & 0x0f) | fch);
+                else 
+                {
+                    bitmap[offset] =  ((bitmap[offset] & 0xf0) | fcl);
+                    ++offset;
+                }
+            }
+            else
+            {
+                if (highNibble) bitmap[offset] =  ((bitmap[offset] & 0x0f) | bch);
+                else 
+                {
+                    bitmap[offset] =  ((bitmap[offset] & 0xf0) | bcl);
+                    ++offset;
+                }
+            }   
+            highNibble = !highNibble; 
+        }               
+    }    
+}
+
+
+void compose(Sprite &sprite, int x, int y, bool flip)
+{   
+    uint8_t *bitmap = _composedBitmap.getBitmapData();    
+    
+    int offsetRow = (y * _composedBitmap.getStride()) + (x >> 1);
+    
+    uint8_t fc = sprite.getForegroundColor();    
+    uint8_t fch = (fc << 4) & 0xf0;
+    uint8_t fcl = fc & 0x0f;
+    
+    if (!flip)
+    {
+        for (int iy = 0; iy < 16; ++iy, offsetRow += _composedBitmap.getStride())
+        {
+            int offset = offsetRow;
+            uint8_t *p = sprite.getBits(iy);
+            bool highNibble = ((x & 0x01) == 0);
+            for (int ix = 0; ix < 2; ++ix)
+            {
+                uint8_t b = *p++;
+                for(int c = 0; c < 8; ++c, b <<= 1)
+                {
+                    if (b & 0x80)
+                    {                                                
+                        if (highNibble) bitmap[offset] = ((bitmap[offset] & 0x0f) | fch);
+                        else 
+                        {
+                            bitmap[offset] = ((bitmap[offset] & 0xf0) | fcl);
+                            offset++;
+                        }
+                    }
+                    else if (!highNibble) 
+                    {
+                        offset++;
+                    }   
+                    highNibble = !highNibble; 
+                }               
+            }
+        }    
+    }
+    else
+    {
+        for (int iy = 0; iy < 16; ++iy, offsetRow += _composedBitmap.getStride())
+        {
+            int offset = offsetRow; 
+            uint8_t *p = sprite.getBits(iy) + 1;
+            bool highNibble = ((x & 0x01) == 0);
+            for (int ix = 0; ix < 2; ++ix)
+            {
+                uint8_t b = *p--;
+                for(int c = 0; c < 8; ++c, b >>= 1)
+                {
+                    if (b & 0x01)
+                    {                            
+                        if (highNibble) bitmap[offset] = ((bitmap[offset] & 0x0f) | fch);
+                        else 
+                        {
+                            bitmap[offset] = ((bitmap[offset] & 0xf0) | fcl);
+                            offset++;
+                        }
+                    }
+                    else if (!highNibble)
+                    {
+                        offset++;
+                    }   
+                    highNibble = !highNibble; 
+                }               
+            }
+        }    
+    }
+}
+
+void drawScreen(uint8_t *screen)
+{
+    int yOffset = 0;
+    for(int y = 0; y < 16; ++y, yOffset += 20)
+    {        
+        for (int x = 0; x < 20; ++x)
+        {
+            uint8_t blockId = screen[yOffset + x];
+            if (blockId != 0)
+            {
+                Block &block = blocks[blockId];
+                draw(block, x * 8, y * 8);                                
+            }
+        }
+    }
+}
+
+void drawSprite(uint8_t *screen, Sprite &sprite, int x, int y, int dx, int dy, bool flip)
+{
+    int cellX = x / 8;
+    int cellY = y / 8;
+    int rx = x % 8;
+    int ry = y % 8;
+    
+    if (rx == 0 && dx == 1 && cellX > 0) { --cellX; rx += 8; }    
+    if (ry == 0 && dy == 1 && cellY > 0) { --cellY; ry += 8; }
+    
+    _composedBitmap.clear();
+    // Compose blocks
+    for (int cy = 0; cy < 3; ++cy)
+    {
+        int yOffset = (cellY + cy) * 20;
+        for (int cx = 0; cx < 3; ++cx)
+        {
+            uint8_t blockId = screen[yOffset + cellX + cx];
+            if (blockId != 0)
+            {
+                Block &block = blocks[blockId];
+                compose(block, cx * 8, cy * 8);
+            }
+        }
+    }
+    
+    // Compose sprite    
+    compose(sprite, rx, ry, flip);
+    
+    // Render the composed image
+    lcd.drawBitmap(cellX * 8, cellY * 8, _composedBitmap, 0, 0, 24, 24);
+}
+
+int main()
+{
+    DigitalOut led1(P0_9);
+    led1 = 1;
+        
+    lcd.setOrientation(LCD_ST7735::Rotate270, false);
+    
+    drawScreen(screen0);
+    
+    int px = 80;
+    int py = 15;
+    int dx = 0;
+    int dy = 0;
+    bool flip = false;
+    char buffer[50];
+    while (true)
+    {   
+        if (GameInput::isLeftPressed() && px > 8) { dx = -1; px--; flip = false; wait_ms(16); sprites[0].animate(); }
+        if (GameInput::isRightPressed() && px < 136) { dx = 1; px++; flip = true; wait_ms(16); sprites[0].animate();}
+        if (GameInput::isUpPressed() && py > 8) { dy = -1; py--; wait_ms(16); sprites[0].animate();}
+        if (GameInput::isDownPressed() && py < 104) { dy = 1; py++; wait_ms(16); sprites[0].animate();}
+        
+        drawSprite(screen0, sprites[0], px, py, dx, dy, flip);        
+        drawSprite(screen0, sprites[0], 50, 50, 0, 0, flip);            
+        
+        sprintf(buffer, "%d, %d        ", px, py);
+        lcd.drawString(font_ibm, 0, 0, buffer);
+    }
+*/    
+/*  
+    lcd.drawCircle(50, 50, 35, Color565::Red);
+    char buffer[100];
+    Timer timer;
+    timer.start();
+    while(true)
+    {
+        timer.reset();
+        for (int sy = 0; sy < 5; sy++)
+        {
+            for (int sx = 0; sx < 6; sx++)
+            {
+                for (int y = 0; y < 3; y++)
+                {
+                    for (int x = 0; x < 3; x++)
+                    {
+                        draw(blocks[0], x * 8, y * 8);                        
+                    }
+                }
+                
+                draw(sprite, 5, 5, false);                
+                lcd.drawBitmap(8 + (sx * 24), (sy * 24), _composedBitmap, 0, 0, 24, 24);
+            }            
+        }    
+        sprite.animate();
+        float seconds = timer.read_ms();
+        sprintf(buffer, "%f", seconds);
+        lcd.drawString(font_ibm, 0, 120, buffer);
+        //wait_ms(250);
+    }
+    
+*/  
+    
+/*    
+    Bitmap4bpp image(8, 8);
+    
+    int x = 5; 
+    int y = 5;
+    int dx = 1;
+    int dy = 1;
+    while (true)
+    {
+        lcd.drawBitmap(x, y, image, 0, 0, 8, 8);
+        lcd.drawBitmap(x, y + 12, image, 1, 0, 7, 8);
+        lcd.drawBitmap(x, y + 24, image, 0, 0, 7, 8);
+        lcd.drawBitmap(x, y + 36, image, 1, 0, 6, 8); 
+        
+        if (x > 151) dx = -1; else if (x < 1) dx = 1;
+        if (y > 83) dy = -1; else if (y < 1) dy = 1;
+        x += dx;
+        y += dy;
+    }
+*/    
+    
+/*    
+    _composedBitmap[0] = 24;
+    _composedBitmap[1] = 24;
+    for (int i = 0; i < 24*24; ++i)
+    {
+        _composedBitmap[2+i] = Color565::Green;
+    }
+
+    SpaceGame game;    
+    
+    draw(blocks[0], 0, 0);
+    draw(blocks[0], 8, 0);
+    draw(blocks[0], 16, 0);
+    
+    draw(blocks[0], 0, 8);
+    draw(blocks[0], 8, 8);
+    draw(blocks[0], 16, 8);
+    
+    draw(blocks[0], 0, 16);
+    draw(blocks[0], 8, 16);
+    draw(blocks[0], 16, 16);
+    
+    draw(sprite, 0, 0);
+    
+    Game::Screen.drawCircle(50, 50, 25, Color565::Blue);
+    Game::Screen.drawBitmap(50, 50, (const uint16_t*)_composedBitmap);
+
+    game.run();
+
+    while(true){}
+}
+*/
+
+/*
+#include "Color565.h"
+#include "font_IBM.h" 
+#include "SpriteSheet.h"
+
+enum PlayerState
+{
+    Stopped,
+    Walking,
+    Falling,
+    Flying, 
+};
+
+void drawEnvironment(LCD_ST7735 lcd);
+void drawPlatform(LCD_ST7735 lcd, int x, int y, int width, uint16_t color);
+
+int main()
+{
+    LCD_ST7735 lcd(
+        P0_19,
+        P0_4,  // Reset
+        P0_5,   // DS
+        P0_21,
+        P0_22,
+        P1_15,
+        P0_2,   // CS
+        LCD_ST7735::RGB);
+        
+    lcd.setOrientation(LCD_ST7735::Rotate270, false);
+    lcd.clearScreen();
+            
+    DigitalOut led1(P0_9);
+    DigitalOut led2(P0_8);
+    
+    DigitalIn up(P0_13, PullUp);
+    DigitalIn down(P0_12, PullUp);
+    DigitalIn left(P0_14, PullUp);
+    DigitalIn right(P0_11, PullUp);
+    DigitalIn square(P0_16, PullUp);
+    DigitalIn circle(P0_1, PullUp);
+    
+    PwmOut sound(P0_18);
+    sound = 0;
+    sound.period(0.0);
+
+    int x = 100;
+    int y = 5;
+    bool flip = false;
+    
+    int frame = 0;
+    
+    PlayerState state = Walking;
+    int walkSpeed = 24;
+    int flySpeed = 6;
+    int fallSpeed = 10;
+    int speed = 0;
+    int speedCounter = 0;
+    
+    while (true) 
+    {
+        wait_ms(1);
+        
+        if (--speedCounter <= 0)
+        {           
+            if (x < 0) x = 0;
+            if (x > 160 - 16) x = 160 - 16;
+            if (y < 0) y = 0;
+            if (y > 128 - 24) y = 128 - 24;
+            
+            if (state == Stopped)
+            {
+                lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 0, 0, 16, 16, flip);
+            }
+            else if (state == Flying || state == Falling)
+            {
+                switch(frame)
+                {
+                    case 0 : lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 0, 16, 16, 16, flip); break;
+                    case 1 : lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 16, 16, 16, 16, flip); break;
+                    case 2 : lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 32, 16, 16, 16, flip); break;
+                    case 3 : lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 48, 16, 16, 16, flip); break;
+                }
+                frame = (frame + 1) % 4;                
+            }
+            else if (state == Walking)
+            {
+                switch(frame)
+                {
+                    case 0 : lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 0, 0, 16, 16, flip); break;
+                    case 1 : lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 16, 0, 16, 16, flip); break;
+                    case 2 : lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 32, 0, 16, 16, flip); break;
+                    case 3 : lcd.drawGlyph(x, y, Color565::White, Color565::Black, bmp, 48, 0, 16, 16, flip); break;
+                }
+                frame = (frame + 1) % 4;                
+            }
+            
+            if (!up)
+            {
+                if (state != Flying)
+                {
+                    state = Flying;                
+                    speed = flySpeed;
+                    frame = 0;
+                }
+                else if (y > 2)
+                {                    
+                    y--;                    
+                }
+            }
+            else if (y < 128 - 24)
+            {    
+                if (state != Falling)
+                {
+                    state = Falling;                
+                    speed = fallSpeed;
+                    frame = 0;
+                }
+                y++;                
+            } 
+            else 
+            { 
+                if (state != Stopped && state != Walking)
+                {
+                    state = Stopped;                    
+                    frame = 0;
+                }                
+            }
+            
+            if (!left || (!flip && state == Walking)) 
+            {             
+                if (flip) 
+                {
+                    flip = false;
+                    frame = 0;
+                }
+                else
+                {                
+                    if (state == Stopped) 
+                    {
+                        state = Walking;
+                        speed = walkSpeed;
+                        frame = 1;
+                    }     
+                    else if (state == Walking)
+                    {
+                        if (frame == 0) state = Stopped;
+                        x -= 1;
+                    }               
+                    else if (state == Flying || state == Falling)
+                    {
+                        x -= 1;
+                    }                    
+                }
+            }
+            
+            if (!right || (flip && state == Walking))
+            {             
+                if (!flip) 
+                {
+                    flip = true;
+                    frame = 0;
+                }
+                else
+                {              
+                    if (state == Stopped) 
+                    {
+                        state = Walking;
+                        speed = walkSpeed;
+                        frame = 1;
+                    }     
+                    else if (state == Walking)
+                    {
+                        if (frame == 0) state = Stopped;
+                        x += 1;
+                    }               
+                    else if (state == Flying || state == Falling)
+                    {
+                        x += 1;
+                    }
+                }
+            }
+            speedCounter = speed;
+        }
+    }    
+}
+
+void drawEnvironment(LCD_ST7735 lcd)
+{
+    for (int y = 0; y < 16; ++y)
+    {
+        for (int x = 0; x < 20; ++x)
+        {
+            int i= y * 20 + x;
+            switch(environment[i])
+            {                    
+                case 1: lcd.drawGlyph(x * 8, y * 8, Color565::Green, Color565::Black, bmp, 0, 81, 8, 8, false); break;
+                case 2: lcd.drawGlyph(x * 8, y * 8, Color565::Green, Color565::Black, bmp, 8, 81, 8, 8, false); break;
+                case 3: lcd.drawGlyph(x * 8, y * 8, Color565::Green, Color565::Black, bmp, 16, 81, 8, 8, false); break;
+            }
+        }
+    }
+}
+
+void drawPlatform(LCD_ST7735 lcd, int x, int y, int width, uint16_t color)
+{
+    lcd.drawGlyph(x, y, color, Color565::Black, bmp, 0, 81, 8, 8, false);
+    lcd.drawGlyph(x + (width - 1) * 8, y, color, Color565::Black, bmp, 16, 81, 8, 8, false);    
+    
+    for(int i = 1; i < width - 1; ++i)
+    {
+        lcd.drawGlyph(x + (i * 8), y, color, Color565::Black, bmp, 8, 81, 8, 8, false);
+    }
+    
+}
+*/