Jump to content
IGNORED

fast Vertical player/missile motion


tschak909

Recommended Posts

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

 

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

Link to comment
Share on other sites

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
Link to comment
Share on other sites

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....

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.com/forums/index.php?s...=0entry686352

(Really, I should go back and complete this port!)

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...