It is currently Mon Oct 22, 2018 3:53 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 30 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Tue Jul 24, 2018 9:24 am 
Offline
Formerly WheelInventor

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1783
Location: Gothenburg, Sweden
A wholly different collision topology is metroid and project blue (sorry for all the self-referencing lately). Both are per tile-based in different ways. Under variants, some other ideas are mentioned.

In metroid, collision is implied by ranges of tile ID:s. I don't remember the exact ranges and properties, but say $00-$BF are solid, $C0-$EF are nonsolid, $F0-F8 are hurting nonsolid, and $F9-FF are breakable. Metroid is using most of its tilespace for solids.

Even in a scroller, detecting a hit is just the matter of checking screen position / 8 vs the NT cell tile ID (in the relevant nametable) every 8th pixel (you can AND position with #7 to branch past reduntant checks).

Project Blue differs in that it lets you set what tiles have what properties freely and combinable with the help of separate property arrays.

Pros:
-Easy/Requires no collision map sliver updates/collision is automatically derived from NT content.
-Promotes big worlds / levels
-Tile-granularity collision (not that metroid ever uses it - but the engine is perfectly capable to do so!)
-(valid for metroid) no significant ROM storage cost whatsoever
-(valid for project blue) relatively low ROM storage cost per level
-(valid for metroid) Doesn't need RAM.
-(valid for project blue) overlapping, combinable physics attributes
-(valid for project blue) no need to organize your tiles, other than for convenience.

Cons:
-If you plan on using the same tiles multiple time for solid/nonsolid, you either need to
a)waste some tile space with duplicates (this is the metroid method)
b)make collisions conditional on PPU attribute as well (this is part of the project blue method), which as a downside locks in the function of subpalettes pretty tight. Anyway, i've found that this is pretty natural and not much of a hindrance since you want to keep solids and far background differently coloured in a platformer anyway. An isometric game or the like would fare worse.
-(valid for metroid): you need to organize your tiles in accordance to the physics attribute ranges you've defined.
-(valid for metroid, but not necessarily for you): you may need different ratios of differently propertized tiles in different levels/stages/boards. Of course, you can just keep different definitions or routines for different levels.

Variants:
-Going with the metroid "range scheme", you can of course have overlapping ranges. So maybe there's a hurt property that overlaps with both solid and nonsolid.
-Something like project blue could instead bitpack 8 properties in a byte corresponding to each tileID. It would be free of RAM, small in ROM.

edited to be less confusing.

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


Last edited by FrankenGraphics on Tue Jul 24, 2018 3:58 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Tue Jul 24, 2018 9:48 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
It's all very interesting to consider the various ways I could approach this now, with some thought. Thinking this out here....

Choice 1: Keep collision data as full bytes, still loading into RAM. Rearrange memory to try to free up RAM space for second collision table, and just mirror exactly how the nametable updates function, but on a metatile basis.

Pro: Probably how it SHOULD be done, as the infrastructure is already lain for when to update and column checking.
Con: Would have to sacrifice some of the RAM space allocated to many of the customizable things inherent. This might be hugely detrimental.


Choice 2: Use only 4 bit nibbles for each collision tile. That way, two screens worth of data could be packed in to the same space as one screen worth of full bytes. I could actually pretty easily determine which nametable is being looked at (2000 or 2400); if it's the former, read the first nibble, if it's the latter, read the second. That's a quick enough read to essentially behave the same way. And then on updates, it would blank that nibble and ora in new data, placing it in the proper nibble dependent on which of the two 'screens' is being updated.

Pro: Wouldn't have to reorganize RAM map at all.
Con: Any potential uses for those extra bits in real time game play would be gone, and the logic is a little bit more convoluted.


Choice 3: Shifting columns. Use a 16 byte buffer (this is clean, since the screen has 240 collision bytes...one ram page fits that + the buffer) for next potential row of collision. When threshold is reached, shift each column, column by column, filling the last with the buffer, and filling the buffer with the next potential column.

Pro: No reorganization of RAM map, OR loss of extra "real time" collision bits.
Con: Very complex relatively, and I'd imagine terribly slow.


