Jump to content
IGNORED

The INTV, IntyBASIC, and Collision Detection


freewheel

Recommended Posts

OK gang, here's a whopper. I can't figure out what is a limitation in the hardware, what is a limitation in IntyBASIC, and what is a limitation with my own stupid brain.

 

The IntyBASIC manual has this little tidbit, when discussing collision detection:

 

Obtains collision between sprites for frame, better used after WAIT

 

And by "better used", he really means "you absolutely must use it". I've seen nothing but problems otherwise. Now, here's my dilemma:

 

As a basic case (stolen directly from pINg which is where this problem is rampant), let's say I'm moving a MOB across the screen horizontally, and I want to see when it hits another MOB. Now let's have the 2 MOBs each be 2 pixels wide. In order to "catch" the collision, I can only move a MOB 2 pixels at a time before checking for collision, otherwise the MOB might move past the target before I check for collision again.

 

WAIT pauses the game for one full frame. There are 60 frames in a second. That means I can only move a MOB at 120 pixels per second, which isn't even an entire screen. We're talking 1.5 seconds to move a MOB, at the highest possible speed, across the screen (if you want collision detection on a small MOB like this - it's even worse if you wanted a 1-pixel wide MOB).

 

So. I'm pretty sure people have written things that move faster than this. What's the answer? Or more to the point, where is this limitation? When you check for collision detection in ASM, do you have to do this WAIT (or equivalent) every time? Or can you check the collision register much more frequently? ie: is this an IntyBASIC limitation?

 

Failing that... is the answer to just abandon using hardware collision detection, and code up my own bounding boxes etc and check for it manually? ie: is this a basic hardware limitation? The CPU can clearly keep up, but man that's a lot of bounds checking etc. Which then becomes a brain limitation :P

 

 

Link to comment
Share on other sites

OK gang, here's a whopper. I can't figure out what is a limitation in the hardware, what is a limitation in IntyBASIC, and what is a limitation with my own stupid brain.

 

The IntyBASIC manual has this little tidbit, when discussing collision detection:

 

 

And by "better used", he really means "you absolutely must use it". I've seen nothing but problems otherwise. Now, here's my dilemma:

 

As a basic case (stolen directly from pINg which is where this problem is rampant), let's say I'm moving a MOB across the screen horizontally, and I want to see when it hits another MOB. Now let's have the 2 MOBs each be 2 pixels wide. In order to "catch" the collision, I can only move a MOB 2 pixels at a time before checking for collision, otherwise the MOB might move past the target before I check for collision again.

 

WAIT pauses the game for one full frame. There are 60 frames in a second. That means I can only move a MOB at 120 pixels per second, which isn't even an entire screen. We're talking 1.5 seconds to move a MOB, at the highest possible speed, across the screen (if you want collision detection on a small MOB like this - it's even worse if you wanted a 1-pixel wide MOB).

The good thing here is that probably your 2-pixel MOB will impact a 8-pixel MOB, so it will not "evade" it.

 

So. I'm pretty sure people have written things that move faster than this. What's the answer? Or more to the point, where is this limitation? When you check for collision detection in ASM, do you have to do this WAIT (or equivalent) every time? Or can you check the collision register much more frequently? ie: is this an IntyBASIC limitation?

The STIC registers are only available for a short period, so these are copied into memory in the interruption routine.

 

I recommend to check collision registers just after WAIT, because IntyBASIC programs could be slow and the data could be effectively "lost" if not read in time.

 

So in fact any Intellivision games out there using STIC registers check these at same speed that video refresh so isn't an IntyBASIC limitation.

 

Failing that... is the answer to just abandon using hardware collision detection, and code up my own bounding boxes etc and check for it manually? ie: is this a basic hardware limitation? The CPU can clearly keep up, but man that's a lot of bounds checking etc. Which then becomes a brain limitation :P

It's a possibility if you want to move things very fast.

Link to comment
Share on other sites

OK gang, here's a whopper. I can't figure out what is a limitation in the hardware, what is a limitation in IntyBASIC, and what is a limitation with my own stupid brain.

 

The IntyBASIC manual has this little tidbit, when discussing collision detection:

 

 

And by "better used", he really means "you absolutely must use it". I've seen nothing but problems otherwise. Now, here's my dilemma:

 

As a basic case (stolen directly from pINg which is where this problem is rampant), let's say I'm moving a MOB across the screen horizontally, and I want to see when it hits another MOB. Now let's have the 2 MOBs each be 2 pixels wide. In order to "catch" the collision, I can only move a MOB 2 pixels at a time before checking for collision, otherwise the MOB might move past the target before I check for collision again.

 

WAIT pauses the game for one full frame. There are 60 frames in a second. That means I can only move a MOB at 120 pixels per second, which isn't even an entire screen. We're talking 1.5 seconds to move a MOB, at the highest possible speed, across the screen (if you want collision detection on a small MOB like this - it's even worse if you wanted a 1-pixel wide MOB).

 

So. I'm pretty sure people have written things that move faster than this. What's the answer? Or more to the point, where is this limitation? When you check for collision detection in ASM, do you have to do this WAIT (or equivalent) every time? Or can you check the collision register much more frequently? ie: is this an IntyBASIC limitation?

 

Failing that... is the answer to just abandon using hardware collision detection, and code up my own bounding boxes etc and check for it manually? ie: is this a basic hardware limitation? The CPU can clearly keep up, but man that's a lot of bounds checking etc. Which then becomes a brain limitation :P

 

 

 

First, let me say that if you're trying to detect collisions between two MOBs each of one or two pixels wide, you're going to hit this limitation on any system. You either will have to limit your MOB velocities, or use a different detection algorithm, such as detecting traversal or intersection rather than pixel-collisions.

 

-dZ.

Link to comment
Share on other sites

 

First, let me say that if you're trying to detect collisions between two MOBs each of one or two pixels wide, you're going to hit this limitation on any system. You either will have to limit your MOB velocities, or use a different detection algorithm, such as detecting traversal or intersection rather than pixel-collisions.

 

-dZ.

 

Maybe I'm not explaining very well. I don't see why you couldn't run this code (let's assume MOB2 is stationary):

 

MOB1_x = MOB1_x - 1

IF MOB1 col MOB2 THEN collision!

REPEAT

 

That could be looped thousands of times in a single second, give how fast CPUs are. So you could move MOBs across the screen a hell of a lot faster than 120 pixels per second!

 

Does the limitation stem from having to draw the MOB to the screen in order to check the collision register, and the drawing can only happen once per frame, therefore you can only check for collision once per frame? ie: this is why the WAIT has to occur?

Link to comment
Share on other sites

When I program on the ColecoVision, I usually put all the gameplay code inside the NMI routine, which for the CV, runs 60 times per second. Something like this pseudo code:

 

nmi()

{

move player

move enemy/bullets

check for collision

}

 

 

For the Intellivision, I would consider doing the following pseudo code:

 

gameplayroutine

wait

check for collision (edited as per nanochess)

move player

move enemy/bullets

if gameplay should still be active goto gameplayroutine

 

In the above, I'm assuming that the wait command waits for the next "cycle", ie. a fraction of 1/60 second. There may be other wrinkles, such as routines that need extra time (keypad??). If all your calculations can be done in 1/60 second or less, then life is good. Otherwise, one method is to only update certain things on odd or even cycles.

 

To move items faster, move them 2 or more pixels per cycle, or if required, use the fractional numbers (to move 1.5 pixels per cycle, for instance).

 

The 120 movements per second sounds like too much trouble to keep track of. Make the movements, and like others have said, if you're checking for a collision between an 8 pixel item and a 2 pixel item, things should work okay if the items aren't moving too many pixels each 1/60 second.

Edited by 5-11under
Link to comment
Share on other sites

The collision register in Intellivision are like those in Atari.

 

These are changed per scanline once the video is rendered. In Atari VCS you can read immediately the collision registers, but in Intellivision you cannot read the registers until the frame has finished display.

 

Anyway there is no point in reading immediately the collision register unless you could be sure the STIC would be displaying the exact line of your MOB, but that would be impossible to determine. So effectively the collision register can only be checked consistenly after each frame and because Intellivision only allows access then.

Link to comment
Share on other sites

So in conclusion, it's how the hardware works, and if you want to move things any faster, you need to write your own collision detection logic.

 

Makes sense, just a bit frustrating :)

 

One of the things I've already figured out is that I can simply double the width of the paddles in pINg, and effecitvely double the speed of the ball without problems. It just won't look as close to PONG :(

  • Like 1
Link to comment
Share on other sites

So in conclusion, it's how the hardware works, and if you want to move things any faster, you need to write your own collision detection logic.

 

Makes sense, just a bit frustrating :)

 

One of the things I've already figured out is that I can simply double the width of the paddles in pINg, and effecitvely double the speed of the ball without problems. It just won't look as close to PONG :(

 

 

Hardware collision detection actually kinda sucks. I (mostly1) avoided it in Space Patrol. The problem is that it's too easy to have very non-intuitive behavior.

 

If you do like IntyBASIC does and copy the collision registers out of the STIC once per frame, and then act on the collisions you see, then your response to the collision will be visible 2 frames after the game state that caused it instead of only 1 frame later.

 

The problem is that you have your actions spread out over three frames:

  1. Compute game state for display (ie. MOB positions)
  2. Display game state (MOBs display on screen) and determine collisions
  3. Read back collision information from the just-displayed frame so we can react to it

These three steps all happen roughly concurrently, with step 3 dovetailing with step 1 two frames later.

 

Concrete example: Suppose you have a ball traveling rightward at 1px / frame. Suppose its X coordinate is 99, and it should "bounce" once it reaches 100 because there's an object at X=100. Bouncing involves negating the X velocity. Here's what can happen as a result. (Each step is one frame time.)

  1. Read previous frame's collision: No collision. Ball displays at X=99 and STIC does not register a collision. New X computed: X = 100. MOB scheduled to display at 100 next frame.
  2. Read previous frame's collision: No collision. Ball displays at X=100 and STIC registers a collision. New X computed: X = 101. MOB scheduled to display at 101 next frame.
  3. Read previous frame's collision: Collision! Reverse velocity. Ball displays at X=101 and STIC registers a collision. New X computed: X = 100. MOB scheduled to display at 100 next frame.
  4. Read previous frame's collision: Collision! Reverse velocity. Ball displays at X=100 and STIC registers a collision. New X computed: X = 101. MOB scheduled to display at 101 next frame.
  5. Read previous frame's collision: Collision! Reverse velocity. Ball displays at X=101 and STIC registers a collision. New X computed: X = 100. MOB scheduled to display at 100 next frame.
  6. Read previous frame's collision: Collision! Reverse velocity. Ball displays at X=100 and STIC registers a collision. New X computed: X = 101. MOB scheduled to display at 101 next frame.

Uh... well that didn't end well. :-) You could fix the wackiness in step 4 onward by adding a test to keep from re-negating the velocity. Even then, though, the ball penetrated into the object one extra pixel. If you get the order of the steps wrong it could be two extra pixels.

 

If you do bounding box checks, you don't have to wait a full frame for the data, and thus can react to the game state as soon as you compute it. For example, once you compute X=100, you know to flip the velocity to affect the next frame. You don't have to wait an entire frame to find out.

 

 

Anyway there is no point in reading immediately the collision register unless you could be sure the STIC would be displaying the exact line of your MOB, but that would be impossible to determine. So effectively the collision register can only be checked consistenly after each frame and because Intellivision only allows access then.

 

Well, in the Intellivision the STIC collision registers are only visible during vblank (that's the small window you pointed out in a different comment), so it's not actually possible to see them during the scanline the MOB's being displayed anyway.

 

 

I recommend to check collision registers just after WAIT, because IntyBASIC programs could be slow and the data could be effectively "lost" if not read in time.

 

Would it be possible to have IntyBASIC accumulate collisions, and then have the program clear them when it's acknowledged them? That's effectively what Imagic's engine in Atlantis does, IIRC. It reads out the collision registers and merges them with a buffer in RAM. Once the game acts on a collision, it clears it from the merged copy. That way no collision is ever lost due to a slow path in the game logic.

 

--------------------------------------------------------------------------------------------

1 The only place I used hardware collision detection in Space Patrol was between bad-guys and the player's tank, in part because I wanted pixel preciseness there, and in part because I was getting low on cycles. I wanted to tilt things so that space ships were a little easier to hit than the player. I used bounding box for the player's bullets against all the bad guys. I didn't want the player cursing that a bullet went through an opponent. When the opposite happens (bad guy bullet passes through the player's tank), they're more likely to get a sigh of relief.

  • Like 1
Link to comment
Share on other sites

 

Would it be possible to have IntyBASIC accumulate collisions, and then have the program clear them when it's acknowledged them? That's effectively what Imagic's engine in Atlantis does, IIRC. It reads out the collision registers and merges them with a buffer in RAM. Once the game acts on a collision, it clears it from the merged copy. That way no collision is ever lost due to a slow path in the game logic.

 

Aha, here's the code in Atlantis that I was referring to:

.

        MVII    #$0350, R3                      ; 49B9   02BB 0350
        MVII    #$0018, R2                      ; 49BB   02BA 0018
        CLRR    R5                              ; 49BD   01ED
        MVII    #$0008, R4                      ; 49BE   02BC 0008
L_49C0:
        MVI@    R2,     R0                      ; 49C0   0290
        MVO@    R5,     R2                      ; 49C1   0255
        MVI@    R3,     R1                      ; 49C2   0299
        COMR    R0                              ; 49C3   0018
        ANDR    R0,     R1                      ; 49C4   0181
        COMR    R0                              ; 49C5   0018
        XORR    R0,     R1                      ; 49C6   01C1
        MVO@    R1,     R3                      ; 49C7   0259
        INCR    R2                              ; 49C8   000A
        INCR    R3                              ; 49C9   000B
        DECR    R4                              ; 49CA   0014
        BNEQ    L_49C0                          ; 49CB   022C 000C

.

Of course, if you use negative logic, (ie. 0 means hit, 1 means no hit), then you can reduce this to:

.

        MVII    #$0350, R3      ; replace $350 with address of coll. table
        MVII    #$0018, R5      ; collision registers
        MOVR    R5,     R4
        CLRR    R1

        REPEAT  7
        MVI@    R5,     R0      ; get hardware collision
        MVO@    R0,     R4      ; clear hardware collision
        AND@    R3,     R0      ; merge collision bits
        MVO@    R0,     R3      ; and store them
        INCR    R3        
        ENDR

        MVI@    R5,     R0      ; get hardware collision (last MOB)
        MVO@    R0,     R4      ; clear hardware collision 
        AND@    R3,     R0      ; merge collision bits
        MVO@    R0,     R3      ; and store them

Edited by intvnut
  • Like 1
Link to comment
Share on other sites

When I made Pong for the Colecovision, I used 7 box collusion for each paddles. Each box collusion change the ball's y projection depending where it is hit. If the paddle is 4 pixel wide, the invisible box collusion itself is 8 pixel wide to ensure that the ball wouldn't pierce through the paddle at maximum speed.

Link to comment
Share on other sites

When I made Pong for the Colecovision, I used 7 box collusion for each paddles. Each box collusion change the ball's y projection depending where it is hit. If the paddle is 4 pixel wide, the invisible box collusion itself is 8 pixel wide to ensure that the ball wouldn't pierce through the paddle at maximum speed.

 

 

 

And on the Colecovision, you needed to implement collision detection in software, correct?

 

As I recall, the TMS9918A VDP doesn't have pixel-level hardware collision detection. It just has a global flag indicating "there was a collision somewhere."

Link to comment
Share on other sites

OK, so it's not just me. I'm a bit relieved because you guys are describing all of the things I've learned - the hard way. I've had some late nights where I was convinced I just wasn't "getting it".

 

So now if I may ask, without being all "hey can you write my code for me" - any tips/pointers on coding bounding box hit detection? It's been - oh I'd say 20 years since I've looked at this. I got SOOO excited with hardware collision detection, too bad it has limited use cases. Am I in for one of those exercises where eventually I'll cobble it together, or is this something relatively obvious and standard to implement?

Link to comment
Share on other sites

OK, so it's not just me. I'm a bit relieved because you guys are describing all of the things I've learned - the hard way. I've had some late nights where I was convinced I just wasn't "getting it".

 

So now if I may ask, without being all "hey can you write my code for me" - any tips/pointers on coding bounding box hit detection? It's been - oh I'd say 20 years since I've looked at this. I got SOOO excited with hardware collision detection, too bad it has limited use cases. Am I in for one of those exercises where eventually I'll cobble it together, or is this something relatively obvious and standard to implement?

 

 

Bounding box is the easiest. You basically want to figure out if two rectangles overlap.

 

If you have the coordinates of the centers of the boxes, you can determine if the two boxes overlap like this:

.

      IF ( abs( x0 - x1 ) * 2 < ( width0 + width1 ) ) AND ( abs( y0 - y1 ) * 2 < ( height0 + height1 ) ) THEN collision

.

 

That assumes that <x0, y0> points to the middle of box #0, and <x1, y1> points to the middle of box #1. If they point to the upper left corner, then you'll need to adjust the math appropriately.

 

Since you're testing collision between a fixed-sized object (a goat or a paddle in the case of pINg), you can pre-bias the numbers a bit to make most of the computations fixed. For example, if you're always comparing a 2x2 box against an 8x4 box, and your coordinates are naturally for "upper left", then the statement above looks more like this:

.

      IF ( abs( (x0 + 1) - (x1 + 4) ) * 2 < 10  ) AND ( abs( (y0 + 1) - (y1 + 2) ) * 2 < 6 ) THEN collision

..

And that can optimize down further:

.

      IF ( abs( x0 - x1 - 3 ) < 5  ) AND ( abs( y0 - y1 - 1 ) < 3 ) THEN collision

.

That can maybe optimize further still (say by breaking the 'abs' open), but you get the idea.

 

 

That's perhaps a shallow treatment of bounding box. But, it gives you a taste of what it looks like.

 

For something like pINg, you can get more specialized than bounding box. You can have one test for "did the ball enter the plane of the paddle" (ie. previously its Y coord was less, and now it's greater) followed by a computation to figure out where it crossed that line, and whether it crossed the line within the boundaries of your paddle. That's going to be much more accurate and reliable if done right.

 

Once you compute where the ball hit the paddle exactly, you can even compute where it reflected to exactly. (Which, for perfect reflection, is a very easy calculation.)

Link to comment
Share on other sites

Would it be possible to have IntyBASIC accumulate collisions, and then have the program clear them when it's acknowledged them? That's effectively what Imagic's engine in Atlantis does, IIRC. It reads out the collision registers and merges them with a buffer in RAM. Once the game acts on a collision, it clears it from the merged copy. That way no collision is ever lost due to a slow path in the game logic.

 

--------------------------------------------------------------------------------------------

1 The only place I used hardware collision detection in Space Patrol was between bad-guys and the player's tank, in part because I wanted pixel preciseness there, and in part because I was getting low on cycles. I wanted to tilt things so that space ships were a little easier to hit than the player. I used bounding box for the player's bullets against all the bad guys. I didn't want the player cursing that a bullet went through an opponent. When the opposite happens (bad guy bullet passes through the player's tank), they're more likely to get a sigh of relief.

 

Yes, it could be possible but I prefer not to steal further cycles from user.

Link to comment
Share on other sites

Yes, it could be possible but I prefer not to steal further cycles from user.

Well, if it is a commonly used feature (and it appears that it will be), then you are not "stealing" the cycles from the user; you are merely taking over functionality that he would have had to implement himself.

 

Moreover, it leads to a more efficient program, since you do not have to constantly poll with the expectation of missing a collision if you don't get it on time.

  • Like 2
Link to comment
Share on other sites

 

Yes, it could be possible but I prefer not to steal further cycles from user.

 

A noble goal!

 

It's an extra 64 cycles to do the merge (one add'l AND@ per word), and ~94 cycles to do the clear. (2 x MVII, 8 x MVO@, and one NOP for interruptibility.) So, about 158 cycles/frame, or 1.2% of the available program cycles. Certainly measurable. As I discovered in SP and again helping John with Kroz, sometimes 150 cycles makes a big difference.

 

If the programmer is strict about keeping their time from WAIT to WAIT below one frame time, then the added cost is pure overhead that buys you nothing. If their game has activity spikes, however, where some frames consume way more than others, then you might get the occasional WAIT that comes too late. In those cases, they'll lose events, and start pulling out hair. It's a tradeoff.

 

Maybe it's better to go in a different direction, especially since hardware collision detection has issues no matter what you do.

 

What about a keyword that does a bounding box collision detect? You give it two MOBs and the box sizes to use, and it does the rest? :-) Something like:

