Jump to content



1

fast Vertical player/missile motion


6 replies to this topic

#1 tschak909 OFFLINE  

tschak909

    Chopper Commander

  • 161 posts
  • Location:USA

Posted Thu Jun 1, 2006 1:31 AM

In the book De Re Atari, Chris Crawford talks about moving the player vertically through memory. It's very straightforward...

Quote

VERTICAL MOTION

Animating this image is very easy. Vertical motion is obtained by moving the image data through the player RAM. This is, in principle, the same method used in playfield animation, but there is a big difference in practice; the move routine for vertical motion is a one-dimensional move instead of a two-dimensional move. The program does not need to multiply by 40 and it often does not need to use indirection. It could be as simple as:

			LDX $01
LOOP		LDA PLAYER,X
			STA PLAYER-1,X
			INX
			BNE LOOP 

This routine takes about 4 milliseconds to move the entire player, about half as long as the playfield animation routine which actually moves only 50 bytes where this one moves 256 bytes. If high speed is necessary, the loop can be trimmed to move only the image bytes themselves rather than the whole player; then the loop would easily run in about 100-200 microseconds. The point here is that vertical motion with players is both simpler and faster than motion with playfield objects.

of special note, is that next to last sentence in the paragraph.

If high speed is necessary, the loop can be trimmed to move only the image bytes themselves rather than the whole player; then the loop would easily run in about 100-200 microseconds.

What is he talking about there?

-Thom

#2 Rybags ONLINE  

Rybags

    Quadrunner

  • 10,323 posts
  • Location:Australia

Posted Thu Jun 1, 2006 1:42 AM

Presumably, that example should read LDX #$01, not LDX $01.

In such case, then 255 bytes are moved, which is very time consuming especially if it's just a 16 pixel high sprite.

Moving just the image bytes, you use an index for the YPOS of the sprite, then another to access the original data. Or, better still - just use the one register and use self-modifying code:

   LDA SPRITE_YPOS
   STA PLR0ADR
   LDX #SPRITE_HEIGHT; number of pixels (-1) height of player
LOOP:
   LDA SPRITE_DATA,X
PLR0ADR=*+1
   STA $4C00,X
   DEX
   BPL LOOP

Note: PLR0ADR should be the address of the operand of the STA instruction - some assemblers will need adjustment on the "*+1" equate.

The only problem with that method is that you are not erasing the previous rendition of the sprite - extra logic is needed.

One method is to keep track of the previous YPOS of the sprite.
Then, compare the new vs old YPOS. If it is > oldY, then you will need to store zeros from the oldY to currentY (-1).
If it is equal, you don't need to do anything (in fact, you don't even need to draw the sprite).
If it is < oldY, then you need to store zeros from the end of the sprite (+1) to the end of the old sprite.

I use that method in my Impossible Mission routine (see av.) - it's well suited since I'm animating 3 Players to generate 1 sprite, and they all have a similar Y position.

If it's the case that you're only moving one player and it's not very tall, and doesn't move much between renders, then it would probably be quicker to just unconditionally write several zeros before and after the sprite. You can even have your sprite defined with zeros packed either side.

Another alternative is to just erase the sprite between rendering them... which is fine if it's not a tall sprite too.

Edited by Rybags, Thu Jun 1, 2006 1:52 AM.


#3 Heaven/TQA OFFLINE  

Heaven/TQA

    Quadrunner

  • 8,111 posts
  • Location:Baden-Württemberg, Germany

Posted Thu Jun 1, 2006 2:03 AM

the de re atari routine is not very usable for a game imho...

in venus/boinxx i use a similar method rybags describes...

and you can avoid the erasing of the player data while repositioning with the extra 0 on top and below the actual sprite data...

why the hell do we not have "real" sprites....

#4 tschak909 OFFLINE  

tschak909

    Chopper Commander

  • 161 posts
  • Location:USA

Posted Thu Jun 1, 2006 2:13 AM

well, see.. I do understand that the atari 800 method for rendering "players" and "missiles" is a direct evolution of the way it's done in the 2600.. Instead of a kernel shoving bits into the TIA's registers at just the right time, we've got ANTIC shoving the bits in there for us, given that the Atari 800's design started almost immediately after the finishing of the 2600's design, this does make sense...

all the same, I do share your frustration. Ultimately i am trying to figure out how to eventually handle multiple incarnations of an enemy player on the screen in "R-Type" like patterns, while using maybe two players for the main ship....

-Thom

#5 Rybags ONLINE  

Rybags

    Quadrunner

  • 10,323 posts
  • Location:Australia

Posted Thu Jun 1, 2006 2:36 AM

You might (if you can follow it) get something useful out of this.

It is the sprite routine for IM. For the sprite, I only keep 1 copy - since there are about 27 animation frames x 3 players. The right side of the man is stored facing the other way - I use a table which the init routine generates to "flip" the data when needed so that it faces the desired way.

The routine has the logic needed to erase what's needed without extra unnecessary stores - I only did it this way as it saves time, since it's doing 3 player renders at a time.

Code is a bit messy and some comments are wrong, but it works:

 *=$5800
pxpos = $630; xpos of player
pypos = $631; ypos of player (bottom)
spritep = $632; sprite # (0-27)
oldpypos = $633; previous ypos of player (bottom)
pystart = $634; ypos of player (top)
oldpystart = $635; previous ypos of player (top)
p0pointer = $e0; indirect - point to sprite data
p1pointer = $e2
p2pointer = $e4
plrheight = $e6; height of player
ycount = $e7; counter (from plrheight)
plrend = $e7; index into players for 2nd clear operation
ztemp1 = $e7; temp store for init
;
 jmp draw_player
 jmp init
