Jump to content

Andrew Davie

Members
  • Content Count

    4,553
  • Joined

  • Last visited

  • Days Won

    8

Everything posted by Andrew Davie

  1. It gives access to the pins on the dev board for installation of a shunt (=bridge). The shunt is removed for reprogramming manually, and installed when using the cart. The cart has auto-update, so you only need to remove the shunt if you're doing a self-build and for the very first programming/update. So, the small cutout is big enough to push the shunt through onto the underneath pins.
  2. Well sometimes you can come back to heavily optimised code and see simple and obvious changes that optimise it even more. Such was the case with presenting the draw code above; I don't know why it was so complicated, as a bit of thinking and it's now much cleaner and even faster... unsigned char *pf0 = arenas[half] + scanline; unsigned char *pf1 = pf0 + _ARENA_SCANLINES; unsigned char *pf2 = pf1 + _ARENA_SCANLINES; for (int y = 0; scanline < SCANLINES && y < PIECE_DEPTH; y++) { int p = (*image[0]++ & 0b1111) << 20 | (*image[1]++ & 0b1111) << 16 | (*image[2]++ & 0b1111) << 12 | (*image[3]++ & 0b1111) << 8 | (*image[4]++ & 0b1111) << 4 | (*image[5]++ & 0b1111); p >>= 4 - shift; if (lcount++ >= 0 ) { *pf0++ = BitRev[(p >> 16) & 0b1111]; *pf1++ = p >> 8; *pf2++ = BitRev[p & 0b1111] | (BitRev[(p >> 4) & 0b1111] >> 4); scanline++; } } A fair improvement, and much more readable/understandable - and speed is important in this particular section of the code. Each optimisation has the benefit of 198 times the optimisation amount because the code inside the loop there happens on every scanline. It's pretty neat, though, for fetching data from a character shape and mangling the bits into PF format and drawing into the video memory.
  3. Here are some notes on how it all works, from a technical standpoint... Firstly, at the most fundamental level is the 6507 kernel. All this kernel does is "stream" bytes from the ARM and write them to hardware registers. These streams consist of PF0 PF1 PF2 (left), PF0 PF1 PF2 (right), P0, P1, COLUPF, COLUBK. There's just enough time do do immediate load/store for all these registers on a single scanline, so that's exactly what the kernel does. So streams are a CDFJ feature that allow you to load an immediate value, and get back the next entry in a "stream" of data. So there are multiple streams operating here, one for each hardware register. On the ARM/C side of things, then we have vertical "strips" of bytes -- the data that gets sent to the stream requestor. Each strip is the height of the visible screen - say, 198 scanlines. So that's effectively our screen video memory - vertical strips of bytes which, once it's all setup, automatically get drawn. So now we have a video buffer and have abstracted away the vertical position of sprites, or the playfield pixels. Or the colours. All I have to do is makes sure that video memory is "drawn" to correctly, and I'll see the result on the screen. Next, we have the "board" -- this is a large chunk of memory representing our "character graphics" screen. The board is 40 x 24 characters in size. That is, 40 characters across, and 24 characters deep. Each "character" is 4 pixels across and 21 scanlines deep. So, a 40 x 24 board is actually 160 x 504 pixels in size -- and the screen being 198/21 = 9.4... character lines deep, and 40/4 = 10 characters wide. So "drawing" a scrolling playfield consists of updating the aforementioned video memory "strips" with appropriate data from that large 160 x 504 pixel area. The draw is done in real-time, every frame. It's done on a line-by-line basis, first the left side of a line (PF0, PF1, PF2) and then the right side of a line (again, PF0, PF1, PF2). So to do a half of a line, we're looking at 20 pixels that we need to fill with correct data. A half-line can overlap 20/4 = 5 characters from the board, so we fetch the 5 characters first and use those to determine what the 20 pixels should be. Adjusting (shifting) by up to 3 pixels based on the horizontal scroll. So, firstly we've fetched a character NUMBER from the board. This is an index into the character set (the visible pixels). But first we do a neat trick. When we fetch a character from the board, we use that as an index into a TYPE table. For example, CH_DIRT is TYPE_DIRT. But there are several different character visuals for dirt (CH_DIRT1, CH_DIRT2, etc.) They all have TYPE_DIRT. This has several advantages, most of all when we are doing creature logic, because we can very quickly determine WHAT a character is by just going "CharToType[*this]" and we know what type of creature is o the board. Anyway, for drawing purposes, we have fetched the character name (shape #) from the board, first thing we do is convert it to a TYPE (as above) and then we look up an Animate table. This table has one entry for each TYPE (DIRT, ROCK, WATER, LAVA, etc.,) which either has zero (no animation), or points to a short animation program/list for that type. For example, the entry for TYPE_DOGECOIN looks like this... const char AnimDogeCoin[] = { CH_DOGE,8, CH_DOGE_PULSE_1,6, CH_DOGE_PULSE_2,4, CH_DOGE_PULSE_3,3, CH_DOGE_PULSE_4,3, CH_DOGE_PULSE_5,4, CH_DOGE,8, CH_DOGE_PULSE_5,4, CH_DOGE_PULSE_4,3, CH_DOGE_PULSE_3,3, CH_DOGE_PULSE_2,4, CH_DOGE_PULSE_1,6, CH_DOGE,8, 255, }; So, that's a simple "programming language" which is basically saying show the character CH_DOGE for 8 frames, then CH_DOGE_PULSE1 for 6 frames, etc., etc. And at the end that 255 is a "loop" command. So it's a little animation program for the dogecoin that effecively happens automatically for ALL objects of TYPE_DOGE on the screen. I don't have to think about animating; I just put the character in the board, and if it has an animation then it will animate automatically. Of course ALL objects of that type will have the same frame of animation shown at any time. There are ways around this, but the fundamentals are thus. So with that little bit of indirection - we've fetched the character number from the board, converted it to the type of object, then looked up the correct character to display for this frame for that object type... now we can lookup the actual character shape data for the character we should see. This shape data is 4 pixels x 21 scanlines in the ChronoColour(TM) format (that is, triplets of R/G/B) lines, where R/G/B can be replaced with any colour trio that you like. Because COLUPF is written by the kernel on every scanline, you can in theory change the ChronoPixel R/G/B value on every pixel. This is how I get the gradated dirt colouring on the screen - it's just changing one of the R/G/B values. Here's an actual character shape definition... const unsigned char CHAR_ROCK1[] = { X_ _XX_ // 00 < A XX _XX_ // 01 B XX __X_ // 02 C __ __X_ // 03 < A XX XX_X // 04 B __ ___X // 05 C _X ___X // 06 < A XX XXXX // 07 B __ ____ // 08 C ____ // 09 < A XXXX // 10 B ____ // 11 C ____ // 12 < A XXXX // 13 B ____ // 14 C __X_ // 15 < A _XXX // 16 B ____ // 17 C ____ // 18 < A ____ // 19 B ____ // 20 C }; That rather odd-looking bit with the small block at left top, and larger block in the middle are the character definitions for overview screen (1 bit wide character) and the regular screen (4 pixel wide characters). I have a few macros setup so that I can type in character shapes like this. The comments show the A/B/C triplet lines, and the _ means "0" and "X" means "1" as far as shapes go. There are 2 shapes defined for the overview character definitions because I display either in a checkerboard pattern to make the shapes more distinct from each other when side-by-side. Each line here resolves to just one byte (and there are still bits free), so it's 21 bytes to define each ICC character shape. I currently have about 130 characters, give or take. So after determining which line in the character shape we want (remember, we're drawing into the video buffer strips), then we copy the shape data for the 5 characters we're looking at in the current left/right half of the screen into a character shape "line". And we shift it based on the scroll (up to 3 pixels, as with 4 or more we'd be looking at the next character ... so only shift up to 3 *inside* this characters shape data we just fetched, and note that it won't overflow. The appropriate pixels from the character line shape data are then copied into a 20 bit buffer (for speed, one byte per bit) representing the 20 pixels onscreen as we see them (again, left side) in left-to-right order. Once we have that, we are ready to mangle the bits into the weird PF order. This can be done very quickly, so that's what we do - we want to produce the 3 PF bytes PF0 (mirrored) PF1 PF2 (mirrored) that would be copied to the screen. But remember we're not copying them to the screen, we're just dumping them into the next scanline of the PF strips... which represent our video memory. The kernel will copy them to the screen at the appropriate scanline. So, here's the code to fetch the 20 bits from our character line shape data and produce the PF register write values... unsigned char *pf0 = arenas[half] + scanline; for (int y = 0; scanline < SCANLINES && y < PIECE_DEPTH; y++) { int p = 0; for (int pix = 0, pshift=20; pix < 6; pix++, pshift -= 4) p |= ((*(image[pix])++) & 0b1111) << pshift; p >>= 4-shift; if (lcount >= 0 ) { *pf0 = ((BitRev[(p >> 16) & 0b1111])); *(pf0 + _ARENA_SCANLINES) = ((((p >> 12) & 0b1111) << 4) | (((p >> 8) & 0b1111))); *(pf0 + (_ARENA_SCANLINES << 1)) = (( BitRev[p & 0b1111] | (BitRev[(p >> 4) & 0b1111] >> 4) )); pf0++; scanline++; } lcount++; } Now I acknowledge that this code is incomprehensible and rather nothing like what I just described - but trust me, it is the same process in operation just highly optimised. This code is retrieving character shape data, shifting it, then unpacking the bits and rearranging into PF format. Since the strips of video memory are consecutive in memory, I use "+ARENA_SCANLINES" to index into the next PF register. So it's writing PF0, PF1, PF2 for the current scanline, based on the graphics content of the characters retrieved from the character shape number retrieved from the animation program for the character type retrieved from the character number retrieved from the board. Whew! The upshot of this; a smooth scrolling pixel-precision (vertical/horizontal) playfied using ChronoColour(TM) that can be fully drawn in a single frame and thus give 60Hz smooth playfield scrolling. So, this process is running ever frame. But asynchronously to that is the board processor. This packs as much work as it can into spare time -- typically processing many, many board "squares" every frame. It scans the board from top to bottom, left to right... and it either handles each "character" on the board based on the character number (e.g., CH_WATER0), or character type (e.g., CharToType[*this] is TYPE_WATER). There are some times you need the actual character and some times you need the character type. Both are possible/handled, depending on the object and its behaviour. There are an awful lot of potential interactions between the player and objects and objects and objects. Rather than code much of this as conditional code, it's all table driven, based on the character type. I have a bitfield table which indicates exactly what sort of interaction is available for each character type. Here's a subset of that table (in reality I have about 50+ types of objects)... const int Attribute[] = { // index via ObjectType value #define _ 0 #define RKF ATT_ROCKFORDYBLANK #define PSH ATT_PUSH #define SSP ATT_SEMIBLANK #define WTR ATT_WATER #define LAV ATT_LAVA #define QUI ATT_NOROCKNOISE #define XIT ATT_EXIT #define HRD ATT_HARD #define SQB ATT_SQUASHABLE_TO_BLANKS #define ACT ATT_ACTIVE #define BNG ATT_EXPLODES #define GRB ATT_GRAB #define SQD ATT_SQUASHABLE_TO_DOGES #define SPC ATT_BLANK #define PER ATT_PERMEABLE #define XPD ATT_EXPLODABLE #define FLY ATT_KILLS #define ROL ATT_ROLL #define DRP ATT_DRIP #define RND ATT_ROUNDDIRT // e k // s n m e // t k i a a e l // r y n o l s i l b // i d a n B e D b a // D r l k h e d h a d // d e b r c s v o s k e o s // n p y h i e a o t d a i l b a n m l l 1 // u i a s m t v R i r u t p a u a r p l l // o r l u e a a o x a q c x r q l e x i o // R D P P S W L N E H S A E G S B P E K R // | | | | | | | | | | | | | | | | | | | | // RND DRP PKF PSH SSP WTR LAV QUI XIT HRD SQB ACT BNG GRB SQD SPC PER XPD FLY ROL RND| _ |PKF| _ | _ |WTR|LAV|QUI| _ | _ | _ | _ | _ | _ | _ |SPC|PER|XPD| _ | _ , // 00 BLANK_PARALLAX RND| _ |PKF| _ | _ |WTR|LAV|QUI| _ | _ | _ | _ | _ | _ | _ |SPC|PER|XPD| _ | _ , // 01 BLANK _ |DRP| _ | _ | _ |WTR|LAV| _ | _ | _ | _ | _ | _ | _ | _ | _ |PER|XPD| _ | _ , // 02 DIRT _ |DRP| _ | _ | _ | _ | _ | _ | _ |HRD| _ | _ |BNG| _ | _ | _ | _ |XPD| _ |ROL , // 03 BRICKWALL RND| _ | _ | _ | _ | _ | _ |QUI| _ | _ | _ |ACT| _ |GRB| _ | _ | _ |XPD| _ | _ , // 09 DOGE RND| _ | _ | _ | _ | _ | _ | _ | _ | _ | _ |ACT| _ | _ | _ | _ | _ | _ | _ | _ , // 10 EXPLODE_SPACE_0 RND| _ | _ | _ | _ | _ | _ | _ | _ | _ | _ |ACT| _ | _ | _ | _ | _ | _ | _ | _ , // 15 EXPLODE_DOGE_0 RND| _ |PKF| _ | _ | _ |LAV| _ | _ | _ | _ |ACT| _ | _ | _ |SPC|PER| _ | _ | _ , // 23 DRIP RND| _ |PKF| _ | _ | _ |LAV| _ | _ | _ | _ |ACT| _ | _ | _ |SPC|PER| _ | _ | _ , // 24 DRIP_SPLASH RND| _ | _ | _ | _ |WTR|LAV| _ | _ | _ | _ | _ | _ | _ | _ | _ |PER|XPD| _ | _ , // 28 RUBBLE RND| _ | _ | _ | _ | _ | _ | _ | _ | _ | _ |ACT| _ | _ | _ |SPC| _ |XPD| _ | _ , // 35 DOGE_GRAB RND| _ |PKF| _ | _ | _ | _ |QUI| _ | _ | _ |ACT| _ | _ | _ |SPC|PER|XPD| _ | _ , // 37 DUST RND| _ |PKF| _ | _ | _ | _ | _ | _ | _ | _ |ACT| _ | _ | _ |SPC|PER|XPD| _ | _ , // 47 LAVA RND| _ |PKF| _ | _ | _ | _ | _ | _ | _ | _ |ACT| _ | _ | _ |SPC|PER|XPD| _ | _ , // 48 WATER RND| _ | _ | _ | _ | _ | _ | _ | _ | _ |SQB|ACT| _ | _ | _ | _ |PER| _ | _ | _ , // 49 EGG _ | _ | _ |PSH| _ | _ | _ | _ | _ |HRD| _ |ACT|BNG| _ | _ | _ | _ |XPD| _ |ROL , // 52 ROCK _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ , // 53 DRILL _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ |ACT| _ | _ | _ | _ | _ | _ | _ | _ , // 54 DRILLBODY _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ , // 55 BELT _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ , // 56 BELT2 // RND|DRP PKF PSH SSP WTR LAV QUI XIT HRD SQB ACT BNG GRB SQD SPC PER XPD FLY ROL // | | | | | | | | | | | | | | | | | | | | // R D P P S W L N E H S A E G S B P E K R // o r l u e a a o x a q c x r q l e x i o // u i a s m t v R i r u t p a u a r p l l // n p y h i e a o t d a i l b a n m l l 1 // d e b r c s v o s k e o s // D r l k h e d h a d // i d a n B e D b a // r y n o l s i l b // t k i a a e l // s n m e // e k }; Each of the acronym entries is a unique bit value (bit 0 - bit31). So, for example... _ | _ | _ |PSH| _ | _ | _ | _ | _ |HRD| _ |ACT|BNG| _ | _ | _ | _ |XPD| _ |ROL , // 52 ROCK The above entry is for TYPE_ROCK. So as I'm processing the board, I can switch the object on CharToType[*this] == TYPE_ROCK, Let's say I wanted to know if I could push the object (it might not be a rock...) so ALL pushable objects have the PSH bit set. So I go... "if (Attribute[CharToType[*this]] & ATT_PUSH)" and that will be non-zero (i.e., true) if the object -whatever it may be- is pushable. Or, if it should explode during an explosion (XPD), or if objects can roll off the top of it (ROL), etc., etc. It makes for a very nice system that doesn't require much of any specific conditional handling for the basics. It lets me modify and change the behaviour of many objects in the world just by changing the attributes in the table above. Finally, "doing stuff" -- this is simply a matter of objects writing character numbers into the board. For example, to grab a coin, all the player's code does is something like -- if the object in the new square is grabbable, then blank the new square. Of course there's a bit more finesse - animations, scoring, etc., but that's the fundamental. The board is scanned. The object there is processed based on character number and/or type. It checks squares around it (if required) and decides what to do. Move there. Blow up. Grab the object... whatver. This is the object behaviour that gives the game its look/feel. Once the decision is made, then modify the approrpiate surrounding squares on the board and exit. The board scanner will then process the next board character/square. If there's time available. If there isn't time, then the scanner will exit, and the code executes the normal overscan/vertical blank, screen draw (as described above), and then once again continue board scanning from where it left off. So, the board scan is asynchronous occurring over (say) 10 frames. The draw is every frame. Animation is every frame (automatic) or at 10Hz (programmatic). Movement of objects on the board is only at 10Hz, but the player is a special-case which happens at 60Hz. Well that's about all I feel like describing at the moment. It's a very generic system that allows me to switch to new behaviour and visuals very quickly. It is not challenged by lack of memory or processing power at the moment, so i really can see using this system for lots of games/demos. Having an (effective) pixel-resolution (8 colours) bitmapped video buffer opens up a lot of possibilities.
  4. Thinking about how to draw a rotating planet... cheaply....
  5. Improving... So a bit of a "stock take", so to speak, and it appears I have about 8K left to make this a complete game. I'm thinking - why not throw in a bit of "lunar lander" here, too... if you have to pilot the spaceship off the planet, why shouldn't you be responsible for landing on the planet, too. See the above image with a bit of terrain, so to speak. So let's have the start of the level where you're in your ship, coming down to land... then you jump out and collect doge coins.
  6. The engine has an "overview" mode. This is a complete view of the whole playing area (not just what the screen shows) implemented as a one pixel per "character" display. In other words, it's challenging to make it look reasonable. So in this video the overview mode is starting to look OK; most of the elements (but not the spaceship, yet) are shown in the overview... and most (after a bit of familarity) are distinct enough to be able to identify. In theory, you should be able to play the game in overview mode...
  7. All ARM-based games, AFAIK. But don't forget Boulder Dash which did full-screen coloured scrolling graphics a decade or two back.
  8. Here's the thing in slow-mo - noting the real-time calculation of the circular mask, so that gameplay can happen during the wipe/transition...
  9. So, rocks. The problem is now that because you can't dig, and you need navigable passages... how are these passages *defined*? If we allow the rocks to fall, then as soon as a navigable passageway is opened ... the rocks will fall down and close it up. So, I've been thinking about these things; how to maintain passageways, and how to open up passageways in the first place. I think I've solved both of these, and found a use for the "alien eggs" in the process. We're going to switch the "eggs" to "soil eating mega-microbes". You can see them in operation at the start of this video. Basically they eat dirt nearby, but die off pretty quickly. Husk may very well use these microbes as a tool to clear out passageways. In any case, you'll notice from earlier versions that the rocks clump together in giant conglomerations of rocks. But now, there's quite a bit of behaviour differences; firstly the individual rocks will fall if unsupported. But the conglomerate rocks will be sturdy. How sturdy depends on what's happening; earthquakes and individual rocks can become loose (and you see them shaking before they fall). Also, conglomerations can just spontaneously start to disintegrate - and I can vary this based on time, or difficulty, etc. So although the passageways are maintained in esentially the same form, they do slowly disintegrate - but in a way that I consider "fair" and "avoidable" - in that you're not going to have a ton of rocks fall on you unfairly. In the video you see the mega-microbes at first, clearing out some passageways. Then you see the earthquakes disrupting/destroying the dirt, and also the individual rocks falling as appropriate. Finally, you see the conglomerates slowly disintegrate over time. I move around to show that there are still clear and distinct passages for most of the entire time. Pretty happy with this. Edit: it's pretty clear Husk is going to need a grappling hook, so let's chalk that in.
  10. Just FYI if this ever makes it to a release, I'll naturally only be accepting purchase made with dogecoin. Just sayin....
  11. I really enjoyed watching the @ZeroPage Homebrew twitch stream today with James and Tanya. They reviewed "Wen Hop?" and it was a hoot. I particularly enjoyed the smirky reactions to the implicit innuendo... the relevant part starts at the 55 minute mark.. https://www.twitch.tv/videos/1100209904 As I wrote to James... You'll notice I've animated the ship's "balldoges". Known in the industry as "the ship's balls", for short. These hold the dogecoin you collect, and feed them to the engine by shaking when in flight. SO when you're flying high, your (doge)balls are in action. These are actually implemented as separate sprites (the ship is 4 sprites - the base ship itself, the two balldoges, and the flag). The balldoges will change in size depending on how much dogecoin they're holding. So you will see your (doge)balls shrinking if you spend too much time goofing around. If your balls shrink to nothing, you're dead as a dodo. I'm thinking that you don't actually get true credit for the dogecoin you collect, until you actually return to the ship and fill the balldoges with your loot. So it will be a balance between "banking" coin and available time to complete a level. Of course like with everything, too much of a good thing and you might bust your (doge)balls. Haha. Great job James/Tanya - you did justice to the engine/demo... TY
  12. Yes, I think it's missing. I recall getting a similar question from board manufacturer. I'll look into it, but in any case anyone knowledgeable should be able to regenerate the correct files from the brd/sch provided.
  13. First look at conveyor belts. Fast and slow shown. Stairs coming soon.
  14. But... does it run on actual hardware? Well, yes it does. Mostly! This is my first test... I can get two of the ships going at the same time with not too many issues. Along with all the scrolling and other background animation/processing. It starts to disintegrate with 3 or more ships, so you see it reset/crash a fair bit. This is fine; the resets are probably the Harmony Cart deciding to gracefully exit when overtime. Still some optimisations in the pipeline, so all good. And the colours on an actual TV - excellent!
  15. Though not really practical (with scrolling background) on real hardware, the stand-alone sprite system should be able to handle this many bitmap sprites (and more...). Maybe with slowdown. Just putting this video up for posterity.
  16. A quick test in Stella with PAL hardwired shows that the default (automatic) conversion of colours will look OK... I can tweak this later...
  17. Good point. No idea at the moment. Well, sort of no idea. The colours are formed from a tricolour selection - say, red/green/blue and all 8 combinations of those together giving you the colours you see. So for PAL I find similar colours to that red/green/blue and we get similar colours on PAL. It will be quite similar, in other words. There is automatic PAL/NTSC detection in there; that's possibly just not working at the moment. I have been testing on a NTSC machine. I'll get to the PAL stuff after I'm done with work on the engine.
  18. I hope one or two notice/appreciate that circular wipe at about 16s!
  19. The spaceship is now "tied" to the background scroll, rather than being drawn on the front of the screen, so to speak. the lock isn't perfect, but I'll get there. This is looking pretty busy... now to make it a game... See my other thread for a binary with Husk's antagonist's first appearance... the evil Space Pirate Billionaire Dozeoff. Well, his ship, anyway....
  20. Uh oh, the arch nemesis seems to have shown up.... BIGASS20210727.bin
  21. This is the dumping thread for demo binaries of my new ChronoColour(TM) Sprite System. This first demo just has spaceships; I plan to have it running some fighter sprites to see how it looks. This will probably bork on hardware; it's not too far away, though, from working if it does. No interaction here... purely a demo. My goal is to refine the system and see how much speed I can get, and thus how many/big sprites can be drawn. Also, to see if a fighting game using this is feasible. I suspect so.... BIGASS20210726.bin
  22. One of those ideas on my bucket list for some time has been a "big sprite system" using PF graphics. With the spaceship, I've now managed to get this functioning. The ship-draw is actually a generic draw of any sized (up to 32 PF wide, unlimited depth) sprite, with masking. In this demo - which is not truly practical due to timing - you see 5 ships moving around the same time and the background still doing all its stuff. But the point of showing this is that I can imagine/see a 4-player fighting game with sprites this size. Each PF sprite has a configurable centerpoint, so when you draw at a particular coordinate, the frame(s) shift around to keep the center point in the same spot on the screen. This draw is happening at 60Hz. I may work on a stand-alone engine to allow this big sprite system to be used by others; it's extremely simple to define shapes, and even easier to draw... Here's the definition of the ship (and mask)... side by side for ease of editing... const unsigned char rocketShip[] = { 1, // width in BYTES (=8 pix/byte) (MAX =4) 105, // height in SCANLINES (pref multiples of 3 -- TRIPIXs) 3,105, // center point (PIXELS) from 0,0 top left ________ ________ // colour 0 ________ ________ // colour 1 ________ ________ // colour 2 etc... ___X____ ___X____ ________ ________ ________ ________ ________ ________ ___X____ ___X____ ________ ___X____ ___X____ ___X____ ___X____ ___X____ ________ ___X____ __XXX___ __XXX___ __XXX___ __XXX___ ___XX___ __XXX___ __XXX___ __XXX___ __XXX___ __XXX___ ___XX___ __XXX___ ___X____ __XXX___ __X_X___ __XXX___ ___XX___ __XXX___ ___X____ __XXX___ // 00 __XXX___ __XXX___ // 01 ___X____ __XXX___ // 02 __XXX___ __XXX___ // 03 _XXXXX__ _XXXXX__ // 04 __XXX___ __XXX___ // 05 _XXXXX__ _XXXXX__ // 06 _XXXXX__ _XXXXX__ // 07 __XXXX__ _XXXXX__ // 08 _XXXXX__ _XXXXX__ // 09 _XXXXX__ _XXXXX__ // 10 __XX_X__ _XXXXX__ // 11 _XXX_X__ _XXXXX__ // 12 _XX_____ _XXXXX__ // 13 __X_____ _XXXXX__ // 14 _XXX_X__ _XXXXX__ // 15 _XXX_X__ _XXXXX__ // 16 __XXXX__ _XXXXX__ // 17 XXXX_XX_ XXXXXXX_ // 18 XXXX_XX_ XXXXXXX_ // 19 _XXXXXX_ XXXXXXX_ // 20 XXX___X_ XXXXXXX_ // 00 XXX___X_ XXXXXXX_ // 01 _XXXXXX_ XXXXXXX_ // 02 XXX___X_ XXXXXXX_ // 03 XXX___X_ XXXXXXX_ // 04 _XXXXXX_ XXXXXXX_ // 05 XXXX_XX_ XXXXXXX_ // 06 XXXX_XX_ XXXXXXX_ // 07 _XXXXXX_ XXXXXXX_ // 08 XXXXXXX_ XXXXXXX_ // 09 XXXXXXX_ XXXXXXX_ // 10 _XXXXXX_ XXXXXXX_ // 11 XXXX__X_ XXXXXXX_ // 12 XXXXXXX_ XXXXXXX_ // 13 _XXXXXX_ XXXXXXX_ // 14 XXXX__X_ XXXXXXX_ // 15 XXXXXXX_ XXXXXXX_ // 16 _XXXXXX_ XXXXXXX_ // 17 XXXX__X_ XXXXXXX_ // 18 XXXXXXX_ XXXXXXX_ // 19 _XXXXXX_ XXXXXXX_ // 20 XXXXXXX_ XXXXXXX_ // 00 XXXXXXX_ XXXXXXX_ // 01 _XXXXXX_ XXXXXXX_ // 02 XXXXXXX_ XXXXXXX_ // 03 XXXXXXX_ XXXXXXX_ // 04 _XXXXXX_ XXXXXXX_ // 05 XXXXXXX_ XXXXXXX_ // 06 XXXXXXX_ XXXXXXX_ // 07 _XXXXXX_ XXXXXXX_ // 08 XXXXXXX_ XXXXXXX_ // 09 XXXXXXX_ XXXXXXX_ // 10 _XXXXXX_ XXXXXXX_ // 11 XXXXXXX_ XXXXXXX_ // 12 XXXXXXX_ XXXXXXX_ // 13 ________ XXXXXXX_ // 14 _XXXXX__ _XXXXX__ // 15 __XXXX__ _XXXXX__ // 16 __XXXX__ _XXXXX__ // 17 __XXX___ __XXX___ // 18 _____X__ _XXXXX__ // 19 __XXXX__ _XXXXX__ // 20 __XXX___ __XXX___ __XXX___ __XXX___ __XXX___ __XXX___ __XXX___ __XXX___ _XX_XX__ _XXXXX__ _XXXXX__ _XXXXX__ __XXX___ __XXX___ XXXXXXX_ XXXXXXX_ XXXXXXX_ XXXXXXX_ __XXX___ __XXX___ __XXX___ XXXXXXX_ X_XXX_X_ XXXXXXX_ __XXX___ _XXXX___ ________ XXXXXXX_ X_XXX_X_ X_XXX_X_ ___X____ X_XXX_X_ ________ X__X__X_ X__X__X_ X_XXX_X_ ________ X_XXX_X_ ________ X_____X_ X_____X_ X_____X_ }; Here's how to draw the ship... and flickery flame.... you don't need to erase... just draw things where you want... drawBitmap(&rocketShip[0], x, y, false); if (!(getRandom32() & 3)) drawBitmap(&rocketShipFlame[0], x, y, false); In the above example, a ship and the rocket flame share the same x,y coordinate (centerpoint). The flame will appear below the engine, because the center point of the ship is at bottom center of engine, and the centerpoint of the flame is top center. Anyway, I digress... it's a lovely thing. Using the ChronoColour(TM) system, as this does, each sprite can have 9 ICC colours (that is, 8 colours of ICC AND a transparent "colour" due to the mask capability).
  23. I'll try and get a public binary in a week or two...
×
×
  • Create New...