Retro Invaders a space invaders clone by Chris Favreau. Written for the RetroMbuino development board from outrageouscircuits.com for the game programming contest.

Dependencies:   mbed

This is a space invaders clone written for the Retro Mbuino from outrageous circuits.

Development board: http://www.outrageouscircuits.com/shop/product/15 ).

The game itself is basic space invaders. Shoot them before they get to the bottom of the screen. It has a UFO saucer which you can shoot for extra points. You get 4 shields and each shield can be hit up to 4 times before it is gone. Hmm... as each level increases the speed of the invaders shots goes up. The invaders only speed up when there is less of them. You complete the level when you shoot all the invaders. The game ends when a) you run out of lives (you start with 3) or the invaders get to the bottom.

The LEDs turned out to be a pretty cool addition to the game. I wrote a class that blinks them and turns them on for a specified amount of time. They add a nice extra to the game. I use them on the intro screen and when the UFO is present.

The sound turned out to be really difficult for a few reasons. The biggest was that I had never written a sound engine before. The interrupt service routine working off the timer was the easier part. I also had a lot of trouble because there is no filter to filter out the PWM frequency to the speaker... so I had to run the PWM frequency way up there 30 kHz.

The graphics turned out to be a bit of a bear too. Thanks to Chris Taylor for his really great LCD API. I picked up a couple of frames per second from that. I had modified the DisplayN18 class for blitting a single line buffer to the LCD panel however his is a little faster for some reason? I used a different approach to doing the graphics (as I have very little experience with anything other than double buffered displays). I have a tile map and a list of sprites. Each tile/sprite is 1 bit 8x8. They could be bigger. I ran out of time. That much is not special. What is different from what I can tell is that I use a 1 line buffer that is 160 shorts long. The render function first adds the tile map data into the line buffer first. Then the sprites are added over the existing data. You can have a great deal of different sprites and maps going to the screen and just have to rewrite the LCD memory once per frame. After each line is composited, the line is then drawn to the LCD. Kind of like an Atari 2600. Each sprite/tile has a foreground and background color and can be different from the other tiles/sprites. There is one color reserved for Transparency.

There are 16 colors to choose from. I chose a palette based on the Macintosh OS 4.1 palette I found on WikiPedia. It is a very nice mix of colors.

