Random movement - smooth, based on bezier

Hi!

Let’s say I want to make an object move across the screen in a random way (like an irritating fly!). It’s pretty easy to figure out how to get some random movement with transitions and random values for the x and y each time. Fair enough. Easy.

But what if I wanted to add some flair to everything, and make the object move in a more natural circular/bezier way (again… like a fly!). Basically having a path that is a continous bezier, so that the motion of an object is continously smooth, instead of just jagged jumping to different points. How would I accomplish this?

I found Carlos code for bezier movement and played around with it a bit just to see what I could come up with, and managed to create something barely functional - the object flies according to a bezier path that gets re-created at random every time the object reaches the end of it. I have attached the code block if anyone is interested - but be warned, the code is not pretty, and except for the glaring things like graphics being there but just hidden, the major gripe with this code is that the bezier path is not continous, and therefore the “turns” at every end is jittery and too quick. Smooth movement would be the ideal here, following a continous bezier path!

So – I just wanted to throw out an open question to the community, because I imagine more people like me being on the lookout for this. Has anyone accomplished smooth random movement using a bezier curve? What would be your ideas for accomplishing it?

[code]
module(…, package.seeall)

– Main function - MUST return a display.newGroup()

– Director function, we set up the scene

function new()

– Set up group
local localGroup = display.newGroup()


– Abstract: Bezier Handle Manipulation in Corona SDK

– Sample code is MIT licensed, see http://developer.anscamobile.com/code/license
– Copyright © 2010 ANSCA Inc. All Rights Reserved.

– Demonstrates how to create bezier segment.
– Not optmized. Just done. Feel free to modify at your hearts content.


local segMents = {};
local bezierSegment = {x,y};
local endCaps = {};
local tempCaps = {};
local tempBG = {};
local handleDot = {};

local FALSE = 0;
local TRUE = 1;
local moved = FALSE;
local numPoints = 0;
local NUMPOINTS = 200;
local gSegments;

local PIE = 3.14159265358

local line;

local speedFactor = 2

local bbg = nil;


local function destroyBezierSegment()

segMents = nil;
segMents = {};
handleDot = nil
handleDot = {};
end



local function drawBezierSegment(granularity,r,g,b)

if line then
line:removeSelf()
end

line = display.newLine(bezierSegment[1].x,bezierSegment[1].y,bezierSegment[2].x,bezierSegment[2].y);

localGroup:insert(line)

for i = 3, granularity, 1 do
line:append( bezierSegment[i].x,bezierSegment[i].y);
end
line:setColor(r,g,b, 0);
line.width=2;
end


local function setupBezierSegment(granularity)

local inc = (1.0 / granularity);

for i = 1, #endCaps,4 do

local t = 0;
local t1 = 0;
local i = 1;

for j = 1, granularity do

t1 = 1.0 - t;

local t1_3 = t1*t1*t1
local t1_3a = (3*t)*(t1*t1)
local t1_3b = (3*(t*t))*t1;
local t1_3c = (t * t * t )

local p1 = endCaps[i];
local p2 = endCaps[i+1];
local p3 = endCaps[i+2];
local p4 = endCaps[i+3];

local x = t1_3 * p1.x;
x = x + t1_3a * p2.x;
x = x + t1_3b * p3.x;
x = x + t1_3c * p4.x

local y = t1_3 * p1.y;
y = y + t1_3a * p2.y;
y = y + t1_3b * p3.y;
y = y + t1_3c * p4.y;

bezierSegment[j].x = x;
bezierSegment[j].y = y;
t = t + inc;

end
end
end


–[[
local function drawBezierHandles()

if ( gSegments ) then
gSegments:removeSelf()
end

gSegments = display.newGroup()

for i = 1,#endCaps,2 do
local line = display.newLine(endCaps[i].x,endCaps[i].y,endCaps[i+1].x,endCaps[i+1].y);
line:setColor(128,128,128, 0);
line.width=1;
gSegments:insert( line )
table.insert(segMents,line);
end

end

–]]


– moveAlongThesegment self explanatory

local prevX = 1;
local prevY = 1;
local prevAngle = 1;
local bFirst = true;
local object = nil;

local function createObject()

local objectGroup = display.newImageRect(“f15.png”,100,100);

return objectGroup

end
local function moveAlongThesegment( inc )
if (bFirst == true ) then
object = createObject();
localGroup:insert(object)
prevX = bezierSegment[1].x;
prevY = bezierSegment[1].y;
bFirst = false;

return;
end

local p = {};

p.x = bezierSegment[inc].x;
p.y = bezierSegment[inc].y;

local angle = math.atan2( p.y - prevY, p.x - prevX)
angle = angle * 180 / PIE

object.x = p.x;
object.y = p.y;

object:rotate(angle-prevAngle);
prevAngle = angle;

prevY = p.y;
prevX = p.x;

end



local function setupBezier(granularity,r,g,b)

– 1st draw anchor points and handles
–drawBezierHandles();

– 2nd setupBezierSegment
setupBezierSegment(granularity);

– 3rd draw the segment
drawBezierSegment(granularity,r,g,b);

– 4th destroy the segment
destroyBezierSegment();
end


local pointInc = 2;

local function randomizeBezierPoints()

for i=1,#endCaps,1 do

if i == 1 then

