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

main.cpp

Committer:
taylorza
Date:
2014-11-29
Revision:
0:2ee0812e2615
Child:
1:ecf7bbccddc1

File content as of revision 0:2ee0812e2615:

#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);
    }
    
}
*/