Jump to content
IGNORED

Lynx Programming Part 2: Sprites


Ze_ro

Recommended Posts

Despite the fact that the subject of this topics sounds like a chapter from a manual or something, I'm actually stumped and looking for help from those knowledgable in this subject. I've been working for a while today on understanding on how sprites work. Unfortunately, the "documentation" to the Lynx libraries is very minimal, and doesn't have anything to say on SCB's, and only a small mention of DrawSprite().

 

Now, I've read through some of the relevant stuff in the Epyx documentation, but most of that is written assuming that you're working in 6502 assembly, which isn't the case here. So, to figure things out, I tore apart some of the example programs that I found with the compiler I got. Specifically, I found one that showed a Space Invaders type of demo (examples.c65/demos/sprite.c65) where 11 sprites moved back and forth, up and down the screen.

 

After tearing everything extra out of this program that I could possibly do and still have one sprite on screen (I'm trying to simplify things down to one sprite controlled by the joystick), I still had an array with two sprites in it... one was a chunk of blank space that kept getting drawn, and the other was the invader itself. I'm not sure why the blank sprite was there, but if I remove it, I get repeated sprites on the screen (Does the hardware not blank out sprites that have been moved? Or am I expected to redraw the background on which the sprite is then drawn?). I tried to tear the array into two seperate structures, which I figured would make things a bit easier to work with... however, then I noticed that the program was calling DrawSprite() and sending it the whole array, which I found confusing. I had assumed that DrawSprite() would just get passed the first sprite to be drawn, and then just follow scb.next links to find the rest of the sprites. How exactly do I use this function?

 

Another issue I've been having is with the Sprite Control Blocks... of the demos I've been looking through, I keep seeing them handled in different ways. The "Invaders" demo that I've been working with mostly does things like this:

 

struct _scb {

 char sprctl0;

 char sprctl1;

 char sprcoll;

 char *next;

 char *sprdata;

 int x,y;

 int hscale, vscale;

 int stretch;

 int tilt;

};



struct scb_bit4 {

 struct _scb scb4;

 char cpal4[8];

 char coll4deposit;

};

#define B4COLLOFFSET (27)



struct scb_bit3 {

 struct _scb scb3;

 char cpal3[4];

 char coll3deposit;

};

#define B3COLLOFFSET (23)



struct scb_bit2 {

 struct _scb scb2;

 char cpal2[2];

 char coll2deposit;

};

#define B2COLLOFFSET (21)



struct scb_bit1 {

 struct _scb scb1;

 char cpal1[1];

 char coll1deposit;

};

#define B1COLLOFFSET (20)

 

Which sort of makes sense... although a little complicated. Another demo that I was looking through (examples.c65/minimal.c) did things like this:

 

#asm

         xref _title

         

_titelSCB dc.b $c0,$10,$20

         dc.w 0,_title

         dc.w 0,0,$100,$100

         dc.b $01,$23,$45,$67,$89,$Ab,$cd,$ef

#endasm

 

This seemed MUCH harder to understand to me, but when I think about it now, it looks like it would work well with the SCB macros. Is this the way everyone does things? A struct seems much simpler to me, but I've read that CC65 handles them strangely.

 

And one final question... the invaders demo had two chunks of ASM that looked to me to be reading the timers ($FD0A to be exact). Is this to wait for Suzy (or any other hardware) to be done working with the sprite? Or is this just a delay loop?

 

Thanks in advance for any help with these issues....

 

--Zero

Link to comment
Share on other sites

The sprite engine doesn't automatically blank out sprites. The best method I've found to do it is to chain the sprites. The sprites are drawn in the order that they are linked and since the sprite engine is actually the one processing them directly (instead of having to move back and forth between it's processes and your code) it can draw them virtually flicker free. In order to move a sprite you can either have a blank sprite with the next sprite set to your main sprite. Or (my prefered method) is set the entire background as a sprite (even if it's just one color) and chain the remainder of my sprites from it. To chain sprites you just need to set the "next" variable of the _scb structure to the next sprite you want to draw (This would be the 0 before _label in the asm data segment). To draw all the sprites at one time you simply call drawsprite w/ the first sprite in the chain (in my case the background sprite). Remember that the sprite engine draws all sprites in order so your background or blanking sprites must be first otherwise the sprites you actually wish to draw will be overdrawn with them. By doing it this way you can get flicker free movement. If you were to draw your main sprite, and then use the DrawFBox (which creates a sprite by the way) or call DrawSprite w/ the background sprite, and then draw your main sprite in it's new location you would start to see a lot of flicker, especially as you add more processing steps (button checking, bounds checking, AI algorthims, etc...) between redraws. By doing the single call to DrawSprite it draws everything in one pop between refreshes. NOTE: If you have a lot of sprites even the sprite engine takes time to draw it. I've found the limit to be anywhere between 10-30 sprites (varies a lot) after which the sprite engine cannot draw all of them between refreshes and some slow down & flicker may occur. After that it's better to either combine your sprites into fewer sprites or use screen buffering.

 

As far as using either the structure or the ASM data segments I think it's really a matter of preference. I have read that the CC65 compiler does have some issues w/ structures but I believe it is when you go to use them in arrays (don't quote me on that). Personally I use ASM data segments but mine are broken down a little bit more for readablility:

_status_scb:

dc.b $c0 ; 4 bits/pixel

dc.b $10 ; packed , reload size

dc.b $20 ; non-collidable

dc.w _mouse_scb ; link

dc.w _status ; data

dc.w 0,0 ; x,y

dc.w $100,$100 ; size : x1

