Jump to content
Sign in to follow this  
Grebnedlog

Questions about playfield collision detection in DPC+ kernel

Recommended Posts

Hi. Hoping someone out there can offer some insight on how the 'collision(playfield,player1)" kernel option currently functions in batari's 1.1d DPC+ kernel. As I understand it, a collision between a virtual player1 sprite (player1-9) will return a y-value in temp4

 

1). Is the value returned in temp4 the Y of the pixel that collided, or the origin Y of the player sprite (i.e. line zero of the defined sprite). From experiments, it seems like the former, but after some strange results have me wondering if I'm understanding it properly.

 

2). When does the collision test take place? Is the value returned in temp4 four obtained during a drawscreen or is it a test that runs independent of it (perhaps during vertical blanking)?

 

3). In either case of 2, what will the resulting value in temp4 be if two virtual sprites are in contact with the playfield simultaneously during a single drawscreen? I assume the kernel will return only the value of the first scanline to be drawn in a collision state, but is there any way to gain some control over that, or perhaps a workaround during v-blank to identify two (or more) sprites in a playfield collision state? I've tried turning bits on and off to track which sprites have collided, but that hasn't worked out well so far. It seems the main obstacle is that new playfield collisions with sprites on later scanlines won't be recognized so long as the earlier sprite is still in collision.

 

Thanks.

Share this post


Link to post
Share on other sites

I realize that the OP might not have been very clear, so I worked up a demo to illustrate the problem as I see it.

 

The bb 1.1d program imitates the style of a Donkey Kong-like platform game to show what's happening. A fuller explanation is contained in the source comments, but the basic idea here was to build a basic game framework that would benefit from multiple simultaneous collision detection for the virtual players (and actually 'breaks" without it). I'm hoping someone with experience can run it, take a look at the code, and explain what's going on (and maybe even propose a solution).

 

- Press the joystick left and right to move the "1' sprite.

- Press and release the fire button to shuffle the starting platforms for all sprites.

- Sprites should be able to move through ladders.

- Sprites should not be able to move through walls or gaps.

- The Bug: When a sprite is overlapping a ladder, all sprites on platforms below it ignore walls and gaps.

 

Thanks. :)

 

multi_pf_collisions_test.bas

multi_pf_collisions_test.bas.bin

Edited by Grebnedlog

Share this post


Link to post
Share on other sites

I don't use the DPC+ kernel YET so this is not an answer. I'm still hoping the others who do use it will respond.

 

In my experience temp vars are unreliable and buggy. I always store the values in my own "temp" variables. Maybe immediately storing the temp var to a real var THEN doing calculations will help. Also, DPC+ is bleeding edge stuff. Maybe built in collisions still aren't implemented correctly. If I were starting to use DPC+ I'd highly consider doing soft collisions.

 

Just to be clear what I mean is perform a DRAWSCREEEN then IMMEDIATELY record the collision values to real vars.

Edited by theloon

Share this post


Link to post
Share on other sites

Just to be clear what I mean is perform a DRAWSCREEEN then IMMEDIATELY record the collision values to real vars.

 

That kind of why I asked question #2. It's not clear to me when the value is returned in temp4. I'm not sure what you mean by the temp variables being unreliable, though. It seems to me that while they are obliterated on a drawscreen, they work as expected as temporary variables.

 

That's why I am interested in precisely when the Y is returned. If temp4 is reset on a drawscreen command like the other temps, then I gather that the value is being returned outside of the drawscreen (i.e., you don't have to actively test for player1/playfield collisions before retrieving the current value; you can just issue the conditional "if temp4 =.... then" whenever you want). As far as storing the values to permanent RAM, if you look at my program I think you'll see that's exactly what I'm tring to do, by trying to turn on a collision bit whenever temp4 corresponds to a given sprite's Y range.

 

I assume by "soft collisions" you are talking about bounding boxes/hit zones, but unfortunately that isn't going to cut it for playfield collisions, which normally don't neatly correspond to rectangular hit zones like sprites do. The ability to read the state of pfpixels would sidestep the problem completely, but unfortunately from what I can tell playfield reading is not implemented yet in bB 1.1d.

Share this post


Link to post
Share on other sites

The temp variables are used throughout bB where needed, not just in drawscreen. For example, some of them are used in the math routines, some are used in the drawing routines, some are used when calling user-defined functions, and so on. That's why it's best to store or process the temp4 variable right after drawscreen, before there's any chance for it to get wiped out by some other routine.

Share this post


Link to post
Share on other sites

The temp variables are used throughout bB where needed, not just in drawscreen. For example, some of them are used in the math routines, some are used in the drawing routines, some are used when calling user-defined functions, and so on.

 

I didn't say they were used in drawscreen, just that they were cleared after one (at least, that's what seems to be going on).

 

