This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Collision detection problem with my project.

Collision detection problem with my project.
by on (#219017)
Hello to everyone,
I'm working on an Arkanoid clone for NES all in ASM 6052 just for fun, currently, in my game, the ball collide fine with the walls, with the paddle player and I'm really happy for this. But, when I try to compute the collision with the bricks, somethings goes wrong.

Code:
;=====================================
; Ball Collision Detection With Bricks
;===================================== 
CheckCollisionOnAllBricks:
  LDX #$00
CheckCollisionOnAllBricksLoop:
  LDA brick_sprites, x
 
  ;Y MIN
  CMP BALL_POS_Y
  BCS PerformBallNotHitBrick

  ;Y MAX
  CLC
  ADC #$08
  CMP BALL_POS_Y
  BCC PerformBallNotHitBrick

  ;X MIN
  INX
  INX
  INX
  LDA brick_sprites, x
  DEX
  DEX
  DEX
  CMP BALL_POS_X
  BCS PerformBallNotHitBrick

  ;X MAX
  CLC
  ADC #$18
  CMP BALL_POS_X
  BCC PerformBallNotHitBrick

  ; Collisione
PerformBallCollisionWithBrick:
  LDA #$01
  STA $0212
  JMP CCCOABContinueLoop

PerformBallNotHitBrick:
  LDA #$00
  STA $0212
  JMP CCCOABContinueLoop

CCCOABContinueLoop:

  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
 
  CPX #$18
  BNE CheckCollisionOnAllBricksLoop


The collision detection with the ball is simple, i get the position of the ball and i check if the ball is in the brick corner, this for all bricks ( in this code there are only 2 bricks ), every bricks is composed by 3 tiles with 8 pixels of height and 12 pixels of weight. So i increase my X register to get the positions of the corners that are stored here:

Code:
brick_sprites:
      ;yPos tile attr xPos
 
  .db $3A, $04, $0A, $70   ;sprite 0
  .db $3A, $05, $0A, $78   ;sprite 1
  .db $3A, $06, $0A, $7F   ;sprite 2

  .db $22, $04, $0A, $87   ;sprite 0
  .db $22, $05, $0A, $8F   ;sprite 1
  .db $22, $06, $0A, $97   ;sprite 2


The strange things is that if I use only one brick and finish the check by CPX #$0C, everythings works great, BUT if i put two bricks as in the example code and i finish the cicle by compater CPX #$18 for some reason it get just the second brick, the first brick is ignored. I really can't understand where I'm wrong. Anyone can help me?

Thanks a lot and sorry for my English! :)
Re: Collision detection problem with my project.
by on (#219018)
I solved! :D

Code:
;=====================================
; Ball Collision Detection With Bricks
;===================================== 
CheckCollisionOnAllBricks:
  LDX #$00
CheckCollisionOnAllBricksLoop:
  LDA brick_sprites, x
 
  ;Y MIN
  CMP BALL_POS_Y
  BCS PerformBallNotHitBrick ; La pallina si trova SOPRA il mattoncino, non ci sarà collisione

  ;Y MAX
  CLC
  ADC #$08
  CMP BALL_POS_Y
  BCC PerformBallNotHitBrick ; La pallina si trova SOTTO il mattoncino, no collisione

  ;X MIN
  INX
  INX
  INX
  LDA brick_sprites, x
  DEX
  DEX
  DEX
  CMP BALL_POS_X
  BCS PerformBallNotHitBrick

  ;X MAX
  CLC
  ADC #$18
  CMP BALL_POS_X
  BCC PerformBallNotHitBrick

  ; Collisione
PerformBallCollisionWithBrick:
  LDA #$01
  STA $0212
 [b][i] JMP ComputeCollidingWithBrick[/i][/b]

PerformBallNotHitBrick:
  LDA #$00
  STA $0212
  JMP CCCOABContinueLoop

CCCOABContinueLoop:

  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
 
  CPX #$18
  BNE CheckCollisionOnAllBricksLoop

[i][b]ComputeCollidingWithBrick:[/b][/i]



When I get a collision, I've to to stop the cycle and exiting and compute the collision in
Code:
ComputeCollidingWithBrick:
, the previous code works, but if i don't exit to the cycle when I get a collision, the next interaction on the next brick will set the collision as False.

Man, ASM programming is so insane :P.
Re: Collision detection problem with my project.
by on (#219020)
Glad you figured it out kikutano!

Now that it's working here are a few tricks which can speed up your code:

Code:
  BCS PerformBallNotHitBrick ; La pallina si trova SOPRA il mattoncino, non ci sarà collisione

  ;Y MAX
  CLC

CLC is redundant here. The BCS above it ensures that the carry bit will always be 0 at this point.


Code:
  INX
  INX
  INX
  LDA brick_sprites, x
  DEX
  DEX
  DEX

Can be written as "LDA brick_sprites+3, x". No need for INX/DEX at all.


Code:
  ; Collisione
PerformBallCollisionWithBrick:
  LDA #$01
  STA $0212
   JMP ComputeCollidingWithBrick

PerformBallNotHitBrick:
  LDA #$00
  STA $0212
  JMP CCCOABContinueLoop

One thing you can do is use the carry bit to return this information, rather than a memory location. That tends to easier.

Code:
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
  INX
 

Try using this instead:


Code:
CLC
TXA
ADC #12
TAX
 



Then end result would be something like:

Code:
CheckCollisionOnAllBricks:
  LDX #$00
CheckCollisionOnAllBricksLoop:
  LDA brick_sprites, x
 
  ;Y MIN
  CMP BALL_POS_Y
  BCS PerformBallNotHitBrick ; La pallina si trova SOPRA il mattoncino, non ci sarà collisione

  ;Y MAX
  ADC #$08
  CMP BALL_POS_Y
  BCC PerformBallNotHitBrick ; La pallina si trova SOTTO il mattoncino, no collisione

  ;X MIN
  LDA brick_sprites+3, x
  CMP BALL_POS_X
  BCS PerformBallNotHitBrick

  ;X MAX
  ADC #$18
  CMP BALL_POS_X
  BCS PerformBallCollisionWithBrick

PerformBallNotHitBrick:
  CLC
  TXA
  ADC #12
  TAX
 
  CPX #$18
  BCC CheckCollisionOnAllBricksLoop
  CLC
PerformBallCollisionWithBrick:
  RTS
Re: Collision detection problem with my project.
by on (#219021)
BTW, you probably know this, but Arkanoid uses the background to draw blocks and not sprites. Collisions are checked by masking rounding the ball's position to the block size and looking up that position in a bit array.

This doesn't require loops, so it's more efficient than sprites. You can also display a lot more blocks by doing this.
Re: Collision detection problem with my project.
by on (#219023)
Ok, thanks a lot for your suggestions. I never wrote anything in ASM, so I'm a complete newbie. :) Now I will try to put the Bricks on background.
Re: Collision detection problem with my project.
by on (#219049)
Too slow to make myself useful, but it's always good to see a new face here. Welcome to NESdev!
Re: Collision detection problem with my project.
by on (#219066)
Thanks, probably I will post other questions during the development of my Arkanoid Clone :).
Re: Collision detection problem with my project.
by on (#219108)
pubby wrote:
BTW, you probably know this, but Arkanoid uses the background to draw blocks and not sprites. Collisions are checked by masking rounding the ball's position to the block size and looking up that position in a bit array.

This doesn't require loops, so it's more efficient than sprites. You can also display a lot more blocks by doing this.


Did you have some source or link that I can read? Thanks! :)
Re: Collision detection problem with my project.
by on (#219112)
Nope, I don't have any sources or articles :(

If you post what you're having trouble with then me or someone else can help you out though. :) Have you figured out how to change the background?

Also, there is an open source NES breakout game here you can look at: https://github.com/AleffCorrea/BrickBreaker
Re: Collision detection problem with my project.
by on (#219117)
pubby wrote:
Nope, I don't have any sources or articles :(

If you post what you're having trouble with then me or someone else can help you out though. :) Have you figured out how to change the background?

Also, there is an open source NES breakout game here you can look at: https://github.com/AleffCorrea/BrickBreaker


I'm working on using background and not sprites for bricks, if I had some issue I will ask here :P. Btw thanks for the link I will try to learn somethings from that code.
Re: Collision detection problem with my project.
by on (#219400)
I'm putting all on background, now I've to rewrite the collision detection with the bricks :). The hard part is understand how the palette and color works :D. I hope to release the source code as soon as I can.

Image
Re: Collision detection problem with my project.
by on (#219421)
Looks great!
Re: Collision detection problem with my project.
by on (#219422)
I'm the author of the brick breaker game ^^

To interpret collision detection here's what it does:

"Move" the ball temporarily.

Translate the coordinates of the ball in relation to the tilemap's start position (in your case, subtract 16 to the ball's X and Y coords since the "play area" starts at (16,16))

For each corner of the ball bounding box, divide its coordinates by 16 (since my game uses 16x16 blocks, you might want to divide X by 16 and Y by 8 in your case). Those 4 values are the blocks in the background in which the ball is currently touching/inside.

Clear two bytes, to be used as flags for horizontal and vertical movement collision.

For each of the 4 tiles: check what type of tile it is (empty, solid, indestructible, resistant, explosive, hole, you name it) and apply the proper response to the collision (destroy tile?), but don't change the ball direction yet. Evaluate the balls movement vector and if the ball is supposed to change its horizontal/vertical direction after colliding, turn on their respective flags.

After checking all 4 tiles, finally use the flags to change the ball's trajectory.

End.


I hope it's kinda clear, let us know if you still need help.
Re: Collision detection problem with my project.
by on (#219424)
Punch wrote:
I'm the author of the brick breaker game ^^

To interpret collision detection here's what it does:

"Move" the ball temporarily.

Translate the coordinates of the ball in relation to the tilemap's start position (in your case, subtract 16 to the ball's X and Y coords since the "play area" starts at (16,16))

For each corner of the ball bounding box, divide its coordinates by 16 (since my game uses 16x16 blocks, you might want to divide X by 16 and Y by 8 in your case). Those 4 values are the blocks in the background in which the ball is currently touching/inside.

Clear two bytes, to be used as flags for horizontal and vertical movement collision.

For each of the 4 tiles: check what type of tile it is (empty, solid, indestructible, resistant, explosive, hole, you name it) and apply the proper response to the collision (destroy tile?), but don't change the ball direction yet. Evaluate the balls movement vector and if the ball is supposed to change its horizontal/vertical direction after colliding, turn on their respective flags.

After checking all 4 tiles, finally use the flags to change the ball's trajectory.

End.


I hope it's kinda clear, let us know if you still need help.


Thanks a lot! :)
Re: Collision detection problem with my project.
by on (#219450)
One thing that isn’t super trivial is figuring out how to “delete” nametable blocks - calculating the position in the nametable from the ball’s x and y position to draw new tiles to. It’s a bit more complicated than just dividing by 16 and looking up a byte in a collision map.
I can’t recall off the top of my head exactly how to do it, but it involves some 16-bit bit shifting. I know there are forum posts about it though. Basically you find the address of the first tile depending on where you collided, change it, and then since it appears you’re using 16x8 pixel metatiles, you’d just increment whatever PPU address you’re at and change that tile as well. Be sure to update your collision map in regular RAM as well.

Of course, you’d want to put this all into a buffer, and then perform the PPU updates during vblank. I don’t know if you have a VRAM buffer set up just yet; but there’s information on the wiki about it as well. Just something to think about.
Re: Collision detection problem with my project.
by on (#219471)
Just remember NNyyyyyxxxxx, and you're good.
Re: Collision detection problem with my project.
by on (#219475)
And you might have to add 1 or 2 to the sprite's y before converting to nametable address, to get it to visually line up. Otherwise the block might disappear when the Sprite is still 1 pixel away.
Re: Collision detection problem with my project.
by on (#219486)
You shouldn't be converting sprite coordinates into NT addresses directly, though... The "correct" thing to do would be to run the game logic in "game world space", a space that contains all your game objects and doesn't care about sprite evaluation delays or scrolling, and convert coordinatrs from that space into sprite space or NT addresses when needed.

Some simpler games (single screen without scrolling) do end up unifying all these spaces to simplify things, and will even set the scroll to -1 (i.e. 239 or 255) to push the background 1 pixel down so it'll align with sprite coordinates, eliminating the need to compensate for that 1 scanline delay that sprites have.
Re: Collision detection problem with my project.
by on (#219489)
Quote:
You shouldn't be converting sprite coordinates into NT addresses directly


Yes he should. He needs to change a tile. That requires an address.

EDIT, and I always forget about setting Y scroll to -1. I remember that Shiru's example game Chase sets the X scroll to 4 (on a transition screen) so that text is exactly lined up to the middle of the screen. Another thing I frequently forget is possible.
Re: Collision detection problem with my project.
by on (#219499)
[quote="dougeff”]Yes he should. He needs to change a tile. That requires an address.[/quote]
You wanna treat the ball as an object with a hitbox though; not as a sprite.

A full-fledged metasprite system probably isn’t necessary for this game. Kikutano, you could probably get by with just having variables for the ball’s X and Y, as well as the X and Y for the hitbox. In the ball’s code you’d just update both one after the other. This way you can check for collisions on all the corners. Then after the ball’s done running, you can have a simple routine that just stores the ball’s X and Y coordinates into the OAM shadow. You can even subtract 1 from the ball’s Y position to fix the scanline offset problem people were talking about.

Code:
DrawBall:
  lda ball_x
  sta $0203
  lda ball_y
  sec
  sbc #1   ;account for the 1 scanline delay for sprite rendering
  sta $0200
  rts

Directly working with OAM like this is bad practice for an actual game, but at the same time it’s good practice to separate “object code” from “drawing code”.

Edit: BBcode isnt working for some reason. Maybe it’s something I did.
Re: Collision detection problem with my project.
by on (#219500)
Might you have put a checkmark in [ ] Disable BBCode next to Options: below that post?
Re: Collision detection problem with my project.
by on (#219501)
Maybe :wink:
Re: Collision detection problem with my project.
by on (#219505)
dougeff wrote:
Yes he should. He needs to change a tile. That requires an address.

What I meant is that, ideally, he shouldn't be converting sprite coordinates in order to get that address. Positions, collisions, etc. should normally be processed in world space, and that's what you'd convert to other spaces (sprite, NT) as necessary.
Re: Collision detection problem with my project.
by on (#219508)
Coordinate space translation reminds me a lot of working with OpenGL shaders.

Anyway, for a single screen game like his, the translation matrix from world to screen space is simply [{1,0,0},{0,1,-1},{0,0,1}], ie. simply decreasing the Y coordinates of all sprites by 1 when writing the OAM table should be fine. The only relevant coord translation he should be doing right now is playfield space to/from screen space, unless he plans to add multi-screen playfields to the game which would be something incredibly stupid for Arkanoid.

I know you guys like to go deep into discussing things and this is mostly a strength of this forum, but right now discussing metasprite systems, world coordinates, etc. will only confuse him in a simple game concept which only needs basic collision detection and video RAM writes.
Re: Collision detection problem with my project.
by on (#219517)
I'm reading! :mrgreen: I hope to implement the collision detection with the background this weekend and I will update you. Thanks for all suggests! :) I will release the source code on Github when I finish the project. Now I'm reading the code from BrickBreaker to steal some idea :).
Re: Collision detection problem with my project.
by on (#219559)
What's the best way to do the division in ASM 6052? I can't find any instructions, just ADC and SBC.
Re: Collision detection problem with my project.
by on (#219564)
You have to code division routines yourself, the 6502 doesn't know how to do divisions or multiplications. Here's a page with tons of math routines: http://codebase64.org/doku.php?id=base:6502_6510_maths

You want to keep divisions and multiplications to a minimum in 6502 programs, since they're pretty slow compared to addition and subtraction.
Re: Collision detection problem with my project.
by on (#219567)
There are 8x8 bit multiplication and 0.8/0.8 bit division routines in math.s of Thwaite. This is targeted mostly at calculating slopes as part of an arctangent routine.

There are 16x16 bit multiplication and 32/16 bit division routines in muldiv.s of 240p Test Suite, used in the Overclock activity.

There are various multiplication and divisions in the hardware-independent portion of cc65's runtime library.

But for collision with elements of a matrix whose sizes are powers of 2, such as translating pixel positions into which brick was hit in a Breakout clone, you'll probably need only division by powers of 2. To divide A by 2, use the LSR A instruction, which is much faster than the above generic dividers. To divide by larger powers of 2, use more shifts. To divide by 4, shift twice; by 8, thrice; and so on.
Re: Collision detection problem with my project.
by on (#219570)
tepples wrote:
There are 8x8 bit multiplication and 0.8/0.8 bit division routines in math.s of Thwaite. This is targeted mostly at calculating slopes as part of an arctangent routine.

There are 16x16 bit multiplication and 32/16 bit division routines in muldiv.s of 240p Test Suite, used in the Overclock activity.

There are various multiplication and divisions in the hardware-independent portion of cc65's runtime library.

But for collision with elements of a matrix whose sizes are powers of 2, such as translating pixel positions into which brick was hit in a Breakout clone, you'll probably need only division by powers of 2. To divide A by 2, use the LSR A instruction, which is much faster than the above generic dividers. To divide by larger powers of 2, use more shifts. To divide by 4, shift twice; by 8, thrice; and so on.


Yeah, I need to divide by 16 so I can do something like this:

Code:

  LSR A
   LSR A
   LSR A
   LSR A ;Divide by 16

Re: Collision detection problem with my project.
by on (#219572)
Yes. You're getting the hang of it.
Re: Collision detection problem with my project.
by on (#219574)
Code:
LDA BALL_POS_X
  SEC
  SBC #$0F                ; subtrack 16 pixel to put x in the same x space of the tiles
 
  LSR A
   LSR A
   LSR A
   LSR A                   ; Divide by 16
  STA BALL_POS_X_ON_TILE

  LDA BALL_POS_Y
  SEC
  SBC #$0F

  LSR A
  LSR A
  LSR A
  LSR A                   ; Divide by 16
  STA BALL_POX_Y_ON_TILE


Now I've the position ( x, y ) of the ball on Tile. Now I need to understand how to take the tile from the X and Y position stored in $2007 with this method:

Code:
bg_raw:
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 ; 0 raw background
bg_lvl_top_corner: 
  .db $24,$2B,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2B,$24,$24,$24,$24,$24,$24,$24 ; 1 top corner
bg_lvl_1_0:
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 2
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 3
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$1C,$1D,$1E,$0D,$12,$18 ; 4
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$15,$0E,$0A,$1F,$0E,$1C ; 5
  .db $24,$2B, $2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E, $2B,$24,$24,$24,$02,$00,$01,$08 ; 6
  .db $24,$2B, $2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E, $2B,$24,$24,$24,$24,$24,$24,$24 ; 7
  .db $24,$2B, $2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E, $2B,$24,$24,$24,$24,$24,$24,$24 ; 8
  .db $24,$2B, $2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E, $2B,$24,$24,$24,$24,$24,$24,$24 ; 9

bg_lvl_1_1:
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 10
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 11
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 12
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 13
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 14
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 15
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 16
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 17

bg_lvl_1_2
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 18
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 19
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 20
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 21
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 22
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 23
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 24
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 25

bg_lvl_1_3
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 26
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 27
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 28
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 29
Re: Collision detection problem with my project.
by on (#219579)
Kikutano: Check collision.asm on my code if you're trying to figure out angular and subpixel movement, especially this section:

Code:
;This table shows the amount of pixels
;that you should travel in the x/y directions
;in order to move with a certain speed (1-8)
;in 30, 45 or 60 degrees.
Vector_Angle_Table:
   .db 0, 1, 2, 3, 4, 5, 6, 6 ;30 degree X
   .db 0, 1, 1, 2, 2, 3, 3, 4 ;30 degree Y
   
   .db 0, 1, 2, 2, 3, 4, 4, 5 ;45 degree X
   .db 0, 1, 2, 2, 3, 4, 4, 5 ;45 degree Y
   
   .db 0, 1, 1, 2, 2, 3, 3, 4 ;60 degree X
   .db 0, 1, 2, 3, 4, 5, 6, 6 ;60 degree Y
   
;base 256 fractions to represent the angle as floating point
Vector_Fraction_Table:
   .db 223, 186, 153, 119, 84, 50, 23, 246 ;30 deg X
   .db 128, 0, 128, 0, 128, 0, 128, 0 ;30 deg Y
   .db 181, 106, 31, 212, 137, 62, 243, 168 ;45 deg X
   .db 181, 106, 31, 212, 137, 62, 243, 168 ;45 deg Y
   .db 128, 0, 128, 0, 128, 0, 128, 0 ;60 deg X
   .db 223, 186, 153, 119, 84, 50, 23, 246 ;60 deg Y
   
   
   
   


Ain't nothing as fast as reading fron a table... :lol: Not that it would be difficult to generate such a table, but to save you the trouble:

I, the author, release the code snippet above as Public Domain and forfeit my exclusive Author Intellectual Property Rights (so go ahead and copy it if you want without crediting me). :p
Re: Collision detection problem with my project.
by on (#219894)
I'm working on collision, and it start to works finally :D. All bricks are on background, the next step is to compute the bouncing and update the background. Programming for Nes is a pain in the ass, but hey, it's so funny! :D I will post here the collision detection code, I hope to upload on Github the entire project when I will finish it.

Image
Re: Collision detection problem with my project.
by on (#220206)
Image

Hello! Some progress here and new doubts! The collision detection works great with background. But I've a couple of question about the way to address the nametables where I want to compute the collision.

I load the background in this way:

Code:
LDX #$00
LoadBackgroundLVLLoop:
  LDA bg_lvl_1_0, x     
  STA $2007               
  INX                     
  CPX #$00               
  BNE LoadBackgroundLVLLoop
 
  LDX #$00
LoadBackgroundLVLLoop1:
  LDA bg_lvl_1_1, x     
  STA $2007               
  INX                     
  CPX #$00               
  BNE LoadBackgroundLVLLoop1

  LDX #$00
LoadBackgroundLVLLoop2:
  LDA bg_lvl_1_2, x     
  STA $2007               
  INX                     
  CPX #$00               
  BNE LoadBackgroundLVLLoop2

  LDX #$00
LoadBackgroundLVLLoop3:
  LDA bg_lvl_1_3, x     
  STA $2007               
  INX                     
  CPX #$80               
  BNE LoadBackgroundLVLLoop3


With 4 loop because I can't count over 256, so now I'm calculating the collision only on
Code:
bg_lvl_1_0
in this way:

Code:
PerformBallColWithBrickLeft:
  LDX BALL_POS_X_ON_TILE
  LDA bg_lvl_1_0, x
  CMP #$2D
  BEQ PerformBallCollisionWithBrick


So, there is a way to switch dynamically
Code:
bg_lvl_1_0
with, for example,
Code:
bg_lvl_1_1
? I need this not only to compute the collision on the portion of the background, but also to load a new background for a new level.

Can I do something like this? :

Code:
LDA bg_lvl_1_0
  STA NAMETABLE_TO_LOAD

  LDX #$00
LoadBackgroundLVLLoop:
  LDA NAMETABLE_TO_LOAD, x     
  STA $2007               
  INX                     
  CPX #$00               
  BNE LoadBackgroundLVLLoop


So I can change only NAMETABLE_TO_LOAD? Thanks a lot!
Re: Collision detection problem with my project.
by on (#220222)
So your problem it's basically that you don't know how to have dynamic access to more than 256 bytes,is that it? What you need to do is use a pointer. A pointer is a pair of consecutive memory positions in ZP, that together form a 16-bit address, which's able to point anywhere in the 6502's 16-bit address space.

Say you have #$00 stored at $0040, and #$84 stored at $0041. The instruction LDA ($40), Y will load the value at address $8400+Y. This is similar to the regular indexed addressing mode you've been using, except that the base address isn't fixed, it's stored in RAM and you can change it. This way you can update the address in RAM when the index (Y) goes over 255, so you can access tables longer than 256 bytes.
Re: Collision detection problem with my project.
by on (#220223)
missing parentheses.
Re: Collision detection problem with my project.
by on (#220224)
tokumaru wrote:
So your problem it's basically that you don't know how to have dynamic access to more than 256 bytes,is that it? What you need to do is use a pointer. A pointer is a pair of consecutive memory positions in ZP, that together form a 16-bit address, which's able to point anywhere in the 6502's 16-bit address space.


Ok thanks! But I want to know if how can I do something like this:

LDA bg_lvl_1_0
STA NAMETABLE_TO_LOAD

LDX #$00
LoadBackgroundLVLLoop:
LDA NAMETABLE_TO_LOAD, x
STA $2007
INX
CPX #$00
BNE LoadBackgroundLVLLoop


where bg_lvl1_1_0 is the nametable that I want to draw:

bg_lvl_1_0:
.db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 2
....


So there is a way to use NAMETABLE_TO_LOAD as variable to it can point to bg_lvl_1_0 or another level for example bg_lvl_2_0? So I can compute the collision on the current Level?

PerformBallColWithBrickLeft:
LDX BALL_POS_X_ON_TILE
LDA bg_lvl_1_0, x ( LDA NAMETABLE_TO_LOAD, x )
CMP #$2D
BEQ PerformBallCollisionWithBrick


In short words, I need to change and draw the level dynamically and compute the collision using the background information of the level. Maybe it's the wrong way, I don't know, this is my first time on a Nes and on ASM! Thanks a lot! :oops:
Re: Collision detection problem with my project.
by on (#220229)
dougeff wrote:
missing parentheses.

Oh, sorry about that. Yeah, it's supposed to be LDA ($40), Y in my example above. Or LDA [$40], Y if you're using NESASM.
Re: Collision detection problem with my project.
by on (#220231)
The straightforward way to do what you want is to have a list of pointers to the name tables you have, like this:

Code:
Level0Data:
.db $24, $2B, $27, $27, (...)

Level1Data:
.db $20, $4B, $28, $28, (...)

Level1Data:
.db $34, $40, $16, $4B, (...)

LevelPointers:
.dw Level0Data, Level1Data, Level2Data

Then you can copy any of the pointers to ZP and access the data via indirect addressing. Here's an example routine to copy a name table to VRAM:

Code:
;call this subroutine with the map index in register A
CopyBackground:

  ;multiply the index by 2, since each pointer is 2 bytes, and put it in an index register
  asl
  tay

  ;copy the pointer to zero page
  lda LevelPointers+0, y
  sta Pointer+0
  lda LevelPointers+1, y
  sta Pointer+1

  ;copy 1024 bytes from the location indicated by the pointer to VRAM
  ldx #$04
  ldy #$00
CopyByte:
  lda (Pointer), y
  sta $2007
  iny
  bne CopyByte ;go copy another byte if less than 256 have been copied
  inc Pointer+1 ;advance the pointer by 256 positions
  dex
  bne CopyByte ;go copy 256 more bytes if less than 1024 have been copied

  rts

You can use the same pointers for collision purposes, you just need to fetch the base address from the table of level pointers, add the offset of the tile you want, and then use the result as a pointer to read the tile. Something like this:

Code:
;X coordinate in register A, Y coordinate in register X
GetTile:

  ;divide the X coordinate by 8
  lsr
  lsr
  lsr
  tay

  ;simulate a division by 8 followed by a multiplication by 32 of the Y coordinate
  lda #$00
  sta Pointer+1
  txa
  and #%11111000
  asl
  rol Pointer+1
  asl
  rol Pointer+1

  ;add the result to the base address of the NT
  adc CurrentLevelPointer+0
  sta Pointer+0
  lda Pointer+1
  adc CurrentLevelPointer+1
  sta Pointer+1

  ;get the tile
  lda (Pointer), y

  rts

This subroutine expects coordinates in pixel units, since that's what you'll probably have. It also assumes you have cached the current level pointer, so you don't have to read it off the table every time you need it.
Re: Collision detection problem with my project.
by on (#220248)
Thanks a lot! I will try now! :beer:
Re: Collision detection problem with my project.
by on (#220255)
Even though I made an example routine that reads individual tiles from the level maps in RAM, I don't think you'll be doing that in the game, right? I mean, since the blocks are breakable, it makes no sense to read the original state of the map from ROM.

What's your approach, then? Do you keep a copy of the level in RAM, using half of the total RAM to make the level destructible? Or do you read map data from VRAM during vblank? Either way, you still have to calculate the tile's offset using the offset = TileY * 32 + TileX formula.
Re: Collision detection problem with my project.
by on (#220257)
tokumaru wrote:
Even though I made an example routine that reads individual tiles from the level maps in RAM, I don't think you'll be doing that in the game, right? I mean, since the blocks are breakable, it makes no sense to read the original state of the map from ROM.


I don't know yet, probably I will copy the level in RAM. Btw, I'm trying to run your code, but the result is as you can see in the screen.

Code:
;multiply the index by 2, since each pointer is 2 bytes, and put it in an index register
  asl A
  tay

  ;copy the pointer to zero page
  lda LevelPointers+0, y
  sta NAMETABLE_TO_LOAD+0
  lda LevelPointers+1, y
  sta NAMETABLE_TO_LOAD+1

  ;copy 1024 bytes from the location indicated by the pointer to VRAM
  ldx #$04
  ldy #$00
CopyByte:
  lda (NAMETABLE_TO_LOAD), y
  sta $2007
  iny
  bne CopyByte ;go copy another byte if less than 256 have been copied
  inc NAMETABLE_TO_LOAD+1 ;advance the pointer by 256 positions
  dex
  bne CopyByte ;go copy 256 more bytes if less than 1024 have been copied


For some reason the tile pointed is always "0" any idea? Thanks!
Re: Collision detection problem with my project.
by on (#220259)
There's nothing wrong in the code that I can see... It could be something in the variable declarations or somewhere else. Have you extended NAMETABLE_TO_LOAD to 16 bits, now that it has to hold an address?
Re: Collision detection problem with my project.
by on (#220261)
This is my variables declarations:
Code:
BALL_POS_X              .rs 1 ;$0000
BALL_POS_Y              .rs 1 ;$0001
BALL_DIRECTION_X        .rs 1
BALL_DIRECTION_Y        .rs 1
BALL_IS_DIR_X_POSITIVE  .rs 1
BALL_IS_DIR_Y_POSITIVE  .rs 1
PADDLE_RIGHT_X_POSITION .rs 1
PADDLE_LEFT_X_POSITION  .rs 1
BRICK_Y_MAX             .rs 1
BALL_IS_COLL_WITH_BRICK .rs 1
BALL_POS_X_ON_TILE      .rs 1
BALL_POS_Y_ON_TILE      .rs 1
BALL_POS_X_ON_TILE_PREV .rs 1
BALL_POS_Y_ON_TILE_PREV .rs 1
BALL_POS_NEXT_TILE      .rs 1
NAMETABLE_TO_LOAD       .rs 2


The sourcecode complete is here: https://pastebin.com/689fHti0

Btw, I've another question, just to understand how Assembly works. In this example code:

Code:
LDX #$00
  LDA LevelPointers, x
  STA NAMETABLE_TO_LOAD

  LDX #$00
LoadBackgroundLVLLoop:
  LDA (NAMETABLE_TO_LOAD), x     
  STA $2007               
  INX                     
  CPX #$00               
  BNE LoadBackgroundLVLLoop


NAMETABLE_TO_LOAD should point to first elemento of LevelPointers right? So when i do LDA (NAMETABLE_TO_LOAD), x and increase the x by INX it should point to the next element of the NAMETABLE? Right?

I know I can't works on the ROM memory to change the tile and hide the bricks on the screen, but I'm learning step by step so any suggests is welcome, even some article to read about! :P
Re: Collision detection problem with my project.
by on (#220264)
kikutano wrote:
NAMETABLE_TO_LOAD should point to first elemento of LevelPointers right? So when i do LDA (NAMETABLE_TO_LOAD), x and increase the x by INX it should point to the next element of the NAMETABLE? Right?

Well, each entry in the table is 2 bytes (i.e. a 16-bit address), but the 6502 can only load/store one byte at a time. This means that index 0 is the low byte of the first element, index 1 is the high byte of the first element, index 2 is the low byte of the second element, and so on. This is why my example code multiplies the level index by 2. I also used a "trick" to read from the table... I could've used INX/INY to reach the high byte of the address, but I can just as well leave the index untouched and read from a base address 1 byte after the start of the table (i.e. LevelPointers+1), so final address read is the same, and I saved 2 cycles.

Quote:
Code:
LDA (NAMETABLE_TO_LOAD), x

This addressing mode doesn't work with X, only with Y, so plan your register use accordingly.
Re: Collision detection problem with my project.
by on (#220265)
kikutano wrote:
The sourcecode complete is here: https://pastebin.com/689fHti0

You're not setting up stuff before running the code in question... A doesn't hold the level index, it holds whatever the loop above got from bg_lvl_top_corner. You're also not setting a vram address via $2006, so it's whatever the code above left it as.

Also, with this method, tables aren't limited to 256 bytes anymore, so the table of pointers only needs to point to the beginning of each NT:
Code:
LevelPointers:
  .dw bg_lvl_1_0, bg_lvl_2_0

If you put all those labels in the list, you'll be mixing partial name tables if you use indices that are not multiples of 4.

Another thing to consider is that my example code copies 1024 bytes to vram, because I assumed you'd be copying an NT and an AT together in one chunk, which doesn't seem to be the case. Well, there's nothing wrong with copying invalid AT data if you'll overwrite it with the correct data afterward, so I don't think you have to worry about it for now.
Re: Collision detection problem with my project.
by on (#220279)
Thanks a lot! :)