.

    IF BBHIT( 0, 8, 8, 1, 3, 5 ) THEN PRINT "HIT" : REM Syntax: BBHIT( mob0, width0, height0, mob1, width1, height1 )

.

Most of the math could easily be simplified by IntyBASIC under the hood, and you'd be giving cycles back to the programmer.

Edited by intvnut
  • Like 2
Link to comment
Share on other sites

What about a keyword that does a bounding box collision detect? You give it two MOBs and the box sizes to use, and it does the rest? :-) Something like:

.

    IF BBHIT( 0, 8, 8, 1, 3, 5 ) THEN PRINT "HIT" : REM Syntax: BBHIT( mob0, width0, height0, mob1, width1, height1 )

.

Most of the math could easily be simplified by IntyBASIC under the hood, and you'd be giving cycles back to the programmer.

 

Yesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyes. Please!

 

Assuming there's a way to implement this without it being a mess, of course. ;)

Link to comment
Share on other sites

I could not get col0 to work, until i put 2x wait in the lines before the COL0

 

i guess basic have to get the hardware to put it down to the COL0 function?

 

The first WAIT will get the MOB displayed. The second WAIT will allow your program to see what happened while the MOBs were displayed.

 

It's one of the reasons I really don't like hardware collision detection. There is a 1 frame delay because hardware collision detection happens during active display, so you can only be informed after the collision was displayed.

