Modelling a specific sort of curve
Author |
Message |
Insectoid
|
Posted: Fri Apr 04, 2014 1:24 pm Post subject: Modelling a specific sort of curve |
|
|
I'm starting to build a game very similar to Steambirds. I'm having trouble coming up with a way to recreate the movement planning for the aircraft. I tried using Bezier curves, but they don't quite fit the problem (or I can't manipulate them correctly) and seem like overkill for this task. Anyone got any ideas? |
|
|
|
|
|
Sponsor Sponsor
|
|
|
Zren
|
Posted: Fri Apr 04, 2014 4:35 pm Post subject: RE:Modelling a specific sort of curve |
|
|
Constant rotation rate + linear movement. Draw Arc + Draw Line. That might be even harder that beziers though.
Steambirds is deffinitely using a bezier though. |
|
|
|
|
|
Insectoid
|
Posted: Fri Apr 04, 2014 5:08 pm Post subject: RE:Modelling a specific sort of curve |
|
|
I think I might have a solution.
Draw the path as a chain of 3 links (4 points) of the same length and with the same angle between them. Clicking & dragging the last point moves the whole thing to satisfy those conditions. These 4 points describe the bezier curve of the flight path. Figuring out a solver for this will be a pain, but I'm confident it will work. |
|
|
|
|
|
Insectoid
|
Posted: Fri Apr 04, 2014 7:52 pm Post subject: RE:Modelling a specific sort of curve |
|
|
I've been playing around with it a little and came up with this solution (it's a bit messy). Ended up using a trigonometric bezier to model the path. Click & drag to move the bezier around. I can't figure out a way to make the 2 lines that define the bezier the same length.
Turing: | type Point : record
x : real
y : real
end record
function slope (p : Point ) : real %calculates the slope of the line from (0, 0) to p
result arcsind (p.y/ sqrt (p.x** 2 + p.y** 2))
end slope
function slope2 (x : real, y : real) : real
result arcsind (y/ sqrt (x** 2+y** 2))
end slope2
var p0 : Point %Not really p0. Just used as a guide to show the 'original' direction
var p1 : Point %midpoint of the bezier
var p2 : Point %endpoint of the bezier
p0.x := 200 %some random initial values.
p0.y := 200
p1.x := 75
p1.y := 75
p2.x := 150
p2.y := 150
var offsetX := 0 %offset for drawing. Non-zero values kinda mess with the controls
var offsetY := 0
var keys : array char of boolean
View.Set ("offscreenonly")
%this draws the bezier given coordinates. t = time
proc drawBezier (x0 : real, y0 : real, x1 : real, y1 : real, x2 : real, y2: real, t : real)
var x : real
var y: real
x := (1-t )** 2*x0 + 2* (1-t )*t*x1+t** 2*x2
y := (1-t )** 2*y0 + 2* (1-t )*t*y1+t** 2*y2
Draw.Dot (round (x )+offsetX, round (y )+offsetY, green)
end drawBezier
%var angle : real
var mx, my, mb : int
loop
Mouse.Where (mx, my, mb )
if mb = 1 then
p2.y := my
p2.x := mx
p1.x := (p2.x + p0.x ) / 4 %these are the two lines I'm having trouble with. I need length((0, 0), p1) to match length (p1, p2). When slope (p2) = slope (p0), the bezier should be linear.
p1.y := (p2.y + p0.y ) / 4
end if
Draw.Line (0+offsetX, 0+offsetY, round (p0.x )+offsetX, round (p0.y )+offsetY, black)
Draw.Line (0+offsetX, 0+offsetY, round (p1.x )+offsetX, round (p1.y )+offsetY, red)
Draw.Line (round (p1.x )+offsetX, round (p1.y )+offsetY, round (p2.x )+offsetX, round (p2.y )+offsetY, red)
for i : 0.. 60
drawBezier (0, 0, p1.x, p1.y, p2.x, p2.y, 1. 0/ 60*i )
end for
put Math.Distance (0, 0, p1.x, p1.y )
put Math.Distance (p1.x, p1.y, p2.x, p2.y )
%Draw.Line (round (p3.x), round (p3.y), round (p4.x), round (p4.y), red)
%Draw.Line (0, 0, round (p2.x), round (p2.y), black)
View.Update
delay (10)
cls
end loop |
|
|
|
|
|
|
Raknarg
|
Posted: Fri Apr 04, 2014 8:12 pm Post subject: RE:Modelling a specific sort of curve |
|
|
Just out of curiosity, why are you using Turing for this project? |
|
|
|
|
|
Insectoid
|
Posted: Fri Apr 04, 2014 8:39 pm Post subject: RE:Modelling a specific sort of curve |
|
|
It lets you write small graphical prototypes really quickly (and I can't get Allegro to function properly). Eventually, when I get a functional prototype, I might rewrite it in something more iOS/Android friendly and really polish it up, but I'm not planning that far ahead. |
|
|
|
|
|
Raknarg
|
Posted: Fri Apr 04, 2014 8:45 pm Post subject: RE:Modelling a specific sort of curve |
|
|
I used to think so too, but once I got used to Processing, it really changed my mind. Simplicity but with the power of Java.
Not saying you should change what you've already started, though. Just for future reference |
|
|
|
|
|
Dreadnought
|
Posted: Fri Apr 04, 2014 11:07 pm Post subject: Re: Modelling a specific sort of curve |
|
|
Insectoid wrote:
I can't figure out a way to make the 2 lines that define the bezier the same length.
Here's how I thought of doing it. If you force the angles between some line to be the same, you can get lines of the same length.
Let L0, L1, L2 be the lines from (0,0) to p0, p1, p2 respectively.
Force the angle between L0 and L1 to be equal to the angle from L1 to L2. This gives an isosceles triangle with vertices p0, p1 and p2 and lets us solve for the length with the cosine law.
Here's what I got (just modified the way you compute p1).
Turing: | type Point :
record
x : real
y : real
end record
function slope (p : Point ) : real %calculates the slope of the line from (0, 0) to p
result arcsind (p.y / sqrt (p.x ** 2 + p.y ** 2))
end slope
% Distance from origin to p squared
fcn norm2 (p : Point ) : real
result p.x * p.x + p.y * p.y
end norm2
proc drawBezier (x0 : real, y0 : real, x1 : real, y1 : real, x2 : real, y2 : real, t : real)
var x : real
var y : real
x := (1 - t ) ** 2 * x0 + 2 * (1 - t ) * t * x1 + t ** 2 * x2
y := (1 - t ) ** 2 * y0 + 2 * (1 - t ) * t * y1 + t ** 2 * y2
Draw.Dot (round (x ), round (y ), green)
end drawBezier
var p0 : Point %Not really p0. Just used as a guide to show the 'original' direction
var p1 : Point %midpoint of the bezier
var p2 : Point %endpoint of the bezier
p0.x := 200 %some random initial values.
p0.y := 200
const a0 : real := slope (p0 )
p1.x := 75
p1.y := 75
p2.x := 150
p2.y := 150
var mx, my, mb : int
loop
Mouse.Where (mx, my, mb )
if mb = 1 then
p2.y := my
p2.x := mx
% Please excuse the terrible naming, a0, a1, a2 correspond to slope() for p0, p1, p2
var a2 : real := slope (p2 )
var a : real := (a0 - a2 ) / 2 % angle between lines from origin to p0 and p1 (equal to angle between lines from origin to p1 and p2)
var len : real := sqrt (norm2 (p2 ) / (2 * (1 - cosd (180 - 2 * a ))))
var a1 : real := a2 + a
p1.x := len * cosd (a1 )
p1.y := len * sind (a1 )
end if
Draw.Line (0, 0, round (p0.x ), round (p0.y ), black)
Draw.Line (0, 0 , round (p1.x ), round (p1.y ), red)
Draw.Line (round (p1.x ), round (p1.y ), round (p2.x ), round (p2.y ), red)
for i : 0 .. 60
drawBezier (0, 0, p1.x, p1.y, p2.x, p2.y, 1. 0 / 60 * i )
end for
View.Update
delay (10)
cls
end loop
|
|
|
|
|
|
|
Sponsor Sponsor
|
|
|
Insectoid
|
Posted: Sat Apr 05, 2014 1:48 pm Post subject: RE:Modelling a specific sort of curve |
|
|
That works pretty well, thanks. I've added a little more functionality to it, and came up with this. Same deal, click & drag the blue dot to move it. Press enter to advance the bezier, sort of.
There is an issue when p0 is pointing right-to-left; the slope gets reflected around the y axis (you'll see what I mean). Pretty sure this has something to do with the CAST rule of trig, but I can't figure it out right now. It's a long time since I did any trig.
Also, if p2 = origin (the opposite end of the bezier), the program will crash due to division by zero in the slope function. This is normal and will be fixed by limiting the values of p2 later on.
Turing: | type Point :
record
x : real
y : real
end record
function slope (p : Point ) : real %calculates the slope of the line from (0, 0) to p
var out : real := arcsind (p.y / sqrt (p.x ** 2 + p.y ** 2))
result out
end slope
function subtractPoint (a : Point, b : Point ) : Point
var out : Point
out.x := a.x - b.x
out.y := a.y - b.y
result out
end subtractPoint
% Distance from origin to p squared
fcn norm2 (p : Point ) : real
result p.x * p.x + p.y * p.y
end norm2
proc drawBezier (x0 : real, y0 : real, x1 : real, y1 : real, x2 : real, y2 : real, t : real)
var x : real
var y : real
x := (1 - t ) ** 2 * x0 + 2 * (1 - t ) * t * x1 + t ** 2 * x2
y := (1 - t ) ** 2 * y0 + 2 * (1 - t ) * t * y1 + t ** 2 * y2
Draw.Dot (round (x ), round (y ), green)
end drawBezier
var p0 : Point %Not really p0. Just used as a guide to show the 'original' direction
var p1 : Point %midpoint of the bezier
var p2 : Point %endpoint of the bezier
p0.x := 200 %some random initial values.
p0.y := 200
var a0 : real := slope (p0 )
p1.x := 75
p1.y := 75
p2.x := 150
p2.y := 150
var offsetX : real := 0
var offsetY : real := 0
var mx, my, mb : int
var keys : array char of boolean
var keyPressed : boolean := false
View.Set ("offscreenonly")
var s : real
var track : boolean := false
loop
Mouse.Where (mx, my, mb )
Input.KeyDown (keys )
mx - = round (offsetX )
my - = round (offsetY )
if keys (KEY_ENTER ) and keyPressed = false then
keyPressed := true
offsetX + = p2.x
offsetY + = p2.y
p0.x := p2.x - p1.x
p0.y := p2.y - p1.y
p2.x := p0.x
p2.y := p0.y
p1.x := p2.x/ 2
p1.y := p2.y / 2
a0 := slope (p0 )
% p1.x := 75 * cosd (a1)
% p1.y := 75 * sind (a1)
elsif not keys (KEY_ENTER ) then
keyPressed := false
end if
if mb = 1 and mx < p2.x + 5 and mx > p2.x - 5 and my < p2.y + 5 and my > p2.y - 5 then
track := true
elsif mb = 0 then
track := false
end if
if track = true then
p2.y := my
p2.x := mx
% Please excuse the terrible naming, a0, a1, a2 correspond to slope() for p0, p1, p2
var a2 : real := slope (p2 )
var a : real := (a0 - a2 ) / 2 % angle between lines from origin to p0 and p1 (equal to angle between lines from origin to p1 and p2)
var len : real := sqrt (norm2 (p2 ) / (2 * (1 - cosd (180 - 2 * a ))))
var a1 : real := a2 + a
p1.x := len * cosd (a1 )
p1.y := len * sind (a1 )
end if
%Draw.Line (round (offsetX), round (offsetY), round (p0.x+offsetX), round (p0.y+offsetY), black)
Draw.Line (round (offsetX ), round (offsetY ), round (p1.x+offsetX ), round (p1.y+offsetY ), red)
Draw.Line (round (p1.x+offsetX ), round (p1.y+offsetY ), round (p2.x+offsetX ), round (p2.y+offsetY ), red)
Draw.FillOval (round (p2.x + offsetX ), round (p2.y + offsetY ), 5, 5, blue)
for i : 0 .. 60
drawBezier (offsetX, offsetY, p1.x+offsetX, p1.y+offsetY, p2.x+offsetX, p2.y+offsetY, 1. 0 / 60 * i )
end for
View.Update
delay (10)
cls
end loop |
|
|
|
|
|
|
Dreadnought
|
Posted: Sun Apr 06, 2014 12:44 am Post subject: Re: Modelling a specific sort of curve |
|
|
Insectoid wrote:
There is an issue when p0 is pointing right-to-left; the slope gets reflected around the y axis (you'll see what I mean). Pretty sure this has something to do with the CAST rule of trig, but I can't figure it out right now. It's a long time since I did any trig.
You could try changing the computation of a2 to something like this
Turing: | var a2 : real := slope (p2)
put dot (p2, p0)
if (p2.x < 0) then
% The angle is greater than 90 deg (or less than -90 deg)
% We need to adjust the value based on whether p2 is above or below the line from (offsetX, offsetY) to p0
if (p2.x * p0.y / p0.x < p2.y) then
a2 := 180 - a2
else
a2 := -180 - a2
end if
end if |
Just causes a problem if a2 = -a0 (division by zero when computing len) |
|
|
|
|
|
|
|