I found a sprite editor called SpriteX ( https://code.google.com/p/spritesx-ed/ )... it works nicely except that the 16x16 sprites are in a weird format. Time limited me to 8x8 sprites. Oh well.

I used nokring to make the music. It makes RTTTL formatted ring tones which my sound api can play. Here is a useful site that has lots of arcade/video game ring tones with a link to nokring in the utilities page. http://arcadetones.emuunlim.com/files.htm

Other than all that stuff I used state machines to do most of the game logic. Please excuse the horrible coding as I tried to comment a lot of it however it is not very nice to look at. Lots of long functions...

Display/display.h

Committer:
cfavreau
Date:
2015-03-03
Revision:
0:c79e1f29f029

File content as of revision 0:c79e1f29f029:

#include <stdio.h>
#include <string.h>
//#include "DisplayN18.h"
#include "LCD_ST7735.h"     // Retro LCD Library by Chris Taylor
#include "font8x8.h"        // 8x8 Font

#define CHAR_WIDTH      FONT8X8_WIDTH
#define CHAR_HEIGHT     FONT8X8_HEIGHT
#define CHAR_MAP_WIDTH  (LCD_WIDTH / CHAR_WIDTH)
#define CHAR_MAP_HEIGHT (LCD_HEIGHT / CHAR_HEIGHT)
#define CHAR_MAP_LEN    (CHAR_MAP_WIDTH * CHAR_MAP_HEIGHT)
#define CHAR_MAP_LAST_LINE (CHAR_MAP_LEN - CHAR_MAP_WIDTH)
#define COLOR_PAL_LEN   16
#define LINE_BUFFER_LEN (LCD_WIDTH * sizeof(uint16_t))

enum SHIFT_DIRECTION
{
    eShiftUp,
    eShiftDown,
    eShiftLeft,
    eShiftRight,
};

/*
#define PAL_TRANSPARENT         0
#define PAL_BLACK               1
#define PAL_MEDGREEN            2
#define PAL_LIGHTGREEN          3
#define PAL_DARKBLUE            4
#define PAL_LIGHTBLUE           5
#define PAL_DARKRED             6
#define PAL_CYAN                7
#define PAL_MEDRED              8
#define PAL_LIGHTRED            9
#define PAL_DARKYELLOW          10
#define PAL_LIGHTYELLOW         11
#define PAL_DARKGREEN           12
#define PAL_MAGENTA             13
#define PAL_GREY                14
#define PAL_WHITE               15
*/

#define PAL_WHITE               0
#define PAL_YELLOW              1
#define PAL_ORANGE              2
#define PAL_RED                 3
#define PAL_MAGENTA             4
#define PAL_DARKBLUE            5
#define PAL_BLUE                6
#define PAL_LIGHTBLUE           7
#define PAL_GREEN               8
#define PAL_DARKGREEN           9
#define PAL_BROWN               10
#define PAL_LIGHTBROWN          11
#define PAL_LIGHTGREY           12
#define PAL_GREY                13
#define PAL_BLACK               14
#define PAL_TRANSPARENT         15
#define PAL_DEFAULT             255

struct SPRITE
{
    bool enabled;
    uint8_t char_index;
    uint8_t x;
    uint8_t y;
    uint8_t fore_color;
    uint8_t back_color;
    bool midhandle;
};

#define MAX_SPRITES             64
#define MAX_USER_CHAR           64
#define MAX_USER_CHAR_BUF_LEN   (MAX_USER_CHAR * 8)

//class display : public DisplayN18
class display : public LCD_ST7735
{
public:

    display();
    ~display();

    void render();
    
    // Character Map Functions
    void shift_map(SHIFT_DIRECTION direction, int inc = 1, bool clear_after_shift = false);
    uint16_t get_map_info(uint8_t x, uint8_t y);
    uint8_t get_map_color(uint8_t x, uint8_t y);
    uint8_t get_map(uint8_t x, uint8_t y);
    void set_map(uint8_t char_index, uint8_t x, uint8_t y, uint8_t fore_color, uint8_t back_color);
    void set_map_info(uint16_t tile_info, uint8_t x, uint8_t y);
    void setcharat(uint8_t x, uint8_t y, uint8_t character, uint8_t fore_color = PAL_DEFAULT, uint8_t back_color = PAL_DEFAULT);
    
    // Character based printing
    void clear();
    void crlf();
    void setforecolor(uint8_t color) { m_fore_color = color; };
    void setbackcolor(uint8_t color) { m_back_color = color; };
    void setwordwrap(bool enable) { m_word_wrap = enable; };
    
    void print(char *pString);
    void print(int iNumber);
    void print(float fNumber);
    void print(const char *format, ...);
    
    void println(char *pString);
    void println(int iNumber);
    void println(float fNumber);
    void println(const char *format, ...);
    
    void printat(uint8_t x, uint8_t y, char *pString);
    void printat(uint8_t x, uint8_t y, int iNumber);
    void printat(uint8_t x, uint8_t y, float fNumber);
    void printat(uint8_t x, uint8_t y, const char *format, ...);
    
    // Sprite functions
    void AddSprite(int iSpriteNum, int iChar, int x, int y, int fore_color, int back_color, bool midhandle = false);  // width/height is a multiple of 8
    void EnableSprite(int iSpriteNum, bool bEnable);
    void RemoveSprite(int iSpriteNum);
    void SetSpriteChar(int iSpriteNum, int iChar);
    void SetSpritePos(int iSpriteNum, int x, int y);
    void SetSpriteColor(int iSpriteNum, int fore_color, int back_color);
    void SetMidHandle(int iSpriteNum, bool bEnable);
    bool SpriteCollision(int iSpriteNum1, int iSpriteNum2);
    
    void SetCustomChar(int iCharNum, uint8_t *pBuf, int iNum = 1, bool isMSX = false);         // Custom Characters can be used for Sprites and Tiles
    
protected:

    bool m_word_wrap;
    uint8_t m_fore_color, m_back_color;     // Indecies into the color palette
    uint8_t m_cursor_x, m_cursor_y;
    uint8_t m_char_map[CHAR_MAP_LEN];
    uint8_t m_color_map[CHAR_MAP_LEN];
    uint16_t m_color_pal[COLOR_PAL_LEN];
    uint8_t *m_line_buffer;
    
    void blit_line(uint8_t line);
    void bounds_check_and_scroll();
    int get_linear_cursor_pos() { return (m_cursor_x + (m_cursor_y * CHAR_MAP_WIDTH)); };
    int get_linear_pos(int x, int y) { return (x + (y * CHAR_MAP_WIDTH)); };
    
    SPRITE sprite_list[MAX_SPRITES];
    
    uint8_t user_char[MAX_USER_CHAR_BUF_LEN];
    void SetCustomChar8x8Norm(int iCharNum, uint8_t *pBuf);
    void SetCustomChar8x8MSX(int iCharNum, uint8_t *pBuf);
};