draw_player
 pla; for BASIC only
wait1
 lda $d40b
 cmp #8
 bne wait1
 lda #4
 sta 77; kill attract mode
 sta $d01a
 lda pxpos
 sta $d000
 clc
 adc #8
 sta $d001
 lda spritep
 php; save N flag for direction
 and #$1f
 tay
 lda playerl,y
 sta p0pointer; address of sprite data
 lda playerh,y
 sta p0pointer+1
 sta p1pointer+1
 sta p2pointer+1
 lda plrheighttab,y; player height
 tax
 stx plrheight; height of player (-1)
 stx ycount; counter
 sec
 adc p0pointer
 sta p1pointer
 bcc noinc1
 inc p1pointer+1
 inc p2pointer+1
noinc1
 sec
 adc plrheight
 sta p2pointer
 bcc noinc2
 inc p2pointer+1
noinc2
 lda pypos
 sec
 sbc plrheight
 sbc yoffset,y; y offset for each ani frame 
 sta pystart
 sta storep0+1
 sta storep1+1
 sta storep2+1
 sta storep0a+1
 sta storep1a+1
 sta storep2a+1
 sec
 adc plrheight
 sta plrend; end of player index (+1) for 2nd clear op
 lda #0
 ldx oldpystart
zero
 sta $7c00,x
 sta $7d00,x
 sta $7e00,x
 inx
 cpx pystart 
 bcc zero
 ldy plrheight
 plp; check n flag from before
 bmi backwards; do move for facing backwards
;
; store sprite facing forwards
;
forwards
 lda (p0pointer),y
storep0
 sta $7c00,y; left side
 lda (p1pointer),y
 tax
 lda fliptable,x
storep1
 sta $7d00,y; right side - player 1
 lda (p2pointer),y
storep2
 sta $7e00,y; arms/head - plr 2
 dey
 bpl forwards
 lda pxpos
 ldy spritep
 clc
 adc xadjust,y
 sta $d002
wipe_old
 lda #0
 ldx plrend
zero2
 sta $7c00,x
 sta $7d00,x
 sta $7e00,x
 cpx oldpypos
 bcs finished
 inx
 bne zero2
;
; store sprite facing backwards
;
backwards
 lda (p0pointer),y
 tax
 lda fliptable,x
storep0a
 sta $7d00,y; right side -plr 1
 lda (p1pointer),y
storep1a
 sta $7c00,y; left side - plr 1
 lda (p2pointer),y
 tax
 lda fliptable,x
storep2a
 sta $7e00,y; arms/head - player 2
 dey
 bpl backwards
 lda spritep
 and #$1f
 tay
 lda pxpos
 clc
 adc xadjustb,y
 sta $d002
 jmp wipe_old
finished
 lda pypos
 sta oldpypos
 lda pystart
 sta oldpystart
 lda $2c8
 sta $d01a
 rts
; 
; inititalize - generate "flip" table, set various variables
;
init
 pla
 ldx #0
dotable
 txa
 and #$f
 tay
 lda fliplow,y
 asl a
 asl a
 asl a
 asl a
 sta ztemp1
 txa
 lsr
 lsr
 lsr
 lsr
 tay
 lda fliplow,y
 ora ztemp1
 sta fliptable,x
 inx
 bne dotable
 stx oldpystart
 dex
 stx oldpypos
 rts
; x adjustment for player 2 (white part)
xadjust
 .byte 4,4,4,4,4,4,4,4,4,4
 .byte 4,4,4,4,4,4,4,4,4,4
 .byte 4,4,4,1,4,4,4,4,4,4
 .byte 4,4
xadjustb
 .byte 4,4,4,4,4,4,4,4,4,4
 .byte 4,4,4,4,4,4,4,4,4,4
 .byte 4,4,4,7,4,4,4,4,4,4
 .byte 4,4
; y offset for each animation frame
yoffset
 .byte 0,0,0,0,2,2,1,0,0,0
 .byte 0,2,2,1,0
 .byte 1,5,13,21
 .byte 22,24,26,25,23,16,4,0,0
 .byte 0,0,0,0,0; filler
fliplow
 .byte $00,$08,$04,$0c
 .byte $02,$0a,$06,$0e
 .byte $01,$09,$05,$0d
 .byte $03,$0b,$07,$0f
; set org to page boundary
neworg .=(*+$100)&$ff00
 *=neworg
fliptable
 *=*+$100
playerl 
 .ds 1
 *=*+31
playerh 
 .ds 1
 *=*+31
plrheighttab
 .ds 1
 *=*+31


#6 Wrathchild OFFLINE  

Wrathchild

    Stargunner

  • 1,373 posts
  • Location:Reading, UK.

Posted Thu Jun 1, 2006 6:43 AM

The Players and Missiles don't necessarily require DMA to be displayed, you can directly pump values into the data register, e.g. in a DLI or by writing your own kernel. So that would be more like doing things in 2600 style. Check this thread for an example:
http://www.atariage....=0
(Really, I should go back and complete this port!)

#7 tschak909 OFFLINE  

tschak909

    Chopper Commander

  • 161 posts
  • Location:USA

Posted Thu Jun 1, 2006 9:05 AM

I am familiar with that particular technique as well, that is how Atari Basketball works, primarily so that it was possible to get that much colour on screen at once.

-Thom




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users