Jump to content
IGNORED

Spinning in circles and other questions


Jess Ragan

Recommended Posts

Hey, I'm working on a GameBoy Advance version of the classic Midway arcade game GORF. So far, I've been able to get a few objects onscreen, moving independently of one another. I'd post the ROM but I'm getting a lot of flack from the site that I can't do that. I know the extension is .GBA, but it's a homebrew program, honest!

 

Anyway, I've been thinking about the logic for this game and am having problems figuring out just how it's done. First, there are specific scenes in the game where lines emanate from a starting point then spread outward, forming a circle. How would you go about doing something like this... making lines that grow outward, but stop at a certain point to create a perfect circle? There's a bitmapped mode in Dragon BASIC so I can plot pixels wherever I please, but what's the formula I would need to pull off this special effect?

 

Another part I'm having trouble wrapping my head around is the Space Warp scene, where ships spiral around the screen lobbing fireballs at you. The trajectory for the fireballs is easy enough to figure out... it's the X/Y positions of the player's ship, minus the X/Y positions of the enemy ship, divided by thirty (the divider can be increased or decreased to speed up or slow down the fireball). However, it's that damn spinning I can't figure out! I suspect it takes a lot of nasty math in order to make this work, and math was never really my subject. (Great idea picking computer programming as a hobby, huh?)

 

There are various other questions I've got, but these are the biggies. Thanks in advance for any advice you can offer!

Link to comment
Share on other sites

The end points are almost certainly precalculated and stored in tables in the original game. For projecting sprites along points between X1,Y1 and X2,Y2 you need something like Bresenhams algorithm.

 

For drawing circles I'd recommend you look at Doros/Bresenham Circles algorithm.

 

No complex maths are required in either case. Its just additions/subtracts and comparisons.

Link to comment
Share on other sites

I banged up a circular positioning routine for a 2600 project I started once...

 

The theory is pretty simple; just a few trig functions.

 

From a starting point X,Y, to plot a point on a circle x,y

 

x = X + radius * cosine angle

y = Y + radius * sine angle

 

See diagram. I don't know if the GBA has trig functions built in; the 2600 certainly doesn't :lol: so I just made a couple big lookup tables.

post-6060-1248368650.png

To change the position, just vary the radius or the angle.

 

So to plot your lines going outward, just leave the angle unchanged and gradually increase the radius to the desired point. E.g., for 20 lines going out from the center, space the lines out equally around the circle (line 1 at 0 degrees, line 2 at 18 degrees, line 3 at 36 degrees, etc.) with a radius of 0. Figure the position using the formula above and plot all 20 points. Then increase radius to 1. Refigure and plot all 20 points again, and etc., stopping when the radius is at the desired point (e.g., 50).

 

To plot a spiral motion, vary the angle and the radius. E.g., the ship starts at a distance (radius) 60 from the center of the screen (128, 100) at angle 90 degrees (straight up). So the x position of the ship, given by X + radius * cosine angle, is 128 + 60 * cosine of 90 degrees, which is 128+60*0 = 128. The y position of the ship, given by Y + radius * sine angle, is 100 + 60 * sine of 90 degrees, which is 100+60*1 = 160. So position the ship at (128,160). To spiral counter-clockwise and inwards, gradually increase the angle and decrease the radius. So, next frame decrease the radius by 1 (to 59) and increase the angle to 95 degrees. Then:

x = 128 + 59 * cosine of 95 degrees = 128 + 59 * (-0.0871557) = 128 - 5.14 = 123 (with rounding)*

y = 100 + 59 * sine of 95 degrees = 100 + 59 * 0.9961947 = 100 + 58.78 = 159 (with rounding)*

 

And etc.

 

It's all really very easy, especially when you have a processor that can actually do multiplication and handle 16-bit numbers easily (unlike the 2600). You just build your lookup tables; for plotting on a 256 * 200 screen (that's my vague memory of the GBA's screen dimensions) you probably will just need tables with 256 values. Then the math is pretty easy and fast, you just need your center point, the radius, and the angle - which, by the way, is arranged with 0 degrees = 360 degrees = straight to the right, 90 degrees is straight up, 180 degrees is straight left, and 270 degrees is straight down.

 

And to post the binary, just zip it first!

 

*EDIT: Fixed some of the math.

Edited by vdub_bobby
Link to comment
Share on other sites

I banged up a circular positioning routine for a 2600 project I started once...

 

The theory is pretty simple; just a few trig functions.

 

From a starting point X,Y, to plot a point on a circle x,y

 

x = X + radius * cosine angle

y = Y + radius * sine angle

 

See diagram. I don't know if the GBA has trig functions built in; the 2600 certainly doesn't :lol: so I just made a couple big lookup tables.

Found this:

