Since the previous tutorial on Moving Objects Along a Path, I’ve received many requests for a tutorial that deals with curved paths, either generated via a bezier curve algorithm or “drawn” by the user’s touch on the screen. Today’s tutorial will cover both methods, including sample projects for download. In addition, we’ll walk through a basic module that makes an object follow the curved path.

Bezier Method

bezier

The first sample project, available for download, lets the user touch one point on the screen to set the starting point of the curve, then the user may drag outward to create a “handle” that will adjust the curve. Next, the user may touch a second point to indicate the ending point of the curve and likewise drag outward to adjust the handle. For anybody who has worked with paths in an image/vector editing program like Photoshop or Illustrator, this process will be very familiar.

Dissecting the entire sample project is beyond the scope of this tutorial, but a few elements near the top of the code are important to understand:

This is simply an up-value reference to what will become, during manipulation of the anchors and handles, the display object (display.newLine()) for the generated curve. Instead of creating a separate object for each segment in the curve, we’ll use the convenient append() function to add segments to a core line object.

This table will contain an ordered series of sub-tables, each of which will contain the x and y position of a point along the curve. The structure of each sub-table is very simple, for example: x=10, y=24 . These points will also be passed to the follow.lua module that places an object at the starting point and transitions it from point to point — this will be discussed further down.

This variable is important to mention since it allows us to easily adjust the “smoothness” of the curve. More specifically, this value represents the total number of segments which will constitute the curve, and thus, higher values will yield a smoother curve. The default is 100 which should be sufficient for most scenarios.

This table is also passed to the follow.lua module and it allows us to adjust the behavior of the routine via the following key-value pairs:

  • segmentTime — The time of the transition between each point along the curve.
  • constantRate — Because the distance between points will vary, this sets the rate of movement to be more constant by using the length of the first segment as a basis and then adjusting the transition time of subsequent segments accordingly.
  • showPoints — If true, this will place a dot along each point in the curve.

Drawing Method

pathFor generating a more “organic” path, we can use a path drawing module, also available for download. This method simply lets the user draw a path of any length by touching and dragging around the screen.

As above, dissecting the entire project is beyond the scope of this tutorial, but a few elements should be explained:

Similar to the bezier method, this is simply an up-value reference to what will become, as the user begins drawing, the display object for the path.

This table serves the same purpose as in the bezier module. It will contain an ordered series of sub-tables, each of which will contain the x and y position of a point along the curve.

This value represents the minimum distance between any two points along the path. This is especially important because, in the "moved" phase of a touch event, the user’s touch will be registered at very small increments and, if we created a path point on each increment, the pathPoints table would potentially be populated by hundreds or even thousands of coordinate sub-tables. That would result in an extremely “smooth” curve, but it’s more detail than necessary in most cases. Thus, the drawing routine will only register a new coordinate point if the distance from the previous point is greater than or equal to pathPrecision.

This table is serves the same purpose as in the bezier example, where segmentTime sets the transition time between points on the path, constantRate makes the movement speed more even, and showPoints plots the points along the path.

Object Following the Curve/Path

For this tutorial, our follow.lua module uses basic transitions to move an object from point to point along the path. In addition, it uses a basic angleBetween() function to make the object face toward the next point as it moves along the path:

init() Function

The follow.lua module is initially set up via the init() function which is called from either of the demo projects outlined above. First, this function creates a polygon display object, places it at the x and y location of the path starting point (passed in as the startPoint argument), and sets its rotation to face the second point:

A few lines after, we set a local variable precision with a default value equal to the pathPrecision argument. This is intended for compatibility between both the bezier example and the drawing example. In the bezier example, this argument can simply be passed in as a value of 0 because, in that module, there is no explicit set value for the distance between path points — instead, the algorithm creates the bezier based on a total number of segments. As a result, we must calculate a precision value based on the distance between the starting point and the second point, as indicated on line 75.

Next, we check if the showPoints parameter is true, we generate a dot along each point in the path by looping through the pathPoints table. Each point is added to a display group, pathPointsGroup, for easier cleanup when the curve is re-drawn.

Finally, we call the follow() function and pass some core arguments to it:

Follow Function

The follow() function essentially performs some calculations and begins a series of transitions where each subsequent transition is queued by the completion of the previous transition. The calculations include an adjustment of the transition time if the constantRate boolean is true.

Additionally, we rotate the object to face the next point using the angleBetween() function:

For the actual transition, we simply pass in some core parameters including the transTime calculated above and the x and y destination point. Additionally, we tag the transition with "moveObject" so it can easily be paused, resumed, or canceled, and we set the onComplete function to nextTransition so the process repeats until the object reaches the ending point. Finally, as each iteration occurs, we increment obj.nextPoint so the next transition moves to the next point along the path.

Pausing, Resuming, Canceling

Because we tag each transition in the sequence with "moveObject", pausing, resuming, or canceling is simple — just pass the tag name to one of the transition control APIs:

In Summary

Hopefully this tutorial gets you started on curve-based path movement in Corona. Remember to download and carefully inspect the sample code used in this tutorial:

Source:

Tutorial: Working With Curved Paths