Out of the three, I think the second may keep the current skeleton if things in tact the most and might be the simplest to implement. What do you guys think?


Top
 Profile  
 
PostPosted: Tue Jul 24, 2018 10:25 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6899
Location: Canada
Sumez wrote:
What are your reasons for loading collision data into RAM?

In Lizard's case, there are several reasons. Here's 3 big ones I can think of immediately:

  • Uncompressed 1-bit per tile collision data for the world would have been an extra 100k+ of data for the game (which is a pretty full 512k already).
  • RAM means it can be modified (doors and platforms can be created and destroyed), though I wish I'd used this feature more.
  • RAM also means I don't have to bankswitch that data in to do a collision test. (Was able to do interesting stuff like colliding snow particles partly because of this.)

There are some good reasons to use ROM collision tables too though. I'm certainly not saying that there's only one way to do it. Part of why it'd be so much data for Lizard is that it has 8x8 tiles instead of 16x16.


Top
 Profile  
 
PostPosted: Tue Jul 24, 2018 2:30 pm 
Offline
User avatar

Joined: Thu Sep 15, 2016 6:29 am
Posts: 773
Location: Denmark (PAL)
An advantage to Lizard though, is that as far as I recall, no room uses more than two nametables? So scrolling is less of an issue, if any at all.


Top
 Profile  
 
PostPosted: Tue Jul 24, 2018 2:52 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2308
Location: DIGDUG
Quote:
no room uses more than two nametables?


The river (surfing) and the frog boss scroll seamlessly.

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


Top
 Profile  
 
PostPosted: Tue Jul 24, 2018 3:04 pm 
Offline
User avatar

Joined: Thu Sep 15, 2016 6:29 am
Posts: 773
Location: Denmark (PAL)
I'd wager collisions also work quite differently in those two examples? :)


Top
 Profile  
 
PostPosted: Tue Jul 24, 2018 5:04 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6899
Location: Canada
Yes, those two are special.

...but there's no fundamental incompatibility with RAM collision and scrolling, either, and compression is still a bonus there.


Top
 Profile  
 
PostPosted: Wed Jul 25, 2018 8:44 am 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 937
Location: cypress, texas
I don't know if I'm too late, but tepples gave me an excellent idea for scrolling horizontally using two 8bit variables visible_left and valid_left. After lots of my rereading, prayer, and efforts his method works excellent for me!! :mrgreen: :D tepples' visible_left valid_left scrolling advice (bottom of page 69)


Top
 Profile  
 
PostPosted: Wed Jul 25, 2018 12:59 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
So, trying some things. Give or take, got a few things *sort of* working the way I want. Graphically, it's all good (and has been). But collisions...oh collisions. Going to sort of do some public rubber duck debugging here....

Something simple enough like just determining which collision table to pull from (one loading for when NT 1 is showing, one loading for when NT 2 is showing) I think I'm overcomplicating terribly.

So, essentially, I have a columnTracker variable that is keeping track of the left side of the scroll camera (and in tandem what column to update in the opposite nametable). The player has about an 80 px padding area in the center (which can be adjusted by changing a constant) where the scroll doesn't kick in. So now I have to figure out what nametable the player "is in". Fairly simple, xScroll + playerX...if the carry is set, we're in the second nametable....

But wait, no. That's not right. If columnTracker is a value of 00-15 AND xScroll+playerX yields a set carry, we've moved into the second nametable (because that means the left of our camera window is still in the first screen). If columnTracker is 16-31 in the same condition, it means we've cycled back around to the first (because that means the left of our camera window is now in the second screen).

