What I was trying to do is understand the equation I put forward an started making it a little more useful to mine (and hopefully your needs)
Right lets look at some code. Only two files here and have tried commenting as much as possible
main.lua
-- This line makes the physics engine features available under the “physics” namespace require( "physics" ) -- A further file to create a smooth curve when drawing require( "smoothCurve") -- ither instantiates or resumes the world. IMPORTANT: it must be called before any of the other functions in this section. physics.start() -- ets the x,y components of the global gravity vector, in units of m/s2. The default is ( 0, 9.8 ) to simulate standard -- Earth gravity, pointing downwards on the y-axis. physics.setGravity(0,0) -- Creates a group in which you can add and remove child display objects. Initially, there are no children in a group. -- The local origin is at the parent’s origin; the reference point is initialized to this local origin local mainGroup = display.newGroup() local lineGroup = display.newGroup() mainGroup:insert(lineGroup) local objectGroup = display.newGroup() mainGroup:insert(objectGroup) -- Calculation interval of drawing between the dotted lines local lineDrawInterval = 50 -- Method called when our object (blue box) is touched function onObjectTouched(self, event) --[[ Touch events are a special kind of hit event. When a user's finger touches the screen, they are starting a sequence of touch events, each with different phases. event.name is the string "touch". event.x is the x-position in screen coordinates of the touch. event.y is the y-position in screen coordinates of the touch. event.xStart is the x-position of the touch from the "began" phase of the touch sequence. event.yStart is the y-position of the touch from the "began" phase of the touch sequence. event.phase is a string identifying where in the touch sequence the event occurred: "began" a finger touched the screen. "moved" a finger moved on the screen. "ended" a finger was lifted from the screen. "cancelled" the system cancelled tracking of the touch. ]] if(event.phase == "began") then -- Determine whether we are to draw a line self.allowLineDraw = true -- Determine the stype of line output self.lineType = "dotted" -- Instantiate our table to hold the points self.points = {} -- Insert our initial touch table.insert(self.points, {x = event.x, y = event.y}) -- Returns a reference to the current stage object, which is the root group for all display objects and groups. -- Currently, Corona has a single stage instance, so this function always returns a reference to the same object display.getCurrentStage():setFocus( self ) -- Set the focus to our object (box) self.isFocus = true elseif (event.phase == "moved" and self.isFocus == true) then -- Make a check to see we have at least one point if(#self.points == 0) then -- Instantiate our table to hold the points self.points = {} -- Insert our current position table.insert(self.points, {x = self.x, y = self.y}) table.insert(self.points, {x = event.x, y = event.y}) end -- Determine the distance between our points -- math.sqrt: Returns the square root of a value. -- math.pow: Returns the result of raising a number to the power of another number. local distance = math.sqrt(math.pow(self.points[#self.points].x - event.x, 2) + math.pow(self.points[#self.points].y - event.y, 2)) -- Insert our current position table.insert(self.points, {x = event.x, y = event.y}) -- If we have set our object to draw a line first check if(self.allowLineDraw == true) then -- Draw the line drawLine(self) -- Now get our object to follow it followPoints(self) -- Since we have followed it and moved over our control point, remove the lone self.allowLineDraw = false -- Dependent on our line draw interval recall our method timer.performWithDelay(lineDrawInterval, function(new) self.allowLineDraw = true end) end elseif(event.phase == "ended") then -- Now that we have lifted our touch determined the distance local distance = math.sqrt(math.pow(self.points[#self.points].x - event.x, 2) + math.pow(self.points[#self.points].y - event.y, 2)) -- Returns a reference to the current stage object, which is the root group for all display objects and groups. -- Currently, Corona has a single stage instance, so this function always returns a reference to the same object display.getCurrentStage():setFocus(nil) -- Remove the focus from our object now self.isFocus = false -- Follow to the end point followPoints(self) -- Show on the stage the line to be solid self.lineType = "solid" -- Draw it solid rather than dotted drawLine(self) end end function drawLine(box) -- Do we have a line already if(box.lineGroup ~= nil) then -- Make sure we destory anything in memory box.lineGroup:removeSelf() box.lineGroup = nil end -- Retrieve our point (see other description) i.e. our catmull spline local smoothPoints = smoothCurve.getSmoothCurvePoints(box.points) -- If we don't have any points then exit now if(smoothPoints == nil) then return end -- Lets create a new linegroup display object for our box box.lineGroup = display.newGroup() --insert this into our linegroup display object on our stage lineGroup:insert(box.lineGroup) -- Return modulus dependent on line type local modNumber = box.lineType == "dotted" and 2 or 1 -- Iterate through our points for i = 0 ,#smoothPoints do if(i % modNumber == 0 and smoothPoints[i] ~= nil and smoothPoints[i + 1] ~= nil) then local line = display.newLine(smoothPoints[i].x, smoothPoints[i].y, smoothPoints[i + 1].x, smoothPoints[i + 1].y) -- Determine line width dependent on modulus line.width = modNumber == 1 and 1 or 3 -- Insert the line into our display object box.lineGroup:insert(line) end end -- Return the points now we have them all return smoothPoints end function followPoints(box) -- Check to see if we have any points if not exit if(box.points == nil or #box.points == 0) then return end -- Get our point to follow point = box.points[1] -- Get the angle for our box to move local angle = math.atan2((box.y - point.y) , (box.x - point.x) ) * (180 / math.pi) -- Detemine the x and y velocity to move our object local velocityX = math.cos(math.rad(angle)) * 50 * -1 local velocityY = math.sin(math.rad(angle)) * 50 * -1 -- Now set the object (box) velocity box:setLinearVelocity( velocityX, velocityY) -- Initialise our method local enterFrameFunction = nil local checkForNextPoint checkForNextPoint = function(box) -- Do we have an object (box) if(box ~= nil) then -- Get our destination local dest = box.dest -- Return the velocity we have just set local velX, velY = box:getLinearVelocity() -- Cbeck a load of stuff if( (velX < 0 and box.x < dest.x and velY < 0 and box.y < dest.y) or (velX > 0 and box.x > dest.x and velY < 0 and box.y < dest.y) or (velX > 0 and box.x > dest.x and velY > 0 and box.y > dest.y) or (velX < 0 and box.x < dest.x and velY > 0 and box.y > dest.y) or #box.points == 0) then -- Remove our event listener Runtime:removeEventListener("enterFrame", enterFrameFunction) -- Remove the point as we have just got to it so not to keep drawing the same one over and over table.remove(box.points, 1) -- Now draw it drawLine(box) -- If we have at least another point left then repeat all this again if(#box.points > 0) then followPoints(box) else -- If we don't then stop the object (box) waiting for another touch box:setLinearVelocity( 0, 0) end end else -- Nothing to do so remove our listener Runtime:removeEventListener("enterFrame", enterFrameFunction) end end -- Set the box destination to the current point box.dest = point -- Lets do it all again enterFrameFunction = function(event) checkForNextPoint(box) end -- Add another listener Runtime:addEventListener("enterFrame", enterFrameFunction) end -- Lets create our object which is a blue box local box = display.newImage( "bluesquare.jpeg") -- Position it roughly top left corner box.x = 50 box.y = 50 -- Sets its calling method for when the object (box) is touched box.touch = onObjectTouched -- Add a listener so it will do something box:addEventListener("touch", box) -- Apply physics so that velocity etc will have some effect physics.addBody(box, "dynamic", {density = 100, isSensor = true, radius = 40}) -- Now add our local box to the main object display group objectGroup:insert(box)
and the all important catmull splines magic
smoothCurve.lua
--[[ Creates a module. If there is a table in package.loaded[name], this table is the module. Otherwise, if there is a global table t with the given name, this table is the module. Otherwise creates a new table t and sets it as the value of the global name and the value of package.loaded[name]. This function also initializes t._NAME with the given name, t._M with the module (t itself), and t._PACKAGE with the package name (the full module name minus last component; see below). Finally, module sets t as the new environment of the current function and the new value of package.loaded[name], so that require returns t. This function may receive optional options after the module name, where each option is a function to be applied over the module. ]] module(..., package.seeall) function getSmoothCurvePoints(points) -- Number of points must be at least 3 to calculate the catmull spline if(#points < 3) then return nil end -- Create our table to hold our points local smoothPoints = {} -- Steps Per Segment - The Higher The Number - The Smoother The Curve - The Longer It Takes To Calculate local curveSteps = 30 -- First Segment (Remember the complication control points in the introduction blog) local firstSegement = drawCatmullSpline( points[1] , points[1] , points[2] , points[3] , curveSteps ) -- Increment through control points and add it to our table for i = 1 , #firstSegement , 1 do table.insert(smoothPoints, {x = firstSegement[i].x, y = firstSegement[i].y}) end -- Now increment through our segments inbetween for i = 2 , #points - 2 , 1 do local middleSegment = drawCatmullSpline( points[i-1] , points[i] , points[i+1] , points[i+2] , curveSteps ) for i = 2 , #middleSegment , 1 do --Add our smoothpoints between our control points table.insert(smoothPoints, {x = middleSegment[i].x, y = middleSegment[i].y}) end end -- Just to finish it all off apply our last segment local lastSegment = drawCatmullSpline( points[#points-2] , points[#points-1] , points[#points] , points[#points] , curveSteps ) for i = 2 , #lastSegment , 1 do table.insert(smoothPoints, {x = lastSegment[i].x, y = lastSegment[i].y}) end -- Now return our nice smooty curve return smoothPoints end function drawCatmullSpline( p0 , p1 , p2 , p3 , steps ) -- Lets create a table for our points local points = {} for t = 0 , 1 , 1 / steps do -- Remember that complicated catmull spline equation in my blog. Here we use it to work with our control points supplied local xPoint = 0.5 * ( ( 2 * p1.x ) + ( p2.x - p0.x ) * t + ( 2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x ) * t * t + ( 3 * p1.x - p0.x - 3 * p2.x + p3.x ) * t * t * t ) local yPoint = 0.5 * ( ( 2 * p1.y ) + ( p2.y - p0.y ) * t + ( 2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y ) * t * t + ( 3 * p1.y - p0.y - 3 * p2.y + p3.y ) * t * t * t ) -- Now insert these into the our table table.insert( points , { x = xPoint , y = yPoint } ) end -- Finished with them all then return our points table return points endRight now we are cooking on gas what is this going to give us. Well here are a series of screenshots to give you a taster but why not give it a go to see what happens.
Now I have an object following a path what I want to implement is a chase and evade AI element to it. I'll post just as soon as I have worked it out.
Stay tuned ...
0 comments:
Post a Comment