Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411804 Posts in 69416 Topics- by 58462 Members - Latest Member: Moko1910

May 28, 2024, 09:31:13 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Homebrew Creator's Club: Meeting One
Pages: 1 [2]
Print
Author Topic: Homebrew Creator's Club: Meeting One  (Read 13257 times)
Cthulhu32
Level 6
*


Brawp


View Profile WWW
« Reply #20 on: February 16, 2010, 02:49:49 PM »

Now, we have all of our raw data to work with, think of these as the "ingredients" to the recipe. Now we need the actual code that tells our application what to do. And to help this, we will declare several defines that are automatically converted to numbers at compile. You could simply leave these parts out and write the numbers yourself, but this sometimes makes code management easier. This also makes it easier to change a define later on.
Code:
#define NBDFRAMES 0x18                  /* Nb frames for the door */
#define NBSFRAMES 0x07                  /* Nb frames for the sprite */
#define WINSZX    0x80                  /* Size of the picture in the window */
#define WINSZY    0x50
#define MINWINX   (MAXWNDPOSX-WINSZX+1) /* Bounds of the window origin */
#define MINWINY   (MAXWNDPOSY-WINSZY+1)
#define MAXWINX   MAXWNDPOSX
#define MAXWINY   MAXWNDPOSY
#define FADESTEP  0x10                  /* Nb steps for the fading effect */
#define STARTFADE (0x06*FADESTEP)       /* Initial value for the fading effect */

#define CLOSED  0x00
#define OPENING 0x01
#define OPENED  0x02
#define CLOSING 0x03

Next, declare some global variables that will be used throughout the program, including positions and state numbers.
Code:
UBYTE time;         /* Global "time" value (counter) */
UBYTE doorstate;    /* State of the door (OPENED, CLOSED...) */
UBYTE doorpos;      /* Current position in the door animation */
UBYTE color;        /* Current color for fading effect */
UBYTE sframe;       /* Current frame of the sprite */
fixed bposx, bposy; /* Background position (fixed point) */
fixed bspx, bspy;   /* Background speed (fixed point) */
fixed wposx, wposy; /* Window position (fixed point) */
fixed wspx, wspy;   /* Window speed (fixed point) */
fixed sposx, sposy; /* Sprite position (fixed point) */
fixed sspx, sspy;   /* Sprite speed (fixed point) */

Next, declare our functions for good practice.
Code:
void fade();
void scroll();
void door();
void animate_sprite();
void tile_sprite();
void place_sprite();

Next, we have the first function with a little meat to it. Fade is a function that changes the screen register to modify the palette. The gameboy allows 4 colors to be in the palette at all times, and by giving the programmer access to this palette, we can do fun little tricks like slowly darkening each color until the entire screen is black. Fade does just this by settings 4 particular numbers, F9, FE, FF, E4, each of these are a step along fading to black.
Code:
void fade()
{
  if(color == 0)
    return;
  switch(color)
    {
    case STARTFADE:
    case STARTFADE-4*FADESTEP:
      BGP_REG = 0xF9U;
      break;
    case STARTFADE-FADESTEP:
    case STARTFADE-3*FADESTEP:
      BGP_REG = 0xFEU;
      break;
    case STARTFADE-2*FADESTEP:
      BGP_REG = 0xFFU;
      break;
    case STARTFADE-5*FADESTEP:
      BGP_REG = 0xE4U;
      break;
    }
  color--;
}

So, we have a fading function, next up we want a scrolling function that makes the stars in the backdrop move. We accomplish this by modifying two registers inside of the Gameboy, SCX_REG, and SCY_REG. By modifying these numbers, the background will "scroll" or move around the background tile (check out the VRAM in BGB). There are also two registers for the window layer, WX_REG and WY_REG, which we will also modify to move around the scrolling window. Finally, the sprite is updated by the global variable we declared earlier, sposx and sposy. Note: the place_sprite() function is being declared at the end, because the OAM must be written to in order to move the globe properly.
Code:
void scroll()
{
  /* Update background */
  bposx.w += bspx.w;
  bposy.w += bspy.w;
  SCX_REG = bposx.b.h;
  SCY_REG = bposy.b.h;

  /* Update window */
  wposx.w += wspx.w ;
  wposy.w += wspy.w ;
  /* X position */
  if(wposx.b.h >= MAXWINX) {
    wposx.b.h = MAXWINX;
    /* Invert speed */
    wspx.w = -(WORD)wspx.w;
  } else if(wposx.b.h <= MINWINX) {
    wposx.b.h = MINWINX;
    /* Invert speed */
    wspx.w = -(WORD)wspx.w;
  }
  WX_REG = wposx.b.h;
  /* Y position */
  if(wposy.b.h >= MAXWINY) {
    wposy.b.h = MAXWINY;
    /* Invert speed */
    wspy.w = -(WORD)wspy.w;
  } else if(wposy.b.h <= MINWINY) {
    wposy.b.h = MINWINY;
    /* Invert speed */
    wspy.w = -(WORD)wspy.w;
  }
  WY_REG = wposy.b.h;

  /* Update sprite */
  sposx.w += sspx.w;
  sposy.w += sspy.w;
  place_sprite();
}

