Jump to content
IGNORED

PF pixels-to-Sprite pixels?


Recommended Posts

Sorry if I'm having is a RTFM moment, but I was just curious about the relationship between "playfield" pixel position versus "sprite" positions. It seems as though pfpixels are roughly 4.5 times the width of sprite pixels - but not exactly 4.5, since odd numbered pfpixels will produce fractional results.

 

I'm working on simulating sprite / playfield collisions based on datasets right now, and am realizing it would be much easier if I could just translate sprite position to pfpixel position on the fly (or, vice-versa). I'm just curious if anyone has experimented with such a technique on one of their games.

 

Thanks in advance.

 

J

Link to comment
https://forums.atariage.com/topic/131795-pf-pixels-to-sprite-pixels/
Share on other sites

I translated pfpixel positions to sprite position like this:

 

  player0x=(pfieldx*4)+15 : player0y=(pfieldy*8)+8

 

But I had trouble doing the reverse. And I'm still working on it. You'd think doing the reverse would work, but I can't get it to work so far.

Converting between player positions and playfield positions is fairly easy in batari Basic, although the formulas can vary a bit depending on a number of factors.

 

First, let's use the term "screen position" to mean each spot on a scan line where we can position a player or other sprite. There are 160 screen positions, which are usually numbered 0 to 159.

 

Depending on the size of the players (i.e., the width of their pixels), the player positions are as follows:

 

Standard kernel, single-wide player (1 pixel = 1 color clock):

Leftmost player position = 1

Rightmost player position = 160

 

Standard kernel, double-wide player (1 pixel = 2 color clocks):

Leftmost player position = 0

Rightmost player position = 159

 

Standard kernel, quadruple-wide player (1 pixel = 4 color clocks):

Leftmost player position = 0

Rightmost player position = 159

 

(It should be noted that the multisprite kernel is very different, and is more complicated than I can get into right now, so we'll leave it for another time.)

 

The players' horizontal coordinates (player0x and player1x) can be set to 0 through 255, but anything over 159 or 160 will be equivalent to smaller values, so it's best to keep their X coordinates restricted to the values given above.

 

Also, the players will wrap around the screen, meaning the left portion of a player can be at the right edge of the screen, while the right portion of the player is at the left edge of the screen, so you might want to restrict a player's X coordinate so that the player doesn't wrap around.

 

Note that the player's X coordinate is for the first color clock of its leftmost pixel, so the player's 8 pixels will stretch across several screen positions. For instance, if player0 were set to single-width, then it would stretch across 8 screen positions-- so if player0x were set to 7, its 8 pixels would occupy positions 7, 8, 9, 10, 11, 12, 13, and 14. Or if player0 were set to quadruple-width, then it would stretch across 32 screen positions-- so if player0x were set to 7, its 8 pixels would occupy positions 7 through 38.

 

To keep things as simple as possible, I strongly suggest that you left-justify the player's shape within its 8 pixel columns. In other words, if you're drawing a player that's only 6 pixels wide, you should leave the last 2 pixel columns blank (0), as in the following example:

 

   player0:
  %11111100
  %10000100
  %10000100
  %10000100
  %11111100
end

The playfield is only 40 pixels wide, so each playfield pixel is 4 color clocks wide (160 / 40 = 4). But batari Basic uses only the 32 centermost playfield pixels-- i.e., there are 4 blank playfield pixels, then 32 playfield pixels that you can draw with, then 4 more blank playfield pixels (4 + 32 + 4 = 40).

 

This means we can convert a player0x position to an equivalent playfield column by the following formulas:

 

playfield X position = (player0x - 17) / 4 for a single-wide player0

playfield X position = (player0x - 16) / 4 for a double-wide or quadruple-wide player0

 

The division by 4 is necessary because 1 playfield position equals 4 player positions. And the subtraction is necessary because the first usable playfield pixel is 16 color clocks to the right of the screen's left edge. In other words, the first 4 playfield pixels aren't used, so those unused playfield positions correspond to the following player positions:

 

| 1,2,3,4 | 5,6,7,8 | 9,10,11,12 | 13,14,15,16 | for single-wide players

| 0,1,2,3 | 4,5,6,7 | 8,9,10,11 | 12,13,14,15 | for double-wide or quadruple-wide players

 

Thus, the leftmost color clock of the first usable playfield pixel is at either player position 17 or player position 16, depending on whether the player is single-wide or double/quadruple-wide.

 

To convert in the other direction-- from a playfield position to a player position-- we reverse the formulas:

 

For a single-wide player0:

playfield X position = (player0x - 17) / 4

4 * (playfield X position) = player0x - 17

player0x = 4 * (playfield X position) + 17

 

For a double/quadruple-wide player0:

playfield X position = (player0x - 16) / 4

4 * (playfield X position) = player0x - 16

player0x = 4 * (playfield X position) + 16

 

The conversions are similar for the vertical coordinates, but the details are different. This is complicated by the height of each playfield row-- and also whether we're using vertical playfield scrolling, as well as whether we're using the option for no_blank_lines-- so to keep things simple we'll assume that the standard playfield is being (no scrolling, and no special options like pfheights or no_blank_lines).

 

This time, the player0y coordinate ranges from 1 to 88. However, there are two important points to keep in mind: (1) The player0y position is for the *bottom* of player0. And (2) the 88th player0y position is partially covered up by the blank lines just above the score. This means (1) you must adjust the player0y position based on the height of player0 if you want the entire player to be visible; and (2) you might want to limit player0y to 87 or less if you want the last row of pixels to be fully visible.

 

The standard playfield pixels are 8 lines tall. These are actually double lines, so they're really 15 lines tall-- the 16th line of each playfield is blank-- but they correspond to 8 player lines.

 

This means we can use the following conversions:

 

playfield Y position = (player0y - 1) / 8

player0y = 8 * (playfield Y position) + 1

 

You might need to adjust these numbers based upon how many lines tall your player is.

 

This really just scratches the surface, and it's kind of difficult to explain it all in writing-- it would really be easier to see with something like a PowerPoint presentation, or other visual aids-- but hopefully it's enough to get you started. Also, I'm kind of pressed for time right now, since I'm going on a business trip this weekend, so I hope I didn't make any glaring errors in what I've explained thus far!

 

Michael

This is great! It answered so many of my questions... even if it posed a few new ones. Maybe instead of a Powerpoint, I might try to code a small screen position demo, that uses the score objects to track position. It seems like such a fine point, but as I've been experimenting with different techniques, the calculation seems like it would be useful for all kinds of games (whereas simply detecting pf collisions didn't seem very helpful at all).

 

