Chris Dick
/
Gameduino_Invaders_game
Invaders game for the Gameduino
utils.h@4:e82f4a87df9e, 2013-10-26 (annotated)
- Committer:
- TheChrisyd
- Date:
- Sat Oct 26 22:32:18 2013 +0000
- Revision:
- 4:e82f4a87df9e
- Parent:
- 2:20a89dc286d5
Shields are now destroyed when hit, corrected score displays
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
TheChrisyd | 2:20a89dc286d5 | 1 | #ifndef COM_ARTLUM_GDUTILS_INCLUDED |
TheChrisyd | 2:20a89dc286d5 | 2 | #define COM_ARTLUM_GDUTILS_INCLUDED |
TheChrisyd | 2:20a89dc286d5 | 3 | #include "arduino.h" |
TheChrisyd | 2:20a89dc286d5 | 4 | #include "GD.h" |
TheChrisyd | 2:20a89dc286d5 | 5 | //#include "WProgram.h" |
TheChrisyd | 2:20a89dc286d5 | 6 | |
TheChrisyd | 2:20a89dc286d5 | 7 | /*--------------------------------------------------- |
TheChrisyd | 2:20a89dc286d5 | 8 | Coprocessor controller - makes it easy to do |
TheChrisyd | 2:20a89dc286d5 | 9 | raster effects (eg. split screens) without |
TheChrisyd | 2:20a89dc286d5 | 10 | writing any FORTH. |
TheChrisyd | 2:20a89dc286d5 | 11 | |
TheChrisyd | 2:20a89dc286d5 | 12 | The way this works is that you build a list of |
TheChrisyd | 2:20a89dc286d5 | 13 | instructions in memory (a "copperlist") and |
TheChrisyd | 2:20a89dc286d5 | 14 | the coprocessor will execute the list on every |
TheChrisyd | 2:20a89dc286d5 | 15 | video frame. |
TheChrisyd | 2:20a89dc286d5 | 16 | |
TheChrisyd | 2:20a89dc286d5 | 17 | Code samples: |
TheChrisyd | 2:20a89dc286d5 | 18 | |
TheChrisyd | 2:20a89dc286d5 | 19 | void setup() { |
TheChrisyd | 2:20a89dc286d5 | 20 | ... |
TheChrisyd | 2:20a89dc286d5 | 21 | // Make a copperlist to change a palette color on line 100 |
TheChrisyd | 2:20a89dc286d5 | 22 | // The copperlist is stored on the Gameduino at address 0x3f80 |
TheChrisyd | 2:20a89dc286d5 | 23 | Coprocessor::reset(); |
TheChrisyd | 2:20a89dc286d5 | 24 | CopperlistBuilder cp; |
TheChrisyd | 2:20a89dc286d5 | 25 | cp.begin(0x3f80); |
TheChrisyd | 2:20a89dc286d5 | 26 | cp.wait(100); |
TheChrisyd | 2:20a89dc286d5 | 27 | cp.write16(PALETTE4A, 0x7fff); |
TheChrisyd | 2:20a89dc286d5 | 28 | cp.end(); // And start using the new copperlist |
TheChrisyd | 2:20a89dc286d5 | 29 | } |
TheChrisyd | 2:20a89dc286d5 | 30 | |
TheChrisyd | 2:20a89dc286d5 | 31 | Modifying an existing copperlist: |
TheChrisyd | 2:20a89dc286d5 | 32 | |
TheChrisyd | 2:20a89dc286d5 | 33 | // Make a copperlist to set the horizontal scroll register |
TheChrisyd | 2:20a89dc286d5 | 34 | // on line 220 and modify the copperlist. |
TheChrisyd | 2:20a89dc286d5 | 35 | // a) Build the list |
TheChrisyd | 2:20a89dc286d5 | 36 | unsigned int XscrollInst; |
TheChrisyd | 2:20a89dc286d5 | 37 | void setup() { |
TheChrisyd | 2:20a89dc286d5 | 38 | ... |
TheChrisyd | 2:20a89dc286d5 | 39 | CopperlistBuilder cp; |
TheChrisyd | 2:20a89dc286d5 | 40 | cp.begin(0x3f80); |
TheChrisyd | 2:20a89dc286d5 | 41 | cp.wait(220); |
TheChrisyd | 2:20a89dc286d5 | 42 | XscrollInst = cp.position(); // Get current output location |
TheChrisyd | 2:20a89dc286d5 | 43 | cp.write16(SCROLL_X, 0); |
TheChrisyd | 2:20a89dc286d5 | 44 | cp.end(); |
TheChrisyd | 2:20a89dc286d5 | 45 | } |
TheChrisyd | 2:20a89dc286d5 | 46 | // b) Modify the list with current 'xscroll' |
TheChrisyd | 2:20a89dc286d5 | 47 | void loop() { |
TheChrisyd | 2:20a89dc286d5 | 48 | CopperlistBuilder cp; |
TheChrisyd | 2:20a89dc286d5 | 49 | cp.begin(XscrollInst) // Where the 'write16()' instruction is |
TheChrisyd | 2:20a89dc286d5 | 50 | cp.write16(SCROLL_X, xscroll); // Overwrite the previous instruction |
TheChrisyd | 2:20a89dc286d5 | 51 | } |
TheChrisyd | 2:20a89dc286d5 | 52 | |
TheChrisyd | 2:20a89dc286d5 | 53 | Notes: |
TheChrisyd | 2:20a89dc286d5 | 54 | The Coprocessor object uses the "COMM" block |
TheChrisyd | 2:20a89dc286d5 | 55 | of memory at 0x2980 so don't mess with it. |
TheChrisyd | 2:20a89dc286d5 | 56 | |
TheChrisyd | 2:20a89dc286d5 | 57 | The coprocessor also provides support for the |
TheChrisyd | 2:20a89dc286d5 | 58 | sound functions below. |
TheChrisyd | 2:20a89dc286d5 | 59 | ---------------------------------------------------*/ |
TheChrisyd | 2:20a89dc286d5 | 60 | class Coprocessor { |
TheChrisyd | 2:20a89dc286d5 | 61 | public: |
TheChrisyd | 2:20a89dc286d5 | 62 | // This initializes everything and load the coprocessor code. |
TheChrisyd | 2:20a89dc286d5 | 63 | // You should call this in your setup() function after "GD.begin();" |
TheChrisyd | 2:20a89dc286d5 | 64 | // |
TheChrisyd | 2:20a89dc286d5 | 65 | // The sample page is the page of Gameduino memory to |
TheChrisyd | 2:20a89dc286d5 | 66 | // use as a buffer for sample playback. It *must* be |
TheChrisyd | 2:20a89dc286d5 | 67 | // on a page boundry (ie. bottom 8 bits are 0) and |
TheChrisyd | 2:20a89dc286d5 | 68 | // the whole page will be used so you can't use it |
TheChrisyd | 2:20a89dc286d5 | 69 | // for anything else. |
TheChrisyd | 2:20a89dc286d5 | 70 | static void reset(unsigned int samplePage=0x3f00); // Default: Use memory from 0x3f00 to 0x3fff |
TheChrisyd | 2:20a89dc286d5 | 71 | |
TheChrisyd | 2:20a89dc286d5 | 72 | // Select a copperlist. The list will start running on the next video frame. |
TheChrisyd | 2:20a89dc286d5 | 73 | // nb. The "CopperlistBuilder::end()" function normally calls this automatically |
TheChrisyd | 2:20a89dc286d5 | 74 | // (unless you tell it not to...) |
TheChrisyd | 2:20a89dc286d5 | 75 | static void setCopperlist(unsigned int addr); |
TheChrisyd | 2:20a89dc286d5 | 76 | |
TheChrisyd | 2:20a89dc286d5 | 77 | // Where the sample playback page is located |
TheChrisyd | 2:20a89dc286d5 | 78 | static unsigned int samplePage(); |
TheChrisyd | 2:20a89dc286d5 | 79 | |
TheChrisyd | 2:20a89dc286d5 | 80 | // Where the coprocessor is currently reading samples |
TheChrisyd | 2:20a89dc286d5 | 81 | // from in the sample page. Use this info to keep the |
TheChrisyd | 2:20a89dc286d5 | 82 | // page full of fresh sample data for playback |
TheChrisyd | 2:20a89dc286d5 | 83 | static byte sampleReadPos(); |
TheChrisyd | 2:20a89dc286d5 | 84 | |
TheChrisyd | 2:20a89dc286d5 | 85 | // A copy of the coprocessor YLINE register |
TheChrisyd | 2:20a89dc286d5 | 86 | static int yline(); |
TheChrisyd | 2:20a89dc286d5 | 87 | }; |
TheChrisyd | 2:20a89dc286d5 | 88 | |
TheChrisyd | 2:20a89dc286d5 | 89 | // This object helps you make a copperlist in memory |
TheChrisyd | 2:20a89dc286d5 | 90 | class CopperlistBuilder { |
TheChrisyd | 2:20a89dc286d5 | 91 | unsigned int out,start; |
TheChrisyd | 2:20a89dc286d5 | 92 | void put(byte); |
TheChrisyd | 2:20a89dc286d5 | 93 | void put16(unsigned int); |
TheChrisyd | 2:20a89dc286d5 | 94 | public: |
TheChrisyd | 2:20a89dc286d5 | 95 | // Available commands are: |
TheChrisyd | 2:20a89dc286d5 | 96 | // * Wait for a raster line |
TheChrisyd | 2:20a89dc286d5 | 97 | // * Write a byte to a memory location |
TheChrisyd | 2:20a89dc286d5 | 98 | // * Write a 16-bit word to a memory location |
TheChrisyd | 2:20a89dc286d5 | 99 | // * Copy a block of memory from one place to another |
TheChrisyd | 2:20a89dc286d5 | 100 | void begin(unsigned int dest); |
TheChrisyd | 2:20a89dc286d5 | 101 | void wait(int line); |
TheChrisyd | 2:20a89dc286d5 | 102 | void write(unsigned int addr, byte val); |
TheChrisyd | 2:20a89dc286d5 | 103 | void write16(unsigned int addr, unsigned int val); |
TheChrisyd | 2:20a89dc286d5 | 104 | void copy(unsigned int src, unsigned int dst, unsigned int numBytes); |
TheChrisyd | 2:20a89dc286d5 | 105 | void end(bool executeNow=true); |
TheChrisyd | 2:20a89dc286d5 | 106 | |
TheChrisyd | 2:20a89dc286d5 | 107 | // Where I'm currently outputting |
TheChrisyd | 2:20a89dc286d5 | 108 | unsigned int position(); |
TheChrisyd | 2:20a89dc286d5 | 109 | }; |
TheChrisyd | 2:20a89dc286d5 | 110 | |
TheChrisyd | 2:20a89dc286d5 | 111 | |
TheChrisyd | 2:20a89dc286d5 | 112 | /*------------------------------------------------------------------ |
TheChrisyd | 2:20a89dc286d5 | 113 | Sound functions - play synthesized sounds and sound samples |
TheChrisyd | 2:20a89dc286d5 | 114 | |
TheChrisyd | 2:20a89dc286d5 | 115 | These functions require support from the Coprocessor object |
TheChrisyd | 2:20a89dc286d5 | 116 | above. If you load different microcode they'll stop working... |
TheChrisyd | 2:20a89dc286d5 | 117 | ------------------------------------------------------------------*/ |
TheChrisyd | 2:20a89dc286d5 | 118 | |
TheChrisyd | 2:20a89dc286d5 | 119 | /*------------------------------------------------------------------ |
TheChrisyd | 2:20a89dc286d5 | 120 | Synthesized sounds |
TheChrisyd | 2:20a89dc286d5 | 121 | ------------------------------------------------------------------*/ |
TheChrisyd | 2:20a89dc286d5 | 122 | #define SYNTHSOUNDS 0 |
TheChrisyd | 2:20a89dc286d5 | 123 | #if SYNTHSOUNDS |
TheChrisyd | 2:20a89dc286d5 | 124 | // ADSR volume envelope |
TheChrisyd | 2:20a89dc286d5 | 125 | struct ADSR { |
TheChrisyd | 2:20a89dc286d5 | 126 | byte attack; // Attack time (in clock ticks) |
TheChrisyd | 2:20a89dc286d5 | 127 | byte decay; // Decay time (in clock ticks) |
TheChrisyd | 2:20a89dc286d5 | 128 | byte sustain; // Sustain level [0..255] |
TheChrisyd | 2:20a89dc286d5 | 129 | byte release; // Release time (in clock ticks) |
TheChrisyd | 2:20a89dc286d5 | 130 | ADSR(byte a=4, byte d=4, byte s=90, byte r=30); |
TheChrisyd | 2:20a89dc286d5 | 131 | byte evaluate(unsigned int elapsedTime, unsigned int releaseTime); |
TheChrisyd | 2:20a89dc286d5 | 132 | }; |
TheChrisyd | 2:20a89dc286d5 | 133 | |
TheChrisyd | 2:20a89dc286d5 | 134 | // Change pitch of a sound as it plays |
TheChrisyd | 2:20a89dc286d5 | 135 | struct PitchModulator { |
TheChrisyd | 2:20a89dc286d5 | 136 | // Vibrato |
TheChrisyd | 2:20a89dc286d5 | 137 | byte vibratoDelay; // Delay before vibrato starts (in clock ticks) |
TheChrisyd | 2:20a89dc286d5 | 138 | byte vibrato; // Amount of vibrato (percentage of note pitch) |
TheChrisyd | 2:20a89dc286d5 | 139 | char vibratoDelta; // Added to 'vibrato' every clock tick |
TheChrisyd | 2:20a89dc286d5 | 140 | // Pitch Sweep |
TheChrisyd | 2:20a89dc286d5 | 141 | byte sweepDelay; // Delay before sweep starts (in clock ticks) |
TheChrisyd | 2:20a89dc286d5 | 142 | byte sweep; |
TheChrisyd | 2:20a89dc286d5 | 143 | char sweepDelta; |
TheChrisyd | 2:20a89dc286d5 | 144 | PitchModulator() { |
TheChrisyd | 2:20a89dc286d5 | 145 | vibratoDelay = 0; |
TheChrisyd | 2:20a89dc286d5 | 146 | vibrato = 0; |
TheChrisyd | 2:20a89dc286d5 | 147 | vibratoDelta = 0; |
TheChrisyd | 2:20a89dc286d5 | 148 | } |
TheChrisyd | 2:20a89dc286d5 | 149 | int evaluate(int startpitch, unsigned int time); |
TheChrisyd | 2:20a89dc286d5 | 150 | }; |
TheChrisyd | 2:20a89dc286d5 | 151 | |
TheChrisyd | 2:20a89dc286d5 | 152 | // All the parameters for a sound |
TheChrisyd | 2:20a89dc286d5 | 153 | struct Sound { |
TheChrisyd | 2:20a89dc286d5 | 154 | ADSR adsr; |
TheChrisyd | 2:20a89dc286d5 | 155 | PitchModulator pm; |
TheChrisyd | 2:20a89dc286d5 | 156 | }; |
TheChrisyd | 2:20a89dc286d5 | 157 | |
TheChrisyd | 2:20a89dc286d5 | 158 | class SoundPlayer { |
TheChrisyd | 2:20a89dc286d5 | 159 | // Playback parameters |
TheChrisyd | 2:20a89dc286d5 | 160 | Sound sound; |
TheChrisyd | 2:20a89dc286d5 | 161 | byte volume; |
TheChrisyd | 2:20a89dc286d5 | 162 | unsigned int pitch; |
TheChrisyd | 2:20a89dc286d5 | 163 | unsigned int duration; |
TheChrisyd | 2:20a89dc286d5 | 164 | // Internal vars |
TheChrisyd | 2:20a89dc286d5 | 165 | unsigned int ticks; |
TheChrisyd | 2:20a89dc286d5 | 166 | bool active, releasing; |
TheChrisyd | 2:20a89dc286d5 | 167 | // Sound players form a linked list |
TheChrisyd | 2:20a89dc286d5 | 168 | bool isLinked; |
TheChrisyd | 2:20a89dc286d5 | 169 | SoundPlayer *link; |
TheChrisyd | 2:20a89dc286d5 | 170 | // The SoundPlayer is updated from SoundController |
TheChrisyd | 2:20a89dc286d5 | 171 | void update(); |
TheChrisyd | 2:20a89dc286d5 | 172 | friend class SoundController; |
TheChrisyd | 2:20a89dc286d5 | 173 | // You can't copy me... |
TheChrisyd | 2:20a89dc286d5 | 174 | SoundPlayer(const SoundPlayer&); |
TheChrisyd | 2:20a89dc286d5 | 175 | void operator=(const SoundPlayer&); |
TheChrisyd | 2:20a89dc286d5 | 176 | public: |
TheChrisyd | 2:20a89dc286d5 | 177 | SoundPlayer(); |
TheChrisyd | 2:20a89dc286d5 | 178 | |
TheChrisyd | 2:20a89dc286d5 | 179 | // Set the sound parameters |
TheChrisyd | 2:20a89dc286d5 | 180 | SoundPlayer& setSound(const Sound&); |
TheChrisyd | 2:20a89dc286d5 | 181 | |
TheChrisyd | 2:20a89dc286d5 | 182 | // Set playback volume in range [0..255], default = 255 |
TheChrisyd | 2:20a89dc286d5 | 183 | SoundPlayer& setVolume(byte); |
TheChrisyd | 2:20a89dc286d5 | 184 | |
TheChrisyd | 2:20a89dc286d5 | 185 | // Play a sound, pitch in Hz, duration in clock ticks. |
TheChrisyd | 2:20a89dc286d5 | 186 | // If duration is 'infinite' then the sound plays until you call 'release()' |
TheChrisyd | 2:20a89dc286d5 | 187 | // One "clock tick" is equivalent to one call to "SoundController::update()" |
TheChrisyd | 2:20a89dc286d5 | 188 | enum { infinite=0x7fff }; |
TheChrisyd | 2:20a89dc286d5 | 189 | void play(unsigned int pitchHz, unsigned int duration=infinite); |
TheChrisyd | 2:20a89dc286d5 | 190 | |
TheChrisyd | 2:20a89dc286d5 | 191 | // Make the current sound enter the 'release' phase |
TheChrisyd | 2:20a89dc286d5 | 192 | void release(); |
TheChrisyd | 2:20a89dc286d5 | 193 | |
TheChrisyd | 2:20a89dc286d5 | 194 | // Stop sound playback immediately |
TheChrisyd | 2:20a89dc286d5 | 195 | void stop(); |
TheChrisyd | 2:20a89dc286d5 | 196 | }; |
TheChrisyd | 2:20a89dc286d5 | 197 | #endif |
TheChrisyd | 2:20a89dc286d5 | 198 | |
TheChrisyd | 2:20a89dc286d5 | 199 | /*------------------------------------------------------------------ |
TheChrisyd | 2:20a89dc286d5 | 200 | Main sound controller object |
TheChrisyd | 2:20a89dc286d5 | 201 | ------------------------------------------------------------------*/ |
TheChrisyd | 2:20a89dc286d5 | 202 | struct SoundController { |
TheChrisyd | 2:20a89dc286d5 | 203 | // Reset all sounds, return to silence |
TheChrisyd | 2:20a89dc286d5 | 204 | static void reset(); |
TheChrisyd | 2:20a89dc286d5 | 205 | |
TheChrisyd | 2:20a89dc286d5 | 206 | // Update all sound - call this at least once every video frame. |
TheChrisyd | 2:20a89dc286d5 | 207 | // The frequency at which you call this function is one "clock tick" for synthesised sounds |
TheChrisyd | 2:20a89dc286d5 | 208 | static void update(); |
TheChrisyd | 2:20a89dc286d5 | 209 | |
TheChrisyd | 2:20a89dc286d5 | 210 | // Play a sound sample from program memory |
TheChrisyd | 2:20a89dc286d5 | 211 | // Samples are eight bits and will be played at |
TheChrisyd | 2:20a89dc286d5 | 212 | // the system sample rate (see "Coprocessor"). |
TheChrisyd | 2:20a89dc286d5 | 213 | // if "numBytes" is negative, the sample will repeat until you play |
TheChrisyd | 2:20a89dc286d5 | 214 | // another sample on the channel (eg. pass a null pointer to "playSample()"...) |
TheChrisyd | 2:20a89dc286d5 | 215 | static void playSample(prog_char*, int numBytes, byte channel); |
TheChrisyd | 2:20a89dc286d5 | 216 | }; |
TheChrisyd | 2:20a89dc286d5 | 217 | |
TheChrisyd | 2:20a89dc286d5 | 218 | |
TheChrisyd | 2:20a89dc286d5 | 219 | |
TheChrisyd | 2:20a89dc286d5 | 220 | /*------------------------------------------------------------ |
TheChrisyd | 2:20a89dc286d5 | 221 | Useful little functions |
TheChrisyd | 2:20a89dc286d5 | 222 | ------------------------------------------------------------*/ |
TheChrisyd | 2:20a89dc286d5 | 223 | // Show a number on screen |
TheChrisyd | 2:20a89dc286d5 | 224 | void showNumber(int n, byte x, byte y); |
TheChrisyd | 2:20a89dc286d5 | 225 | void showHexNumber(unsigned int n, byte x, byte y); |
TheChrisyd | 2:20a89dc286d5 | 226 | |
TheChrisyd | 2:20a89dc286d5 | 227 | // Send screenshot to serial port |
TheChrisyd | 2:20a89dc286d5 | 228 | |
TheChrisyd | 2:20a89dc286d5 | 229 | void sendScreenshot(); |
TheChrisyd | 2:20a89dc286d5 | 230 | |
TheChrisyd | 2:20a89dc286d5 | 231 | // COM_ARTLUM_GDUTILS_INCLUDED |
TheChrisyd | 2:20a89dc286d5 | 232 | #endif |