Next up, we have a door function, which actually draws the door frames opening and closing. Remember our doorstate is a global variable, so when it is set in main, this function will catch it, increment the door position (which is its current frame), and set the window layer tiles based on our film[] data pointer array. Same goes for when you close the door.
Code:
void door()
{
  if(doorstate == OPENING) {
    doorpos++;
    /* Draw the door in the window */
    set_win_tiles(2, 2, 12, 6, film[doorpos]);
    if(doorpos == NBDFRAMES)
      doorstate = OPENED;
  } else if(doorstate == CLOSING) {
    doorpos--;
    /* Draw the door in the window */
    set_win_tiles(2, 2, 12, 6, film[doorpos]);
    if(doorpos == 0)
      doorstate = CLOSED;
  }
}

To actually give the globe a rotation, we have a nice little function that looks at the time keeper, recorded further down. Whenever this number is &7 (which is basically a super fast mod Cool, the frame is incremented, and the sprite is updated with tile_sprite()
Code:
void animate_sprite()
{
  if((time&0x07) == 0) {
    sframe++;
    if(sframe == NBSFRAMES)
      sframe = 0;
    tile_sprite();
  }
}

And to complement the animate sprite declaration, we have our tile_sprite function, which sets the sprite tile. set_sprite_tile takes the ID of the sprite you want to animate, and the data pointer to the tile data you're animating from.
Code:
void tile_sprite()
{
  UBYTE s;

  s = sframe<<1;
  set_sprite_tile(0, earth_tiles[s]);
  set_sprite_tile(1, earth_tiles[s+1]);
}

Place sprite calls a GBDK function, which essentially writes a quick OAM vram write to give the sprite its proper X/Y value.
Code:
void place_sprite()
{
  move_sprite(0, sposx.b.h, sposy.b.h);
  move_sprite(1, sposx.b.h+8, sposy.b.h);
}

And finally, the entry point to the application, main. We start off main by setting up our screen properly, disabling all interrupts and turning off the display. This is done so an interrupt such as a VBlank (vertical blank) is not triggered, or something such as a button being pressed by the user.
Code:
void main()
{
  UBYTE i, j;

  disable_interrupts();
  DISPLAY_OFF;
  LCDC_REG = 0x67;
  /*
   * LCD        = Off
   * WindowBank = 0x9C00
   * Window     = On
   * BG Chr     = 0x8800
   * BG Bank    = 0x9800
   * OBJ        = 8x16
   * OBJ        = On
   * BG         = On
   */

Take note how LCDC_REG was written. 0x67 might just look like an arbitrary number, but because we're dealing with such low level systems, you have to think in binary here. Each binary number in LCDC_REG deals with a part of the Gameboy. So in this case:

0x67 = 01100111 = [LCD Off][WindowBank at 0x9c00][Window Layer ON][BG Chr at 0x8800][BG Bank at 0x9800][OBJ size is 8x16][Sprites are ON][BG is ON]
Now you might be wondering, why do we even set 0 or 1 for a bank such as 0x9800. This is because we can share banks between the window and background, or we can do quick bank swaps by setting the various LCD registers to 0 or 1.

Next, we want to setup a few more vital bits of data, including the palette.
Code:
  doorstate = CLOSED;

  /* Set palettes */
  BGP_REG = OBP0_REG = OBP1_REG = 0xE4U;

Notice the palettes are all set to E4, this is simply because in binary, this looks like 11100100. Or divide that up, [11][10][01][00] [4th color][3rd color][2nd color][1st color] which is our entire palette. Notice in that function we had way up there, Fade(), we were modifying this. This simply messed with each color.

Next, we want to write some background data, GBDK supplies us with some nice functions to do this.
Code:
  /* Initialize the background */
  set_bkg_data(0xFC, 0x04, std_data);
  set_bkg_data(0x00, 0x2D, bkg_data);
  /*
   * Draw the background
   *
   * Width  = 0x100 = 0x20 * 8
   * Height = 0x100 = 0x20 * 8
   */
  for(i = 0; i < 32; i+=8)
    for(j = 0; j < 32; j+=8)
      set_bkg_tiles(i, j, 8, 8, bkg_tiles);
  bposx.w = 0;
  SCX_REG = 0;
  bposy.w = 0;
  SCY_REG = 0;
  bspx.w = 0xFF00UL;
  bspy.w = 0x0080UL;

Set the windows data the same way.
Code:
  /* Initialize the window */
  set_win_data(0x80, 0x21, frame_data);
  /*
   * Draw the frame in the window
   *
   * Width  = 0x80 = 0x10 * 8
   * Height = 0x50 = 0x0A * 8
   */
  set_win_tiles(0, 0, 16, 10, frame_tiles);
  /*
   * Draw the door in the window
   *
   * Width  = 0x60 = 0x20 * 12
   * Height = 0x30 = 0x20 * 6
   */
  set_win_tiles(2, 2, 12, 6, door1_tiles);
  wposx.b.h = MAXWNDPOSX;
  wposx.b.l = 0;
  WX_REG = MAXWNDPOSX;
  wposy.b.h = MAXWNDPOSY;
  wposy.b.l = 0;
  WY_REG = MAXWNDPOSY;
  wspx.w = 0xFF80UL;
  wspy.w = 0xFFC0UL;

Initialize the sprite, call our functions to tile and place the sprite.
Code:
  /* Initialize the sprite */
  set_sprite_data(0x00, 0x1C, earth_data);
  set_sprite_prop(0, 0x00);
  set_sprite_prop(1, 0x00);
  sframe = 0;
  sposx.w  = 0x1000U;
  sposy.w  = 0x1000U;
  sspx.w  = 0x0040UL;
  sspy.w  = 0x0040UL;
  tile_sprite();
  place_sprite();

And we're back and ready to start rolling. We want to turn the display back on (register bit flip), enable our interrupts so everythign is firing again, and jump on into our infinite while loop.

Code:
  DISPLAY_ON;
  enable_interrupts();

  while(1) {

Now, the contents of this while loop should look fairly familiar. We have a fade(), door(), scroll(), and animate_sprite() right off the bat. We also see a wait_vbl_done() which is essentially a freebie timer we can use from the hardware of the Gameboy. This will only trigger when the display is ready to update, and by delaying 4 times, we slow down the entire program. You could very easily get rid of the for portion of this, and the program would be 4 times faster Smiley
Code:
    /* Skip four VBLs (slow down animation) */
    for(i = 0; i < 4; i++)
      wait_vbl_done();
    time++;
    fade();
    door();
    scroll();
    animate_sprite();

Next, we notice a joypad() call, which will set the binary bits inside of i based on joystick input. We can simply AND this with various defines such as joystick A, joystick B, etc. to get the desired results. In this example, we have many options including moving the window layer, settings the color to start a fade, or changing the door state.
Code:
    i = joypad();
    if(i & J_B) {
      if(i & J_UP)
bspy.w -= 0x0010UL;
      if(i & J_DOWN)
bspy.w += 0x0010UL;
      if(i & J_LEFT)
bspx.w -= 0x0010UL;
      if(i & J_RIGHT)
bspx.w += 0x0010UL;
    } else if(i & J_A) {
      if(i & J_UP)
wspy.w -= 0x0010UL;
      if(i & J_DOWN)
wspy.w += 0x0010UL;
      if(i & J_LEFT)
wspx.w -= 0x0010UL;
      if(i & J_RIGHT)
wspx.w += 0x0010UL;
    } else {
      if(i & J_SELECT)
color = STARTFADE;
      if(i & J_START)
if(doorstate == CLOSED) {
 doorstate = OPENING;
 doorpos = 0;
} else if(doorstate == OPENED) {
 doorstate = CLOSING;
 doorpos = NBDFRAMES;
}
      if(i & J_UP)
sspy.w -= 0x0010UL;
      if(i & J_DOWN)
sspy.w += 0x0010UL;
      if(i & J_LEFT)
sspx.w -= 0x0010UL;
      if(i & J_RIGHT)
sspx.w += 0x0010UL;
    }
  }
}


And that about does it for galaxy.c. It is really a simple program once you get the hang of it, and you can see it introduces all layers of drawing we have available on the gameboy. I would HIGHLY suggest compiling this yourself, popping it into BGB, and taking a look at the VRAM viewer. By experimenting with this, and trying to come up with other ways to mess with the sprites, you should have a very good understanding of everything that goes into making a sprite and manipulating it, along with manipulating the background.

Let me know if you need anything clarified or corrected. I'm hoping to maybe do a tools tutorial, then break away and get into some C vs. ASM comparisons of why we need to write the rest of our programs in ASM.
« Last Edit: February 16, 2010, 02:52:52 PM by Cthulhu32 » Logged

skyy
Level 2
**


[ SkyWhy ]


View Profile
« Reply #21 on: February 18, 2010, 06:35:24 AM »

Cthulhu32:
Thank you. I salute you and recognize your work as a work of a  Wizard.


Will try this out later. I installed Linux, for no obvious reason, to do my GB dev from that side. Just to fuck myself over really badly.  Big Laff

Logged

Cthulhu32
Level 6
*


Brawp


View Profile WWW
« Reply #22 on: February 18, 2010, 08:43:21 AM »

GBDK is fully open source, so he has linux builds and steps to compile in a Unix environment. But yeah, sometimes Linux can be a pain if you're not used to it. Let me know if you need any help setting up bash scripts or anything Smiley

Haha, if only I had a big white beard I'd totally be like "ITS MAGIC".
Logged

skyy
Level 2
**


[ SkyWhy ]


View Profile
« Reply #23 on: February 21, 2010, 11:09:10 AM »

Anyone actually managed to hack together a IDE to work with the GBDK? Currently doing my work with GBDK through Programmers notepad and with the help of the batch file. I cannot seem to be able to hook up the Makefile to Programmers notepad. If I could get that done I could use sexy, one click F5 to compile instead of running the batch file every time.
Logged

ITS_Mike
Level 3
***


Programmer


View Profile WWW
« Reply #24 on: February 22, 2010, 07:07:48 AM »

A heads up to anyone trying to get tiles to work...  The colors don't work the way you would expect.

The color code is 2-bits, allowing for 4 total colors.  Tiles are 8x8 pixels.  Each "line" of a tile takes up 2 bytes of memory (2bits * 8pixels = 16bits = 2bytes).  However, the color code for each pixel is not contained in sequential bits in a byte.  In other words, the color code for the first pixel in the line is not contained in bits 0-1 of the first byte, the second pixel's color code is not contained in bits 2-3 of the first byte, etc.  Rather, the color code for the first pixel on the line is taken from a bit from each byte.  An example is probably required.

00110000 01010000

These are the two bytes for a line in a tile (it doesn't matter which line).  The color code for the first pixel is the first bit of the first byte and the first bit of the second byte.  So the color code is 00 (lightest color).  The second pixel's color code is retrieved in a similar fashion, except now it is the second bit from each byte.  So the second pixel's color code is 01 (slightly darker).  The third pixel's color code is 10 (even darker).  The fourth pixel's color code is 11 (darkest color).  The rest of the pixels have a color code of 00.

A way of arranging the bytes so that you can easily tell the color code for each pixel is to align the second byte below the first byte, like this:
00110000
01010000

Hopefully this helps Coffee

I have also setup an IRC channel for GameBoy development, and with Cthulhu32's permission it is the official IRC channel for this club.
Server: irc.ifthensoftware.net:6667
Channel: #gbdev

You can access it easily in your web browser by visiting this address (powered by Mibbit): http://widget.mibbit.com/?settings=87d7439bc669ce0c52405e8c5f40deab&server=irc.ifthensoftware.net&channel=%23gbdev
Logged

skyy
Level 2
**


[ SkyWhy ]


View Profile
« Reply #25 on: February 22, 2010, 07:09:07 AM »

I seriously need to get my linux box up and running again so I can screen irssi and hang on the channels I like all the time... huff.. that's more of future stuff again. Not going to bother now since a bit busy.  Ninja
Logged

Shiny
Level 0
***


View Profile
« Reply #26 on: February 22, 2010, 07:34:23 AM »

There's also a #gbdev channel on EFNet that's been around for years.
Last I went there, there were still a few GB(C) wizards floating about.

Also, the GBDK compiler is somewhat buggy from what I remember. Diving into assembly might be a good idea, if you're willing (and insane enough).

(Knowledge from back when I was into GB dev, a year or so ago.)
Logged
Cthulhu32
Level 6
*


Brawp


View Profile WWW
« Reply #27 on: February 22, 2010, 08:19:05 AM »

There's also a #gbdev channel on EFNet that's been around for years.
Last I went there, there were still a few GB(C) wizards floating about.

Also, the GBDK compiler is somewhat buggy from what I remember. Diving into assembly might be a good idea, if you're willing (and insane enough).

(Knowledge from back when I was into GB dev, a year or so ago.)

Yeah the guys on #gbdev are very helpful, sometimes they are a little grumpy like most homebrew irc chats Smiley The first thing they will all say is use ASM, don't use C. When I was working on my platformer and it was outputting random crap, I found out it was because I was missing my chance to write to the VRAM, and the hardware was having errors. There used to be a really good forum on the gbdev website, and there is a gbdev wiki, but sometimes you just have to bug people who have done all this before.
Logged

Cthulhu32
Level 6
*


Brawp


View Profile WWW
« Reply #28 on: February 24, 2010, 08:19:37 AM »

Alright, I'm not sure if I'll have to time to do a Tutorial 3 or not, cause introducing Assembly is pretty difficult. But I think after talking to Invisible Man this thread will remain active after the month is over, so when February is over this will focus just purely on Gameboy Dev. Next time I'll do Nintendo DS so we can do some C++ and get a little more familiar with a system thats much more new-to-homebrew friendly.

I've also attached a Tetris game without scoring I did in C. You can see how slow the rotations are, this is actually not how you'd want to do Tetris. But it gives you an overview of how to move OAM sprites to the Background, and how to use the timer.

Tetris (written in C) Gameboy Full Source - http://cthulhu32.kraln.com/misc/hcc/gb/Tetris.GB.1-15.zip

Tetris (written in C) Gameboy Rom - http://cthulhu32.kraln.com/misc/hcc/gb/tetris.1-15.gb

Next month, either Nintendo DS Homebrew or Nintendo Wii. I'll put up a poll right now.
Logged

Shiny
Level 0
***


View Profile
« Reply #29 on: February 24, 2010, 11:27:59 AM »

Regarding OAM and tile map access, it's common practice to just create a 'shadow' of them in RAM, and then copy it all over during every VBlank. This way, you can modify it whenever you see fit, and without worry of access timings. There are also a few hardware DMA commands to quicken this process, but that involves setting up some code in HRAM, which may require some assembly.

Also, the Pan Doc, I've found, is very useful if you need to know anything hardware related.
Logged
Cthulhu32
Level 6
*


Brawp


View Profile WWW
« Reply #30 on: February 25, 2010, 12:06:45 PM »

Alright, here's the quick Platformer in ASM that I was working on a few weeks ago. It still has an off-by-one in the loop, so you'll see the tiles copy down, but you cannot do this code in C without doing what Shiny said and shadowing the RAM. Otherwise you run out of time in the V-Blank and it winds up corrupting your map data.

Source: http://cthulhu32.kraln.com/misc/hcc/gb/PlatformerASM.2-25.zip
Rom: http://cthulhu32.kraln.com/misc/hcc/gb/Platformer.2-25.gb
« Last Edit: February 25, 2010, 01:42:43 PM by Cthulhu32 » Logged

Shiny
Level 0
***


View Profile
« Reply #31 on: March 01, 2010, 11:52:32 AM »

I just wrote this multi-axis (both x and y) scrolling thing. Here it has a 64 x 64 map, but it can go up to 256 x 256 as well.

https://dl.dropbox.com/u/4443026/scroller.gb

It's Game Boy Color only, because it's doing nearly everything in VBlank, and it probably isn't well optimized. Tongue
(On non-color, it runs out of VBlank time scrolling diagonally with about seven tiles left to update.)
Logged
Cthulhu32
Level 6
*


Brawp


View Profile WWW
« Reply #32 on: March 02, 2010, 12:58:51 PM »

Thats pretty slick Shiny, I am guessing you did this on the Gameboy Color to take advantage of the CPU speed increase so you don't conflict on your vblank right? My platformer demo was 100% Gameboy in ASM, but I had an off by one Sad

Also, I will be posting the Meeting 2 shortly, but in no means does it mean meeting 1 is over!! Continue to post Gameboy builds, tech docs, source code (shiny, source it up!), ideas, tricks and tools, whatever relating to Gameboy homebrew in this thread.

Coming very soon: Meeting 2.. Nintendo DS!
Logged

Shiny
Level 0
***


View Profile
« Reply #33 on: March 02, 2010, 05:58:21 PM »

You're right, it was for the extra CPU speed.

Here's version two:
... Now optimized to run on non-colors. Cool

https://dl.dropbox.com/u/4443026/scroller2.zip

Source and .gb are included.

Some notes, though: *
  • The way I format assembly is kind of odd, I guess.
  • scrollPixel is kinda broken unless the coordinates are evenly divisible by eight.
  • Only 64 x 64 maps can be displayed without adjusting a few bits of code, and even then, only ones with a power of 2 width can be used (making due without multiply, ugh).
  • It uses almost all of VBlank and ~13% (max) of the CPU when scrolling diagonally, though this could be alleviated in a few different ways.
  • The ;{ and ;} comment things are code folding hints.

* Probably only relevant if you wanted to use it for something.


Also, Cthulhu, your .gb download link is down. ):
« Last Edit: March 02, 2010, 09:18:52 PM by Shiny » Logged
Shiny
Level 0
***


View Profile
« Reply #34 on: March 04, 2010, 03:37:19 PM »

If anyone is still interested in this, and wants to try out assembly, then I suggest going here first:
http://duo.gbadev.org/asmschool.html

It's where I first began learning GBZ80 assembly, from never having used assembly before. It's somewhat well written and... in English! By that I mean it's technical, but without being overwhelming. It's a great starting point.

Though, be prepared for some over-enthusiastic bad programmer jokes.
Try not to learn that part. *shudder*
Logged
ITS_Mike
Level 3
***


Programmer


View Profile WWW
« Reply #35 on: March 04, 2010, 03:44:20 PM »

Thanks!  I tried to find a good tutorial on GB assembly, but I couldn't.  That link should help greatly Smiley
Logged

Shiny
Level 0
***


View Profile
« Reply #36 on: March 08, 2010, 07:14:00 AM »

I'm still working on this, actually.

Lots of improvements, too:
  • Can now handle scrolling off the map, with a customizable border tile.
  • Set a custom camera range, or have none at all.
  • Support for huuuuge non-power of two maps (thousands of tiles tall / wide, if you'd like).
  • Somewhat more modularized.
  • Dynamic interrupt vector system so you can easily add and remove calls from them at run-time (uses self modifying code for minimal performance hit, which probably isn't necessary...).
  • Support for tile data in other ROM banks, using a bank switching stack.
  • Most things aren't in VBlank anymore, so plenty of that is free for animated tiles and whatnot.

Could be useful to someone, I guess.

https://dl.dropbox.com/u/4443026/scroller3.zip (src+bin)

See main.z80 if you're interested in seeing how to use it.
Logged
Shiny
Level 0
***


View Profile
« Reply #37 on: March 14, 2010, 11:58:54 AM »

I've instated that this thread will not fall to ruin ... for the time being. Gentleman

I've just finished adding a meta-sprite system. This means you can group individual sprites together to form larger sprites. So instead of cumbersomely moving each and every one yourself, you can instead do so as a whole. It also supports animations and sprite mirroring (horizontal and vertical).

Also new is sub-pixel camera movement, so it scrolls verrry smoothly.

https://dl.dropbox.com/u/4443026/scroller4.zip (source and .gb included)

Controls:
  • D-pad: Move.
  • A: Place (cheaply animated) 'power up' things.
  • B: Hold to pan the camera freely.

It's worth noting that working with meta-sprites is very CPU intensive, so it will slow down if there are too many (in the range of 40+ or so). There is also a default cap of 50 meta-sprites, but that limit can easily be modified.

(I'm sure it could use some more optimization as well.)
« Last Edit: March 14, 2010, 12:19:43 PM by Shiny » Logged
Pages: 1 [2]
Print
Jump to:  

Theme orange-lt created by panic