Thanks again the explanation, Michael.

 

J

This is great! It answered so many of my questions... even if it posed a few new ones. Maybe instead of a Powerpoint, I might try to code a small screen position demo, that uses the score objects to track position. It seems like such a fine point, but as I've been experimenting with different techniques, the calculation seems like it would be useful for all kinds of games (whereas simply detecting pf collisions didn't seem very helpful at all).

 

Thanks again the explanation, Michael.

 

J

If you haven't looked at it already, check out the "target practice" thread or whatever its exact name is-- I posted a little program in there that displays the sprite's position in the score as you move around. Sometimes that's the best way to do this, just to be sure you get the numbers exactly right-- especially if you're using some "oddball" pixel in the player, like the tip of the dart in the Darts WIP (which is "oddball" because it isn't the bottommost, leftmost pixel in the sprite, which is the pixel that would normally correspond to the sprite's player0x and player0y coordinates), or if you're checking for all four edges of your sprite, or something like that.

 

Michael

Edited by SeaGtGruff
  • 1 month later...
The standard playfield pixels are 8 lines tall. These are actually double lines, so they're really 15 lines tall-- the 16th line of each playfield is blank-- but they correspond to 8 player lines.

 

This means we can use the following conversions:

 

playfield Y position = (player0y - 1) / 8

player0y = 8 * (playfield Y position) + 1

 

You might need to adjust these numbers based upon how many lines tall your player is.

 

Would the adjustment be 1-to-1? In other words, if I have a 16 pixel player sprite and wanted to determine his exact y playfield position, would it be:

playfield Y position = (player0y - 9) / 8

 

Also, what if you are doing subpixeling with fixed point variables? I assume that this simply would not work if you wanted to, say, do collision detection using pfread, such as:

 spritePFX = (spriteX-17)/4
spritePFY = (spriteY-1)/8

if spriteXdir = [right] then spriteNextPFX = spritePFX + 0.5 else spriteNextPFX = spritePFX - 0.5
if spriteYdir = [down] then spriteNextPFY = spritePFY + 0.5 else spriteNextPFY = spritePFY - 0.5

if pfread(spriteNextPFX,spritePFY) then spriteX = spriteLastX
if pfread(spritePFX,spriteNextPFY) then spriteY = spriteLastY

