It is currently Mon Oct 15, 2018 7:18 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Wed Feb 21, 2018 8:52 am 
Offline

Joined: Sun Jun 30, 2013 7:59 am
Posts: 17
I'd just like to say thank you first for the replies to my last thread. I now understand how LOW and HIGH work, and also .dl and .dh too, and can now use them to make jump tables :) (sorry for being offtopic at the very start but didn't want to bump my previous thread)

When defining (2x2) metasprites, how do you offset them from your character x and y positions? Assuming the reference point for your player is the top left (I heard this isn't the best option but it's easier for me to comprehend than centre bottom for now) and the sprites are arranged around that point.

Do you store your metasprite data (if this is possible) as both negative and positive values? For example, the bottom-left sprite in the 2x2 arrangement would be the same x value, but of a negative y value, as it is lower than the playery value (top left corner). Right now I'm doing everything in a rather hardcoded way (for just one metapsprite) and loading the player y and x values before SBC or ADCing a fixed amount for each individual sprite in the metasprite (excepting the top left sprite) so they are arranged in a 2x2 grid.

Hope this makes sense, I don't have my (edited NN) code right now, but I'll upload it if needed. Sorry for the haphazard explanation, I am trying...

Thank you! :)


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 9:30 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2296
Location: DIGDUG
I've been using the NES screen tool's metasprite creator, and Shiru's neslib code to render it. I modified it slightly...

Code:
_oam_meta_spr: ;changed from neslib...
;a = low byte address to metasprite
;x = high byte..
;temp = x
;temp2 = y

   sta <pointer
   stx <pointer+1
   ldx sprid
   ldy #0

@1:

   lda (pointer),y      ;x offset
   cmp #$80
   beq @2
   iny
   clc
   adc <temp ;x position
   sta SPRITES+3,x
   lda (pointer),y      ;y offset
   iny
   clc
   adc <temp2 ;y position
   sta SPRITES+0,x
   lda (pointer),y      ;tile
   iny
   sta SPRITES+1,x
   lda (pointer),y      ;attribute
   iny
   sta SPRITES+2,x
   inx
   inx
   inx
   inx
   jmp @1

@2:

   stx sprid
   rts


Here's an example of 2 metaprite definitions...
4 bytes = relative x position, relative y position, tile #, attributes
terminated with 128 ($80)

Code:
Mario_Right:
.db 0, 0,$00,0
.db 8, 0,$01,0
.db 0, 8,$02,0
.db 8, 8,$03,0
.db 0,16,$04,0
.db 8,16,$05,0
.db 0,24,$06,0
.db 8,24,$07,0
.db 128

Mario_Left:
.db 8,0, $00,0|OAM_FLIP_H
.db 0,0, $01,0|OAM_FLIP_H
.db 8,8, $02,0|OAM_FLIP_H
.db 0,8, $03,0|OAM_FLIP_H
.db 8,16,$04,0|OAM_FLIP_H
.db 0,16,$05,0|OAM_FLIP_H
.db 8,24,$06,0|OAM_FLIP_H
.db 0,24,$07,0|OAM_FLIP_H
.db 128


EDIT-
so you would lda x position, sta temp, lda y position, sta temp2, lda #<Mario_Right, ldx #>Mario_Right. jsr _oam_meta_spr

sprid keeps track of the index into the sprite buffer

EDIT2 - changed slightly, haven't refined this apparently.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 9:44 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10892
Location: Rio de Janeiro - Brazil
I always use 4 bytes per sprite in a metasprite definition, with X and Y being signed values relative to an object's reference point, which is not fixed.

I did not use 2's complement for the coordinates though, I used something that simplified out-of-screen tests, but I can't remember what that was right now...


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 9:50 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20656
Location: NE Indiana, USA (NTSC)
Might it have been excess-128? The Curse of Possum Hollow uses that representation for X and Y coordinates of sprites within each metasprite.

  • (128, 128) means a sprite whose top left is at the hotspot
  • (132, 128) means a sprite whose top left is four pixels to the right of the hotspot
  • (120, 128) means a sprite whose top left is eight pixels to the left of the hotspot
  • (128, 112) means a sprite whose top left is sixteen pixels above the hotspot

Each metasprite consists of a set of horizontal strips, where each strip begins with X, Y, and attributes, followed by 1 to 8 tile numbers. The length is stored in the otherwise unused bits 4-2 of attributes, and the X position increases by 8 for each sprite in a strip.


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 10:16 am 
Offline
Formerly WheelInventor

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1782
Location: Gothenburg, Sweden
In graphics and animation, the point of origin is called the anchor point (confusingly, sometimes all handles on an object are called that too but i suspect this is a mispractice).