– print(endCaps[i].x … " blir " … endCaps[4].x)

endCaps[i].x = endCaps[4].x
endCaps[i].y = endCaps[4].y;

else

endCaps[i].x = math.random(screenWidth);
endCaps[i].y = math.random(screenHeight);

end

end
pointInc = 2;
moved = FALSE;
bFirst = false;
setupBezier(100,128,128,128)
end

local function draw (event )
if ( moved == TRUE ) then
setupBezier(50,128,128,128)
pointInc = 2;
bFirst = false;
return true;
elseif (moved == FALSE ) then

if ( pointInc < #bezierSegment ) then
moveAlongThesegment(pointInc);
pointInc = pointInc + speedFactor;

– print(pointInc)

if (pointInc >= #bezierSegment) then

– print(“moved”)
randomizeBezierPoints()

–randomizePositionPoints()
end
end
end
end



–[[

local function dragHandles(event)

local t = event.target

local phase = event.phase
if “began” == phase then
– Make target the top-most object
local parent = t.parent
parent:insert( t )
display.getCurrentStage():setFocus( t )

t.isFocus = true

t.x0 = event.x - t.x
t.y0 = event.y - t.y

elseif t.isFocus then
if “moved” == phase then
– Make object move (we subtract t.x0,t.y0 so that moves are
– relative to initial grab point, rather than object “snapping”).
t.x = event.x - t.x0
t.y = event.y - t.y0

moved = TRUE;

elseif “ended” == phase or “cancelled” == phase then
display.getCurrentStage():setFocus( nil )
t.isFocus = false
moved = FALSE;
setupBezier(100,255,128,0);
end
end

return true

end
–]]


–[[
local function addListeners()

for i = 1 , #endCaps, 1 do
endCaps[i]:addEventListener(“touch”,dragHandles);
end

end

–]]



–[[
local function addPoints (event )

numPoints = numPoints + 1;

if ( numPoints <= 4) then

local point = {}
point.x = event.x;
point.y = event.y;

–local c = display.newCircle(point.x, point.y,10);
–c:setFillColor(255,0,0);
local c = display.newText(#endCaps+1, 0, 0, native.systemFont, 16)
table.insert (endCaps,c);

end

if (numPoints == 4 ) then
Runtime:removeEventListener(“tap”,addPoints)
addListeners()
setupBezier(100,255,128,0);
Runtime:addEventListener(“enterFrame”,draw);
end
end

–]]



– MW RANDOM POSITIONS OF POINTS


local function randomizePositionPointsStart()

for i=1,4,1 do

numPoints = numPoints + 1;

if ( numPoints <= 4) then

local point = {}
point.x = math.random(screenWidth);
point.y = math.random(screenHeight);

local c = display.newCircle(point.x, point.y,0);
c:setFillColor(255,0,0,0);

– local c = display.newText(#endCaps+1, 0, 0, native.systemFont, 16)

table.insert (endCaps,c);

end

if (numPoints == 4 ) then
– Runtime:removeEventListener(“tap”,addPoints)
– addListeners()
setupBezier(100,255,128,0);
Runtime:addEventListener(“enterFrame”,draw);
end

end
end


local function initBezierSegment()

for j = 1, 100, 1 do

local pt = {};
pt.x = 0;
pt.y = 0;
table.insert(bezierSegment,pt);

end
end



local function main()

–[[
for i = 0, display.contentWidth, 20 do
local ll = display.newLine(i,0,i,display.contentHeight);
ll:setColor(98,98,98)
ll.width = 1;
end

for i = 0, display.contentHeight, 20 do
local ll = display.newLine(0,i,display.contentWidth,i);
ll:setColor(98,98,98)
ll.width = 1;
end
–]]

initBezierSegment()

randomizePositionPointsStart()

end



main();

return localGroup

end
[/code] [import]uid: 13935 topic_id: 11605 reply_id: 311605[/import]

see my object along a path entries a www.carlosicaza.com

for smooth movement, you can reparametize the curve by finding the arch length.

in my blog you will find the links to pages describing reparametization and other snipplets and suggestions to make movement along a path silkier.

c. [import]uid: 24 topic_id: 11605 reply_id: 42164[/import]

how to make Bezier curve with more than 4 point(to be exact 10 point)? [import]uid: 45566 topic_id: 11605 reply_id: 108463[/import]

For the movement of game characters, it is very helpful to check the literature on game AI and simple algorhytms for steering behaviors including “smooth arrive at”, chasing, intercepting, flocking and more…
Although these words seem frighteningly complex, very often the maths behind these behaviors is far more simple than calculating bezier paths (and therefore is easier to code and performs better).

Sometimes bezier paths are required when you specifically want character to follow the predefined path, but much more often we just want to recreate some realistic behavior and *interaction* between characters, and that’s why steering behaviors come in as the best and simples solution.

Some online resource:
http://my.safaribooksonline.com/book/programming/game-programming/0596005555

I would also greatly recommend fantastic book by Mat Buckland:
Programming Game AI by Example.
It contains theory and complete code behind all basic AI behaviors. Although the code is written in C++, it is very simple and easy to convert to Lua even if you never used C.

Alternatively, there is a huge amount of online information, just search for “steering behaviors” and game AI.

[import]uid: 80100 topic_id: 11605 reply_id: 108470[/import]