Jump to content
IGNORED

how to disable specific copy of enemy


easmith

Recommended Posts

Ok so my "retirement " lasted 2 days.... going down the hole..

Thanks for the awesome help. Makes it easier to keep going.

 

 

 

I now have a my P1 enemy copied three times . My code skips the enemy draw if the enemy is hit my missile.

 

This makes all three copies disappear of course if any of them are hit.

 

How to get the specific enemy hit to disappear is my current challenge.

 

Perhaps this involves more complicated timing and redrawing a single copy on multiple lines. But even this seems like it would eliminate all enemy images if any are hit.

 

Changing the NUSIZ1 register would reduce the copies but would not remove the correct one.

 

Again, probably missing something obvious. This might be a level 10 skill for a level 1 user.

shooter4.asm

shooter.bin

Link to comment
Share on other sites

I suppose it will take two consecutive hmoves of 8 to accomplish the shift right to remove left alien after NUSIZE1 from 3 med to 2 med ....

To remove middle , 3 med to 2 wide I suppose

to remove right, 3 med to 2 med ....

But how to keep track of which copy was hit?

Link to comment
Share on other sites

I don't know how existing games have chosen to implement this, but if I were to do it I'd view the three enemies as a single enemy. This aggregate enemy would have 8 states corresponding to which of the three copies is active. (3 copies, 2 states, 2^3=8 states total)

 

The collision flag can tell you that the enemy was hit somewhere, then you have to compare missile position with enemy position and transition to a different state based on which copy was hit and which state you're currently in. Since there's only 8 states and 3 copies you could have a table to track which state to transition to. That should only be 24 bytes of ROM. Simply take copyHit, shift left 3, bitwise OR with currentState and you have the index into the table to update currentState with. You'll probably want some tables to lookup the NUSIZ1 value for each state (8 ROM bytes), and the horizontal offset for each transition (24 ROM bytes). Still it's only taking up 56 bytes of ROM total for the state management lookup tables.

 

I wouldn't use hmoves to adjust the position when the left copy is removed. Instead I'd just add 16 to the variable which stores P1's horizontal position. You're going to have to keep track of it's position for the collision detection anyway so I'd use the PositionObject() routine each frame instead of using hmove to move around the enemies.

 

Hope that helps.

Link to comment
Share on other sites

I don't know how existing games have chosen to implement this, but if I were to do it I'd view the three enemies as a single enemy. This aggregate enemy would have 8 states corresponding to which of the three copies is active. (3 copies, 2 states, 2^3=8 states total)

 

The collision flag can tell you that the enemy was hit somewhere, then you have to compare missile position with enemy position and transition to a different state based on which copy was hit and which state you're currently in. Since there's only 8 states and 3 copies you could have a table to track which state to transition to. That should only be 24 bytes of ROM. Simply take copyHit, shift left 3, bitwise OR with currentState and you have the index into the table to update currentState with. You'll probably want some tables to lookup the NUSIZ1 value for each state (8 ROM bytes), and the horizontal offset for each transition (24 ROM bytes). Still it's only taking up 56 bytes of ROM total for the state management lookup tables.

 

I wouldn't use hmoves to adjust the position when the left copy is removed. Instead I'd just add 16 to the variable which stores P1's horizontal position. You're going to have to keep track of it's position for the collision detection anyway so I'd use the PositionObject() routine each frame instead of using hmove to move around the enemies.

 

Hope that helps.

 

Well I sure learned something here.

 

This is amazing help, in my opinion.

Link to comment
Share on other sites

I don't know how existing games have chosen to implement this, but if I were to do it I'd view the three enemies as a single enemy. This aggregate enemy would have 8 states corresponding to which of the three copies is active. (3 copies, 2 states, 2^3=8 states total)

 

The collision flag can tell you that the enemy was hit somewhere, then you have to compare missile position with enemy position and transition to a different state based on which copy was hit and which state you're currently in. Since there's only 8 states and 3 copies you could have a table to track which state to transition to. That should only be 24 bytes of ROM. Simply take copyHit, shift left 3, bitwise OR with currentState and you have the index into the table to update currentState with. You'll probably want some tables to lookup the NUSIZ1 value for each state (8 ROM bytes), and the horizontal offset for each transition (24 ROM bytes). Still it's only taking up 56 bytes of ROM total for the state management lookup tables.

 

I wouldn't use hmoves to adjust the position when the left copy is removed. Instead I'd just add 16 to the variable which stores P1's horizontal position. You're going to have to keep track of it's position for the collision detection anyway so I'd use the PositionObject() routine each frame instead of using hmove to move around the enemies.

 

Hope that helps.

 

