About meta-meta sprite, my current idea

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
About meta-meta sprite, my current idea
by on (#50509)
I read in other threads how people make their sprites. It seems a lot of people goes with an approach like this:

Code:
mySprite:   
   .byte $03                           ; Number of hardware tile
   .byte $0F, $2A, %01000000, $00
   .byte $0F, $29, %01000000, $08
   .byte $0F, $29, %01000000, $10


This approach is quite simple since it's exactly the same structure as the OAM. My only issue with it was on how to flip the sprite. From what I saw and I could be wrong, you would need your sprite to be a specific ratio, for example 3x3, to be able to flip them. If not, doesn't work well.

In the case of my mega man sprite, it didn't cut it. Some sprites like the face overlay cannot be flipped that way since the is only one sprite and some parts of mega man repeats often so if you just copy/paste.. That will duplicate data for nothing.

So I came up with a simple idea of meta-meta sprite. What it means is one animation frame can be composed of more than one meta-sprite of any shape, it doesn't matter much. But I still had the problem on "how" to flip that darn things.

At first I came out with the idea of duplicating the complete data. Not very good and take too much space. Then, after talking with Tokumaru, one idea came to me: why not just add an extra byte to my tile definition? Instead of 4 bytes, let's put 5 of them. The last byte will be the flipped X location. This mean when you want to flip the X location, you just need to EOR the attribute byte for horizontal position and load the approriate X location only. There is only one if condition to check the direction of the sprite, that's it.

The data looks like this:
Code:
megamanStandStillAnim:   ; tentative structure
   .byte $02             ; anim frame count
   .word megamanStillFrame1   
   .word megamanStillFrame2 

megamanStillFrame1:     ; Meta-meta sprite
   .byte $60            ; How many frame (not used yet)
   .byte $03            ; how many meta-sprite to load
   .word megamanStillMSprite      ; Meta sprite
   .word megamanStillLegMSprite   
   .word megamanStillEyeOpenMSprite

megamanStillFrame2:      ; Meta-meta sprite
   .byte $20             ; How many frame
   .byte $03             ; how many meta-sprite to load
   .word megamanStillMSprite      ; Meta sprite
   .word megamanStillLegMSprite   
   .word megamanStillEyeClosedMSprite

       ; Hero facing right, stand still, leg not included   
megamanStillMSprite:
   .byte $07              ; Number of attributes required for this meta-sprite
   .byte $00, $0B, %01000000, $08 , $18    ; Head top 1
   .byte $00, $0A, %01000000, $10 , $10    ; Head top 2
   .byte $00, $09, %01000000, $18 , $08    ; Head top 3
   .byte $08, $1B, %01000000, $08 , $18    ; face/torso 1
   .byte $08, $1A, %01000000, $10 , $10    ; face/torso 2
   .byte $08, $19, %01000000, $18 , $08    ; face/torso 3
   .byte $0F, $2B, %01000000, $08 , $18    ; Torso/feet 1

   ; Not moving leg for stand still
megamanStillLegMSprite:   
   .byte $02                           ; Number of attributes required for this meta-sprite
   .byte $0F, $2A, %01000000, $10 , $10     ; Torso/feet 2
   .byte $0F, $29, %01000000, $18 , $08     ; Torso/feet 3   

   ; Stand still face overlay: eyes open
megamanStillEyeOpenMSprite:
   .byte $01
   .byte $06, $00, %01000001, $11 , $0F     ; face overlay: eye opens

   ; Stand still face overlay: eye closed
megamanStillEyeClosedMSprite:
   .byte $01
   .byte $06, $20, %01000001, $11 , $0F     ; face overlay: eye closed


So the bytes are:
- byte 0 is the Y location
- Byte 1 is the hardware tile number
- Byte 2 is the attribute byte
- Byte 3 is the X location in it's original location
- Byte 4 is the X location in it's inverted location

Anybody uses a system like this? For now, it seems to be working well and the cost in data is not big. If I would have duplicated the data, it would have cost maybe 360 bytes for all frames (not shown here). By adding 1 extra byte per line, it cost 90 bytes. That's a 75% save in size.

Any other ideas on the subject will be appreciated.

by on (#50510)
To flip a sprite horizontally, exclusive-or attribute 3 with $FF and attribute 2 with $40.

by on (#50514)
tepples wrote:
To flip a sprite horizontally, exclusive-or attribute 3 with $FF

I don't think it can be that simple, because these coordinates are signed. The problem is that we need more than 8 bits in order to know if the resulting coordinate is still inside the screen, but we don't have a 9th bit stored as part of the sprite's attributes, we have to copy it from the 8th.

For a while I did all of this, generating a high byte for each relative coordinate by cloning bit 7 before adding them to the object's coordinates, but it was pretty slow.

Now I use number $80 as the center, $00 is 128 pixels to the left and $ff is 127 pixels to the right (same happens vertically) so that the relative coordinate is always positive. since it's always positive, the high byte will always be equal to the flipping mask ($00 when not flipped, $ff when flipped), so I don't have to calculate it.

Of course, before looping through the sprites, I have to subtract $80 from the center coordinate so that an $80 in a sprite definition has in fact the effect that $00 would.

To tell the routine I want to flip sprites I give it a mask in the accumulator, with the flipping bits at the correct position so that they can be EOR'ed to each sprite's attributes quickly.

Here is the basic structure of my sprite routine (I omitted checks for availability of sprite slots and such, as that's not relevant):

Code:
Subtract camera's coordinates from object's coordinates to find central point;

If flipped horizontally
    Subtract $86 from the X coordinate of central point;
    Set horizontal flipping mask to $ff;
Else
    Subtract $80 from the X coordinate of central point;
    Set horizontal flipping mask to $00;
End if;

If flipped vertically
    Subtract $8f from the Y coordinate of central point;
    Set vertical flipping mask to $ff;
Else
    Subtract $81 from the Y coordinate of central point;
    Set vertical flipping mask to $00;
end if;

Repeat for each sprite
    Add the central X coordinate to (relative X coordinate EOR horizontal mask), using mask as the high byte;
    If the high byte of the result is not 0, skip this sprite;

    Add the central Y coordinate to (relative Y coordinate EOR vertical mask), using mask as high byte;
    If the high byte of the result is not 0, skip this sprite;

    Copy the pattern index;

    Copy and modify (with EOR command) the attributes of the sprite;
End repeat;


The value $86 is subtracted from the vertical coordinate to compensate for the width of the sprite, which is 8 pixels, and the fact that we'll be using 1's complement when inverting the coordinates. The value $81 is subtracted from the Y coordinate to compensate for the vertical delay of 1 pixel, and $8f is used to compensate for the height of 16 pixels and the use of 1's complement.

I admit I haven't tested it very much, because I just replaced my old routine with this, and my sprites aren't exactly complex yet (mostly boxes). But it apparently works well, and I don't spend nearly as much time on each sprite as I used to, although it needs a little more time to initially find the correct central point.

by on (#50532)
@Tepples:

Basically, you have to put your sprite in the middle of the screen for the origin so you can flip it with EOR #$FF. Is it what you mean?

The way I'm working now is that my origin is at (0,0). This is maybe an old habit from dos and reading old graphic programing book. My current logic for flipping the sprite is like this:

Code:
   if not opposite direction
      Use attribute byte "as is"
      Load normal X attribute
      Add sprite location to it
      Skip next byte
   else
      EOR attribute with bit mask #%01000000
      Skip next byte
      Load inverted X attribute
      Add sprite location to it
    end if


This is over simplified since the coding is not finished but basically the only logic is to know when to use one X attribute over the other. It seems quite easy to use. I don't know yet the pitfalls about using it a (0,0) origin system and adding that extra byte but I will know soon I guess.

by on (#50533)
The instruction EOR #$FF always does A = -A - 1. So if X = 0, then EOR #$FF produces -1. If X = 11, then EOR #$FF produces -12.

by on (#50536)
Quote:

This approach is quite simple since it's exactly the same structure as the OAM. My only issue with it was on how to flip the sprite. From what I saw and I could be wrong, you would need your sprite to be a specific ratio, for example 3x3, to be able to flip them. If not, doesn't work well.

Sorry, this is wrong, I use exactly the format you posted in my game and I can flip size of any size horizontally.
If not flipped :
X Pos = Global X Pos + Sprite X Pos
If flipped :
X Pos = Golbal X Pos - Sprite X Pos -7

The -7 was needed for me so that a sprite at position 0 will be exactly flipped and won't move, so it'll be the center of the sprite. If it bothers you you'll have to live with position 4 being the center, which I consider annoying.

However it's true this system is limited : I'm currently desining a big boss, and because a lot of it is the same for many frames, it would end up wasting a ridiculous amount of ROM. So I split it in two object and one just follow the other so I don't have to repeat the same data over and over, only pointers for a half of the boss would be identical. This give me a second advantage when it comes to priorities, but it's only applicable to my game engine.

So yeah 1 enemy = 1 sprite is limited, which may or may not be acceptable depending on your game's complexity.

by on (#50538)
The 1 sprite = 1 thing was too limited for mega man from my point of view. For now the meta-meta sprite concept is working fine and the extra byte seems not to be a big impact yet. If it does become one at a later stage, I will see how to overcome that issue.

I still want to see if there is other ways of doing sprite flipping. The more I see ways of doing it, the more I can learn new way to approach the issue.

by on (#50540)
Banshaku, I agree that it should be possible to flip sprites without the need for an extra coordinate. If you take some time alone with pencil and paper you should figure something out that works for you.

My scheme works fine for me, the only weird thing I had to do was push the origin back so that the coordinates were always positive.

by on (#50541)
Well it depends how you deal with coordinates.
My approach is to always make it center, but Celius said he found it makes more sense to have the origin topleft of the sprite, and after all he is right : The origin of the screen is it's topleft, and the origin of each hardware sprite is it's topleft as well, and that on all systems I know. I still won't rewrite my whole game engine just for that.

And you should probably get rid of this extra byte. In the long run you'll waste a lot of space just because your were too lazy to think of a proper solution. Either way you handle your coordinates (center or topleft) you can come up with a better way of doing it.

by on (#50544)
I find the the extra byte for flipped X to be kind of clever, actually it seems pretty obvious but I didn't think of it, and haven't seen it mentioned before this thread. Most of the time I'm more concerned about speed than ROM space. Hell, 512kB is like $2, and I'd bet 1 or 2 MBytes could be had for that or cheaper if it was designed for cost (mostly by using more modern packaging instead of DIP).

I wouldn't want my program's "worst-case scenario" code timing to get any worse if I can avoid it. NES won't be getting any faster, but ROMs are always getting larger/cheaper.

Ultimately for the best speed I guess it'd come down to duplicating the animations, but that is a bit extreme (especially if you have a lot of animation frames).

by on (#50546)
Memblers wrote:
I find the the extra byte for flipped X to be kind of clever, actually it seems pretty obvious but I didn't think of it, and haven't seen it mentioned before this thread.

I agree it's pretty clever. But there are ways to do actual flipping that are not any slower, so I'd still prefer those.

Quote:
Most of the time I'm more concerned about speed than ROM space.

Me too. I often make small look-up tables for things that could be easily, but slowly, calculated with shifts and stuff. It even makes the logic look simpler in the end.

Quote:
Ultimately for the best speed I guess it'd come down to duplicating the animations, but that is a bit extreme (especially if you have a lot of animation frames).

Another option is to make 4 sprite-drawing loops, and pick one of them depending on the combination of flips. Each routine would add or subtract the coordinates and modify the flipping bits as necessary without having to check for flipping every time or even relying on too many preset variables.

by on (#50547)
tokumaru wrote:
Memblers wrote:
Ultimately for the best speed I guess it'd come down to duplicating the animations, but that is a bit extreme (especially if you have a lot of animation frames).

Another option is to make 4 sprite-drawing loops, and pick one of them depending on the combination of flips. Each routine would add or subtract the coordinates and modify the flipping bits as necessary without having to check for flipping every time or even relying on too many preset variables.


Ah, now that's the smart way of doing it! The same routine (basically), doing a fundamentally different thing, but taking roughly the same amount of time for each case, is the kind of routine that I can easily appreciate. :)

by on (#50549)
@Tepples:

Thanks for the comment. I still don't "get it" how I could use it with my current system with the origin at the top left but at the least now I know what it does.

tokumaru wrote:
My scheme works fine for me, the only weird thing I had to do was push the origin back so that the coordinates were always positive.


Since my origin is at the top left and I don't use signed values (I don't think it's the case in your scheme), I don't have any strange thing yet. I will know more once I start using it in the engine, which should be soon.

Bregalad wrote:
but Celius said he found it makes more sense to have the origin topleft of the sprite, and after all he is right : The origin of the screen is it's topleft, and the origin of each hardware sprite is it's topleft as well, and that on all systems I know. I still won't rewrite my whole game engine just for that.


This is how I approached my sprites from the beginning. In dos in mode 13h (320x200), the origin was at the top left too so this was just natural for me to try it that way.

Bregalad wrote:
And you should probably get rid of this extra byte. In the long run you'll waste a lot of space just because your were too lazy to think of a proper solution. Either way you handle your coordinates (center or topleft) you can come up with a better way of doing it.


Sorry to sound rude but I think it' doesn't sound like constructive criticism to me to call something "lazy" without understanding why it was done that way in the first place. It's like if I was saying that it was lazy for your boss sprite to duplicate the data that you said yourself will take a ridiculous amount of rom. I know why you may do that and it's because you had to make a compromise with the way your current system is implemented. You cannot just change your game to all support meta-meta sprite suddenly without some big re-factoring. There's nothing wrong with that since it only happens for boss scenario.

I always say that there is no good or wrong way to do something: it's all depend on the problem you're trying to solve in the first place. There is more than one way to achieve your goal based on your current constraints.

The reason of the 5th byte is simple. I was going to duplicate all the sprite at the beginning. Since space is not as much an issue anymore (where not in 1983 after all), I made a compromise of bigger size against complexity of the drawing routine. I was happy with it since the code was simpler. But after talking to Tokumaru, I realized that since it's a side scroller and only the attribute data and the X coordinate change, 50% of the data was duplicated.

Since I already started with the meta-meta sprite thing and Tokumaru was talking about EORing data, I realized that I could just easily EOR the attribute based on the direction and if I would have that extra X for the opposite direction, that would make my routine very simple: just take the right X based on the direction.

By adding the extra X to the data, I made a 75% save on data compared to duplicating the complete set. And the logic for flipping is so simple that the impact on performance is small. If I'm that worried about it, I can make 2 routine and save a pointer to the current routine to use for that meta-sprite based on the current direction. This way, the only time I check when to switch method is when the sprite change direction.

So for now I wouldn't call it lazy. I think it's a good compromise for making the logic a lot simpler. I just went with the KISS principle.

Memblers wrote:
I find the the extra byte for flipped X to be kind of clever, actually it seems pretty obvious but I didn't think of it, and haven't seen it mentioned before this thread.


When I realized about it I was like "doh!, why I didn't think about that sooner?". Like you said, I didn't saw it mentioned on the board, same thing about meta-meta sprite so I thought that I should talk about it to share my current experience with that approach. This way we can all talk about it and see the pro and cons. The pro is the simplicity, at the cost of an extra byte.

Memblers wrote:
I wouldn't want my program's "worst-case scenario" code timing to get any worse if I can avoid it. NES won't be getting any faster, but ROMs are always getting larger/cheaper.


Yep. We should stop worrying about saving space, unless the project as this requirement. For me, I stopped to worry about that already. It doesn't mean that I don't try to get my data smaller when possible thought.

Memblers wrote:
Ultimately for the best speed I guess it'd come down to duplicating the animations, but that is a bit extreme (especially if you have a lot of animation frames).


This is what I was going to do. The meta-meta sprite was a way to reduce data because of my current approach. Now with that extra byte, I have almost the same advantage as duplicating the complete data. Like mentioned above, if I'm worried about speed, just make 2 routines and save the pointer for the routine used for the current direction.

Edit:

I still think about saving space since that 5th byte thing saved 75% for my frames.

As for the pointer for the right direction.. Maybe it's complicate things for nothing. The right loop in the show animation frame method, like Tokumaru said, is maybe better and simpler in the first place.

by on (#50556)
Banshaku wrote:
@Tepples:

Thanks for the comment. I still don't "get it" how I could use it with my current system with the origin at the top left but at the least now I know what it does.

tokumaru wrote:
My scheme works fine for me, the only weird thing I had to do was push the origin back so that the coordinates were always positive.


Since my origin is at the top left and I don't use signed values (I don't think it's the case in your scheme)

For each frame of animation, store one more than the highest X coordinate in the sprite. So if you have a 24-pixel-wide sprite, with x=0, 8, and 16, you store 17.

For a not flipped sprite:
Code:
  lda #$00
  sta flip_attr2_xor
  sta flip_attr3_xor
  sta flip_attr3_add

For a flipped sprite:
Code:
  lda #$40
  sta flip_attr2_xor
  lda #$FF
  sta flip_attr3_xor
  lda spriteWidthMinusSeven
  sta flip_attr3_add

In your rendering loop, you EOR all attribute 2 with flip_attr2_xor, and you EOR all attribute 3 with flip_attr3_xor and then ADC flip_attr3_add. The EOR turns the values $00, $08, $10 into $FF, $F7, $EF, and then adding 17 turns the values into $10,$08,$00.

by on (#50563)
"A picture is worth a thousand words": if I see an example, I can understand it right away since I'm a visual guy. I will give it a try during lunch today and see how if goes with my current sprite data. It does require a little bit more processing compared to the extra byte but one adc is not the end of the world.

by on (#50565)
When you say that the origin is at the top left corner, do you mean that this origin is the point defined by the object's coordinates, meaning that the sprites are always to the right and below that point? If so, I understand why you have problems with the face. I'm pretty sure that with signed coordinates (or fake signed) you wouldn't have problems with that, because I don't have any problems with overlays.

My opinion is that a system where sprites can only be to the right and below the object's locations is very limited. What happens if, in the middle of development, I decide that the player can get a neck stretching power up? I'd need to draw him with a very long neck, but I wouldn't be able to because I only left space for the regular head when I defined all the other frames relative to the top left corner. Had I used signed coordinates for the sprites, the head could be drawn pretty high with negative coordinates, without the need to change any of the other frames.

by on (#50566)
Based on your character long neck analogy, I can see one of the issue I had.

Mega man sprite are in general 3 sprite wide but when you start to use the gun, it changes everything. When I saw that issue, I added 8 extra pixel at the beginning to adjust the center of the sprite. Another "behavior" was the character is always blocked almost 8 pixels on the left when it's a beginning of stage so I thought it was a good compromise to make the coding simpler and avoiding any signed (or faked) values. The extra X value just simplified a few things. But if later I have another similar issue, all sprites for that character would be affected, again.

I will do some tests with that concept and see how it goes. It make me cry just thinking at the fact that I may have to re-edit all the meta-sprite coordinates by hand, again ;) I want my editor!! :P

by on (#50571)
Sorry for the double post thingy.

I tested Tepples's idea and mixed it with Bregalad's one (-7). What was important was to find the center of the actual sprite. To find it, you take the width of the sprite and divide it by 2. Since the sprite I tested was 3 sprites wide, that was 24 / 2 = 12 ($0C). Now that you found this value, you must remove it from sprite when located at (0,0), top-left origin.

My static values in top-left where $00, $08, $10. I removed $0C from them and they became $F4, $FC and $04. When I face left, I EOR first the value with $FF then I must remove 7. The reason for removing 7 is because the origin of the tile (top left) is flipped at the same time too and become top right. Since the nes cannot print sprites from right to left, you have to remove 1 sprite wide to put back the origin in the top/left system.

It seems to work for now. I have one display bug left to figure out to make sure it does works 100%. Once it works, that's mean adjusting all my sprites coordinate again, cry! :P Maybe I should think right away about ajusting the Y coordinate, just in case...

That would mean, if it does work, by finding the center, EORing and removing 7, your sprite is fine. The extra processing seems acceptable.

Once I figure out the last display bug, I will reconfirm if what I said made sense.

edit:

Cannot figure out yet the display bug. There must be 1 thing wrong in my approach. I will have to figure it out later.

by on (#50572)
As others have said, I do my meta-sprites from the top-left corner. I find really no disadvantages to doing it this way. The only thing I could think of would be of you used the object's X/Y coordinates as the origin, and some sprites went outside of the down-and-to-the-right range of that point. However, for each object I usually calculate quickly the position of the reference point for each metasprite, and I guess that could be a disadvantage to some. But if you do any calculations to get the reference point, you might as well go with unsigned.

You might ask, "What if the sprite get's flipped?" Well if the sprite is flipped, I have another routine that will draw the sprite, but instead of adding relative coordinates to the reference point, it subtracts the relative coordinates from the reference point. Some problems arise if you're not careful, though. Say you have a metasprite 24 pixels wide, and you are drawing it with X pixel 200 as the reference point. This sprite will be drawn from pixel 200 to 224. However, if you flip it horizontally, it will be drawn from 200 to 176, which is not what you want to happen, most of the time. If an enemy turns to face the other direction, you want the overall "box" of its graphics to remain stationary. So if it's facing the right, it should be drawn from pixel 200 to 224, and if it's facing left, it should be drawn from pixel 224 to 200. So for each of my metasprite definitions, I define:

.db Number of hardware sprites
.db X Width of metasprite
.db Y Height of metasprite

;1st hardware sprite
.db Relative Y
.db Tile
.db Relative Attribute
.db Relative X

;2nd hardware sprite
.db Relative Y
.db Tile
.db Relative Attribute
.db Relative X
...

and so on. But you see, I define the width and height of the sprites, so when the sprite is flipped horizontally, I add the value of the sprite's width to the reference point before starting to draw it. In the example before, I said the sprite was 24 pixels wide, at pixel 200. So if it was flipped horizontally, I would add 24 to 200 to get 224, and start drawing backwards from there.

Another thing I didn't consider until a while ago was a Relative attribute. This way, you can have a flipped hardware sprite in the metasprite definition. It just gets EORd with the current flip status. I don't know if this is anything new though...

I also have different "types" of sprites. For my current NROM platformer, I have Complex, Complex Horizontally Flipped, Single, and Literal sprite types. Complex and Complex Horizontally Flipped are the types that are mostly used. That's basically any metasprite like an enemy or something big. No sprites are flipped vertically, as that saved time and space to sacrifice for my game. And then Single sprites are just single-tiled sprites. These are processed very quickly. Then the next type of sprite is a Literal, which does not have any relative coordinates of any kind. It is a table of coordinates and tiles that is copied directly to the OAM page.

Having routines for each type really saves time, as I do little calculation during it. I don't know if this helps, but I'm happy to share ideas!

by on (#50573)
Celius:

Your idea is interesting. It could work in my case for horizontal flipping. For vertical, that would make things harder for meta-meta sprites.

For my bug, it was simple. Since my coordinates starts negative in some case, I must check if carry flag is set after adding. If set, I must add 1 to position. The only issue with it is the extra processing. Maybe I should use that extra byte instead. hmmm.. I will think about something and test everyone solution to see which one is better for my case.

Edit:

I was not clear in the last comment. Extra byte is the one that Tepples recommended. I want to test it and see how it goes.

Edit2:

By the way, the thing I tested during lunch about finding the center and putting the value directly in your relative data, making some of the coordinates negatives: some people use this approach? Brelagad, is it the way you do your sprites?

Edit3:

I hate when I have plenty of ideas coming when I'm working.. Cannot concentrate. Here's the idea:

- Go with a top-left coordinate to make it simple to define data
- Find center of sprite (width / 2) and keep that value for each meta-meta sprite
- before processing coordinate, change coordinate system (top left) to center of sprite system (X - center of sprite)
- If must flip sprite EOR value and remove 7 to re-ajust origin of tile to top-left.

by on (#50574)
Quote:


By the way, the thing I tested during lunch about finding the center and putting the value directly in your relative data, making some of the coordinates negatives: some people use this approach? Brelagad, is it the way you do your sprites?

I guess so. I use my logical coordinate (the one used for collision detetion) "as-it" to the absolute cordinate of the meta sprite, without any adding. The metatile data itself is signed relative so that way I have a minumum bytes and minimum codes : It all is about putting the proper values in metasprites. Don't worry I have to adjust them a lot too and this is a little annoying, but all right. When I changed the size of my status bar I had to adjust all Y position for all sprites of the whole game, and it wasn't THAT bad (I only have several complete enemies right now).

However if you go the meta-metasprite way (which is a good idea overall, I already have meta-metatiles for my maps and it works great), you'd definitely want to do it like Celius, and have relative positions for each metasprite, which itself is only made of positive positions. To get them flipped, I use bit 15 of pointer : Since ROM is always located at $8000-$ffff, the 15th bit of your pointer is useless, so you can use it for a flip flag instead. Add -$8000 to the adress if you don't want to see your metasprite flipped.[/url]

by on (#50576)
I see. This mean I may have to edit often my data. An editor would be useful in that case. hmmm...

I just tested the concept of changing coordinate before applying the flipping. My sprite data use the origin at the top left so it's quite easy to edit. What I did is I find the center of the sprite (width / 2 for symetric sprite) and this become the object coordinate system. I save that as meta data on the meta-meta sprite, not meta-sprite since all meta-sprite have the same coordinate system.

Inside the display method, the code as 2 path: writing normally and flipping on the left. This is to avoid branching on every attribute row like I was doing in debug mode (and like Tokumaru said).

When writing normally, there is no EOR for attribute byte or the X byte. The only thing it does is adjust the coordinate system to the object one (X - center value) for each relative sprite X attribute byte then add the global X position for the sprite.

When branch is in flipping mode, before starting the process, it will remove 7 to the object coordinate system (center position) so we don't have to do it on every attribute row. You EOR the attribute byte, adjust sprite relative X to the object coordinate system (center position), EOR with $FF the result then then add the global X position for the sprite.

For now it's very simple and works with a normal symetric sprite. I must test it tomorrow with the one with the gun to see if it still work when the center is not the center of the meta-meta sprite but a different one.

Basically there is 2 function in one. This is just to abtract that concept from the caller.

Edit:

I had to test before I go to bed. I really wanted to know if it does work. I did the test with the gun frame and even if center is not in middle of sprite, it works fine: everything is flipped properly. It has less data, not much strange processing and I can edit my sprite in the top left way. That's useful.

I'm using for testing my software stack but I don't know if I will keep it or not for that case since it's quite an intensive method.

So the cost is 1 byte per meta-meta sprite and a little bit of processing. That's quite some saving compared to before.

by on (#50579)
Yeah great work. The only drawback is that you have to store the width, which I don't do with my system (well in fact I do store the width for collision detection, but it's not used for sprites, as I like having sprites larger than their corresponding collision window).

But you get noting for nothing : Store width or accept the "complications" of having your reference point in the center of the sprite instead of topleft. You took 1, I took 2, and there is nothing wrong with that.

by on (#50581)
Bregalad wrote:
Yeah great work. The only drawback is that you have to store the width, which I don't do with my system (well in fact I do store the width for collision detection, but it's not used for sprites, as I like having sprites larger than their corresponding collision window).


Actually, I don't store the width. What I store is the logical center of the sprite. The reason is, for example, the standing sprite of mega man is 24 pixel wide and the center is 12. When you have the gun, the sprite is 31/32 pixels wide but the center is still 12. So you cannot divide the width to get the logical center of the sprite, you have to know it.

I guess I may need to store elsewhere the width for hit boxes and things like that. For now, I'm still not at that point in my game engine.

by on (#50583)
Oh yes. So in summary there is 2 "optimal" ways to handle things :

1) You always deals with sprite's topleft coordinates for hitboxes and for graphics. Your metasprites are defined from the topleft, however, you have to store the distance between topleft and center in order to flip the sprite

2) You always deals with sprite's center coordnates, for hitboxes and for graphics, metatiles are defined from the center. No need to store width, but complicate collision logic and sprite definitions a little.

by on (#50584)
I see. For now I don't have a definitive choice yet. I'm testing plenty of ways of doing it. I seems to be heading for type #1. With the top left approach, Either I always translate the coordinate so the resulting X/Y is the center of the sprite or X/Y is always the top of the sprite and only when I flip I have extra processing.

I guess during the development phase, depending how complicated things becomes, I may have to test again those coordinate system and see how it goes. At the least for now I removed the complete duplicated sprite, which is good.

by on (#50586)
Some games make the "Hot spot" the bottom center of the sprite.

by on (#50587)
That's pretty much what my game is doing. The center on the horizontal plan saves the headache to flip, and the bottom on the vertical plan makes sense because it's where the sprite stands on the floor, no matter the perspective or the size of the sprite.

by on (#50593)
Dwedit wrote:
Some games make the "Hot spot" the bottom center of the sprite.

I chose to do this for Sonic, because this makes it easier to keep him attached to the ground/surface, even when he runs through loops, walls and ceilings.

by on (#50594)
Hot spot at the bottom, hmm.. I guess now that I improved my meta-meta sprite data format I should try to move my sprites around. That way, I will be able to see what works best for me for the hot spot.