CPCWiki forum

General Category => Programming => Topic started by: ssr86 on 21:34, 26 August 13

Title: Preserving background when drawing sprite
Post by: ssr86 on 21:34, 26 August 13
What are the best practices for preserving the background of the area where a sprite is to be drawn?

are these the only possibilities:
- keeping somewhere a copy of the entire background screen and "erasing" the sprite using that
- saving only the rectangle to be occupied by the sprite and erasing sprite using that
?

First needs too much memory and the second is quite cputime consuming (when more than one sprite occupies the same area then we copy the same area more than one time).

Could someone give me a source code example of the second? (can I find it somewhere in Edgegrinder's source?) :-[

Title: Re: Preserving background when drawing sprite
Post by: andycadley on 00:50, 27 August 13
I can think of more ways.  ;)

3) Re-draw any tiles (or whatever background sources you're using) that were covered by the sprite in the previous frame. This tends to be easier if your sprites don't move by huge amounts and the sprite routine is masking the background anyway, since in that case you're really just extending the sprite drawing process somewhat to include a slightly bigger "transparent" area around the sprite.

4) Re-draw the entire screen, including background tiles and sprites and just forget all about what used to be where. This is actually very common in games that scroll the screen, since they're going to end up redrawing most of the screen anyway. And it means you can have things like animated background tiles and such "for free", to some extent, since they won't really add much extra processing at that point.
Title: Re: Preserving background when drawing sprite
Post by: AMSDOS on 07:10, 27 August 13
Quote from: ssr86 on 21:34, 26 August 13
What are the best practices for preserving the background of the area where a sprite is to be drawn?

are these the only possibilities:
- keeping somewhere a copy of the entire background screen and "erasing" the sprite using that
- saving only the rectangle to be occupied by the sprite and erasing sprite using that
?

First needs too much memory and the second is quite cputime consuming (when more than one sprite occupies the same area then we copy the same area more than one time).