perhaps you could give me a bit more of a hint on what you are envisioning with the table. I am not sure I can see it/ too dumb to see it.

 

 

As for determining which copy was hit, here is an example of my logic for 3 active copies:

 

1. determine missile x pos ( player x pos + 8 at time of fire , player is double wide) ( assuming player x position is left edge of player)

2. if collision detected

add 12 to enemy left copy x and store ( position half way between copy 1 and 2)

add another 16 to enemy left copy x and store ( position half way between copy 2 and 3)

3. if missile x pos> (left copy x +28) then copy 3 was hit

else

if missile xpos < (leftcopy x +12) then copy one was hit

else copy 2 was hit.

 

Seems like this should work but it is not......

Edited by easmith
Link to comment
Share on other sites

perhaps you could give me a bit more of a hint on what you are envisioning with the table. I am not sure I can see it/ too dumb to see it.

 

 

As for determining which copy was hit, here is an example of my logic for 3 active copies:

 

1. determine missile x pos ( player x pos + 8 at time of fire , player is double wide) ( assuming player x position is left edge of player)

2. if collision detected

add 12 to enemy left copy x and store ( position half way between copy 1 and 2)

add another 16 to enemy left copy x and store ( position half way between copy 2 and 3

)

3. if missile x pos> (left copy x +28) then copy 3 was hit

else

if missile xpos < (leftcopy x +12) then copy one was hit

else copy 2 was hit.

 

Seems like this should work but it is not......

 

Your hit detection algorithm appears to be correct. You probably just have a bug in the implementation of it. Don't forget to post asm and bin files for problems like this. We can't help with debugging without them.

 

Regarding the tables. Here's some more details.

 

Variables:

enemyHitIndex - Should be 0 for the left copy, 1 for the middle copy and 2 for the right copy

enemyX - X position of enemy stored as distance between left side of screen and left side of left copy in pixels. This should always apply to the left copy regardless of it being active to keep hit detection simple.

enemyXOffset - offset to be added to the enemyX position when calling the PostionObject routine.

enemyState - 3 bit value indicating the state of the enemy. Spawn with state 7 for 3 active copies, and don't draw for state 0 because there are no copies left. Each bit corresponds to a single copy. 1 indicates the copy is active(visible), and 0 indicates it is inactive(hidden) I.E. %00000101 = X _ X and %00000110 = X X _

 

Lookup Tables:

EnemyXOffsetLooup - Use the same index as the EnemyStateTransitionLookup to determine what value to add to the current enemyX value. This compensates for deactivating the leftmost active copy of the enemy by moving it right to where the next copy starts.

EnemyStateTransitionLookup - Index into this table with the hit detection result and enemyState to determine what the next state should be. Index is the concatenation of the 2 Hit bits (H) and 3 State bits (S) in the format %000HHSSS

EnemyNusizLookup - Index into this table with the enemyState value to determine the correct NUSIZ1 value.

 

Here's some sample code. It's untested, but should be close to what you need.

 

; Calculate state transition index
  enemyHitIndex
  asl
  asl
  asl
  ora enemyState
  tax

; update X offset
  ; assumes C flag is cleared by code above. If something else comes between this two sections add a CLC instruction here
  lda EnemyXOffsetLooup,x
  adc enemyXOffset
  sta enemyXOffset

; update current state
  ; x still has transition index
  lda EnemyStateTransitionLookup,x
  sta enemyState

; Set NUSIZ1.
  ; assumes A contains enemyState. If this is done late you must replace tax with ldx enemyState
  tax
  lda EnemyNusizLookup,x
  sta NUSIZ1

 

The tables shouldn't be difficult to generate manually. The states are 3 bits, just visualize which copies are still active after a hit occurs. The offsets will be 0 except for a few cases where the left copy is hit, in those cases the value will be 16. The NUSIZ lookup is just mapping the state to the right count and spacing for the remaining active copies.

 

EnemyXOffsetLooup

00000 - 0 ; 
...
00100 - 0 ; Left copy hit, but already inactive. No action required.
00101 - 16 ; Left copy hit and middle is already gone So move over 32 because right copy is all that's left
00111 - 16 ; Left copy hit which means the middle copy will be the new location. So move over 16
...
01011 - 16 ; middle copy hit leaving only the right copy. So move over 16
...

 

EnemyStateTransitionLookup​:

00011 - 011 ; State unchanged because inactive enemy was hit, normal execution will never go here
...
00111 - 011 ; Left copy hit and deactivated
...
01111 - 101 ; Middle copy hit and deactivated
...
10111 - 110 ; Right copy hit and deactivated

 

EnemyNusizLookup:

000 - 7 ; make it quad cause we should never draw this
001 - 0 ; single copy for the right copy
010 - 0 ; single copy for the middle copy
011 - 1 ; 2 copies close for middle and right copies
100 - 0 ; single copy for the left copy
101 - 2 ; 2 copies med for left and right copies
110 - 1 ; 2 copies close for left and middle
111 - 3 ; 3 copies close for left, middle, right
Link to comment
Share on other sites

thanks.. I am pretty darn impressed by your skills....

 

 

But I couldn't move on to yet to transitions from 3 enemies until I figured out the correct detection of which enemy is hit

 

For simplicity, I reduced to 2 enemy copies to get the basic detection logic correct .

This seems to be working now, the key was keeping track of enemy x and repositioning each frame, as you suggested.

 

I think before, when I was positioning before the main loop, my enemy x position counter was ending up out of step with the actual screen positon due to the horizontal wraparound?

 

At any rate , now my enemies have a "jitter" . I can't figure out why this is happening. When I positioned the enemy before main loop the enemy motion was smooth ( decreasing right to left)

 

see difference from shooter 4 to shooter 7

shooter4.asm

shooter4.bin

shooter7.asm

shooter7.bin

Edited by easmith
Link to comment
Share on other sites

I think what happened is that the position alien routine has been broken, but since you only used it for the initial position in version 3 it went unnoticed. Now that you're using it every frame it's noticeable. The problem is that you can't write to any of the HMxx registers immediately after you strobe HMOVE. You must wait at least 24 cycles or it will interfere with the HMOVE process. (See stella programmer guide pg9)

 

This may also explain the problems you're having with detecting which copy was hit. That's not going to work so well if the objects are not positioned where you think they are.

 

You're routine is also a few cycles off for other reasons. Why don't you just use the code from the collect tutorial? No need to make your own unless you're trying to do something advanced like positioning objects while also drawing a playfield.

    sta WSYNC
   sta HMOVE
    lda #0
    sta HMP1 ;TOO SOON!!!

Link to comment
Share on other sites

 

I think what happened is that the position alien routine has been broken, but since you only used it for the initial position in version 3 it went unnoticed. Now that you're using it every frame it's noticeable. The problem is that you can't write to any of the HMxx registers immediately after you strobe HMOVE. You must wait at least 24 cycles or it will interfere with the HMOVE process. (See stella programmer guide pg9)

 

This may also explain the problems you're having with detecting which copy was hit. That's not going to work so well if the objects are not positioned where you think they are.

 

You're routine is also a few cycles off for other reasons. Why don't you just use the code from the collect tutorial? No need to make your own unless you're trying to do something advanced like positioning objects while also drawing a playfield.

    sta WSYNC
   sta HMOVE
    lda #0
    sta HMP1 ;TOO SOON!!!

 

 

 

ok ----I was missing a shift in my positioning code. That and removing the hmp1 clear too soon fixed it . I remember reading that in the Stella guide , but it's a lot to keep track of. Man what a finicky sucker this is....

Thanks again, see attached. ( Keyboard 1 resets )

shooter8.bin

Edited by easmith
Link to comment
Share on other sites

I remember reading that in the Stella guide , but it's a lot to keep track of. Man what a finicky sucker this is....

True, but doing the wrong thing often leads to useful results. Hitting the HMxx registers at the wrong time leads to stars, while hitting HMOVE at the wrong time can eliminate the black bars that normally occur when repositioning objects mid-screen.

 

  • Like 1
Link to comment
Share on other sites

ok,something weird has happened . All of a sudden the game stopped firing when the button pressed , although I did not change the code..........

 

Could this be a corrupt dasm ? A few times I got a message saying dasm could not open when trying to compile .

 

That is one problem.

 

The other problem is that my new version , now just still has 2 enemies, but I added enemy fire ( M1) and collision detection with player.

 

but now nothing seems to work : collision detection , firing of either missile. I think my programming logic is sound...

 

 

can someone please compile the shooter7.asm and see if the player fire works and perhaps my dasm is broken

 

Also , can someone help me figure out why the shooterefire.asm with shooterefire.bin player fire and enemy fire are not registering?

 

thanks.

Link to comment
Share on other sites

I figured out the player fire bug in the old file , I had changed a bcs to bcc by accident . I think I lost version control.

 

still can't figure out the enemy fire or collision detection bug in new version shooterefire

 

the "shooter " is old version where collision works

 

 

 

shooterefire.asm

shooterefire.bin

Shooter.bin

Edited by easmith
Link to comment
Share on other sites

 

I think what happened is that the position alien routine has been broken, but since you only used it for the initial position in version 3 it went unnoticed. Now that you're using it every frame it's noticeable. The problem is that you can't write to any of the HMxx registers immediately after you strobe HMOVE. You must wait at least 24 cycles or it will interfere with the HMOVE process. (See stella programmer guide pg9)

 