That's why it's best to store or process the temp4 variable right after drawscreen, before there's any chance for it to get wiped out by some other routine.

 

I've done that in my test program, but it doesn't change the result. I honestly don't think this is the issue here, anyway. There's not much going on in my program itself, besides a scenario that favors multiple simultaneous p1 collisions with the playfield. The problem seems to be (and, I'm not sure, which is why I'm asking) that only the collision on the highest scanline is returned in temp4, and while collisions on other scanlines are ignored. I'm just asking if anyone here can explain why this is the case, and if there's any known workaround. Sorry if I've broken some sort of protocol in asking this sort of question, I am just a novice trying to understand the result I'm seeing.

Share this post


Link to post
Share on other sites

The problem seems to be (and, I'm not sure, which is why I'm asking) that only the collision on the highest scanline is returned in temp4, and while collisions on other scanlines are ignored. I'm just asking if anyone here can explain why this is the case, and if there's any known workaround. Sorry if I've broken some sort of protocol in asking this sort of question, I am just a novice trying to understand the result I'm seeing.

I apologize if my previous post frustrated you-- my comment about the temp variables being used in other routines besides drawscreen was mainly in response to what theloon said about them being "unreliable." In my own experience they're safe to use as long as you don't expect them to keep their values after calling drawscreen, but it also depends a great deal on what other things are going on in your program. For example, the mul8 routine uses temp1 and temp2, so if you store something in temp1 and temp2, then do some multiplication that requires calling the mul8 routine, your temp1 and temp2 values will be toast afterward.

 

Going back to your original questions, I've looked at the assembly listing generated by compiling a simple DPC+ test program, and as far as I can tell the answers are as follows:

 

Question 1 - There's a comment on the line of code where the collision is stored in temp4 that says "approx line of first pf coll," so it seems you're correct that the value in temp4 is for the y position of the pixel that collided with the playfield, but apparently it might not be the exact y position.

 

Question 2 - All collision detection on the 2600 occurs as the active portion of the screen is being drawn (i.e., during the display kernel), and normally there's no way to tell where a collision occurred, since the TIA sets the appropriate collision bit as soon as a particular type of collision is detected, and the bit remains set until you clear it. So if you forget to clear the collisions (in an assembly program) after the screen has been drawn then you can't even tell if the collision occurred on that frame or some previous frame. Fortunately for us, batari Basic clears the collision registers at the very beginning of drawscreen, so we can always be sure that any collision that was detected must have occurred on the frame that was just drawn, but we still can't tell which line the collision occurred on or whether more than one of that type of collision occurred. Since the multisprite and DPC+ kernels reuse player1 to achieve additional sprites, the ability to tell which line a player1/playfield collision occurred on was added so we can tell which of the virtual copies of player1 was involved in the collision-- at least, that's my understanding (Fred/batari could tell you for sure).

 

Question 3 - Only the line number for the first collision is detected this way, so if additional collisions between player1 and the playfield occur further down on the screen their positions won't be returned. I think the only way to detect the y positions of additional collisions would be to modify the kernel, and I'm not sure that's even feasible to do for a generic solution. A better suggestion would be to write your own kernel for the game, or at least edit the assembly code that's generated by compiling your batari Basic code. Assuming your test program is similar to the game you're making, with three levels and an enemy on each level, you could divide the screen into horizontal bands-- each level being a band-- and check the collisions at the end of each band, store the results, and then clear the collision registers before drawing the next band.

Share this post


Link to post
Share on other sites

I apologize if my previous post frustrated you--

 

No, not at all. I was just worried I wasn't explaining my questions right (and still am, sort of). Thank you very much for the detailed reply.

 

my comment about the temp variables being used in other routines besides drawscreen was mainly in response to what theloon said about them being "unreliable." In my own experience they're safe to use as long as you don't expect them to keep their values after calling drawscreen, but it also depends a great deal on what other things are going on in your program.

 

That's kind of what I thought. For instance, in the program I was using them to draw the hit zones for the ladders (what I assume theloon meant by "soft collisions), and it seemed to work as expected. That's why I was asking about temp4, since I wasn't sure at what point bB was returning the value.

 

Going back to your original questions, I've looked at the assembly listing generated by compiling a simple DPC+ test program, and as far as I can tell the answers are as follows:

 

Question 1 - There's a comment on the line of code where the collision is stored in temp4 that says "approx line of first pf coll," so it seems you're correct that the value in temp4 is for the y position of the pixel that collided with the playfield, but apparently it might not be the exact y position.

 

Ah, okay. I noticed this was a little unpredictable as well, so just decided to track a hit zone the covers the full vertical range of the sprite with a top buffer of one pixel:

 

 temp2 = sprite1y + sprite1height : temp3 = sprite1y - 1