Link to comment
Share on other sites

  • 2 months later...

Resurrecting this to discuss a different flavour of collision detection: collisions with background cards.

 

Again we have a situation where hardware detection works - but not ideally. For all of the above reasons, plus the fact that you have to calculate *which* card was collided with (all the registers tell you is that SOMETHING was collided with).

 

I've been struggling with making it work well, off and on for a couple of months now, both with hardware assistance and purely in software. Nothing easy or simple works 100% of the time. So my question is generic - how do people typically handle this? I see games like Christmas Carol and Ms. Pac-Man, which obviously are doing it and doing it well (not counting the infamous Red Ghost glitch-that-people-don't-think-is-a-glitch). So what's the secret? Let's take your typical maze game - you have dozens upon dozens of dots. Do we store the entire maze state (in this sense I mean the existence of each dot) in an array/table, and every time we move, check to see if we're eating one, and if so, remove it from our array/table and blank out whatever card we're eating? Rinse-repeat for each move? And does the checking simply involve bounding boxes and the like?

 

Intuitively it seems like an awful lot of calculations - but I'll admit that I haven't yet gone down the full path yet.

Link to comment
Share on other sites

(not counting the infamous Red Ghost glitch-that-people-don't-think-is-a-glitch).

 

You mean this one?

 

 

Let's take your typical maze game - you have dozens upon dozens of dots. Do we store the entire maze state (in this sense I mean the existence of each dot) in an array/table, and every time we move, check to see if we're eating one, and if so, remove it from our array/table and blank out whatever card we're eating? Rinse-repeat for each move? And does the checking simply involve bounding boxes and the like?

 

Assuming your maze fits on the screen, then you already have the array you hypothesize: The screen itself. In Space Patrol, I actually look at BACKTAB to figure out where rocks and craters are. I don't have a separate table. For bullet collisions, I simply look at non-empty cards. For moon-buggy vs. rock/crater collisions, I have a "minimum jump height" table I index based on what type of thing I found on the screen (type of rock or crater) and the exact relative position horizontally.

 

In 4-Tris, I look at the exact pattern of 'pixels' (bloxels?) on the display (directly in BACKTAB) to determine whether my piece has hit the pile at the bottom of the well. To make this easier (and not get a 'self-collision'), the falling piece is displayed in color 7. In colored-squares mode, color 7 comes from the color stack. I set the color stack up so that my active piece displays in the correct color, even though numerically it has a different number than any piece in the 'pile'. Once the piece lands, it gets redrawn in its final color.

 

In Christmas Carol (and dZ, correct me if I get any of this wrong), the exact pattern of dots and the presence/absence of snowflakes and presents are also stored in BACKTAB. There's add'l state tracked separately (ie. number of presents remaining on screen, pieces of candy collected, etc.) but the exact pattern of things collected is stored in BACKTAB. Carol's position is used to index into BACKTAB to determine whether she's picked up candy, a snowflake or a present, IIRC. (dZ, again, correct me if I got that wrong.) Now, movement bounding is something else: In Christmas Carol, all of the characters move on a grid, and the constraints imposed by that grid are stored in a table in ROM. For each grid point, you have 4 potential exits, and dZ's scripts build up a bitmap indicating which exits are valid from each grid point from a textual/ASCII-art representation of the maze.

 

KnechtRuprecht can detail how he handles collisions in Kroz if he wishes. He has an elaborate scripting engine that handles interactions between the player and objects on the screen, and a couple different strategies for looking things up. I do believe, though, the first level of lookup involves looking directly at BACKTAB.

 

If you look through the code in Utopia (dissected in this article), it too looks directly at BACKTAB to determine collisions between parked ships and pirates, weather and structures, boats and islands. It also looks directly at BACKTAB to determine what structures you've built and their interactions for scoring, rebel placement, etc.

 

Notice a theme? :)

 