BIOS Arithmetic Functions:

Div

DivArm

Sqrt

ArcTan

ArcTan2

 

BIOS Rotation/Scaling Functions

 

Among other things on the same site.

 

Also, the GBA has an ARM CPU which has a MUL instruction, so that helps.

Edited by batari
Link to comment
Share on other sites

There's good news and bad news. VDub's formula for the "death blossom" worked perfectly... it looks just like the arcade game, aside from the GameBoy Advance's chunkier resolution. However, the spiral was a bit more problematic. I had to lower the amount added to the radius for each frame and greatly increase the amount added to the angle in order to reproduce the pattern in the arcade game, and fit it on the GameBoy Advance's tiny screen.

 

Also, for some perplexing reason, everything goes bonkers and the system just stops plotting points on the screen after the angle reaches 180 degrees. I'm not sure if there's something wrong with my formula or if it's a fault of the compiler I'm using, but if you'd like to take a look at my work so far I'd be happy to share it with you.

Link to comment
Share on other sites

After 180 degrees sine will be negative (at 180 degrees, sine is zero). So maybe something is wrong with how your code handles negative sine?

 

Or...I dunno.

 

EDIT: And if you are using a sine/cosine function, make sure you know whether it takes radians or degrees!

Edited by vdub_bobby
Link to comment
Share on other sites

Hm, I didn't know that SINE had a limit of 180 degrees. There's a lot about these functions I don't understand, to be perfectly honest.

 

Anyway, here are the important parts of the code. If you want a copy of Dragon BASIC, you can find it from this link, but note that this segment of code alone won't work... you'll need everything I've trimmed away too.

 

x# = fix#(120)
y# = fix#(80)

angle = 0
for radius = 0 to 60

; WRAPANGLE is supposed to prevent the angle variable from going higher than 359 or lower than 0.

; I've tried it with both this function and with my own safeguards.  Neither keeps the dot from vanishing.

angle = wrapangle (angle + 30)

; INC works as a substitute for the traditional "x = x + 1" in BASIC or "x++" in C.

inc x#, fix#(radius) * cos#(angle)
inc y#, fix#(radius) * sin#(angle)

; PLOT sets a pixel on the screen.