if temp4 < temp2 && temp4 >= temp3 then sprite1pf = 1

 

I wonder what accounts for the slight variance? Also, how do you see the assembly listing for a compiled bB program? Does DASM spit it out in a default directory?

 

Question 2 - All collision detection on the 2600 occurs as the active portion of the screen is being drawn (i.e., during the display kernel), and normally there's no way to tell where a collision occurred, since the TIA sets the appropriate collision bit as soon as a particular type of collision is detected, and the bit remains set until you clear it. So if you forget to clear the collisions (in an assembly program) after the screen has been drawn then you can't even tell if the collision occurred on that frame or some previous frame. Fortunately for us, batari Basic clears the collision registers at the very beginning of drawscreen, so we can always be sure that any collision that was detected must have occurred on the frame that was just drawn, but we still can't tell which line the collision occurred on or whether more than one of that type of collision occurred. Since the multisprite and DPC+ kernels reuse player1 to achieve additional sprites, the ability to tell which line a player1/playfield collision occurred on was added so we can tell which of the virtual copies of player1 was involved in the collision-- at least, that's my understanding (Fred/batari could tell you for sure).

 

Oh, okay. Again, I'm not sure I'm thinking about this the right way... but when you put it that way, the problem with my program seems to be unsolvable in bB. I can't do a manual check to see whether I should clear the player1 collision register or not, because it always begins its loop in a cleared state, starts drawing the screen, and sets the register whenever the first collision scanline is reached? I wonder if it would be possible to revise the drawscreen function to use more temp variables to store multiple collisions, with each variable assigned a certain vertical range (which sounds well above my pay grade, skill-wise).

 

Question 3 - Only the line number for the first collision is detected this way, so if additional collisions between player1 and the playfield occur further down on the screen their positions won't be returned. I think the only way to detect the y positions of additional collisions would be to modify the kernel, and I'm not sure that's even feasible to do for a generic solution. A better suggestion would be to write your own kernel for the game, or at least edit the assembly code that's generated by compiling your batari Basic code. Assuming your test program is similar to the game you're making, with three levels and an enemy on each level, you could divide the screen into horizontal bands-- each level being a band-- and check the collisions at the end of each band, store the results, and then clear the collision registers before drawing the next band.

 

Yeah, I was wondering too whether a solution could be generalized to store multiple lines of collision, or if the display would have to be divided into the sort of vertical bands you describe. Either way, it sounds like a new kernel would have to be written from scratch -- which is beyond my abilities, and I suppose it sort of defeats the purpose of using bB.

 

Still, if a feasible general solution exists I wonder if this might be something batari might consider incorporating into the new version. It seems to me like that, in the absence of a pfread, it would be a very useful feature in all sorts of games that use the DPC+ kernel and virtual sprites.

Share this post


Link to post
Share on other sites

I wonder what accounts for the slight variance? Also, how do you see the assembly listing for a compiled bB program? Does DASM spit it out in a default directory?

 

