Diagonal movement with arbitrary angle
Moderator: Moderators
Diagonal movement with arbitrary angle
Hi,
So in my game, there's an object that can move diagonally. But the angle is not always 45 degree. It can vary depending on the position of the object's target. I don't have a clue as to how I can do this on NES. I can do it easily on other engines like Unity 3D or Cocos. AFAIK, it's because we can't just use a vector like other game engine, right? Since floats are not exactly supported. So how do I do this?
Thanks in advance.
So in my game, there's an object that can move diagonally. But the angle is not always 45 degree. It can vary depending on the position of the object's target. I don't have a clue as to how I can do this on NES. I can do it easily on other engines like Unity 3D or Cocos. AFAIK, it's because we can't just use a vector like other game engine, right? Since floats are not exactly supported. So how do I do this?
Thanks in advance.
Re: Diagonal movement with arbitrary angle
"Normally," (that is, with higher-powered computers) you'd normalize the difference vector and multiply by your desired speed […or have someone's modules do it for you]. However, normalizing a vector requires finding the magnitude, which requires squaring [multiplication] and square roots, and division. All of those are abysmally slow on 6502.
Castlevania II has a good solution, involving a table lookup after shifting the X- and Y- differences down equally, and using them as an index. I can't find the topic here where we discussed it.
Here's another topic discussing trying to shoot at a player.
edit: and here's another topic about 6502 trigonometry.
Castlevania II has a good solution, involving a table lookup after shifting the X- and Y- differences down equally, and using them as an index. I can't find the topic here where we discussed it.
Here's another topic discussing trying to shoot at a player.
edit: and here's another topic about 6502 trigonometry.
Last edited by Myask on Fri Nov 18, 2016 8:15 pm, edited 1 time in total.
Re: Diagonal movement with arbitrary angle
Floats are not supported, but q8 fixed-point is.
Aiming a projectile isn't that slow, maybe about 700 cycles at most: one atan2 and two multiplies. Firing one projectile a frame won't contribute that much to slowdown. And even if you're looking for homing missile behavior on 8 different objects, you can have only one per frame adjust its heading.
Aiming a projectile isn't that slow, maybe about 700 cycles at most: one atan2 and two multiplies. Firing one projectile a frame won't contribute that much to slowdown. And even if you're looking for homing missile behavior on 8 different objects, you can have only one per frame adjust its heading.
- Drew Sebastino
- Formerly Espozo
- Posts: 3496
- Joined: Mon Sep 15, 2014 4:35 pm
- Location: Richmond, Virginia
Re: Diagonal movement with arbitrary angle
To move in something other than a 45 degree angle, you have to move going less than one pixel per frame on an axis. I assume you have some sort of variable for x and y velocity? What you'd do, is add it to the variable for the object's position every frame. The object's position (and velocity) variable should not be measured in pixels, but fractions of pixels. When it's time to display the sprites, you'd only take the bits from the object's position variable that represent whole pixels. How you plan on setting this all up is up to you.
If you want to figure out how to calculate the x and y velocity, you don't. Instead, you have a precalculated table that list different values for each degree. The only need one table, as the values for y are the values for x, just backwards.
If you want to figure out how to calculate the x and y velocity, you don't. Instead, you have a precalculated table that list different values for each degree. The only need one table, as the values for y are the values for x, just backwards.
- rainwarrior
- Posts: 8062
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Diagonal movement with arbitrary angle
If you aim by time rather than direction, you can use efficient power-of-two timings.
For example, if you want a projectile to hit a certain point in 64 frames (~1 second):
velocity = (target - position) / 64
This produces a velocity that will move "position" to "target" over 64 frames. A division by 64 of course can be done with 6 right shifts (and as mentioned above, "fixed point" is an important concept for precision to store those 6 sub-pixel bits).
For example, if you want a projectile to hit a certain point in 64 frames (~1 second):
velocity = (target - position) / 64
This produces a velocity that will move "position" to "target" over 64 frames. A division by 64 of course can be done with 6 right shifts (and as mentioned above, "fixed point" is an important concept for precision to store those 6 sub-pixel bits).
-
tomaitheous
- Posts: 592
- Joined: Thu Aug 28, 2008 1:17 am
- Contact:
Re: Diagonal movement with arbitrary angle
What tepples said; fixed-point. There tends to be a lot of tutorials that are c64 oriented, but since it's basically the same processor - so you can use them. Also look up the fast mul for 65x. It's about a 2k table in rom (you'll know it when you see it described as (x^2)/4 in the f(x)). It's pretty quick, and should easily allow 16bit:16bit multiplication (as in 32bit * 32bit -> 32bit) should you need it. I mean, however you want to set the fixed point precision point (byte segmented for faster usage). As far as angle, a 0 to 90 degree (or whatever point system you pre-calculate) look up table should work (just invert whatever axis for the other coordinate planes).
__________________________
http://pcedev.wordpress.com
http://pcedev.wordpress.com
Re: Diagonal movement with arbitrary angle
one way
determine your deltas dx, dy and what their signs are
decide which has the larger magnitude
develope an index using the signs and which has the larger magnitude
ie three bits two for signs and one for x or y having the larger magnitude
that will define the octant you're moving in and can be used to look up increments
you always move in the direction with the larger magnitude
you'll keep an error accumulator, ea which gets intialized to the greater magnitude
say dy is the greater and dx is the lesser
you'd intialize the ea to dy
take a step in the y direction and subtract dx from the ea
if the ea goes through zero ie if ea is greater after you subtract
you've accumulated y worth of x's and it's time to take a step in x and add dy back into the ea
here's some batari basic code and an excerpt from the asm code it produces
in bB
f{0} = 0
sets bit 0 in variable f to 0
edit: var36 =f is some cruft, ignore it
determine your deltas dx, dy and what their signs are
decide which has the larger magnitude
develope an index using the signs and which has the larger magnitude
ie three bits two for signs and one for x or y having the larger magnitude
that will define the octant you're moving in and can be used to look up increments
you always move in the direction with the larger magnitude
you'll keep an error accumulator, ea which gets intialized to the greater magnitude
say dy is the greater and dx is the lesser
you'd intialize the ea to dy
take a step in the y direction and subtract dx from the ea
if the ea goes through zero ie if ea is greater after you subtract
you've accumulated y worth of x's and it's time to take a step in x and add dy back into the ea
here's some batari basic code and an excerpt from the asm code it produces
in bB
f{0} = 0
sets bit 0 in variable f to 0
edit: var36 =f is some cruft, ignore it
Code: Select all
move
temp1 = ea
if f{0} then skip
player0y = player0y + yinc[f]
ea = ea - dx
if temp1 < ea then ea = ea + dy : player0x = player0x + xinc[f]
return
skip
player0x = player0x + xinc[f]
ea = ea - dy
if temp1 < ea then ea = ea + dx : player0y = player0y + yinc[f]
return
setup_move
if x0 < x1 then f{2} = 1 : dx = x1 - x0 else f{2} = 0 : dx = x0 - x1
if y0 < y1 then f{1} = 1 : dy = y1 - y0 else f{1} = 0 : dy = y0 - y1
if dx > dy then f{0} = 1 : ea = dx else f{0} = 0 : ea = dy
var36 = f
return
data yinc
$FF, $FF, $01, $01, $FF, $FF,$01, $01
end
data xinc
$FF, $FF, $FF, $FF, $01, $01, $01, $01
end
.move
; move
.L08 ; temp1 = ea
LDA ea
STA temp1
.
;
.L09 ; if f{0} then skip
LDA f
LSR
bcs .skip
.L010 ; player0y = player0y + yinc[f]
LDA player0y
LDX f
CLC
ADC yinc,x
STA player0y
.L011 ; ea = ea - dx
LDA ea
SEC
SBC dx
STA ea
.L012 ; if temp1 < ea then ea = ea + dy : player0x = player0x + xinc[f]
LDA temp1
CMP ea
BCS .skipL012
.condpart0
LDA ea
CLC
ADC dy
STA ea
LDA player0x
LDX f
CLC
ADC xinc,x
STA player0x
.skipL012
.L013 ; return
RTS
.skip
; skip
.L014 ; player0x = player0x + xinc[f]
LDA player0x
LDX f
CLC
ADC xinc,x
STA player0x
.L015 ; ea = ea - dy
LDA ea
SEC
SBC dy
STA ea
.L016 ; if temp1 < ea then ea = ea + dx : player0y = player0y + yinc[f]
LDA temp1
CMP ea
BCS .skipL016
.condpart1
LDA ea
CLC
ADC dx
STA ea
LDA player0y
LDX f
CLC
ADC yinc,x
STA player0y
.skipL016
.L017 ; return
RTS
.
;
.
;
.
;
.setup_move
; setup_move
.L018 ; if x0 < x1 then f{2} = 1 : dx = x1 - x0 else f{2} = 0 : dx = x0 - x1
LDA x0
CMP x1
BCS .skipL018
.condpart2
LDA f
ORA #4
STA f
LDA x1
SEC
SBC x0
STA dx
jmp .skipelse0
.skipL018
LDA f
AND #251
STA f
LDA x0
SEC
SBC x1
STA dx
.skipelse0
.L019 ; if y0 < y1 then f{1} = 1 : dy = y1 - y0 else f{1} = 0 : dy = y0 - y1
LDA y0
CMP y1
BCS .skipL019
.condpart3
LDA f
ORA #2
STA f
LDA y1
SEC
SBC y0
STA dy
jmp .skipelse1
.skipL019
LDA f
AND #253
STA f
LDA y0
SEC
SBC y1
STA dy
.skipelse1
.
;
.L020 ; if dx > dy then f{0} = 1 : ea = dx else f{0} = 0 : ea = dy
LDA dy
CMP dx
BCS .skipL020
.condpart4
LDA f
ORA #1
STA f
LDA dx
STA ea
jmp .skipelse2
.skipL020
LDA f
AND #254
STA f
LDA dy
STA ea
.skipelse2
.L021 ; var36 = f
LDA f
STA var36
.L022 ; return
RTS
.
;
.L023 ; data yinc
yinc
.byte $FF, $FF, $01, $01, $FF, $FF,$01, $01
.skipL023
.
;
.L024 ; data xinc
xinc
.byte $FF, $FF, $FF, $FF, $01, $01, $01, $01
.skipL024
.
;
Re: Diagonal movement with arbitrary angle
Thanks for the replies guys. This is too many information to chug at once, lol. A good thing though, since I have lots of ways to do it now. I have a question though. If I were to use the lookup table, won't it cost to much memory? I mean I need 90 bytes to store all 90 degrees values, right?
Re: Diagonal movement with arbitrary angle
Many games use brads, binary fractions of a turn such as 1/32 or 1/256, instead of degrees or radians. In a 32-step system such as that used in Thwaite, 0 to 31 represent 0 to 31/32*360 = 348.75 degrees in 1/32*360 = 11.25 degree steps.