This may also explain the problems you're having with detecting which copy was hit. That's not going to work so well if the objects are not positioned where you think they are.

 

You're routine is also a few cycles off for other reasons. Why don't you just use the code from the collect tutorial? No need to make your own unless you're trying to do something advanced like positioning objects while also drawing a playfield.

    sta WSYNC
   sta HMOVE
    lda #0
    sta HMP1 ;TOO SOON!!!

 

 

 

 

ok. I used the hor positioning code from the tutorial. I froze the motion of the enemy to check positioning. looks good. However , collisions don't work anymore still.

 

I wonder if I a using too many cycles on a scanline for the main play area?? See code in blue . I am not sure if I can accurately count cycles ( I got around 66 with branches??)

 

Note how when you move player off the screen to the left there is a weird jitter. Also there is part of a brown line on the lower right and the top of the ground .

 

Something has to be off but I just cannot find it .

shooterefire3.asm

shooterefire.bin

Edited by easmith
Link to comment
Share on other sites

The weird jitter you see is because you're writing to GRP1 at that time. You use too many cycles at the beginning of the scan line. Move some of that logic into vblank. If you want to hide the alien you should put its Y position outside the visible screen or set it's graphics pointer to a block of 0's

Link to comment
Share on other sites

I could find no information on this used back in the day technique.

I made a routine in batari Basic, gave it to Random Terrain to post on his site.

He fixed a bug and found some of the code wasn't needed at all.

 

It it does what you are doing in assembly.

Checks for collision, then checks where: left, middle, or right of triple, or left, right of double.

Then it changes NUSIZ and repositions if necessary.

 

It it would be interesting to compare the compiled assembly to the coded assembly.

I would guess the compiled basic would be larger, more wasteful, and possibly redundant.

 

I'm just glad to see others figuring this out.

Link to comment
Share on other sites

The weird jitter you see is because you're writing to GRP1 at that time. You use too many cycles at the beginning of the scan line. Move some of that logic into vblank. If you want to hide the alien you should put its Y position outside the visible screen or set it's graphics pointer to a block of 0's

 

so I have altered the hor. positioning routines to match the Collect tutorial.

I have moved the logic for deciding whether to show enemy if hit twice to overscan.

this removed the enemy jitter.

I have fixed the weird jitter of player0 by restricting the movement range between x=0 and x= 159.

 

But the collisions ( M0-P1 and M1 - P0 ) are still not registering even though the logic is the same as a previous version where the collisions were detected and the appropriate enemy copy removed. Once I added in the Missile 1 dynamic the collisions stopped registering .

 

I am truly stumped at this point. I cannot figure out what is wrong. Case of beer to anyone that can figure out my bug ! :)

shooterefire3.asm

shooterefire.bin

Edited by easmith
Link to comment
Share on other sites

I'd also like to suggest that you clean up your code some. The easier it is to read, the easier it is to debug. Suffix labels with : when you declare them. Keep indentation and white space consistent. Use SUBROUTINE and local labels like .1, .2, ... for procedural branch targets. Use empty lines to separate different pieces of the program. This might seem like a waste of effort, but you spend so much more time reading code than writing it that it pays huge dividends to put in some effort for readability.

 

Here's an example:

PositionObjects:  SUBROUTINE  
  sta HMCLR
  ldx  #3
.1
  lda PlayerX,x
  jsr PositionObject
  dex
  bpl .1
  sta WSYNC
  sta HMOVE

PollForVBlankEnd: SUBROUTINE
  sta WSYNC 
  lda INTIM       
  bne PollForVBlankEnd 
  sta VBLANK    
Link to comment
Share on other sites

Ok, I tried to clean the code up a bit, and fix the stack issue. Sorry about that . I am not a programmer by trade so my etiquette is probably terrible.

 

At any rate I got the collisions to work again!!! I did not mention this , but in the earlier version I had the logic for changing players and missiles x and y , and for collisions, in Overscan .

 

In the newer version I had moved the collision detection to VBlank and left the other logic in Overscan . So changing the order somehow threw everything off.

 

On to next step.

 

Thanks Zach

shooterefire4.asm

shooterefire.bin

Link to comment
Share on other sites

An alternate solution to the original question is to always draw 3 copies...but draw blank bitmaps in place of the one(s) hit. This has the added benefit of each sprite in the group allowed to be separate bitmaps, anyway (for a fancy "I am shot" animation or whatever). Space Invaders does this pretty effectively...NUSIZ registers are only altered as bordering columns are removed completely.

  • Like 2
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...