DASM doesn't create a listing unless you tell it to. What I've done is modified my 2600bas.bat file to add the option to output the listing file. If you open your 2600bas.bat file in a text editor (if you're using Windows, just right-click on it and select "edit"), you can look for the lines that start with "dasm" (there are two of them). Somewhere in the dasm command line insert "-l%1.lst" (or if you prefer, "-l%1.txt"). Just to be sure, that's a minus lowercase el (as in "list") percent one dot whatever extension you want the listing file to have. In my batch file I inserted it between "-f3" and "-o%1.bin" but the order of the command switches isn't important. Anyway, after you edit and save the batch file, compiling your batari Basic programs will generate an assembly listing that can be helpful to look at if you're trying to debug a program. What I usually do is use "find" to look for a particular variable name or register or line label-- in this case, "temp4."

 

By the way, even if you don't create a listing, batari Basic creates an assembly code version of your program with a ".asm" extension, and it can be helpful to look at that as well. The advantage of looking at the full listing is that it shows everything, even the code from the include files and macros, and also shows the machine code as well as the addresses where it put everything. On the other hand, the advantage of looking at just the assembly code for your program is that it *doesn't* show all that other stuff, just the assembly code for your batari Basic code. So the ".asm" file is more convenient to use if you just want to see how your batari Basic code translated into assembly, whereas the full listing is more useful for debugging.

 

Oh, okay. Again, I'm not sure I'm thinking about this the right way... but when you put it that way, the problem with my program seems to be unsolvable in bB.

 

Actually, it might be possible to solve the problem a different way, by checking the x position of each sprite and stopping it (or reversing its direction) when it gets to an x position where you know it will collide with the playfield, rather than trying to use the collisions for that. If the game has multiple screens where the ladders are in different locations then you'll need to vary the checks to account for the different x positions of the ladders, but you could perhaps use a variable for that instead of a literal. For example, after you draw the new screen you could set ladder1x to the x postion of the ladder on level 1, then ladder2x, then ladder3x, and compare the sprites' x positions to the x position of the ladder for whichever level they're on.

Edited by SeaGtGruff

Share this post


Link to post
Share on other sites

Thanks for the advice and insights on assembly outputs. Works like a charm!

 

Actually, it might be possible to solve the problem a different way, by checking the x position of each sprite and stopping it (or reversing its direction) when it gets to an x position where you know it will collide with the playfield, rather than trying to use the collisions for that. If the game has multiple screens where the ladders are in different locations then you'll need to vary the checks to account for the different x positions of the ladders, but you could perhaps use a variable for that instead of a literal. For example, after you draw the new screen you could set ladder1x to the x postion of the ladder on level 1, then ladder2x, then ladder3x, and compare the sprites' x positions to the x position of the ladder for whichever level they're on.

 

I think I grok you here. I guess that could work for a handful of repeating screens that repeats the same collision areas (something like "Donkey Kong" springs to mind). If I grok you right, that's already in the demo program, in a limited way (the sprites shift direction when the reach a minimum/maximum X near the screen edge. I just get the feeling that might be a bit much to store in RAM. For instance, even the very sparsely populated demo program has six such collision zones -- it wouldn't be the ladders that would need to be checked, but the pits and walls.

 

I was wondering if there could be a more general approach. Still, I guess that technique could work fine for certain games. Maybe instead of tracking all the collision zones constantly, we could loop some pointers through an array that contains all the x-y collision zones for that "game screen." The pointer would advance one frame at a time, then check to see if any of the virtual sprites is overlapping that particular collision zone. It might look a little visually sloppy maybe, with sprites constantly "overstepping" gap zones and clipping into wall zones that haven't come up in the pointer queue yet, but I guess it might save some precious RAM.

 

But gosh-golly, this still seems like it be a whole lot simpler with pfread implemented! :) I wonder if there are any plans to do eventually so (or if it's even possible, given all that's going on already in this kernel).

 

Thanks again.

Share this post


Link to post
Share on other sites

What SeaGtGruff said about my "unreliable" comment is spot on. Sorry about mentioning soft collision and leaving you guessing. My thought was to use DATA statements as a collision map for the playfield. I opened a topic before to better understand how to create a binary array specifically for this purpose. Sadly, despite incredible help, I'm still working on it :)

http://www.atariage....s-binary-array/

 

I did get Batari's original Rally-X example to work and he uses a DATA array for playfield collisions but the code doesn't make sense to me

http://www.atariage....ll-examplegame/

Edited by theloon

Share this post


Link to post
Share on other sites

What SeaGtGruff said about my "unreliable" comment is spot on. Sorry about mentioning soft collision and leaving you guessing. My thought was to use DATA statements as a collision map for the playfield. I opened a topic before to better understand how to create a binary array specifically for this purpose. Sadly, despite incredible help, I'm still working on it :)

http://www.atariage....s-binary-array/

 

Interesting thread! That would be a good workaround for the absence of pfread, but I think it might be a bit of overkill for the types of games I'm imagining here. For example, in my demo I don't need the to id the state of every drawn pixel on the screen, just the ones that will trigger certain events. Although that problem would also be solved with a binary array, I think it's possible to solve with byte arrays, too, depending o the complexity of the hitzone shape. For example, I might have a craggy, mountainous shape on the screen composed of 264 playfield pixels, but for the purposes of in-game collision detection my x-y collision map for it might be far simpler. I might only have to check a few rectangles, or a simple square/line.

 

I did get Batari's original Rally-X example to work and he uses a DATA array for playfield collisions but the code doesn't make sense to me

http://www.atariage....ll-examplegame/

 

Hmm... I can't get this one to compile (I'm using 1.1d).

Share this post


Link to post
Share on other sites

The beta beta 1.1 series compiles old .bas files differently. For instance, SDATA seems to be broke. RallyB will only compile on 1.0 builds.

 

How about using the ball for collision detection? Place the ball over the area the object may collide with next, perform the collision routine and use the results.

Share this post


Link to post
Share on other sites

How about using the ball for collision detection? Place the ball over the area the object may collide with next, perform the collision routine and use the results.

 

Yes, that's what I was thinking of as a workaround, too; flickering the ball under each sprites and then testing for those collisions for the gaps. If that was the case, the native Y-temp4 return would probably work okay if limited to the wall detection (turned off when within range of the ladder objects). It would be nicer to have sprite collisions and preserve the ball object, obviously, but that method would probably mimic the effect pretty well.

Edited by Grebnedlog

Share this post


Link to post
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.

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...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...