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...

Committer:
cfavreau
Date:
Tue Mar 03 04:26:01 2015 +0000
Revision:
0:c79e1f29f029
Retro Invaders by Chris Favreau for the RetroMbuino Platform - outrageouscircuits.com game programming contest.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
cfavreau 0:c79e1f29f029 1 #include <stdio.h>
cfavreau 0:c79e1f29f029 2 #include <string.h>
cfavreau 0:c79e1f29f029 3 //#include "DisplayN18.h"
cfavreau 0:c79e1f29f029 4 #include "LCD_ST7735.h" // Retro LCD Library by Chris Taylor
cfavreau 0:c79e1f29f029 5 #include "font8x8.h" // 8x8 Font
cfavreau 0:c79e1f29f029 6
cfavreau 0:c79e1f29f029 7 #define CHAR_WIDTH FONT8X8_WIDTH
cfavreau 0:c79e1f29f029 8 #define CHAR_HEIGHT FONT8X8_HEIGHT
cfavreau 0:c79e1f29f029 9 #define CHAR_MAP_WIDTH (LCD_WIDTH / CHAR_WIDTH)
cfavreau 0:c79e1f29f029 10 #define CHAR_MAP_HEIGHT (LCD_HEIGHT / CHAR_HEIGHT)
cfavreau 0:c79e1f29f029 11 #define CHAR_MAP_LEN (CHAR_MAP_WIDTH * CHAR_MAP_HEIGHT)
cfavreau 0:c79e1f29f029 12 #define CHAR_MAP_LAST_LINE (CHAR_MAP_LEN - CHAR_MAP_WIDTH)
cfavreau 0:c79e1f29f029 13 #define COLOR_PAL_LEN 16
cfavreau 0:c79e1f29f029 14 #define LINE_BUFFER_LEN (LCD_WIDTH * sizeof(uint16_t))
cfavreau 0:c79e1f29f029 15
cfavreau 0:c79e1f29f029 16 enum SHIFT_DIRECTION
cfavreau 0:c79e1f29f029 17 {
cfavreau 0:c79e1f29f029 18 eShiftUp,
cfavreau 0:c79e1f29f029 19 eShiftDown,
cfavreau 0:c79e1f29f029 20 eShiftLeft,
cfavreau 0:c79e1f29f029 21 eShiftRight,
cfavreau 0:c79e1f29f029 22 };
cfavreau 0:c79e1f29f029 23
cfavreau 0:c79e1f29f029 24 /*
cfavreau 0:c79e1f29f029 25 #define PAL_TRANSPARENT 0
cfavreau 0:c79e1f29f029 26 #define PAL_BLACK 1
cfavreau 0:c79e1f29f029 27 #define PAL_MEDGREEN 2
cfavreau 0:c79e1f29f029 28 #define PAL_LIGHTGREEN 3
cfavreau 0:c79e1f29f029 29 #define PAL_DARKBLUE 4
cfavreau 0:c79e1f29f029 30 #define PAL_LIGHTBLUE 5
cfavreau 0:c79e1f29f029 31 #define PAL_DARKRED 6
cfavreau 0:c79e1f29f029 32 #define PAL_CYAN 7
cfavreau 0:c79e1f29f029 33 #define PAL_MEDRED 8
cfavreau 0:c79e1f29f029 34 #define PAL_LIGHTRED 9
cfavreau 0:c79e1f29f029 35 #define PAL_DARKYELLOW 10
cfavreau 0:c79e1f29f029 36 #define PAL_LIGHTYELLOW 11
cfavreau 0:c79e1f29f029 37 #define PAL_DARKGREEN 12
cfavreau 0:c79e1f29f029 38 #define PAL_MAGENTA 13
cfavreau 0:c79e1f29f029 39 #define PAL_GREY 14
cfavreau 0:c79e1f29f029 40 #define PAL_WHITE 15
cfavreau 0:c79e1f29f029 41 */
cfavreau 0:c79e1f29f029 42
cfavreau 0:c79e1f29f029 43 #define PAL_WHITE 0
cfavreau 0:c79e1f29f029 44 #define PAL_YELLOW 1
cfavreau 0:c79e1f29f029 45 #define PAL_ORANGE 2
cfavreau 0:c79e1f29f029 46 #define PAL_RED 3
cfavreau 0:c79e1f29f029 47 #define PAL_MAGENTA 4
cfavreau 0:c79e1f29f029 48 #define PAL_DARKBLUE 5
cfavreau 0:c79e1f29f029 49 #define PAL_BLUE 6
cfavreau 0:c79e1f29f029 50 #define PAL_LIGHTBLUE 7
cfavreau 0:c79e1f29f029 51 #define PAL_GREEN 8
cfavreau 0:c79e1f29f029 52 #define PAL_DARKGREEN 9
cfavreau 0:c79e1f29f029 53 #define PAL_BROWN 10
cfavreau 0:c79e1f29f029 54 #define PAL_LIGHTBROWN 11
cfavreau 0:c79e1f29f029 55 #define PAL_LIGHTGREY 12
cfavreau 0:c79e1f29f029 56 #define PAL_GREY 13
cfavreau 0:c79e1f29f029 57 #define PAL_BLACK 14
cfavreau 0:c79e1f29f029 58 #define PAL_TRANSPARENT 15
cfavreau 0:c79e1f29f029 59 #define PAL_DEFAULT 255
cfavreau 0:c79e1f29f029 60
cfavreau 0:c79e1f29f029 61 struct SPRITE
cfavreau 0:c79e1f29f029 62 {
cfavreau 0:c79e1f29f029 63 bool enabled;
cfavreau 0:c79e1f29f029 64 uint8_t char_index;
cfavreau 0:c79e1f29f029 65 uint8_t x;
cfavreau 0:c79e1f29f029 66 uint8_t y;
cfavreau 0:c79e1f29f029 67 uint8_t fore_color;
cfavreau 0:c79e1f29f029 68 uint8_t back_color;
cfavreau 0:c79e1f29f029 69 bool midhandle;
cfavreau 0:c79e1f29f029 70 };
cfavreau 0:c79e1f29f029 71
cfavreau 0:c79e1f29f029 72 #define MAX_SPRITES 64
cfavreau 0:c79e1f29f029 73 #define MAX_USER_CHAR 64
cfavreau 0:c79e1f29f029 74 #define MAX_USER_CHAR_BUF_LEN (MAX_USER_CHAR * 8)
cfavreau 0:c79e1f29f029 75
cfavreau 0:c79e1f29f029 76 //class display : public DisplayN18
cfavreau 0:c79e1f29f029 77 class display : public LCD_ST7735
cfavreau 0:c79e1f29f029 78 {
cfavreau 0:c79e1f29f029 79 public:
cfavreau 0:c79e1f29f029 80
cfavreau 0:c79e1f29f029 81 display();
cfavreau 0:c79e1f29f029 82 ~display();
cfavreau 0:c79e1f29f029 83
cfavreau 0:c79e1f29f029 84 void render();
cfavreau 0:c79e1f29f029 85
cfavreau 0:c79e1f29f029 86 // Character Map Functions
cfavreau 0:c79e1f29f029 87 void shift_map(SHIFT_DIRECTION direction, int inc = 1, bool clear_after_shift = false);
cfavreau 0:c79e1f29f029 88 uint16_t get_map_info(uint8_t x, uint8_t y);
cfavreau 0:c79e1f29f029 89 uint8_t get_map_color(uint8_t x, uint8_t y);
cfavreau 0:c79e1f29f029 90 uint8_t get_map(uint8_t x, uint8_t y);
cfavreau 0:c79e1f29f029 91 void set_map(uint8_t char_index, uint8_t x, uint8_t y, uint8_t fore_color, uint8_t back_color);
cfavreau 0:c79e1f29f029 92 void set_map_info(uint16_t tile_info, uint8_t x, uint8_t y);
cfavreau 0:c79e1f29f029 93 void setcharat(uint8_t x, uint8_t y, uint8_t character, uint8_t fore_color = PAL_DEFAULT, uint8_t back_color = PAL_DEFAULT);
cfavreau 0:c79e1f29f029 94
cfavreau 0:c79e1f29f029 95 // Character based printing
cfavreau 0:c79e1f29f029 96 void clear();
cfavreau 0:c79e1f29f029 97 void crlf();
cfavreau 0:c79e1f29f029 98 void setforecolor(uint8_t color) { m_fore_color = color; };
cfavreau 0:c79e1f29f029 99 void setbackcolor(uint8_t color) { m_back_color = color; };
cfavreau 0:c79e1f29f029 100 void setwordwrap(bool enable) { m_word_wrap = enable; };
cfavreau 0:c79e1f29f029 101
cfavreau 0:c79e1f29f029 102 void print(char *pString);
cfavreau 0:c79e1f29f029 103 void print(int iNumber);
cfavreau 0:c79e1f29f029 104 void print(float fNumber);
cfavreau 0:c79e1f29f029 105 void print(const char *format, ...);
cfavreau 0:c79e1f29f029 106
cfavreau 0:c79e1f29f029 107 void println(char *pString);
cfavreau 0:c79e1f29f029 108 void println(int iNumber);
cfavreau 0:c79e1f29f029 109 void println(float fNumber);
cfavreau 0:c79e1f29f029 110 void println(const char *format, ...);
cfavreau 0:c79e1f29f029 111
cfavreau 0:c79e1f29f029 112 void printat(uint8_t x, uint8_t y, char *pString);
cfavreau 0:c79e1f29f029 113 void printat(uint8_t x, uint8_t y, int iNumber);
cfavreau 0:c79e1f29f029 114 void printat(uint8_t x, uint8_t y, float fNumber);
cfavreau 0:c79e1f29f029 115 void printat(uint8_t x, uint8_t y, const char *format, ...);
cfavreau 0:c79e1f29f029 116
cfavreau 0:c79e1f29f029 117 // Sprite functions
cfavreau 0:c79e1f29f029 118 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
cfavreau 0:c79e1f29f029 119 void EnableSprite(int iSpriteNum, bool bEnable);
cfavreau 0:c79e1f29f029 120 void RemoveSprite(int iSpriteNum);
cfavreau 0:c79e1f29f029 121 void SetSpriteChar(int iSpriteNum, int iChar);
cfavreau 0:c79e1f29f029 122 void SetSpritePos(int iSpriteNum, int x, int y);
cfavreau 0:c79e1f29f029 123 void SetSpriteColor(int iSpriteNum, int fore_color, int back_color);
cfavreau 0:c79e1f29f029 124 void SetMidHandle(int iSpriteNum, bool bEnable);
cfavreau 0:c79e1f29f029 125 bool SpriteCollision(int iSpriteNum1, int iSpriteNum2);
cfavreau 0:c79e1f29f029 126
cfavreau 0:c79e1f29f029 127 void SetCustomChar(int iCharNum, uint8_t *pBuf, int iNum = 1, bool isMSX = false); // Custom Characters can be used for Sprites and Tiles
cfavreau 0:c79e1f29f029 128
cfavreau 0:c79e1f29f029 129 protected:
cfavreau 0:c79e1f29f029 130
cfavreau 0:c79e1f29f029 131 bool m_word_wrap;
cfavreau 0:c79e1f29f029 132 uint8_t m_fore_color, m_back_color; // Indecies into the color palette
cfavreau 0:c79e1f29f029 133 uint8_t m_cursor_x, m_cursor_y;
cfavreau 0:c79e1f29f029 134 uint8_t m_char_map[CHAR_MAP_LEN];
cfavreau 0:c79e1f29f029 135 uint8_t m_color_map[CHAR_MAP_LEN];
cfavreau 0:c79e1f29f029 136 uint16_t m_color_pal[COLOR_PAL_LEN];
cfavreau 0:c79e1f29f029 137 uint8_t *m_line_buffer;
cfavreau 0:c79e1f29f029 138
cfavreau 0:c79e1f29f029 139 void blit_line(uint8_t line);
cfavreau 0:c79e1f29f029 140 void bounds_check_and_scroll();
cfavreau 0:c79e1f29f029 141 int get_linear_cursor_pos() { return (m_cursor_x + (m_cursor_y * CHAR_MAP_WIDTH)); };
cfavreau 0:c79e1f29f029 142 int get_linear_pos(int x, int y) { return (x + (y * CHAR_MAP_WIDTH)); };
cfavreau 0:c79e1f29f029 143
cfavreau 0:c79e1f29f029 144 SPRITE sprite_list[MAX_SPRITES];
cfavreau 0:c79e1f29f029 145
cfavreau 0:c79e1f29f029 146 uint8_t user_char[MAX_USER_CHAR_BUF_LEN];
cfavreau 0:c79e1f29f029 147 void SetCustomChar8x8Norm(int iCharNum, uint8_t *pBuf);
cfavreau 0:c79e1f29f029 148 void SetCustomChar8x8MSX(int iCharNum, uint8_t *pBuf);
cfavreau 0:c79e1f29f029 149 };
cfavreau 0:c79e1f29f029 150