Could someone give me a source code example of the second? (can I find it somewhere in Edgegrinder's source?) :-[

There were some articles Rob Buckerly(?) once did in a couple of the last Wafer thin issues AA did in their Assembly line segment which releated to drawing Sprites with a background, though I could never get them to work, I even posted them in here, though I think it was in everyone's too hard basket.  :'(
Title: Re: Preserving background when drawing sprite
Post by: arnoldemu on 08:54, 27 August 13
Unofficial Amstrad WWW Resource (http://www.cpctech.org.uk)

Look under sources for examples of moving sprites using the methods you want.

I don't know if there is an example there for redrawing tiles.

OR look through the posts in the sticky topic in programming (assembler examples).
Title: Re: Preserving background when drawing sprite
Post by: ssr86 on 18:51, 27 August 13
@AMSDOS: found it http://www.cpcwiki.eu/imgs/f/f3/Amstrad_Action112_16.jpg (http://www.cpcwiki.eu/imgs/f/f3/Amstrad_Action112_16.jpg). Dou you use such table as in the article? With four states? What's the best practice when you have more than one sprite to draw? It seems that, when having to store only the part of the background the sprite occupies, such table is a must. Because you should first store the backgrounds of all the sprites and only after that draw the sprites...However what's the best practice?   

@arnoldemu: is it the optimal solution (the one in your code) for when you don't have the memory to store a copy of the entire background for direct access during the drawing routine (for getting the rectangle the sprite occupies)?

(offtopic: I hope you don't mind but...what happened with the source for scrolling tilemap screen? Will it ever see the light of day?)
Title: Re: Preserving background when drawing sprite
Post by: AMSDOS on 07:29, 28 August 13
Quote from: ssr86 on 18:51, 27 August 13
@AMSDOS: found it http://www.cpcwiki.eu/imgs/f/f3/Amstrad_Action112_16.jpg (http://www.cpcwiki.eu/imgs/f/f3/Amstrad_Action112_16.jpg). Dou you use such table as in the article? With four states? What's the best practice when you have more than one sprite to draw? It seems that, when having to store only the part of the background the sprite occupies, such table is a must. Because you should first store the backgrounds of all the sprites and only after that draw the sprites...However what's the best practice?

To be honest I cannot really remember, I'm usually only good at things I've done as I do them - I'd have to go over the article(s) again (there was a followup article to the link you posted in 113).

This was the original topic I created (http://www.cpcwiki.eu/forum/index.php?topic=7570.0).
Title: Re: Preserving background when drawing sprite
Post by: AugustoRuiz on 12:04, 29 August 13
It depends A LOT on your specific requirements.

What I'm doing right now is keeping a list of items to draw in the current frame. That list is sorted on the X axis, then on the Y axis.

For each item to draw I store the rectangle it occupies (X, Y, width, height), the rectangle it occupied in the previous frame (oldX, oldY, oldWidth, oldHeight), the union of both rectangles, and the sprite that must be drawn.

So, the drawing routine is:

While there are items to draw
    Initialize the background rectangle with the union rectangle of the current item.
    Check if the next item rectangle intersects with the current background rectangle
        If it does, the background rectangle is the union of the background rectangle and the item rectangle, keep checking next items
        If it doesn't, draw the background in the background buffer, with the specified dimensions.
    Now, we have our background in a buffer, we must draw the items in this buffer (the ones that did intersect!)
    Blit the buffer into the screen with a bunch of LDIs (I mean, as fast as you can)
End While

The trick here is that when you blit the buffer, it has the restored background as well as the sprites. So you don't erase first and then draw, you just draw, draw and draw!
The nice thing with this approach is that you never get flickering. You will have tearing at most. And you don't need to wait for vertical retrace ;)
Title: Re: Preserving background when drawing sprite
Post by: arnoldemu on 13:34, 29 August 13
Ok I understand, you're copying the background into a buffer, drawing the sprites into this buffer and then copying to the screen.

Barbarian does similar, it has a background image it never writes to, it uses this to fill the buffer.
It then draws the sprites to the buffer and finally puts them to the screen just as you do.

With this method do you have to clip sprites into the buffer region? And do you have cases where a sprite can exist in 2 areas so you must first draw it in the first, then draw it in the second?

Or do you not have much sprite overlap in your game?



Title: Re: Preserving background when drawing sprite
Post by: TFM on 20:53, 29 August 13
What a waste of RAM and time! I handle sprites the following way. This is one compete cycle

- Restore picture from buffer (previously stored background)
- Draw new sprite, this does at once:
  x read from V-RAM
  x save in buffer
  x read mask data and mask V-RAM data
  x read sprite data and mix with V-RAM dat
  x write to V-RAM

Remember you can use BC, DE and HL as address registers ;-)

Advantages:
- No copy of the V-RAM needed
- flicker free at 50 FPS
- Masks can be used
- Quick
Title: Re: Preserving background when drawing sprite
Post by: andycadley on 23:47, 29 August 13
That's a very "traditional" approach to sprites, it works but is much more prone to flicker because there is a period in which the background exists in VRAM without the sprite. So a certain amount of syncing is necessary to avoid sprites randomly flickering. Techniques which redraw the background as part of redrawing the sprite are immune to flicker (though not tearing) and can be much more efficient, which is important if you have a lot of sprites to move at a decent speed.

Overlapping sprites is a PITA, but often you can do away with it entirely by just designing the game so that it never happens (or at least only matters for the player sprite, which can be special cased).
Title: Re: Preserving background when drawing sprite
Post by: AugustoRuiz on 09:53, 30 August 13
Quote from: arnoldemu on 13:34, 29 August 13
Ok I understand, you're copying the background into a buffer, drawing the sprites into this buffer and then copying to the screen.

Barbarian does similar, it has a background image it never writes to, it uses this to fill the buffer.
It then draws the sprites to the buffer and finally puts them to the screen just as you do.

With this method do you have to clip sprites into the buffer region? And do you have cases where a sprite can exist in 2 areas so you must first draw it in the first, then draw it in the second?

Or do you not have much sprite overlap in your game?

You only need to clip sprites into the buffer region if there is a part of the sprite offscreen.

The thing is that the loop has an internal loop that checks how many sprites overlap, and calculates the area of the screen that will be written that contains all those sprites AND the background to restore.

Then, you use a buffer that big (and not any more), and write the background there (no masking needed, so it's pretty fast).
And then, you draw in that buffer all the sprites that you calculated earlier that overlap. With masking. So you never draw twice any sprite or part of it.

I'll give you two examples (in the examples, when I say sprite overlaps/doesn't overlap, I really mean the union of sprite old position and new position!!!):

1.- The sprite doesn't overlap with anyone: The BUFFER rectangle is the union of old rectangle and new rectangle.
  1.1.- Draw background at buffer (the background can be tiled or not, depends on your game!)
  1.2.- Draw masked sprite in buffer (beware, the sprite might not be at 0,0, and might not have the same size as the buffer!)
  1.3.- Blit furiously to the screen (no mask)

Another example:

1.- The sprite overlap with another sprite: The BUFFER rectangle is the union of each sprite union rectangles (so it takes into account both sprites old positions and new positions, and their sizes).
  1.1.- Draw background at buffer.
  1.2.- Draw first masked sprite in buffer (again, the sprite might not be at 0,0, and probably will not have the same size as the buffer!)
  1.3.- Draw the second masked sprite in buffer (same as before)
  1.4.- Blit furiously to the screen (no mask)

If there are three sprites overlapping... well, I think you get the idea.

So, when drawing it's very important to have sprites sorted, so that you can check for intersections in order. Actually, you might use the intersection check to update the BUFFER rectangle.

The loop in pseudocode would be:

DO
  firstItemToDraw = currentItem
  lastItemToDraw = currentItem
  BUFFER_RECT = currentItem.UnionRect
  WHILE (currentItem != null) AND (Rect_Intersect(currentItem.UnionRect, BUFFER_RECT))
    BUFFER_RECT = Rect_Union(currentItem.UnionRect, BUFFER_RECT)
    lastItemToDraw = currentItem
    currentItem = next item in drawable list
  END WHILE

  draw background to buffer
  itemToDraw = firstItemToDraw
  DO
    draw itemToDraw in buffer
    itemToDraw = next item in drawable list
  WHILE itemToDraw <= lastItemToDraw
WHILE(currentItem != null)

...or something like that ;)
Title: Re: Preserving background when drawing sprite
Post by: AugustoRuiz on 10:02, 30 August 13
Quote from: TFM on 20:53, 29 August 13
What a waste of RAM and time! I handle sprites the following way. This is one compete cycle

- Restore picture from buffer (previously stored background)

...

Advantages:
- No copy of the V-RAM needed

...


So... how did you store the background in that buffer?
Title: Re: Preserving background when drawing sprite
Post by: andycadley on 10:21, 30 August 13
Quote from: AugustoRuiz on 10:02, 30 August 13
So... how did you store the background in that buffer?

Usually you store it whilst drawing (you're reading the bytes already for masking). It doesn't work for overlapping sprites unless you restore all the backgrounds before drawing, in reverse order, though. Which is why it tends towards being a flickery approach.
Title: Re: Preserving background when drawing sprite
Post by: arnoldemu on 11:29, 30 August 13
@AugustoRuiz: I understand. Thank you.

For others:

Another method that I should mention which is worth considering because it is used for Spectrum 48K.

This is like a double buffer method, but you don't switch the screens with the hardware.

You have 2 screens. 1 which is always visible, and another which is hidden.
You always draw to the hidden screen, you do all your work here, but at the same time you record which regions you updated (or dirtied).

Then you draw these changed regions to the visible screen.

This works quite well. Really it doesn't have any advantage on CPC because there is almost no difference compared to a normal double buffered screen.

Differences:
- do not need to remember regions changed on each screen
- you don't need to worry about the screen being made "clean" because you only send dirty regions to the nice visible clean screen

One day I may make an example code for this.
Title: Re: Preserving background when drawing sprite
Post by: arnoldemu on 11:31, 30 August 13
@andycadley:

In AugustoRuiz's method you can't do this. You must store the background first, then draw all sprites to this to ensure correct masking. Then draw this to the display.

@others:

With AugustoRuiz's method has anyone tried sorting from front to back and marking in the buffer which areas have been drawn to avoid "overdraw"? It is probably too much work to do on the cpc for almost zero benefit.
Title: Re: Preserving background when drawing sprite
Post by: AugustoRuiz on 11:36, 30 August 13
The thing is that my method works for me because the same sprite can have huge changes in size in the same animation. I can have a 10x69 sprite, and the next frame in the animation is 16x48, for example. Using this method I'm drawing quite big sprites, without flickering, and as I don't have to wait for vertical retrace, quite fast.
Title: Re: Preserving background when drawing sprite
Post by: arnoldemu on 11:37, 30 August 13
Quote from: ssr86 on 18:51, 27 August 13
@arnoldemu: is it the optimal solution (the one in your code) for when you don't have the memory to store a copy of the entire background for direct access during the drawing routine (for getting the rectangle the sprite occupies)?
It depends on the game.

If you are making a game with software scrolled tilemap there is no point to storing the background because you will be redrawing the tiles anyway.

In this game, re-draw the scroll and put the sprites over the top.

If it's hardware scroll, you can just redraw the "dirty" tiles (the ones with sprites over them) for the old positions of the sprites - here you need to remember where the old positions were for that buffer last time. You can use the sprite coordinates to calculate the x,y origin and width/height in tiles and redraw that area. Then you can draw your sprites in their new positions.

Barbarian has a small hidden buffer. It copies it's background to the buffer, then it draws the sprite to the buffer, then it draws it to the screen.

A stored background buffer is good if you're background is not made of tiles, e.g. it is a picture, or there is nothing common between areas.
Title: Re: Preserving background when drawing sprite
Post by: Axelay on 15:42, 30 August 13
Quote from: ssr86 on 21:34, 26 August 13Could someone give me a source code example of the second? (can I find it somewhere in Edgegrinder's source?) :-[



Sorry, I've been away all week.  You can find the relevant code in Edgegrinder's source in the file EG_sprites10.asm
In this file look for the labels .SaveSpriteBG and .RestoreSpriteBG
EdgeGrinder is complicated by the horizontal hardware scrolling & double buffering, a while back I posted a similar, simpler example here (http://www.cpcwiki.eu/forum/index.php?topic=2027.msg22570#msg22570).

Title: Re: Preserving background when drawing sprite
Post by: AMSDOS on 10:05, 31 August 13
Quote from: arnoldemu on 11:29, 30 August 13
@AugustoRuiz: I understand. Thank you.

For others:

Another method that I should mention which is worth considering because it is used for Spectrum 48K.

This is like a double buffer method, but you don't switch the screens with the hardware.

You have 2 screens. 1 which is always visible, and another which is hidden.
You always draw to the hidden screen, you do all your work here, but at the same time you record which regions you updated (or dirtied).

Then you draw these changed regions to the visible screen.

This works quite well. Really it doesn't have any advantage on CPC because there is almost no difference compared to a normal double buffered screen.

Differences:
- do not need to remember regions changed on each screen
- you don't need to worry about the screen being made "clean" because you only send dirty regions to the nice visible clean screen

One day I may make an example code for this.

That's the approach they use for the old Design Design Tankbusters game isn't it for the Wire-Frame Graphics?
Title: Re: Preserving background when drawing sprite
Post by: ssr86 on 18:53, 28 December 13
Quote from: AugustoRuiz on 12:04, 29 August 13
So, the drawing routine is:

While there are items to draw
    Initialize the background rectangle with the union rectangle of the current item.
    Check if the next item rectangle intersects with the current background rectangle
        If it does, the background rectangle is the union of the background rectangle and the item rectangle, keep checking next items
        If it doesn't, draw the background in the background buffer, with the specified dimensions.
    Now, we have our background in a buffer, we must draw the items in this buffer (the ones that did intersect!)
    Blit the buffer into the screen with a bunch of LDIs (I mean, as fast as you can)
End While
Just read the interview (http://www.norecess.net/3/post/2013/12/elmar-krieger-interviewed.html) of NoRecess with Elmar Krieger and looks like it's the same (or very similar) method that he used for Prehistorik2 :D

From the interview:
Quote

       
  • Keep track which sprites are overlapping, collect them in 'sprite bundles'.
  • For each sprite, determine the rectangle that encloses the old and new sprite positions.
  • Draw the background graphics within this rectangle to an off-screen buffer.
  • Draw the parts of all sprites in the bundle that intersect with the rectangle into the off-screen buffer.
  • Copy the off-screen buffer to the screen.[/l]
Powered by SMFPacks Menu Editor Mod