Sprite tables can be stored as relative to that anchor point, which means that by offsetting the whole table as such, you effectively move the anchorpoint.


Examples:

Code:
someMetasprite:
   .byte -8,-8,$ee,0
   .byte  0,-8,$ef,0
   .byte -8, 0,$fe,0
   .byte  0, 0,$ff,0


implies a centered anchorpoint (could work for a topdowner, but is a bit unintuitive for a platformer).
Code:
someOtherMetasprite:

   .byte -8,-16,$ee,0
   .byte  0,-16,$ef,0
   .byte -8,-8,$fe,0
   .byte  0,-8,$ff,0


implies a horizontally centered, but bottom-aligned anchorpoint. Pretty convenient for sprites that ought to turn around and check against ground often.

Code:
metasprite3:

   .byte   0,  0,$ee,0
   .byte   8,  0,$ef,0
   .byte   0,  8,$fe,0
   .byte   8,  8,$ff,0


implies a top- and left-aligned anchorpoint. Practical for static overlays. I guess it could be convenient in that it can actually be drawn in the top-left corner of the screen w/o wraparound as well, but the utility isn't that great imo.


When doing static overlays you want to place on an absolute position on the screen, it helps to count in powers of 8 or 16. Example:

Code:
;raw OAM data
.byte (8*8)-1,$01,$00,(8*6)


the parentheses are redundant but helps me decode visually what is going on: the sprite is on the 8th row, 6th column* the -1 is likely an adjustment for the render delay. This example is strictly on the grid, but you can add offsets after the parentheses as you need.

In absolute placement, you can't use 128 as a terminator, so you need to specify an absolute length of data to be deployed in OAM this way.

*(assuming 0*8) is read as the 0th row or column.


Last note... i don't agree with NESST's choice for ordering the sprite data. It had been simpler if it was written just like how OAM is structured. It could have represented this however it is most convenient in its interface, but forcing this shuffle on the output seems unnecessary.

_________________
http://www.frankengraphics.com - personal NES blog


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 10:59 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10892
Location: Rio de Janeiro - Brazil
tepples wrote:
Might it have been excess-128?

Yeah, I think this was it! I didn't even know it had a name... Still don't remember why that made things faster though, will have to check by sprite drawing code.


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 12:24 pm 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 469
Location: Central Illinois, USA
tokumaru wrote:
tepples wrote:
Might it have been excess-128?

Yeah, I think this was it! I didn't even know it had a name... Still don't remember why that made things faster though, will have to check by sprite drawing code.


If you dig it up, I'd be interested to learn more about it. I was just thinking last night about how to quickly handle offscreen checks for individual sprites of the metasprite, so if you have some trick you did, I'd love to hear it.

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 12:29 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2296
Location: DIGDUG
Quote:
Last note... i don't agree with NESST's choice for ordering the sprite data. It had been simpler if it was written just like how OAM is structured. It could have represented this however it is most convenient in its interface, but forcing this shuffle on the output seems unnecessary.


I agree. I would prefer keeping the NES hardware order for the OAM data.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 12:47 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1250
I ended up writing a program that pulled the data out of the .msp file in NESST. (Which if I remember correctly is totally the regular OAM order + 2 grid bytes at the beginning.)

For offscreen checks, I just ensured all offsets would be positive. I shift the entire metasprite's position to the max negative offset. (The program above exports that value as a constant like "ahpMETASPRITEXOFFSET = $0021")

So 0 is "the furthest left a sprite can be for this character" (-$21 for the Ahp), and everything else is to the right of that.

So to start:
Code:
lda OBJxlow,x
sec
sbc #ahpMETASPRITEXOFFSET
sta tempLo

lda OBJxhigh,x
sbc #0
sta tempHi


And then the offscreen check looks like this (for X)
Code:
   lda (reserved4),y;X Position
   clc
   adc tempLo
   sta OAM+3,x
   
   lda tempHi
   adc #$00
   bne spriteOffscreen

Assuming your object's anchor point and your animation's anchor point are the same, you don't even need the first step.

This may be the excess N thing, I dunno. I'm having trouble understanding the article. Will have to dedicate more time to understanding it.
Edit: I also have a max right constant exported per enemy. So if adding that number is offscreen, the entire metasprite is (for that dimension).

_________________
https://kasumi.itch.io/indivisible


