Page 2 of 2

Re: Collision solid objects, level data, tile-based movement

Posted: Sat Aug 08, 2015 7:30 am
by Kasumi
Row one of data is 0-31. Row two is 32-63. Etc.

In the code you posted, vpos is divided by 8. Adding that to the pointer can only add a range of 0-31. So no matter what it is, the row will never change.

The equation is vpos/8 * 32 + xpos/8.

Since you're dividing by 8, and then multiplying by 32, you need two bytes to represent the full range.

Dividing by 8, then multiplying by 8 basically only accomplishes zeroing out the lowest 3 bits. So you can multiply by 4 without the divide and just AND out the lowest 3 bits.

Code: Select all

;There's a faster way, of course
lda vpos
asl;This moves the highest bit of vpos into the carry, and shifts all the other bits
sta pageloc

lda #$00
rol a;the highest bit of vpos is now in A
sta pageloc2

lda pageloc
asl a;This moves the next highest bit of vpos into the carry
and #%11100000;getting rid of the lowest bits.
sta pageloc

rol pageloc2;Now both of the highest two bits of vpos are in the pointer.

;Now we add where the nametable is. You said this was $8000. Let's just assume screendata is a label there.

lda #<screendata
clc
adc pageloc
sta pageloc

lda #>screendata
adc pageloc2
sta pageloc2

;Now we have an address pointing to the first byte of the row we're currently on. So the X position divided by 8 can help us pick which column.

lda xpos
lsr
lsr
lsr
tay

lda(pageloc),y
A final note on the multiply for Y. If the range of a row is 32 bytes, that needs 5 bits.
00YXXXXX. Those 5 X bits are which column, chosen by the X position. To go to the next row, you'd add one to the bit marked Y. So the range of Y is put in the 5 bits higher than X. 3 in the low byte, 2 in the high byte.

Code: Select all

Hi=000000YY
Lo=YYYXXXXX

Re: Collision solid objects, level data, tile-based movement

Posted: Sat Aug 08, 2015 10:40 am
by mikaelmoizt
Thanks a lot guys! Now I got a much better version. I wasn't too far from getting it to work earlier, but it just lacked something. I saw a few flaws in my code, and also learned that the "hotspot" of a sprite is in the top left corner and not in the middle (or any other place).. :roll:
Also, it got me thinking about how I could implement this in a more flexible and better way.

It is pretty "simple" once you get the right idea about how to relate screen and memory.. so far.. without any scrolling and stuff :)

Feel free to use my amazing word processor capabilities.
On a side note, this part of the learning path really opens up a lot of possibilities for actually making something that resembles.. a game. At least for everything not related to puzzle games I guess.. A sprite on sprite collision detection added, and hey - now the game can even have some sort of simple enemies to hunt (walk into) you!

Re: Collision solid objects, level data, tile-based movement

Posted: Sun Aug 09, 2015 3:58 am
by Pokun
mikaelmoizt wrote:Any news Pokun? For having basically the same questions, I will post here. (maybe we should go team Sweden? :) )
Ah yes I did solve tile-based movement a few days before you posted but I didn't have time to post here.
I looked at a dissasembly of Final Fantasy and learned a few tricks. Then I rewrote my tile-based movement code, and after some tweaks I got it to work flawlessly as far as I can tell.

Here's my new code (ASM6 local labels are prefixed with @):

Code: Select all

TILESIZE = 8

InputHandle:
  lda P1MoveSpeed
  bne @skip ;don't check if already in the middle of a move
  lda con_cur
  and #$0F ;check if d-pad is pressed
  bne @prepare_move ;and move only if it is
@skip:
  ;(Check other buttons here)
  jmp @exit
@prepare_move: ;d-pad direction is now in A
  sta P1Facing ;set d-pad direction as facing direction
  ;(I guess collision check goes here)
  lda #$01
  sta P1MoveSpeed ;set speed
@exit:

P1Move:
  lda P1MoveSpeed
  beq @exit ;if 0, player isn't moving
  lda P1Facing ;check facing direction
  lsr A
  bcs @right ;facing right so move right
  lsr A
  bcs @left ;else if left...
  lsr A
  bcs @down ;else if down...
@up: ;else it's up
  lda P1Y
  sec
  sbc P1MoveSpeed
  sta P1Y ;move player up
  and #(TILESIZE-1) ;check if moved a full tile
  bne @exit ;not yet a full tile, continue moving next frame
  lda #$00
  sta P1MoveSpeed ;moved a full tile, stop moving
  beq @exit ;always branches (cheaper than jmp)
@right:
  lda P1X
  clc
  adc P1MoveSpeed
  sta P1X ;move player right
  and #(TILESIZE-1)
  bne @exit
  lda #$00
  sta P1MoveSpeed
  beq @exit
@left:
  lda P1X
  sec
  sbc P1MoveSpeed
  sta P1X ;move player left
  and #(TILESIZE-1)
  bne @exit
  lda #$00
  sta P1MoveSpeed
  beq @exit
@down:
  lda P1Y
  clc
  adc P1MoveSpeed
  sta P1Y ;move player down
  and #(TILESIZE-1)
  bne @exit
  lda #$00
  sta P1MoveSpeed
@exit:
I attached my ROM with this code to this post. It's no longer possible to get stuck between tiles in the grid, since I simply ignore reading the d-pad if currently moving (when P1MoveSpeed is not equal to 0).


Currently I've been drafting a level building engine that builds levels based on data in a certain format I feed it, so it'll be easy to make new levels and compress them. I thought of using data entries that contains information of which 16x16 metatile is used, solid flag, palette and possibly other things.
Considering Nintendo's overscan rules I only need 11 rows and 14 BG tiles per row if I'm using 16x16 metatiles, and reserves some space for the status bar at top. I have have no idea if it will work though.