dc.b $01,$23,$45,$67 ; palette redir.

dc.b $89,$AB,$CD,$EF ; palette redir.

The above references a status bar sprite that I use which in turn chains a mouse pointer sprite. Note the next link is pointed to the mouse scb not the data itself, while the data pointer is actually a pointer to the sprite data.

 

To reference the various parts I use the defines included in lynx.h:

#define SCBCTL0(a) (*(uchar *)(a))

#define SCBCTL1(a) (*(uchar *)((a)+1))

#define SCBCOLL(a) (*(uchar *)((a)+2))

#define SCBNEXT(a) (*(uint *)((a)+3))

#define SCBDATA(a) (*(uint *)((a)+5))

#define SCBX(a) (*(int *)((a)+7))

#define SCBY(a) (*(int *)((a)+9))

#define SCBHS(a) (*(uint *)((a)+11))

#define SCBVS(a) (*(uint *)((a)+13))

 

so to set the x & y coords for a sprite I can use:

SCBX(_status_scb) = 10;

SCBY(_status_scb) = 25;

This gives you the same reference benefits that you receive w/ a structure.

 

One last point about chaining sprites is that not all your sprites have to be chained. You can chain 5 sprites and then "hand draw" 4 more sprites on top of them.

 

As far as the interrupts go I have not seen the invaders demo so I cannot tell how they are utilizing. I do use the vertical refresh interrupt for timing delays and also sometimes to start my drawing algoritms.

 

Hope this wasn't too much rambling. I'm sure there are many others on here that can give you better or more info.

 

Good luck & have fun.

 

----------------------------------

Someday I'll think of a good sig

 

[ 06-06-2002: Message edited by: Glitch ]

Link to comment
Share on other sites

Hi Guys,

 

It is convenient to think of Suzy as a sprite engine, but really all it does is decompress bitmaps and stuff them into display RAM. That's why the first sprite is almost always to clear the display buffer. I've done a few routines (such as the LGSS progress display) where I haven't bothered to clear the screen and I just repaint the parts that change.

 

As far as the number of sprites you can get away with each "frame", that depends on your frame rate. If you double buffer, there will be no flicker. Lynx Othello sends out a single "chain" of over 90 sprites every redraw. It doesn't bother redrawing unless something changes so the effective frame rate could be as low as .02 fps (lower frame rates mean Suzy is idle more and the battery life increases slightly - too bad Mikey sleep is broken).

 

Checking the 2 code samples you have... One person likes readable code at the expense of typing it all in and the other one knows what is going on and makes it short and sweet. I tend to use a combination of the two depending on what is going on. A simple blank screen sprite doesn't need a detailed data structure and explanation IMHO.

 

Harry

Link to comment
Share on other sites

Aha! I've finally gotten past the problems I was having... naturally, it wasn't anything to do with the sprite, but rather a poor use of pointers on my part (as usual, I blame a function for the error, when it turns out all I had to do was add an '&' to fix everything...)

 

Anyways, here's the fruits of my labor:

 

 

What this is, is a program that just moves a sprite (A space invader) around the screen. I also hacked in a few more controls to show off what the sprite engine can do:

 

sprite.jpg

 

  • A+Up/Down: Adjust Vertical Scaling

  • A+Left/Right: Adjust Horizontal Scaling

  • B+Up/Down: Adjust Stretching

  • B+Left/Right: Adjust Tilt

  • Option 1: Reset Sprite Attributes

  • Option 2: Reposition Sprite To Middle Of Screen

 

It's pretty interesting what you can do with tilt and stretch... now I see what Epyx was talking about when they mentioned it could be useful for polygons! There's absolutely no bounds checking whatsoever, so when you adjust anything below zero, you'll get weird results (press Option 1 to get a normal sprite again). I originally had the sprite wrap around the screen at the edges too, but it looked ugly when the sprite was scaled heavily, so I removed it. If you move the sprite really far off the screen, just hit Option 2 to bring it back.

 

I tried to make it simple to read, and I tore out all the ASM that was in there before (It works without it, so I guess it was just a delay loop), so if anyone here is looking to figure out sprites, this might help.

 

Since I managed to get the struct method working, I didn't bother to convert everything to the ASM method of defining sprites. I'm not sure wether this way is better or not, but it seems easy to me, so I'll stick with it Chances are, it wastes memory, and slows things down, but hey, this isn't exactly Battlewheels

 

By the way, thanks for your help guys I'll still need to look over some of the pallette stuff to figure out all the color allocation and such, but I'm not too worried about that just yet.

 

--Zero

Link to comment
Share on other sites

Looks nice, keep up the good work. BTW where did you find the source for the invader demo? I'd like to take a look at it. I've been downloading just about any Lynx source code I can find for samples and haven't seen this one.

 

As far as the structs wasting memory it actually doesn't take up any more space in the final compilation since it's just a data segment. The compiler & assembler use a little more memory to keep track of the symbol names but that doesn't effect the final output so using an ASM structure or a C structure shouldn't make any difference.

Link to comment
Share on other sites

The "invaders" demo is actually just "examples.c65/demos/sprite.c65" from the newcc65 distribution. I downloaded it and compiled from source, so it might not be in a binary distribution (?). What it does is show a Lode Runner picture in the upper-left corner, while space invaders move down the screen.

 

By the way... has anyone else had troubled with the optimizer (xopt)? Sometimes I get weird errors about 'decax' and 'incax' and such. I think I was able to edit the m65 file enough to get rid of them, but instead I usually just choose to forget the optimizations.

 

--Zero

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...