Last edited by Kasumi on Wed Feb 21, 2018 12:53 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 12:51 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20656
Location: NE Indiana, USA (NTSC)
Yes, you are in fact using excess 33. But if you use the same excess value for all characters, your code might be simpler.


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 1:46 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10892
Location: Rio de Janeiro - Brazil
Kasumi wrote:
Code:
   lda tempHi
   adc #$00
   bne spriteOffscreen

Oh yeah, I did it so the high byte of the signed coordinates is always 0! With 2's compliment it can be either $00 or $ff, so you have to sign-extend the 8-bit value of every coordinate to get that high byte. By using the excess 128 format you can just use a constant 0 as the high byte of every offset, like in the above code.


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 1:56 pm 
Offline
Formerly WheelInventor

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1782
Location: Gothenburg, Sweden
So.. to melt that article a bit: excess-128 is a signed 8-bit word that begins with -128 at %000000, rather than 0? And effectively the N flag would actually represent a positive (starting at %100000 and onwards), rather than a negative when excess-128 is used on a system meant for two's complement.

I have a bit of trouble understanding how you even use excess-128 to represent something to begin with if that's not the native system. :oops:

_________________
http://www.frankengraphics.com - personal NES blog


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 2:23 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10892
Location: Rio de Janeiro - Brazil
FrankenGraphics wrote:
So.. to melt that article a bit: excess-128 is a signed 8-bit word that begins with -128 at %000000, rather than 0?

Yeah, $00 representss -128, $80 represents 0, $ff represents 127. The most important aspect of this is that the high byte is always 0, even for negative values, unlike in 2's complement.

Quote:
And effectively the N flag would actually represent a positive (starting at %100000 and onwards), rather than a negative when excess-128 is used on a system meant for two's complement.

Yeah, but at least the N flag is still usable, just with its meaning inverted. Not that we need it in this particular case, though.

Quote:
I have a bit of trouble understanding how you even use excess-128 to represent something to begin with if that's not the native system. :oops:

Different systems always need some sort of conversion, no way around that I guess... To make the excess-128 offsets work, the reference coordinate for sprites needs to have 128 subtracted from it beforehand, so that when excess-128 values are added later, the results are valid 2's complement values. You effectively trade all the sign extensions you'd need for that one subtraction in the beginning.

EDIT: Typos.


Last edited by tokumaru on Wed Feb 21, 2018 9:38 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 2:57 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10892
Location: Rio de Janeiro - Brazil
I know that not many of you are familiar with Atari 2600 development, but in my case, this excess-128 thing for sprites works a bit like the horizontal positioning of sprites in that console.

The 2600 has a very rudimentary video chip (the TIA) with a small number of movable objects, and you don't have direct access to their on-screen coordinates. To position sprites you need to use timed code and "reset" their positions at the exact moment in the scanline where you want them to appear. The CPU runs 3 times slower than the TIA, so you can't really position objects with pixel-perfect precision. For these precise pixel adjustments, the TIA can be told to move sprites left or right using separate signed 4-bit offsets for each object. This is not what happens internally, though: In reality, the 4-bit offsets can only move sprites 0 to 15 pixels to the left. To make it seem like you can move sprites left and right, the TIA simply moves all objects 8 pixels to the right before applying the offsets, giving the final range of -7 to +8 pixels.

I do exactly the same when drawing metasprites on the NES. I want to use positive coordinates only for the individual sprite offsets (so that the implicit high byte is always 0), meaning that I can only put sprites 0 to 255 pixels to the right of an object's reference point. That's no good, since I absolutely need to put sprites to the left of the reference point too, so I essentially "cheat" and move the reference point 128 pixels back, so my effective range becomes -128 to 127 pixels, but using only "positive" offsets.

I hope this makes sense!


Top
 Profile  
 
PostPosted: Wed Feb 21, 2018 4:00 pm 
Offline
Formerly WheelInventor

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1782
Location: Gothenburg, Sweden
The 2600 example made perfect sense! :beer: :D

But wouldn't it suffice to assume that while on-screen, the high byte is 0? If moving out of bounds to the left or up, decrease, get $ff (ie non-zero), if out of bounds to the right or down, increase, get $1 (also nonzero), from where you can beq past routines for checking if the entity should be inactivated/decrease its update rate/"hide" its representation contents by moving the sprites to an unseen lower position.


edit: fixed mixup of directions.

_________________
http://www.frankengraphics.com - personal NES blog


Last edited by FrankenGraphics on Wed Feb 21, 2018 4:15 pm, edited 1 time in total.

Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group