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 <math.h>
cfavreau 0:c79e1f29f029 2 #include "input.h"
cfavreau 0:c79e1f29f029 3
cfavreau 0:c79e1f29f029 4 input::input() : left(P0_14, PullUp), right(P0_11, PullUp), down(P0_12, PullUp), up(P0_13, PullUp), square(P0_16, PullUp), circle(P0_1, PullUp), i2c(P0_5, P0_4)
cfavreau 0:c79e1f29f029 5 {
cfavreau 0:c79e1f29f029 6 // Clear all the button states
cfavreau 0:c79e1f29f029 7 for (int i = 0; i < eLastButton; i++)
cfavreau 0:c79e1f29f029 8 {
cfavreau 0:c79e1f29f029 9 m_iPressedTicks[i] = 0;
cfavreau 0:c79e1f29f029 10 m_iHitCount[i] = 0;
cfavreau 0:c79e1f29f029 11 }
cfavreau 0:c79e1f29f029 12
cfavreau 0:c79e1f29f029 13 m_fAccelX = 0.0;
cfavreau 0:c79e1f29f029 14 m_fAccelY = 0.0;
cfavreau 0:c79e1f29f029 15 m_fAccelZ = 0.0;
cfavreau 0:c79e1f29f029 16
cfavreau 0:c79e1f29f029 17 i2c.frequency(400);
cfavreau 0:c79e1f29f029 18 accelWriteRegister(0x2A, 0x01);
cfavreau 0:c79e1f29f029 19 }
cfavreau 0:c79e1f29f029 20
cfavreau 0:c79e1f29f029 21 void input::dobutton(INPUT_BUTTON button, bool bPressed)
cfavreau 0:c79e1f29f029 22 {
cfavreau 0:c79e1f29f029 23 // If this button is not being debounced then set the ticks we can debounce the switch
cfavreau 0:c79e1f29f029 24 if (bPressed)
cfavreau 0:c79e1f29f029 25 {
cfavreau 0:c79e1f29f029 26 if (m_iPressedTicks[button] < 1)
cfavreau 0:c79e1f29f029 27 {
cfavreau 0:c79e1f29f029 28 // This is the first time this button was pressed since it was UP
cfavreau 0:c79e1f29f029 29 // Increment the hit count
cfavreau 0:c79e1f29f029 30 m_iHitCount[button]++;
cfavreau 0:c79e1f29f029 31 }
cfavreau 0:c79e1f29f029 32 // Reset the debounce counter
cfavreau 0:c79e1f29f029 33 m_iPressedTicks[button] = BUTTON_DEBOUNCE_TICKS;
cfavreau 0:c79e1f29f029 34 }
cfavreau 0:c79e1f29f029 35 // If this button IS not pressed and we have ticks left... deduct 1 tick
cfavreau 0:c79e1f29f029 36 if (!bPressed && (m_iPressedTicks[button] > 0))
cfavreau 0:c79e1f29f029 37 m_iPressedTicks[button]--;
cfavreau 0:c79e1f29f029 38 }
cfavreau 0:c79e1f29f029 39
cfavreau 0:c79e1f29f029 40 void input::tick()
cfavreau 0:c79e1f29f029 41 {
cfavreau 0:c79e1f29f029 42 // Sample all of the buttons
cfavreau 0:c79e1f29f029 43 dobutton(eUp, up.read() == 0);
cfavreau 0:c79e1f29f029 44 dobutton(eDown, down.read() == 0);
cfavreau 0:c79e1f29f029 45 dobutton(eLeft, left.read() == 0);
cfavreau 0:c79e1f29f029 46 dobutton(eRight, right.read() == 0);
cfavreau 0:c79e1f29f029 47 dobutton(eCircle, circle.read() == 0);
cfavreau 0:c79e1f29f029 48 dobutton(eSquare, square.read() == 0);
cfavreau 0:c79e1f29f029 49
cfavreau 0:c79e1f29f029 50 // UGHHHHHHH - accelReadRegisters() is BALLS SLOW!!! Disable for NOW!
cfavreau 0:c79e1f29f029 51 /*
cfavreau 0:c79e1f29f029 52 // Sample the accelerometers
cfavreau 0:c79e1f29f029 53 char buffer[6];
cfavreau 0:c79e1f29f029 54 accelReadRegisters(0x01, buffer, 6);
cfavreau 0:c79e1f29f029 55 m_fAccelX = accelConvert(buffer);
cfavreau 0:c79e1f29f029 56 m_fAccelY = accelConvert(buffer + 2);
cfavreau 0:c79e1f29f029 57 m_fAccelZ = accelConvert(buffer + 4);
cfavreau 0:c79e1f29f029 58 */
cfavreau 0:c79e1f29f029 59 }
cfavreau 0:c79e1f29f029 60
cfavreau 0:c79e1f29f029 61 void input::clear()
cfavreau 0:c79e1f29f029 62 {
cfavreau 0:c79e1f29f029 63 for (int i = 0; i < eLastButton; i++)
cfavreau 0:c79e1f29f029 64 {
cfavreau 0:c79e1f29f029 65 m_iHitCount[i] = 0;
cfavreau 0:c79e1f29f029 66 m_iPressedTicks[i] = 0;
cfavreau 0:c79e1f29f029 67 }
cfavreau 0:c79e1f29f029 68 }
cfavreau 0:c79e1f29f029 69
cfavreau 0:c79e1f29f029 70 int input::hit(INPUT_BUTTON button)
cfavreau 0:c79e1f29f029 71 {
cfavreau 0:c79e1f29f029 72 if (button >= eLastButton) return 0;
cfavreau 0:c79e1f29f029 73 // Store the hit count for this button
cfavreau 0:c79e1f29f029 74 int hit_count = m_iHitCount[button];
cfavreau 0:c79e1f29f029 75 // Clear the hit count
cfavreau 0:c79e1f29f029 76 m_iHitCount[button] = 0;
cfavreau 0:c79e1f29f029 77 // Return the stored hit count
cfavreau 0:c79e1f29f029 78 return hit_count;
cfavreau 0:c79e1f29f029 79 }
cfavreau 0:c79e1f29f029 80
cfavreau 0:c79e1f29f029 81 bool input::pressed(INPUT_BUTTON button)
cfavreau 0:c79e1f29f029 82 {
cfavreau 0:c79e1f29f029 83 if (button >= eLastButton) return false;
cfavreau 0:c79e1f29f029 84
cfavreau 0:c79e1f29f029 85 return (m_iPressedTicks[button] > 0);
cfavreau 0:c79e1f29f029 86 }
cfavreau 0:c79e1f29f029 87
cfavreau 0:c79e1f29f029 88
cfavreau 0:c79e1f29f029 89 int input::accel_x()
cfavreau 0:c79e1f29f029 90 {
cfavreau 0:c79e1f29f029 91 return (int)(m_fAccelX * 100.0);
cfavreau 0:c79e1f29f029 92 }
cfavreau 0:c79e1f29f029 93
cfavreau 0:c79e1f29f029 94 int input::accel_y()
cfavreau 0:c79e1f29f029 95 {
cfavreau 0:c79e1f29f029 96 return (int)(m_fAccelY * 100.0);
cfavreau 0:c79e1f29f029 97 }
cfavreau 0:c79e1f29f029 98
cfavreau 0:c79e1f29f029 99 int input::accel_z()
cfavreau 0:c79e1f29f029 100 {
cfavreau 0:c79e1f29f029 101 return (int)(m_fAccelZ * 100.0);
cfavreau 0:c79e1f29f029 102 }
cfavreau 0:c79e1f29f029 103
cfavreau 0:c79e1f29f029 104 int input::accel_roll()
cfavreau 0:c79e1f29f029 105 {
cfavreau 0:c79e1f29f029 106 // Roll and Pitch are swapped on the Retro...
cfavreau 0:c79e1f29f029 107 return (int)((atan(m_fAccelX / sqrt((m_fAccelY * m_fAccelY) + (m_fAccelZ * m_fAccelZ))) * 180.0) / 3.14159);
cfavreau 0:c79e1f29f029 108 }
cfavreau 0:c79e1f29f029 109
cfavreau 0:c79e1f29f029 110 int input::accel_pitch()
cfavreau 0:c79e1f29f029 111 {
cfavreau 0:c79e1f29f029 112 // Roll and Pitch are swapped on the Retro.. (Pitch is Roll, Roll is Pitch)
cfavreau 0:c79e1f29f029 113 return (int)((atan(m_fAccelY / sqrt((m_fAccelX * m_fAccelX) + (m_fAccelZ * m_fAccelZ))) * 180.0) / 3.14159);
cfavreau 0:c79e1f29f029 114 }
cfavreau 0:c79e1f29f029 115
cfavreau 0:c79e1f29f029 116 void input::accelReadRegisters(char address, char* buffer, int len)
cfavreau 0:c79e1f29f029 117 {
cfavreau 0:c79e1f29f029 118 // BALLS SLOW!!!
cfavreau 0:c79e1f29f029 119 i2c.write(I2C_ADDR, &address, 1, true);
cfavreau 0:c79e1f29f029 120 i2c.read(I2C_ADDR | 1, buffer, len);
cfavreau 0:c79e1f29f029 121 }
cfavreau 0:c79e1f29f029 122
cfavreau 0:c79e1f29f029 123 int input::accelWriteRegister(char address, char value)
cfavreau 0:c79e1f29f029 124 {
cfavreau 0:c79e1f29f029 125 char buffer[2] = { address, value };
cfavreau 0:c79e1f29f029 126 return i2c.write(I2C_ADDR, buffer, 2);
cfavreau 0:c79e1f29f029 127 }
cfavreau 0:c79e1f29f029 128
cfavreau 0:c79e1f29f029 129 float input::accelConvert(char* buffer)
cfavreau 0:c79e1f29f029 130 {
cfavreau 0:c79e1f29f029 131 float val = ((buffer[0] << 2) | (buffer[1] >> 6));
cfavreau 0:c79e1f29f029 132 if (val > 511.0) val -= 1024.0;
cfavreau 0:c79e1f29f029 133 return val / 512.0;
cfavreau 0:c79e1f29f029 134 }