if pfread(spriteNextPFX,spriteNextPFY) then spriteX = spriteLastX : spriteY = spriteLastY
if pfread(spriteNextPFX,spriteNextPFY) then spriteY = spriteLastY : spriteX = spriteLastX

 

Unfortunately, that's exactly what I want to do. But logic tells me it's not possible, since the playfield variables are whole integers. But is there a way to round fractions up or down?

 

Thanks again for the explanation.

 

Jarod

Edited by jrok

I hope somebody like SeaGtGruff can figure it out because I tried a similar thing a day or two ago and my little guy was getting all confused and reversing and doing all kind of crazy stuff.

  • 4 months later...
This time, the player0y coordinate ranges from 1 to 88. However, there are two important points to keep in mind: (1) The player0y position is for the *bottom* of player0. And (2) the 88th player0y position is partially covered up by the blank lines just above the score. This means (1) you must adjust the player0y position based on the height of player0 if you want the entire player to be visible; and (2) you might want to limit player0y to 87 or less if you want the last row of pixels to be fully visible.

 

The standard playfield pixels are 8 lines tall. These are actually double lines, so they're really 15 lines tall-- the 16th line of each playfield is blank-- but they correspond to 8 player lines.

 

This means we can use the following conversions:

 

playfield Y position = (player0y - 1) / 8

player0y = 8 * (playfield Y position) + 1

 

You might need to adjust these numbers based upon how many lines tall your player is.

 

This really just scratches the surface, and it's kind of difficult to explain it all in writing-- it would really be easier to see with something like a PowerPoint presentation, or other visual aids-- but hopefully it's enough to get you started. Also, I'm kind of pressed for time right now, since I'm going on a business trip this weekend, so I hope I didn't make any glaring errors in what I've explained thus far!

 

Michael

 

Even though this explanation is fabulous, I feel like I am mising the big picture when it comes to converting Y pixels for non-standard playfield resolutions and taller player sprite sizes. I'm hoping someone with a better head for math than me can help.

 

From what I can graps, standard Y conversion works out to something like this:

88 positions / 11 visible rows = 8 line-tall playfield pixels

8 line-tall sprite can occupy 11 playfield-Y positions

 

I am trying to convert the playfield Y position of a 14 line-tall sprite with pfres 32. How can the formula be altered to determine the sprite's playfield Y?

 

Thanks,

Jarod.

I am trying to convert the playfield Y position of a 14 line-tall sprite with pfres 32. How can the formula be altered to determine the sprite's playfield Y?

This topic can be pretty complicated if you're trying to determine which pfpixels are being "collided" with by the player, due to the fact that the player might be overlapping more than one pfpixel at the same time, and some of the player's pixels will almost certainly be turned off. Also, it matters which edge of the player you're interested in.

 

The player0x coordinate identifies the position of the player's left edge. In bB's standard kernel, the player0y coordinate identifies the position of the player's bottom edge. But in bB's multiplayer kernel, the player0y coordinate identifies the position of the player's top edge. Likewise, the y coordinates are numbered differently in the two kernels-- the standard kernel numbers them in increasing order going down (i.e., position 1 is at the top of the screen, position 2 is below that, etc.); whereas the multisprite kernel numbers them in increasing order going up, or decreasing order going down (i.e., position 1 is at the bottom of the screen, position 2 is above that, etc.).

 

To keep this as simple as possible, let's ignore the issue of collisions, and deal strictly with the player's x and y coordinates in the standard kernel. The simple formulas would be as follows:

 

pfrow = player0y / 3

player0y = 3 * pfrow

 

That's because each pfpixel is 3 lines tall, whereas each player pixel is 1 line tall, so the conversions are 3-to-1 or 1-to-3.

 

Here's a little program that lets you move an 8x14 player around on a playfield that has pfres 32. The score displays the player's current x and y coordinates-- the first 2 digits of the score are the player0x position, and the last 3 digits are the player0y position. Hopefully, moving the player around and watching the coordinates in the score will help you understand the conversion a bit better.

 

For example, the program starts with the player at player0x = 17 and player0y = 14. The left edge of the player (given by player0x) coincides with the left edge of pfcolumn 0, so the x formulas would be as follows:

 

pfcolumn = (player0x - 17) / 4

player0x = 4 * pfcolumn + 17

 

