Diagonal movement with arbitrary angle

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
bawenang
Posts: 6
Joined: Thu Nov 10, 2016 2:45 pm

Diagonal movement with arbitrary angle

Post by bawenang »

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.
User avatar
Myask
Posts: 965
Joined: Sat Jul 12, 2014 3:04 pm

Re: Diagonal movement with arbitrary angle

Post by Myask »

"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.
Last edited by Myask on Fri Nov 18, 2016 8:15 pm, edited 1 time in total.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Diagonal movement with arbitrary angle

Post by tepples »

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.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Diagonal movement with arbitrary angle

Post by Drew Sebastino »

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.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Diagonal movement with arbitrary angle

Post by rainwarrior »

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).
tomaitheous
Posts: 592
Joined: Thu Aug 28, 2008 1:17 am
Contact:

Re: Diagonal movement with arbitrary angle

Post by tomaitheous »

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
bogax
Posts: 34
Joined: Wed Jul 30, 2008 12:03 am

Re: Diagonal movement with arbitrary angle

Post by bogax »

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

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
.
 ; 
bawenang
Posts: 6
Joined: Thu Nov 10, 2016 2:45 pm

Re: Diagonal movement with arbitrary angle

Post by bawenang »

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?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Diagonal movement with arbitrary angle

Post by tepples »

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