You don't need to do fancy bounding box comparisons to look stuff up in BACKTAB. You can compute which cards a MOB overlaps by dividing its X and Y coordinates by 8. Specifically, (X-8)/8 and (Y-8)/8 give you the column and row in BACKTAB that the upper left corner of the MOB overlaps. This is true regardless of the MOB's size settings. If you're interested in the total span of cards a MOB occupies, then you may need to repeat this computation with different offsets.

 

An 8x8 MOB can overlap up to 4 BACKTAB locations. Those four corners are given by:

  • Upper left: (X-8)/8, (Y-8)/8
  • Upper right: (X-1)/8, (Y-8)/8
  • Lower left: (X-8)/8, (Y-1)/8
  • Lower right: (X-1)/8, (Y-1)/8

In Tag Along Todd 2, I just had a single "hot point" in Todd that was used to determine whether I was over a "can". I used a bias of -4 in both X and Y to see if Todd's middle was over a BACKTAB square containing a can. That's all it needed. Here's the relevant code:

.

            ;; ------------------------------------------------------------ ;;
            ;;  See if we're on a pop can.                                  ;;
            ;; ------------------------------------------------------------ ;;
            MVI     PLYR.YP,R1      ;\
            SWAP    R1              ; |
            SUBI    #4,     R1      ; |
            ANDI    #$F8,   R1      ; |
            MOVR    R1,     R2      ; |-- Generate row offset from Y coord.
            ADDR    R1,     R2      ; |
            SLR     R1,     1       ; |
            ADDR    R1,     R2      ;/
        
            MVI     PLYR.XP,R1      ;\
            SWAP    R1              ; |
            SUBI    #4,     R1      ; |
            ANDI    #$F8,   R1      ; |__ Generate column offset from X coord.
            SLR     R1,     2       ; |
            SLR     R1,     1       ; |
            ADDR    R1,     R2      ;/

 ... followed by code to read from BACKTAB.