Alright, so now I could go three ways...keep track also of "object's" column, which would be different than his position (his position only returns values of 00-ff, but column could keep track of which column he's in and mirror the columnTracker function, just tracking the object, not the left of screen...), or I could create an overflow byte for position, sort of reading world coordinates, for which the last bit would determine whether collisions are in collision table one or two....or I could flip an arbitrary bit somewhere would an object could read to make that determination.

And in any case, I preemptively wonder how ugly collision detection will end up getting when I have to read left points of collision from one collision table, but right points of collision from the next.

These are my fleeting thoughts. You guys are awesome for your responses, as always.


Top
 Profile  
 
PostPosted: Wed Jul 25, 2018 3:45 pm 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 937
Location: cypress, texas
JoeGtake2 wrote:
And in any case, I preemptively wonder how ugly collision detection will end up getting when I have to read left points of collision from one collision table, but right points of collision from the next.
It doesn't have to get super ugly. So far I use one function to check 4 points. Before calling that function I have my assembly code set PointX, PointY, PointXX, and PointYY to different values depending on the purpose for jsring the function (i.e. falling, left or right movement, etc.). An outstanding Kasumi post explaining points checking. I also have a variable FORWARD_last that holds the last left (#$00) or right (#$01) direction pressed. Then for left or right it looks something like this:
Code:
    ldx FORWARD_last
    beq +ei
        ldx #xx ;a value for PointX and PointXX when pressing right
        bne +; <jmp
 +ei    ldx #$xx ;a value for PointX and PointXX when pressing left

  + lda #01
    ldy #30
    stx PointX
    sta PointY
    stx PointXX
    sty PointYY
    jsr function that checks the points (see link to Kasumi's post)
;other code specific to my sister's game here

If you always calculate your character using its left side then PointX would be #$00 when pressing left and #(decimal value of your characters width when pressing right). That way, inside your points checking function, you can simply always add PointX to your character's X value, check the appropriate collision value, store the result, then always add PointXX to your character's X value, check, and store the result. Then include appropriate code that processes the results that were stored inside of your points checking function. It's not ugly at all, to me at least. :)

p.s.
a.) I'm using #01 and #30 because my sister's game runs much better that way... even though Kasumi recommends using #00 and #31.
b.) To prevent my screen from flashing I was very blessed with an understanding that it is extremely important to always order groups of stores (i.e. stx PointX sta PointY stx PointXX sty PointYY) from lowest memory address to highest memory address.


edit: my main loop skips the jsr LRfootCollision..., looks something like code section above, if input is not currently left or right so that use of FORWARD_last will never be used unappropriately. :)

final edits.


Top
 Profile  
 
PostPosted: Thu Jul 26, 2018 12:06 am 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1251
Determining which nametable any given point is in is three instructions.
Code:
lda highxbyte
ror a
bcs nametable1
nametable0:

The scroll doesn't even need to factor into the decision. That's the beauty of have exactly 32 columns in the buffer.

The scroll only determines what's in the buffer/when the buffer gets updated. If you do have 32 columns, you have a buffer of 8 on each side of the screen.

Edit: Similarly, if the left and right points are in different nametables, if you're checking the left point, and you add the width and that sets the carry, now you're in the opposite nametable, otherwise you're still in the same one.

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


Top
 Profile  
 
PostPosted: Thu Jul 26, 2018 2:44 am 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 494
I usually don't need collisions for entities which are off-screen, so I use a plain 16x12 circular buffer (16x16 for vertical scrolling games).

_________________
http://www.mojontwins.com


Top
 Profile  
 
PostPosted: Thu Jul 26, 2018 4:56 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
Kasumi - I assume your xhi var is tracking a 16bit positioning, meaning even=table 1, odd=table 2. Am I understanding this right? If so that’s not exactly how my movement update/positioning code works. I have 16 bit positioning, but the low is just for holding finer speed adjustment. In this example, would right movement keep track of x + xScroll in a 16 bit variable, called posX? Something like this maybe?

And unregistered - yep...that’s pretty much exactly my method.

***EDIT***

I seem to have worked out a semi-passable method based on the influx of input...thanks everyone! :-)


Top
 Profile  
 
PostPosted: Fri Jul 27, 2018 5:30 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1251
You're understanding that right. I really recommend adding that extra byte for each object for scrolling, I imagine it will save you a lot of trouble down the road.

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


Top
 Profile  
 
PostPosted: Fri Jul 27, 2018 6:21 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10906
Location: Rio de Janeiro - Brazil
If you have an active area wider than 256 pixels (and you really do need one if you scroll horizontally) you absolutely need 16-bit coordinates to keep track of positions properly. Anything else will be a hack, and probably not worth the 1-byte saved per object.


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: NOOPr and 2 guests


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