| | Richard
Registered: Dec 2001 Posts: 621 |
Stopping a sprite when hitting background
How do I stop a sprite from hitting the background, and let the sprite move around the screen, avoiding contact. This is mainly because my games usually make the player die when it touches the background, which I think gets too repetitive. Reason: I don't know how to stop the player from walking when touching a wall - without the player freezing when you try to move it away from the wall.
The collision routine, which I usually use is (As on Lemon64):
BGRCOL LDA $D01F
LSR A
BCC NOBGCOL
DEC STATUS
LDA STATUS
CMP #$00
BNE NOBGCOL
JMP GAMEOVER
NOBGCOL RTS
Please help. |
|
| | Krill
Registered: Apr 2002 Posts: 2980 |
using the hw collision detection on the c64 not a very good idea.
first, you can detect collisions only once every frame (afair), thus you'll get problems when having multiplexers.
then, you can only detect which sprites collided, if you have for example 3 bits set in the collision detection register, you dont know which sprite collided with which of the two other ones, or both.
generally i would always recommend to do complete software collision detection, even the most naive approaches with rectangular bounding boxes give sufficient results in most cases. |
| | Oswald
Registered: Apr 2002 Posts: 5094 |
richard: when you detect that the player collided with the wall automagically move it away from the wall within the same routine. So you will cancell the collision state, and you wont get into the infinite collide loop.
Maybe this simpler aproach is enough for what you want to do. |
| | Stingray Account closed
Registered: Feb 2003 Posts: 117 |
Is this a trick question? not sure If I understood completely. You could give the sprite boundries. You could try a piece of code like this, it's off the top of my head so it could be wrong.
LDA $D000
CMP #$30
BCS SOMEWHERE
LDA #$30
STA $D000
This would stop Sprite 0 from moving any more left then #$30 but you could use a bit of code like this for any sprite in any direction if you know what I mean.
PS You probably know this but I think you could take the fellowing two lines out of the code your useing it should still work the same.
LDA STATUS
CMP #$00
|
| | cadaver
Registered: Feb 2002 Posts: 1160 |
That's a horrible question, because the answer totally depends on your game's architecture, and it's not like we should be deciding that for you. Anyway, you could work out a routine to read a char from the screen based on sprite's X,Y coordinates.
When the sprite moves, save previous coordinates. Check using the new coordinates, if there's a collision-causing char under the sprite (for example, anything other than 32). If there is, revert back to saved coordinates. |
| | Kenho Account closed
Registered: Jan 2003 Posts: 26 |
Sprite coordinates to char coordinates
This works when the sprite x coordinate only is within 0 to 255, additional stuff needs to be done if thats not the case.
(someone add code to that please)
Work out y coord:
the y coord has a formula of:
chary = (spritey-50)/8
to get to the char you use
x + y*40
lda spritey
cec
sbc #$32
why sub with $32 ? that is because the upper border is $32 pixels thick, you need to compensate.
lsr a ; a/2
lsr a ; a/2
lsr a ; a/2
tay
If you divide by two 3 times you have divided by 8. 8 is char width.
you know, on a screen you can have 40 chars. 40 * 8 is 320, ring a bell?
Now here is my solution for easy use of the y coord as i dont want to multiply by 40.
I have made two tables of bytes to be put in 2 consecutive zp adresses. These are screencodes.
As i am in bank 1 my screen starts at $4400. normally if no bankswitching, screen would start at $0400.
The y register now contains a value between 0 and 25 which i can use as an index in my table and put into the zp adresses like this
lda scrlo,y ; 0,28,50 etc
sta $70
lda scrhi,y ;44,44,45 etc
sta $71
Now to get to the x pos of the char you need to do almost the same thing again.
lda spritex
sec
sbc #$18 ; compensate for left border...
lsr a
lsr a
lsr a ;divide by 8
tay
now here comes the trick to get to the char you want:
lda ($70),y ; y in this is the x in x+y*40
cmp #32 ;was there a space char there?
bne hitt ;no space char, then hit
Explanation:
the lda($70),y takes the two consecutive bytes at $70 and $71 and makes them into an adress. for example.
lda #$28
sta $70
lda #$04
sta $71
ldy #3
lda ($70),y would give accum the char at the second row + 3
computer does (($71)*256 + ($70))+y
This are the tables:
scrhi.byte $44,$44,$44,$44,$44,$44,$44,$45,$45,$45,$45,$45
,$45,$46,$46,$46,$46,$46,$46,$46,$47,$47,$47,$47,$47,$47
scrlo .byte$0 ,$28,$50,$78,$a0,$c8,$f0,$18,$40,$68,$90,$b8,
$e0,$08,$30,$58,$80,$a8,$d0,$f8,$20,$48,$70,$98,$c0,$e0
Feel free to suggest other solutions or add something to get the whole spritex (including 9th bit)
Feel free to ask me if someting is unclear (should it be??)
Only my two cents..
|
| | Kenho Account closed
Registered: Jan 2003 Posts: 26 |
Just to add a question to rich about my code. where on the sprite does it hit the char?
|
| | Richard
Registered: Dec 2001 Posts: 621 |
Don't know. The side of corner of the sprite perhaps? |
| | Oswald
Registered: Apr 2002 Posts: 5094 |
kenho:
to get the 9th bit of spriteX working simply do this:
lda $d000 ;x coord
lsr $d010 ;9th bit into -> CARRY
ror ;CARRY->into accu->
lsr
lsr
so ror is the same as LSR, just it pushes the CARRY into the MSB of the accumulator instead of 0.
this works only for sprite 0, but you can have a table to convert the actual sprites MSB from $d010, to an 1 or 0 in the Accumulator. Or without a table do more LSRs until the bit u need will get into carry.
the answer to your question:
as the sprites coordinates refer to their topleft corner, this aproach is very rude. Better to check the whole area under the sprite (3x3 char).
this would change your code to:
ldx #2
l1 lda ($70),y
cmp #32
beq hit
iny
cpy #3
bne l1
lda $70
clc
adc #$28
sta $70
bcc *+4
inc $71
dex
bpl l1
hit: (whatever) |
| | Richard
Registered: Dec 2001 Posts: 621 |
Quote: Just to add a question to rich about my code. where on the sprite does it hit the char?
The top of the sprite hits the bottom of the charset and this is a sort of collision. |
| | Kenho Account closed
Registered: Jan 2003 Posts: 26 |
Thanks for your additions Oswald!
Now its a really great collission routine :-)
edit: cannot get it to work correctly.... i´m lame :-)
|
| | ToD Account closed
Registered: Dec 2003 Posts: 10 |
Quote: Thanks for your additions Oswald!
Now its a really great collission routine :-)
edit: cannot get it to work correctly.... i´m lame :-)
i cant get it to work aswell , seems way out |
| | Richard
Registered: Dec 2001 Posts: 621 |
Same with me.... |
| | Kenho Account closed
Registered: Jan 2003 Posts: 26 |
Some minor changes:
xtmp .byte 0
ldx #3
sty xtmp ;
lda xtmp
clc
adc #4 ;(if x xpanded, #3 otherwize)
sta xtmp
_l1
lda ($70),y
cmp #32
bne _hit
iny
cpy xtmp
bne _l1
lda $70
clc
adc #$28 ;next row
sta $70
bcc _l2
inc $71
_l2
dey
dey
dey
dey ; if x expanded
dex
bne _l1
rts
_hit
inc $d020
rts
|
| | Oswald
Registered: Apr 2002 Posts: 5094 |
kenho: oh yeah, my code had some serious bugs, but this happens when one writes code from the top of his head, without any testing :) Sorry!
one thing: X expanded sprites are 6 chars wide, not 4 :)
one word to the part calculating the positon on the character screen:
its a very common way to calculate the onscreen position like that. But you can make it even faster. The table could be made so, that you dont need the sec sbc lsr lsr lsr before.. just ldy $d001 lda tablelo,y sta $70 lda tablehi,y sta $71, well depends how much speed you need :)
a bit of optimization follows, as I cannot stand not to brush up the code:)
ldx #3
sty selfmod+1
_l1
lda ($70),y
cmp #32
bne _hit
iny
lda ($70),y
cmp #32
bne _hit
iny
lda ($70),y
cmp #32
bne _hit
selfmod ldy #$00
lda $70
clc
adc #$28 ;next row
sta $70
bcc _l2
inc $71
_l2
dex
bne _l1
rts
_hit
inc $d020
rts
|
| | Kenho Account closed
Registered: Jan 2003 Posts: 26 |
Really nice Oswald :-) i couldnt stand that lousy variable solution i made either :-)
|
| | Oswald
Registered: Apr 2002 Posts: 5094 |
to optimize it even more :) :
instead of selfmod and the 9 bit adc:
tya
clc
adc #$28-2
tay
=)
(y will never overflow) |
| | Richard
Registered: Dec 2001 Posts: 621 |
I have been following a routine which Kenho had supplied, on how to make a sprite collide into the background. This routine seems to work quite nicely if the sprite hits 'the A' character.
Source wrote:
;The sprite collides into the background, therefore we have a sprite to background collision Smile
backcollision: lda sprtpos+1
sec
sbc #$32
lsr
lsr
lsr
tay
lda scrlo,y
sta $70
lda scrhi,y
sta $71
lda sprtpos+0
sec
sbc #$0c
lsr
lsr
tay
ldx #3
sty _selfmod+1
_l1: lda ($70),y
cmp #32
bne _hit
iny
lda ($70),y
cmp #32
bne _hit
iny
lda ($70),y
cmp #32
bne _hit
_selfmod: ldy #$00
lda $70
clc
adc #$28 ;next row
sta $70
bcc _l2
inc $71
_l2: dex
bne _l1
rts
_hit: inc $d027
rts
xtmp: dc.b 00
killerchars: dc.b $10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f
dc.b $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35
dc.b $36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f,$40,$41,$42,$43,$44,$45
dc.b $46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f,$50,$51,$52,$53,$54,$55
dc.b $56,$57,$58,$59,$5a,$5b,$5c,$5d,$5e,$5f,$60,$61,$62,$63,$64,$65
dc.b $66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f,$70,$71,$72,$73,$74,$75
dc.b $76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f,$80,$81,$82,$83,$84,$85
dc.b $86,$87,$88,$90,$8a,$8b,$8c,$8d,$8e,$8f,$90,$91,$92,$93,$94,$95
dc.b $96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f,$a0,$a1,$a2,$a3,$a4,$a5
dc.b $a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af,$b0,$b1,$b2,$b3,$b4,$b5
dc.b $b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf,$c0,$c1,$c2,$c3,$c4,$c5
dc.b $c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf,$d0,$d1,$d2,$d3,$d4,$d5
dc.b $d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df,$e0,$e1,$e2,$e3,$e4,$e5
dc.b $e6,$e7,$e8,$e9,$ea,$eb,$ec,$ed,$ee,$ef,$f0,$f1,$f2,$f3,$f4,$f5
dc.b $f6,$f7,$f8,$f9,$fa,$fb,$fc,$fd,$fe,$ff,$ff,$ff,$ff,$ff,$ff,$ff
scrhi: dc.b $44,$44,$44,$44,$44,$44,$44,$45,$45,$45,$45,$45,$45,$46,$46,$46,$46,$46,$46,$46, $47,$47,$47,$47,$47,$47
scrlo: dc.b $00,$28,$50,$78,$a0,$c8,$f0,$18,$40,$68,$90,$b8,$e0,$08,$30,$58,$80,$a8,$d0,$f8, $20,$48,$70,$98,$c0,$e0
However I am still stuck regarding setting 'kill blocks' for my Real Speed We Need game project. I had to keep resetting the code from how it was, but I would like to import my kill tables into this particular routine, so that if a block is located according to the kill table, the sprite to background collision is detected.
|
| | Richard
Registered: Dec 2001 Posts: 621 |
(Continued from Kehno's example)
Hi again. Oh not again, it is the return of an old topic :P
I am having problems with the sprite/background collision routine.
I'm doing a game, but having problems with the player sprite/background collision.
What I am trying to do is make the sprite stop *by* the rocks (which are built as 2x2 chars). The sprite is full size as 1 filled square. When the player sprite moves from the left to the right hand side of the rock, this works nicely, however, when the player moves from the right, to the left of the rock, it somehow stops on the whole char. The routine looks something like this:
;Player sprite to background collision routines
BACKCOLLISION: LDA OBJPOS+$01
SEC
SBC #$32
LSR
LSR
LSR
TAY
LDA SCRLO,Y
STA $70
LDA SCRHI,Y
STA $71
LDA OBJPOS+$00
SEC
SBC #$0C
LSR
LSR
TAY
LDX #3
STY _SELFMOD+1
_L1: INY
LDA ($70),Y
CMP #65
BEQ _HIT
LDA ($70),Y
CMP #65 ;Player touches the food,
BEQ _HIT; if touched the food is off screen
CMP #66;
BEQ _HIT;
CMP #67;
BEQ _HIT;
CMP #68;
BEQ _HIT;
INY
LDA ($70),Y
CMP #65
BEQ _HIT
CMP #67
BEQ _HIT
CMP #66
BEQ _HIT
CMP #68
BEQ _HIT
CMP #$56 ;Player touches the switch char
BEQ _SWITCH ;if touched, the switch will turn
CMP #$57 ;all the water into food
BEQ _SWITCH
CMP #$58
BEQ _SWITCH
CMP #$59
BEQ _SWITCH
CMP #$4A ;Player touches the water chars
BEQ _WATER ;if the player touches the water
CMP #$4B ;he will drown
BEQ _WATER
CMP #$4C
BEQ _WATER
CMP #$4D
BEQ _WATER
CMP #$4E
BEQ _WATER
CMP #$52 ;Player touches the rock chars
BEQ _ROCK ;if touched, then the player
CMP #$53 ;will stop according to the
BEQ _ROCK ;direction he moves
CMP #$54
BEQ _ROCK
CMP #$55
BEQ _ROCK
INY
JMP _SELFMOD
;LDA ($70),Y
;CMP #16
;BPL _HIT
_SELFMOD:
MODE2: LDY #$00
LDA $70
CLC
ADC #$28 ;NEXT ROW
STA $70
BCC _L2
INC $71
_L2: DEX
BNE _L1
RTS
_WATER: JSR PLAYER_DROWN ;Because player touched water, he drowns
RTS
_HIT: LDA #73
STA ($70),Y
JSR SCORE
JSR ADDFOOD
;CMP #$00
;BEQ LEVELDONE
NO: RTS
_SWITCH JSR WATER_TO_FOOD ;Turn water into food
RTS
_ROCK JSR STOP_AT_ROCK ;Stop player moving
RTS
STOP_AT_ROCK LDA DIRFACE
CMP #$01
BEQ STOPATUP
CMP #$02
BEQ STOPATDOWN
CMP #$03
BEQ STOPATLEFT
CMP #$04
BEQ STOPATRIGHT
RTS
Please can you help me solve this problem, why the sprite stops right in the middle of the rock, when it is supposed to stop right next to it (when moving left)? |
|