plot int(x#),int(y#),255

; This starts an infinite loop that can only be broken by tapping the A button.

while waitkey(key_A)=0
loop

plot int(x#),int(y#),9

; Make a blip noise to indicate that a step has been taken

playnote 1,2,12000,WAVE_DUTY_75

if x#<fix#(0) or x#>fix#(239) or y#<fix#(0) or y#>fix#(159) then plot 0,0,5000

next

while
loop

Link to comment
Share on other sites

Hm, I didn't know that SINE had a limit of 180 degrees. There's a lot about these functions I don't understand, to be perfectly honest.

It isn't that sine has a limit of 180 degrees, but rather that the signs of the trigonometric functions vary from quadrant to quadrant:

 

			 90

		 |
  Q II   |   Q I
		 |
180 ---------+----------- 0
		 |
 Q III   |   Q IV
		 |

		270

Quadrant I is from 0 degrees to 90 degrees.

Quadrant II is from 90 degrees to 180 degrees.

Quadrant III is from 180 degrees to 270 degrees.

Quadrant IV is from 270 degrees to 0 degrees (or, if you prefer, 360 degrees).

 

Each trig function is positive in two quadrants, and negative in two quadrants. You can remember which quadrants they're positive in by memorizing this saying:

 

"All Silver Tea Cups."

 

It's the first letter of each word that's important-- A S T C. Each letter goes with a quadrant-- 1 2 3 4. (I don't know why the quadrants are usually listed using Roman numerals, but we can use regular numerals if we want.) The first word-- "All"-- is to remind you that all trig functions are positive in Quadrant I. The second word-- "Silver"-- is to remind you that the Sine function is positive in Quadrant II. The third word-- "Tea"-- is to remind you that the Tangent function is positive in Quadrant III. And the fourth word-- "Cups"-- is to remind you that the Cosine function is positive in Quadrant IV. The other three trig functions-- Cotangent, Secant, and Cosecant-- are inverses of the first three, and have the same signs as the functions they're inverses of:

 

Quadrant I: All are positive-- Sine, Cosine, Tangent, Cotangent, Secant, and Cosecant.

Quadrant II: Sine and Cosecant (1/Sine) are positive; the rest are negative.

Quadrant III: Tangent and Cotangent (1/Tangent) are positive; the rest are negative.

Quadrant IV: Cosine and Secant (1/Cosine) are positive; the rest are negative.

 

Or to put it another way:

 

Sine and Cosecant: Positive in Quadrant I and Quadrant II; negative in Quadrant II and Quadrant IV.

Cosine and Secant: Positive in Quadrant I and Quadrant IV; negative in Quadrant II and Quadrant III.

Tangent and Cotangent: Positive in Quadrant I and Quadrant III; negative in Quadrant II and Quadrant IV.

 

Michael

Link to comment
Share on other sites

I normally just set up a lookup table for the sine depending on the platform I am working on. If I have the ROM space, and cycles are critical, I set up the table for 2pi and make pi = 128. Thereby angles are 8-bits wide. If ROM space is an issue, I will do a table of pi/2 and depending on the quadrant, adjust the results. This takes more cycles though.

 

Cosine can be either extending the table by the phase difference (pi/2) or adjusting the input angle and calling the sine function.

 

Of course, I do this on processors that don't have sine/cosine instructions, or vector units that can do simple Taylor series with the proper coeff.

--Selgus

Link to comment
Share on other sites

I don't understand what that INC command is supposed to be doing.

 

And the easiest way to debug this might be to run a loop going through all values between 0 and 359 for the sine/cosine functions and print them to the screen and see what the output is.

 

I really think it has something to do with the sine function starting to return negative numbers when it goes past 180.

 

But maybe not.

Link to comment
Share on other sites

I don't know dragon BASIC and the web site for it appears to be severely broken but a couple of points.

 

1) The variables x# and y# should always be initialised to the coordinates at the middle of the spiral in every loop. You should then add offsets to them for each angle and radius.

 

2) What is the 24.8 fixed point format in terms of bits laid out in a 32 bit word? Dumb question but does it have a sign bit?

Link to comment
Share on other sites

  • 4 weeks later...

I banged up a circular positioning routine for a 2600 project I started once...

 

The theory is pretty simple; just a few trig functions.

 

From a starting point X,Y, to plot a point on a circle x,y

 

x = X + radius * cosine angle

y = Y + radius * sine angle

 

Thanks, vdub! I gave this formula another shot, and it actually works pretty well with just a few tweaks. I discovered that in order to create the spiraling effect, you have to set an anchor in the center of the screen to keep the sprite from spinning off into oblivion. Then you just increase the radius to move the sprite outward, and decrease the angle slightly after every rotation to make the outer rotations smooth. That's simple enough, and it runs surprisingly fast on the GameBoy Advance even with other objects flying around.

 

My next question is this... some enemies in the Space Warp stage fly in an elliptical pattern; you know, flattened and stretched ovals. How would I reproduce that movement?

Link to comment
Share on other sites

My next question is this... some enemies in the Space Warp stage fly in an elliptical pattern; you know, flattened and stretched ovals. How would I reproduce that movement?

 

That one's pretty easy - you already have the math. You just use a different radius on each the X and Y axes. :)

Link to comment
Share on other sites

I banged up a circular positioning routine for a 2600 project I started once...

 

The theory is pretty simple; just a few trig functions.

 

From a starting point X,Y, to plot a point on a circle x,y

 

x = X + radius * cosine angle

y = Y + radius * sine angle

 

Thanks, vdub! I gave this formula another shot, and it actually works pretty well with just a few tweaks. I discovered that in order to create the spiraling effect, you have to set an anchor in the center of the screen to keep the sprite from spinning off into oblivion.

Rereading what I posted, I guess I wasn't very clear. Rather than "starting point X,Y" that should say "center of circle X,Y." Or, as you put it, an "anchor in the center." Glad you were able to figure it out. ;)

My next question is this... some enemies in the Space Warp stage fly in an elliptical pattern; you know, flattened and stretched ovals. How would I reproduce that movement?

Tursi's idea will work for ovals that are stretched either vertically or horizontally (but not both!).

 

To get crooked ellipses, do something like this:

x = X + radius_x * cosine (angle + offset_x)

y = Y + radius_y * sine (angle + offset_y)

 

Play around with the offsets to get what you want.

 

And try this website to play around with things: http://www.ies.co.jp/math/java/calc/sg_para/sg_para.html

It has a neat little embedded parametric graphing app.

Try putting these functions in:

For an ellipse stretched vertically:

x(t) = 3*sin(t)

y(t) = 4*cos(t)

 

For an ellipse stretched horizontally, twisted a little to the right, and centered at (1, -1) :

x(t) = 1+3*sin(t+(PI/7))

t(t) = -1+2*cos(t)

Edited by vdub_bobby
Link to comment
Share on other sites

Thanks, guys! Without your help, the Space Warp wouldn't have been possible. Well, maybe it would have been possible, but it would have looked really, really terrible!

 

GORF only uses straight horizontal and vertical ellipses, so these formulas should work great. I tried taking the mean of X/Y and the center of the playfield, but then the system went nutsoid and graphics started to garble. Never quite figured out why that was the case...

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