The bottom edge of the player (given by player0y) coincides with the bottom edge of pfrow 4 (although the last line of each pfrow is half blank, assuming you aren't using no_blank_lines). Using the simple formulas given previously, we have

 

pfrow = player0y / 3 = 14 / 3 = 4

 

(after we drop the remainder of 2, since we're dealing with integer values).

 

To get the pfrow for the top edge, we would need to use the following formulas:

 

pfrow = (player0y - playerheight) / 3

player0y = 3 * pfrow + playerheight

 

Keep in mind that the playerheight is 1 less than the number of lines (i.e., a 1-line player has a playerheight of 0). So if player0y were 19, we would calculate the pfrow for the top edge of the player as follows:

 

pfrow = (player0y - playerheight) / 3 = (19 - 13) / 3 = 6 / 3 = 2

 

And for the bottom edge of the player it would be

 

pfrow = player0y / 3 = 19 / 3 = 6

 

(after dropping the remainder of 1), so the 14-line player would be straddling pfrows 2 through 6 if player0y were 19.

 

Michael

pixel_coordinate_conversions.bas

pixel_coordinate_conversions.bas.bin

To get the pfrow for the top edge, we would need to use the following formulas:

 

pfrow = (player0y - playerheight) / 3

player0y = 3 * pfrow + playerheight

 

Keep in mind that the playerheight is 1 less than the number of lines (i.e., a 1-line player has a playerheight of 0). So if player0y were 19, we would calculate the pfrow for the top edge of the player as follows:

 

pfrow = (player0y - playerheight) / 3 = (19 - 13) / 3 = 6 / 3 = 2

 

And for the bottom edge of the player it would be

 

pfrow = player0y / 3 = 19 / 3 = 6

 

(after dropping the remainder of 1), so the 14-line player would be straddling pfrows 2 through 6 if player0y were 19.

 

Michael

 

Thanks Michael! It makes perfect sense now. However, I am having difficulty including the div_mul module to perform the divide by 3 operation. I've tried including the file before specifying the rom size, and have tried making a custom bankswitch_SC.inc file that includes the module after the kernel and startup routine (as per this thread ). I've tried these methodswith both the new 10_25_08 alpha build of bB and bB 1.0 (both with and without the bb_2008y_11m_04_0717t updates and with and without the 3_20_09 updates). I'm a little stumped. Depending on which combination I use, it either fails on "2600basic_SC.h (245): error: EQU: Value mismatch" or fails to resolve the following:

userscore				0000 ????		 (R )
div8					 0000 ????		 (R )
xnotadjusted			 0000 ????		 (R )

 

The only change I've made to the program is to add the div_mul module and add the following line to the loop:

 

	 z=(player0y-14)/3

 

Does this modified program compile for you?

 

pixel_coordinate_conversions.bas

 

Thanks again for your help.

Jarod.

When I compiled the modified source code, it failed as you described. When I looked at the output from the compilation, I noticed the following message up near the beginning:

 

2: Warning: include ignored (includes should typically precede other commands)

 

The number at the beginning indicates the line number, and line 2 is where you included div_mul.asm, so I moved line 2 up above line 1 and it compiled fine.

 

Michael

When I compiled the modified source code, it failed as you described. When I looked at the output from the compilation, I noticed the following message up near the beginning:

 

2: Warning: include ignored (includes should typically precede other commands)

 

The number at the beginning indicates the line number, and line 2 is where you included div_mul.asm, so I moved line 2 up above line 1 and it compiled fine.

 

Michael

 

You are a life saver. I would have never thought to try that.

Tricky topic indeed..

 

I'm looking at what you guys wrote now.. I was thinking of making a program that draws me a pfvline. Then sets the player0 x and y position.. it sets the score to the player0x position and as you press left or right on the joystick it increments or decreases the score. This way I could easily tell where my character is in relation to the PF.

 

I need to do a collision check between player0 and playfield. If the player hits a CERTAIN PORTION of the playfield he loses points. If he hits a different

part of the playfield he dies.

Tricky topic indeed..

 

I'm looking at what you guys wrote now.. I was thinking of making a program that draws me a pfvline. Then sets the player0 x and y position.. it sets the score to the player0x position and as you press left or right on the joystick it increments or decreases the score. This way I could easily tell where my character is in relation to the PF.

 

I need to do a collision check between player0 and playfield. If the player hits a CERTAIN PORTION of the playfield he loses points. If he hits a different

part of the playfield he dies.

Are you talking about certain portions of a single playfield pixel? How big is a playfield pixel, and how are you defining the portions-- i.e., the left half, the top third, the upper left quadrant, etc.?

 

Michael

Like for example I have

X X X

X X X

 

Your player dies if he touches the left and right side, but not if he touches the middle. I'm just going to insert code into my existing game that changes the score to what my player0x is.. this way I can see. I'll define how far left the player can move, and then I'll define a little further in that if you go into that "zone" you'd lose a life.

Like for example I have

X X X

X X X

 

Your player dies if he touches the left and right side, but not if he touches the middle. I'm just going to insert code into my existing game that changes the score to what my player0x is.. this way I can see. I'll define how far left the player can move, and then I'll define a little further in that if you go into that "zone" you'd lose a life.

But are you talking about dividing the entire playfield into zones, or are you talking about dividing individual playfield pixels into zones? And what do the Xs in your example represent? I'm just trying to make sure I understand everything correctly so my answer will be relevant; otherwise I might end up trying to answer the "wrong" question.

 

Michael

Those X's are PFVLINE.

 

For example, say you have PFVLINE 1 1 10 on, PFVLINE 25 1 10 on -- you touch either of those you die.

 

You also have some playfield pieces in the middle of those two PFVLINE.. you touch those and something else happens.

 

Like for example I have

X X X

X X X

 

Your player dies if he touches the left and right side, but not if he touches the middle. I'm just going to insert code into my existing game that changes the score to what my player0x is.. this way I can see. I'll define how far left the player can move, and then I'll define a little further in that if you go into that "zone" you'd lose a life.

But are you talking about dividing the entire playfield into zones, or are you talking about dividing individual playfield pixels into zones? And what do the Xs in your example represent? I'm just trying to make sure I understand everything correctly so my answer will be relevant; otherwise I might end up trying to answer the "wrong" question.

 

Michael

Edited by yuppicide
Those X's are PFVLINE.

 

For example, say you have PFVLINE 1 1 10 on, PFVLINE 25 1 10 on -- you touch either of those you die.

 

You also have some playfield pieces in the middle of those two PFVLINE.. you touch those and something else happens.

Okay, that should make it easy, assuming those pfvlines are the only playfield pixels that are on. When you detect that the player has collided with the playfield, check player0x. If it's less than a certain position, then the player must have collided with the leftmost pfvline. If it's greater than a certain position, then the player must have collided with the rightmost pfvline. And if neither of those is the case, then the player must have collided with the central pfvlines.

 

Michael

Yep.. is what I am going to do soon as I get some free time here.. was having Stella issues at home last night.

 

I am going to modify my program temporarily to have a "debug" mode which will change the score to show my player horizontal position. This way as the game plays I can actually see the number.

 

Those X's are PFVLINE.

 

For example, say you have PFVLINE 1 1 10 on, PFVLINE 25 1 10 on -- you touch either of those you die.

 

You also have some playfield pieces in the middle of those two PFVLINE.. you touch those and something else happens.

Okay, that should make it easy, assuming those pfvlines are the only playfield pixels that are on. When you detect that the player has collided with the playfield, check player0x. If it's less than a certain position, then the player must have collided with the leftmost pfvline. If it's greater than a certain position, then the player must have collided with the rightmost pfvline. And if neither of those is the case, then the player must have collided with the central pfvlines.

 

Michael

  • 3 months later...

Sorry to necropost here, but I'm having trouble making a Pac-Man-type game using this logic. ( a=(player0x-17)/4 : b=(player0y-1)/8 ) Say the player is moving left. All I want to do is have the player not go up when there's a wall in his way, but if he holds up for long enough - and there's no wall in his way - move up (like in Pac-Man). How would I use said logic in a pfread statement in my game if I don't really care which wall he's bumping into, just as long as there is one? I've got the basic movement code established, but I can't seem to use this stuff to put in wall-bumping logic.

Sorry to necropost here, but I'm having trouble making a Pac-Man-type game using this logic. ( a=(player0x-17)/4 : b=(player0y-1)/8 ) Say the player is moving left. All I want to do is have the player not go up when there's a wall in his way, but if he holds up for long enough - and there's no wall in his way - move up (like in Pac-Man). How would I use said logic in a pfread statement in my game if I don't really care which wall he's bumping into, just as long as there is one? I've got the basic movement code established, but I can't seem to use this stuff to put in wall-bumping logic.

I started a Pac-Man style game (without dots) many months ago based on a maze you posted. I seem to have the player/enemy/wall stuff done, I just need to work on sounds and exactly what is going to happen besides having the player evade the enemy. I'm also thinking about calling the game Evade.

 

I'd post the code, but it's kind of messy right now and I was hoping to get more of it done before showing it to people. After I'm done with my adapted version of Robert M's joystick example, I'll see how fast I can get this maze game cleaned up enough to show it to others.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

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...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...