Some maths help needed please.

Hi all,

Once again I’m in need of some maths assistance.

I have some enemies that I would like to fly in from the left of the screen and make their way in a slight downward slope to a point off the screen on the right side.  Not difficult so far.  Easy in fact.  But what I’d also like is for the object to move in a sine wave style pattern. 

I don’t know if this makes any sense to you so I’ve knocked up a quick sketch (apologies for the lack of drawing skills).  I’ve tried studying up on maths but to be honest it’s something I’ve always struggled with and really hurts my head.

 

Any help at all would be gratefully appreciated.

 

You can try Bezier curves:

http://www.carlosicaza.com/?s=bezier

https://forums.coronalabs.com/topic/3107-bezier-curves-in-corona-source-code/

There is one nice example on the web ( tank moving)… I cannot find it now.

You have to pre-define x1, y1, x2, y2 and so on… leave the alpha to 1 until you do so.

Coordinates can be defined in screenWidth/screenHeight ratio… I am using that in my game.

I had a sin wave sample lying around, which only needed a tiny bit of adjustment to do what you needed. I’ve commented it as best I can, but let me know if you have any questions:

--left/top edges of screen local minX, minY = display.screenOriginX, display.screenOriginY --right/bottom of the screen local midX, midY = display.contentCenterX, display.contentCenterY --center of the screen local maxX, maxY = display.viewableContentWidth + -1\* display.screenOriginX, display.viewableContentHeight + -1\* display.screenOriginY --full width/heigt of the screen, regardless of aspect ratio etc local \_W, \_H = maxX - minX, maxY - minY --center reference line local midLineHorz = display.newLine( minX, midY, maxX, midY ) midLineHorz:setStrokeColor( 1, 0, 0, 0.5 ) midLineHorz.strokeWidth = 5 local obj = display.newCircle( minX, midY, 15 )--the main object to be moved obj.baseY = midY --the vertical center point of the wave - gradually adjust this using ySpeed to move the path of the wave obj.ySpeed = -2 --how much the whole wave should gradually move up/down obj.waveYSpeed = 0.05 --how fast the wave should move up and down (frequency of the wave) obj.waveHeight = 100--how far up/down each curve of the wave should go from its center (amplitude of wave) obj.xSpeed = 2 --how fast the end of the obj should move left/right obj.sinCount = 0--used to actually make the sine wave happen --used to store a trail behind the object local objPoints = {} local function gameLoop( event ) --move to the right obj.x = obj.x + obj.xSpeed --move up/down alone sine curve obj.y = obj.baseY + (obj.waveHeight \* math.sin(obj.sinCount\*obj.waveYSpeed)) --this is where the whole wave will be moved up and down obj.baseY = obj.baseY + obj.ySpeed --this is just drawing extra points so you can see the whole wave objPoints[#objPoints+1] = display.newCircle(obj.x, obj.y, 5) objPoints[#objPoints]:setFillColor( 0.7 ) --increase the sin value for next frame obj.sinCount = obj.sinCount + 0.5 --and loop it back to zero once it completes a full circle if (obj.sinCount \* obj.ySpeed) \>= (2 \* math.pi) then obj.sinCount = 0 end end Runtime:addEventListener("enterFrame", gameLoop)

at the risk of stepping on @Alan QuizTix’s toes, who has already given a good answer, I’d approach it as follows…

first, I’d create a parametric function to memoize your curve definition, fe:

local function makeOscillator(x,y,dx,dy,phz,frq,amp) return function(t) return x+t\*dx, y+t\*dy+amp\*math.cos(phz+t\*frq) end end

then i’d make an instance of it with desired values:

-- assuming no config.lua, ie just device pixels, for simplicity: local CW = display.contentWidth local CH = display.contentHeight local osc = makeOscillator(0,CH/8, CW,CH/2, 0,20,CH/10)

then i’d test and tune it by plotting some dots (a la @Alan QuizTix’s trail):

for t = 0,1,0.01 do local x,y = osc(t) local dot = display.newRect(x,y,4,4) end

then i’d create something to follow that curve:

local thing = display.newRect(0,0,10,10)

then, depending on whether you’re doing frame animation or transitions, either

local t = 0 local dt = 1/(5\*30) -- 5 seconds @ 30fps local function enterFrame() thing.x, thing.y = osc(t) t = t + dt end Runtime:addEventListener("enterFrame", enterFrame)

or via transition:  (a bit more voodoo, but essentially equivalent)

local function makeProxy(object, f) local t = 0 return setmetatable({}, { \_\_index = function(\_,\_) return t end, \_\_newindex = function(\_,\_,v) t = v; object.x, object.y = f(t) end }) end local proxy = makeProxy(thing, osc) transition.to(proxy, { time=5000, t=1.0 })

hth

You can also adapt the above to take into account the angle of your path. The previous two posts assume a “forward” axis that goes left-to-right and an “up” that goes down-to-up.

You can compute the forward direction as

local dx, dy = x2 - x1, y2 - y1 local length = math.sqrt(dx^2 + dy^2) local fx, fy = dx / length, dy / length

This is a normalized vector, meaning that each time you add it a point, you move one unit forward.

You can find the up direction by

local ux, uy = fy, -fx

(This is the perp operation. There are actually two possibilities, according to which component you negate; but this one gives you negative values pointing up.)

You then proceed as you might expect, moving forward by x units and up by y units.

This seem slightly easier to adapt from  davebollinger’s example (I think you’d also need to at least track a baseX property with Alan QuizTix’s version):

 return function(t) local px, py = x+t\*dx, y+t\*dy -- "horizontal" part, i.e. P + DX \* x -- this is just the unnormalized fx, fy local ypos = amp\*math.cos(phz+t\*frq) -- "vertical" part return px + ux \* ypos, py + uy \* ypos -- the rest: P + DX \* x + DY \* y end

Assuming I didn’t mess anything up, that will give you a tilted sine wave. Obviously, whether that’s what you want is up to you.

Wow.  Thanks for the answers guys.  This is in no way meant as a slight towards you guys but I have to say honestly that I don’t understand any of it.   

However, when I get home I will have a play about with it, because lets face it, I don’t really need to understand it if it works :slight_smile:

Maybe try

local x, y = 0, 0 local startX,  startY = 10, display.contentCenterY local step = 1 local k = 25  local alpha = math.pi \* 0.1 local obj, newX, newY local function gameLoop( event ) -- compute x and y coordinates of sine wave x = x + step y = math.sin( x / k ) \* k -- make plot bigger k \> 1  -- rotation of plot around (0, 0) through angle alpha newX = x \* math.cos( alpha ) - y \* math.sin( alpha ) newY = y \* math.cos( alpha ) + x \* math.sin( alpha )     obj = display.newCircle(newX + startX, newY + startY, 1)    end Runtime:addEventListener("enterFrame", gameLoop)

Preview

d9KrfDB.gif

You can try Bezier curves:

http://www.carlosicaza.com/?s=bezier

https://forums.coronalabs.com/topic/3107-bezier-curves-in-corona-source-code/

There is one nice example on the web ( tank moving)… I cannot find it now.

You have to pre-define x1, y1, x2, y2 and so on… leave the alpha to 1 until you do so.

Coordinates can be defined in screenWidth/screenHeight ratio… I am using that in my game.

I had a sin wave sample lying around, which only needed a tiny bit of adjustment to do what you needed. I’ve commented it as best I can, but let me know if you have any questions:

--left/top edges of screen local minX, minY = display.screenOriginX, display.screenOriginY --right/bottom of the screen local midX, midY = display.contentCenterX, display.contentCenterY --center of the screen local maxX, maxY = display.viewableContentWidth + -1\* display.screenOriginX, display.viewableContentHeight + -1\* display.screenOriginY --full width/heigt of the screen, regardless of aspect ratio etc local \_W, \_H = maxX - minX, maxY - minY --center reference line local midLineHorz = display.newLine( minX, midY, maxX, midY ) midLineHorz:setStrokeColor( 1, 0, 0, 0.5 ) midLineHorz.strokeWidth = 5 local obj = display.newCircle( minX, midY, 15 )--the main object to be moved obj.baseY = midY --the vertical center point of the wave - gradually adjust this using ySpeed to move the path of the wave obj.ySpeed = -2 --how much the whole wave should gradually move up/down obj.waveYSpeed = 0.05 --how fast the wave should move up and down (frequency of the wave) obj.waveHeight = 100--how far up/down each curve of the wave should go from its center (amplitude of wave) obj.xSpeed = 2 --how fast the end of the obj should move left/right obj.sinCount = 0--used to actually make the sine wave happen --used to store a trail behind the object local objPoints = {} local function gameLoop( event ) --move to the right obj.x = obj.x + obj.xSpeed --move up/down alone sine curve obj.y = obj.baseY + (obj.waveHeight \* math.sin(obj.sinCount\*obj.waveYSpeed)) --this is where the whole wave will be moved up and down obj.baseY = obj.baseY + obj.ySpeed --this is just drawing extra points so you can see the whole wave objPoints[#objPoints+1] = display.newCircle(obj.x, obj.y, 5) objPoints[#objPoints]:setFillColor( 0.7 ) --increase the sin value for next frame obj.sinCount = obj.sinCount + 0.5 --and loop it back to zero once it completes a full circle if (obj.sinCount \* obj.ySpeed) \>= (2 \* math.pi) then obj.sinCount = 0 end end Runtime:addEventListener("enterFrame", gameLoop)

at the risk of stepping on @Alan QuizTix’s toes, who has already given a good answer, I’d approach it as follows…

first, I’d create a parametric function to memoize your curve definition, fe:

local function makeOscillator(x,y,dx,dy,phz,frq,amp) return function(t) return x+t\*dx, y+t\*dy+amp\*math.cos(phz+t\*frq) end end

then i’d make an instance of it with desired values:

-- assuming no config.lua, ie just device pixels, for simplicity: local CW = display.contentWidth local CH = display.contentHeight local osc = makeOscillator(0,CH/8, CW,CH/2, 0,20,CH/10)

then i’d test and tune it by plotting some dots (a la @Alan QuizTix’s trail):

for t = 0,1,0.01 do local x,y = osc(t) local dot = display.newRect(x,y,4,4) end

then i’d create something to follow that curve:

local thing = display.newRect(0,0,10,10)

then, depending on whether you’re doing frame animation or transitions, either

local t = 0 local dt = 1/(5\*30) -- 5 seconds @ 30fps local function enterFrame() thing.x, thing.y = osc(t) t = t + dt end Runtime:addEventListener("enterFrame", enterFrame)

or via transition:  (a bit more voodoo, but essentially equivalent)

local function makeProxy(object, f) local t = 0 return setmetatable({}, { \_\_index = function(\_,\_) return t end, \_\_newindex = function(\_,\_,v) t = v; object.x, object.y = f(t) end }) end local proxy = makeProxy(thing, osc) transition.to(proxy, { time=5000, t=1.0 })

hth

You can also adapt the above to take into account the angle of your path. The previous two posts assume a “forward” axis that goes left-to-right and an “up” that goes down-to-up.

You can compute the forward direction as

local dx, dy = x2 - x1, y2 - y1 local length = math.sqrt(dx^2 + dy^2) local fx, fy = dx / length, dy / length

This is a normalized vector, meaning that each time you add it a point, you move one unit forward.

You can find the up direction by

local ux, uy = fy, -fx

(This is the perp operation. There are actually two possibilities, according to which component you negate; but this one gives you negative values pointing up.)

You then proceed as you might expect, moving forward by x units and up by y units.

This seem slightly easier to adapt from  davebollinger’s example (I think you’d also need to at least track a baseX property with Alan QuizTix’s version):

 return function(t) local px, py = x+t\*dx, y+t\*dy -- "horizontal" part, i.e. P + DX \* x -- this is just the unnormalized fx, fy local ypos = amp\*math.cos(phz+t\*frq) -- "vertical" part return px + ux \* ypos, py + uy \* ypos -- the rest: P + DX \* x + DY \* y end

Assuming I didn’t mess anything up, that will give you a tilted sine wave. Obviously, whether that’s what you want is up to you.

Wow.  Thanks for the answers guys.  This is in no way meant as a slight towards you guys but I have to say honestly that I don’t understand any of it.   

However, when I get home I will have a play about with it, because lets face it, I don’t really need to understand it if it works :slight_smile:

Maybe try

local x, y = 0, 0 local startX,  startY = 10, display.contentCenterY local step = 1 local k = 25  local alpha = math.pi \* 0.1 local obj, newX, newY local function gameLoop( event ) -- compute x and y coordinates of sine wave x = x + step y = math.sin( x / k ) \* k -- make plot bigger k \> 1  -- rotation of plot around (0, 0) through angle alpha newX = x \* math.cos( alpha ) - y \* math.sin( alpha ) newY = y \* math.cos( alpha ) + x \* math.sin( alpha )     obj = display.newCircle(newX + startX, newY + startY, 1)    end Runtime:addEventListener("enterFrame", gameLoop)

Preview

d9KrfDB.gif