.

(Note: I combined dividing Y by 8 and multiplying the result by 20 into one step in the code above.)

 

I'm not sure what the best way to write that in IntyBASIC would be, but I imagine something like this comes close to working:

.

    REM  Look up a value from BACKTAB based on X/Y position, biased to roughly the middle of an 8x8 MOB
    val = PEEK( 512 + (X-4)/8 + ((Y-4)/8) * 20 )

.

And then you have to pick apart 'val' accordingly. 9-bit card number is in bits 11..3. (0 .. 255 are GROM, 256 .. 511 are GRAM).

 

 

One last trick: You can hide bit flags in BACKTAB. I mentioned above that usually card # is enough, but that's not always true. Fortunately, you can stash 2 bits of information in every BACKTAB card. Bits 14 and 15 aren't used by the STIC and so you can put whatever you want in them.

 

Also, in Color Stack mode, bits 9 and 10 aren't used if you're displaying a GRAM card, giving you two more bits you can use for flags. I use these to great effect in Space Patrol. In one place, I use them to encode how many points an item's worth. :-)

Edited by intvnut
  • Like 1
Link to comment
Share on other sites

Hm. Maybe that's the secret - I'm not sure how exactly to look at BACKTAB in IntyBASIC without PEEKs. Back to the drawing board. Care to elaborate on the magic numbers in your example PEEK? :)

 

Also part of my problem is that I'm not using an 8x8 MOB, so it makes calculations a bit more complicated - but I think I have that kinda-sorta squared away with another trick - just a matter of isolating what I'm interested in. Fortunately I'm already doing a lot of what you describe, so at least I managed to muddle my way mostly down the right path. Almost feels like I have a clue what I'm doing sometimes.

 

 

And yes, that is the Pac-Man bug I was referring to.

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