Pie Mask in OpenFL

Hi there,

I’m trying to create a simple game and want to implement something like this: click. I’ve ported the AS3 code but the masks don’t seem to work correctly. I’ve found this gist on GitHub and the masks would work fine trying the code provided.

I had actually tried to just draw a circle with an Actuate MotionPath, but it’s not drawn smooth, it has choppy lines. Here is the a snippet of the code I wrote for that:

   graphics.clear();
   graphics.lineStyle(thickness, color, 1, true);

   var path = new MotionPath();
   var stepsize:Float = .1;
   var angle:Float = 0;
   while (angle < 2 * Math.PI)
   {
      point.x = radius * Math.cos(angle);
      point.y = radius * Math.sin(angle);
      if (angle == 0) graphics.moveTo(point.x, point.y);
      path.line(point.x, point.y);
      angle += stepsize;
   }

   Actuate.motionPath(point, duration, {x: path.x, y: path.y }).ease(Quint.easeOut).onUpdate(render);
}

private function render()
{
   graphics.lineTo(point.x, point.y);
}

It would be greatly appreciated if anyone could help me out here by making a more smooth circle animation.

Thanks in advance!

To achieve this, you should actually draw a filled shape on each frame (update). This means that in the update function you should draw all the lines from the beginning up until the point in the path, and not only the lat section of the path.
The function snippet in the AS3 example displays only one section of the pie polygon. To get the animation in the example, they’ve used an enter frame loop and in it they cleared the graphics and called this function again,with only the “percentage” parameter changed.

I hope I made sense :slight_smile:

What do you mean by that? Can you post a screenshot?

I don’t think that’s necessary. As long as you don’t call clear(), the Graphics object will remember all your previous instructions.

It is possible, doing it the way you describe. However with this method, you need to calculate the exact section that is changed from the last drawing of the polygon, this will require a lot of mathematics. If you’ll draw the poly on top of the old one you’ll get some undesired results such as stacking all of your drawings on top of each other. You will not see it, if you only mask (On flash target, I don’t really know how it is on other targets) but this will consume some more memory and cpu.
Why not just clear the graphics and redraw? It’s the fastest way.

I tested this, and found the following:

  1. You can safely ignore itzikiap. Your strategy works as intended, and it doesn’t stack drawings on top of one another.
  2. You almost certainly want to use a fill, not a line. Replace graphics.lineStyle(...); with graphics.beginFill(color);.
  3. You need to start with a line from the center. Add graphics.moveTo(0, 0); before your for loop.
  4. You do not need the if (angle == 0) graphics.moveTo(...); line. In fact, it messes up your circle slightly. Delete it.
  5. If you’re annoyed that the animation starts at the wrong angle, move angle += stepsize; to the start of your loop.*
  6. The choppy lines are because duration is too low. Actuate only updates once per frame, and you only draw one line per update. That’s why you aren’t drawing enough lines to make a smooth-looking circle. (Switching to Linear.easeNone may also help a little, and step 2 in this list will make the problem less obvious.)

At first, thanks for all the useful replies! I woke up this morning and was thinking that the circle wasn’t drawn very smooth because I didn’t draw a line to all of the points, because the duration was too short. Just after that thought @itzikiap replied and was implying the same thing, haha.

I meant that the circle didn’t show all of the provided points, so the circle wasn’t ‘smooth’. It was if a circle was drawn with like 8 corners (instead of 360 degrees).

Your right about this part, initially I did it without clearing and if not drawn too fast, the circle would show up just fine. Actually I wanted my animation to have a variable time and not have a minimum of two seconds.

After all, this is the easiest way, I think. But the part about the complex math is still true :stuck_out_tongue:.

I’ve tried switching to Linear.easeNone before, but it wasn’t working much better.

So after a lot of thought I decided to draw a wedge and cut off the middle part, so it would become an arc (or curved line). The steps are now angle based and get tweened properly. I’ve tried the same thing with just lines (and no beginFill), but I couldn’t get it to just create an arc, it would always be a wedge (with lines to the mid-point of the arc). Eventually, this is what works and looks pretty darn smooth (and has a nice animation easing):

private var currentAngle:Float = 0;
Actuate.tween(this, duration, { currentAngle: 360 }).ease(Quint.easeOut).onUpdate(render).onComplete(onAnimationComplete);

private function render()
{
   graphics.clear();
   graphics.beginFill(color, 1);
   drawWedge(this, 0, 0, radius, currentAngle, -90);
   // Subtract out the middle by drawing a new wedge over the top of the previous one.
   drawWedge(this, 0, 0, radius - thickness, currentAngle, -90);
   graphics.endFill();
}

private function drawWedge(sprite:Sprite, startX:Int, startY:Int, radius:Float, arc:Float, startAngle:Int = 0)
{
   var segAngle:Float;
   var angle:Float;
   var angleMid:Float;
   var numOfSegs:Int;
   var ax:Float;
   var ay:Float;
   var bx:Float;
   var by:Float;
   var cx:Float;
   var cy:Float;

   // Move the pen.
   sprite.graphics.moveTo(startX, startY);

   // No need to draw more than 360.
   if (Math.abs(arc) > 360)
   {
      arc = 360;
   }

   numOfSegs = Math.ceil(Math.abs(arc) / 45);
   segAngle = arc / numOfSegs;
   segAngle = (segAngle / 180) * Math.PI;
   angle = (startAngle / 180) * Math.PI;

   // Calculate the start point.
   ax = startX + Math.cos(angle) * radius;
   ay = startY + Math.sin(angle) * radius;

   // Draw the first line.
   sprite.graphics.lineTo(ax, ay);

   // Draw the wedge.
   for (i in 0...numOfSegs)
   {
      angle += segAngle;
      angleMid = angle - (segAngle * .5);
      bx = startX + Math.cos(angle) * radius;
      by = startY + Math.sin(angle) * radius;
      cx = startX + Math.cos(angleMid) * (radius / Math.cos(segAngle * .5));
      cy = startY + Math.sin(angleMid) * (radius / Math.cos(segAngle * .5));
      sprite.graphics.curveTo(cx, cy, bx, by);
   }

   // Close the wedge.
   sprite.graphics.lineTo(startX, startY);
}

Thanks again for the help! I hope this will help anyone in the future :smile:.

Btw reading the topic title, kinda ironic that I actually ended up creating pie-slices.

Showcase (I just noticed that the circle is moving a little, does anyone know what may be causing that?):

2 Likes