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
end
Right 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