Rotate arc in pacman animation

Hi ive been reading the tutorial or example were you make an animation of a pacman bit opening and closing the mouth

The code:

function draw() {
  background(0);

  // Style the arc.
  //noStroke();
  fill(255, 255, 0);

  // Update start and stop angles.
  let biteSize = PI / 16; //smaller the denominator, bigger the bite
  let startAngle = biteSize * sin(frameCount * 0.1) + biteSize;
  let endAngle = TWO_PI - startAngle;

  // Draw the arc.
  arc(50, 50, 80, 80, startAngle, endAngle, PIE);
  rotate(90);
}

As you can see im trying to rotate the pacman, in order to move to the left, up and down
 This animation is for moving right
 how can i do it ? The best way ??

Thanks

The following technique will allow you to keep the animation while translating, rotating the arc between pushMatrix()
popMatrix:

float startAngle;
float endAngle;
float biteSize;

void setup() {
  size(410, 200);
}

void draw() {
  background(0);
  fill(255, 255, 0);
  // Update start and stop angles.
  biteSize = PI / 16; //smaller the denominator, bigger the bite
  startAngle = biteSize * sin(frameCount * 0.1) + biteSize;
  endAngle = TWO_PI - startAngle;
  // Draw the arc.
  arc(50, 50, 80, 80, startAngle, endAngle, PIE);
  
  pushMatrix();
  translate(150,50);
  rotate(radians(90));
  arc(0, 0, 80, 80, startAngle, endAngle, PIE);
  popMatrix();
  
  pushMatrix();
  translate(250,50);
  rotate(radians(180));
  arc(0, 0, 80, 80, startAngle, endAngle, PIE);
  popMatrix();

  pushMatrix();
  translate(350,50);
  rotate(radians(270));
  arc(0, 0, 80, 80, startAngle, endAngle, PIE);
  popMatrix();

Alternate method minus the animation:

float startAngle;
float endAngle;
float biteSize;

void setup() {
  size(450, 200);
}

void draw() {
  background(0);
  fill(255, 255, 0);
  // Update start and stop angles.
  biteSize = PI / 16; //smaller the denominator, bigger the bite
  startAngle = biteSize * sin(frameCount * 0.1) + biteSize;
  endAngle = TWO_PI - startAngle;
  // Draw the arc.
  arc(50, 50, 80, 80, startAngle, endAngle, PIE);
// Left
  startAngle = radians(-145);
  endAngle = radians(160);
  arc(150, 50, 80, 80, startAngle, endAngle, PIE);
// Down
  startAngle = radians(-245);
  endAngle = radians(60);
  arc(250, 50, 80, 80, startAngle, endAngle, PIE);
// Up
  startAngle = radians(-55);
  endAngle = radians(250);
  arc(350, 50, 80, 80, startAngle, endAngle, PIE);
}

pm

1 Like

Adding to @svan’s solution:

Check out Shiffman’s video on how to rotate an object. Using push/pop/translate might not the most intuitive of operations, so getting that laid out nicely should help you along.

[Tangential question to someone who might know: I’m not finding any p5.js References entries for pushMatrix() and popMatrix(), though they still work in-sketch. Are they equivalent to push() and pop()?]

Also, be aware that rotate() by default expects you to define an angle in radians. You can still think in degrees, if you change the degree value to radians using radians() – as shown in the code above. You can also change the angleMode() for the sketch back to DEGREES. Though I recommend you also get a hang of radians, as it will pop up in computational trigonometry often enough.

Processing has some pre-defined constants you can use for typical rad values:

  • PI (3.14 radians) is a half-circle (180°)
  • TWO_PI (6.28 radians) is a full circle (360°)
  • HALF_PI (1.57 radians) is a quarter-circle (90°)

So if you want to rotate something by 90°, you can use rotate(HALF_PI).

After locking in translation and rotation, your next step should be to check for specific keypresses and then send PacMan moving in those respective directions.

The general idea is to check for the keycodes – assuming you want to use arrow keys:

if (keyCode == RIGHT_ARROW) and then assign appropriate values based on which keyCode you catch.

Yes my idea was create a class Pacman with some attributes like points, lives and direction, perhaps speed. Then add the operations or methods: moveRight, moveLeft, moveUp, moveDown


But i think id need some bucle for moving/rotating the pacman or only moving the mouth if the pacman is changing its direction ( ex: if pacman is moving right, and you press RIGHT_ARROW then you dont have to make this animation, if pacman is going right and then you press RIGHT_LEFT
then you have to make the animation
and change the mouth from right to left degree by degree passing by up position of mouth


Perhaps this answer your question about pushmatrix and popmatrixuse:

I fixed some errors in @svan code
but im trying to move/rotate smoothly
but my pacman doesnt rotate
I tried to rotate the first pacman, looking to the right, moving and rotating slowly to look up
I put a setTimeout, because probably the movement would be too quick to eye human
i put 3 seconds
but then can be tweaked


Any help would be apreciated


let startAngle;
let endAngle;
let biteSize;

function setup() {
  createCanvas(410, 200);
}

function draw() {
  background(0);
  fill(255, 255, 0);
  // Update start and stop angles.
  biteSize = PI / 16; //smaller the denominator, bigger the bite
  startAngle = biteSize * sin(frameCount * 0.1) + biteSize;
  endAngle = TWO_PI - startAngle;
  // Draw the arc.
  arc(50, 50, 80, 80, startAngle, endAngle, PIE);
  for (let x = 10; x < 90; x += 1) { 
      push();
      translate(150,50);
      rotate(radians(x));
      arc(0, 0, 80, 80, startAngle, endAngle, PIE);
    setTimeout(function(x) {
  // your code to be executed after 3 second
  // Since it is in milliseconds units, multiply it by 1000.
      push();
      rotate(radians(x));
      arc(0, 0, 80, 80, startAngle, endAngle, PIE);
      pop();
}, 3*1000);

      pop();
  }
  
  push();
  translate(250,50);
  rotate(radians(180));
  arc(0, 0, 80, 80, startAngle, endAngle, PIE);
  pop();

  push();
  translate(350,50);
  rotate(radians(270));
  arc(0, 0, 80, 80, startAngle, endAngle, PIE);
  pop();
}

The initial demo could be misleading when it comes to the finished app. I used a PacMan class and had to change the start and end angles for each direction. I could not get rotate to work correctly (in Java mode).

For your sketch with Pacman you actually don’t need to think in separate “mouth animation for facing right” and “mouth animation for facing left”. You just make one “mouth animation”. (As it stands, that animation is pointing right, but that’s somewhat arbitrary).

The magic happens when you apply translate and rotate (and push/pop). And as I mentioned, those operations are not intuitive. Let’s brake it down:

So you know about the coordinate system in p5.js, right? The origin (0, 0) is in the top-left corner. The x-axis is pointing to the right. The y-axis is pointing down. This is called the global space.

But now let’s say that you call translate(width/2, height/2) in your sketch. What this does is shift the origin point of the coordinate system from the top-left corner to the center of your sketch (width/2, height/2).

And a similar thing happens if you apply rotate(HALF_PI) in your code. This rotates the whole coordinate system around by 90° (clockwise), around the current point of origin After this command, the x-axis is pointing down and the y-axis is pointing left.

We’ll investigate push/pop later.

[I believe the proper term for what we’re doing here is a matrix transformation, but I’ll leave it to someone more qualified than me to provide details]

Let’s look at our code:

// 1st Pacman, facing RIGHT
arc(50, 50, 80, 80, startAngle, endAngle, PIE); // draw arc/pacman

// 2nd Pacman, facing DOWN  
push();
translate(150, 50); // shift the origin point
rotate(radians(90)); // rotate the coordinate system
arc(0, 0, 80, 80, startAngle, endAngle, PIE); // draw pacman AT THE NEW origin point
pop();

At the top, we draw the first Pacman, in the global coordinate system, no changes there. It’s facing right, doing it’s waka waka animation.

The second Pacman is where we utilize the aforementioned transformations to the coordinate system.

translate(150, 50);
This makes 150, 50 the new 0, 0. For the time being, we’re no longer thinking in a global coordinate system, but a local coordinate system.

rotate(radians(90));
This rotates the coordinate system (around the new local point of origin) by 90°. x-axis is pointing down, y-axis is pointing left.

arc(0, 0, 80, 80, startAngle, endAngle, PIE);
The code for drawing Pacman is nearly the same as the first, right-facing Pacman. But why is this Pacman pointing down? Because our coordinate system has rotated. Remember, the first Pacman is pointing towards the global (positive) x-axis direction, i.e. right. This second Pacman is still pointing in the (positive) x-axis direction, but since that axis is locally pointing down – because of the transformations – the Pacman also points down.
But why is this arc()/Pacman drawn at 0,0? Since we’re rotating Pacman by 90° we need to make sure that the rotation point is at his centerpoint. And any rotation transformation is applied at 0,0, so we need to draw Pacman at 0,0 as well.

Now, let’s finally introduce push() and pop().

Push means as mush as “save the current state of the coordinate system”.
Pop means as much as “restore the last saved state of the coordinate system”.

After calling push(), you can apply any transformations to the coordinate system you like. As long as you finish with calling pop(), everything will return back to the global coordinate system again. Push/pop essentially limit the effects of transformations to only what you draw in between them.

Addendum:

I’ve dissected the code that was initially given, but as has been posited as well, this will not be the final form of the sketch. After all, we don’t want to deal with four individual Pacmen. Instead, we manage one Pacman and apply transformations to it, based on his current heading.

We’re establishing the building blocks now and then can repurpose them later.

Thanks to your explanation, very clear and step by step


But in this piece of code:

push();
      translate(150,50);
      rotate(radians(x));
      arc(0, 0, 80, 80, startAngle, endAngle, PIE);
    setTimeout(function(x) {
  // your code to be executed after 3 second
  // Since it is in milliseconds units, multiply it by 1000.
      push();
      rotate(radians(x));
      arc(0, 0, 80, 80, startAngle, endAngle, PIE);
      pop();
}, 3*1000);

      pop();
  }

Im trying to make the animation, so that pacman could make a “nice” transition from looking and make waka wada to the right
to look up
with mouth pointing out 0ÂȘ, 1ÂȘ,2.
till 90ÂȘ) and not passing directly from 0Âș to 90Âș as the code does


The first step looking right, left, down and up is done.
The next step is having one direction for example right, if user press down arrow in keyboard
then pacman should move down
so the mouth should move/rotate smoothly from 0Âș to -90Âș
 also i wrote some kind of pause at the moment one second
because if change is to quickly for human eye
 i know if this works i have to adjust that timeout to be as real as possible.
sorry english is not my mother language but i think you can understand me

A concrete answer:

Turning Pacman smoothly requires you to step through the angle you add to rotate(), instead of adding in big 90° chunks. You also can stop thinking in separate the “right-pointing Pacman visua” and the “up-pointing Pacman visual”. There is only one Pacman visual. In code this might look as follows:

// this is the only Pacman that you need to draw
push();
translate(width/2, height/2); // draw Pacman at the center of the screen
rotate(radians(pacAngle)); // this is where the rotation changes
arc(0, 0, 80, 80, startAngle, endAngle, PIE);
pop();
  
pacAngle = pacAngle + 0.5; // grow angle, makes Pacman turn clockwise

The pacAngle variable grows every draw cycle by an amount you define. A higher number makes Pacman turn quicker, a lower number makes him turn slower.

This is just to convey the foundational idea of rotating a thing. All of this still leaves out a lot about knowing in which direction you are going, if you’ve completed a 90° turn or if you are turning clock- or counterclock-wise. We have also not touched Pacman’s own forward movement at all.

A “Let’s think about it” answer:

What is your goal with this sketch? Are you trying to recreate Pacman as we know it? Or are you just using Pacman as a stand-in but are intending to make him move and behave in ways different than what we think of if someone says “Pacman game”?

Because I feel that trying to make Pacman turn smoothly is of secondary (even tertiary) priority for where you are in your sketch.

The next main questions should be about movement and interaction:

  • How do I make Pacman move forward?
  • What happens if Pacman moves off the screen?
  • How do I know which key is pressed on the keyboard?
  • How do I turn a keypress into a direction change for Pacman?

All of the above can be implemented with the instant 90° directional change we have discussed up to now.

Making that choppy change a smooth turn, is visual polish. It may be appealing to you to make it visually great. I’ve often put focus and time into the visual aspect of a thing and then realised that the foundational systems are not there yet, or still lacking. And worst of all, my prioritising visuals left me with no time to implement other aspects.

When tackling a sketch, it helps to break up the big main task into subtasks and put them into a prioritised list.

As you can see above, you are applying my idea of code
applying a bucle for from 0 to 90Âș
and step of one degree
but it doesnt work
it stills point to the right and make waka waka


These would be a project for my students
 you can see a project of a couple of years ago


When pacman changes direction, we change the image
we have the four pacman images looking right, left, up and down
 and we have some images to make waka waka bite
 I dont remember right now


I was remembering code and “getting back to p5.js”
so i come across the pacman bite using arc
and seemed interesting


As general idea i have a pacman class, with attributes and methods. Main methods would be moveRight, moveLeft, moveUp and moveDown
and attributes like direction, lives, score and the most important coordX and coordY

So when left arrow would be pressed → pacman would go left → depends on the direction it was taking we have to do the smoothly transition
then use animation going left


Thanls

1 Like

Right, then we’re indeed approaching this topic from two different angles (ha!).

I was under the impression that we’re starting from scratch and slowly working up in complexity. But looking at this it’s not the movement behaviour I envisioned.

I’ll step back from this thread – off to the weekend! – I hope you can find some further input.

Thanks but any ideas to improve are welcome, taking in account that is for students in vocational eduation and in a course of 8 months, teaching JS

You can think that the pacman class is going to be absolutely rewriten using arc, rotation and waka waka
if i could get it and is reasonable easy for students


Have a nice weekend but did you see why my bucle for isnt rotating? did you test you code ? with angle ++ 
 seems same code

Thanks

This is the most recent code I’ve been using, it works in Processing IDE p5.js Mode and in the p5.js Web Editor.

let startAngle;
let endAngle;
let biteSize;
let pacAngle;

function setup() {
  createCanvas(410, 200);
  
  pacAngle = 0; // start at 0, facing right
}

function draw() {
  background(0);
  fill(255, 255, 0);
  noStroke();

  // Update start and stop angles
  biteSize = PI / 16; // smaller the denominator, bigger the bite
  startAngle = biteSize * sin(frameCount * 0.1) + biteSize;
  endAngle = TWO_PI - startAngle;

  // this is the only Pacman that you are drawing
  push();
  translate(width/2, height/2);
  rotate(radians(pacAngle)); // this angle changes
  arc(0, 0, 80, 80, startAngle, endAngle, PIE);
  pop();
  
  pacAngle = pacAngle + 0.5; // grow angle, makes Pacman turn
  
}

Im going to tell you
why your code works and mine not ???

for (let x = 0; x < 90; x += 1) { 
      push();
      translate(150,50);
      rotate(radians(x));
      arc(0, 0, 80, 80, startAngle, endAngle, PIE);
    setTimeout(function(x) {
  // your code to be executed after 3 second
  // Since it is in milliseconds units, multiply it by 1000.
      push();
      rotate(radians(x));
      arc(0, 0, 80, 80, startAngle, endAngle, PIE);
      pop();
}, 3*1000);

      pop();
}

I made a rotation from 0 to 90
 but pacman stays quiet


  • Notice @eightohnine’s version doesn’t have any for loop much less a setTimeout() inside a loop.
  • Instead it relies on the fact that p5.js’ callback draw() behaves much like a loop!
  • B/c It’s automatically re-invoked at about 60 FPS by the p5.js library.
  • And the pacAngle += .5; is equivalent to the update part of a for ( ; ; ) loop.
2 Likes

Here is another approach which obviates the need for rotation. This technique changes the start and end angles for direction changes. The arrow keys control pacMan’s direction; click on the canvas to start in order to get keyPressed() to work. (Edited to remove PacMan’s overbite which was causing a flash.)

var turnRight = false;
var turnLeft = false;
var turnDown = false;
var turnUp = false;

var startAngle, endAngle;
var biteSize = 0;

class PacMan {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    fill(255, 255, 0);
    biteSize = PI / 16;
  }

  display() {
    if (turnRight) {
      this.x += 1;
      startAngle = biteSize * sin(frameCount * 0.1) + biteSize;
      endAngle = TWO_PI - startAngle;
    }   
    if (turnLeft) {
      this.x -= 1;
      startAngle = radians(-168.75) + biteSize*sin(frameCount*0.1);
      endAngle = radians(168.75) - biteSize*sin(frameCount*0.1);
    }
    if (turnDown) {
      this.y += 1;
      startAngle = radians(-258.75) + biteSize * sin(frameCount * 0.1);
      endAngle = radians(78.75) - biteSize * sin(frameCount * 0.1);
    }
    if (turnUp) {
      this.y -= 1;
      startAngle = radians(-78.75) + biteSize * sin(frameCount * 0.1);
      endAngle = radians(258.75) - biteSize * sin(frameCount * 0.1);
    }
    arc(this.x, this.y, 80, 80, startAngle, endAngle, PIE);
  }
}

function setup() {
  createCanvas(1200, 800);
  pacMan = new PacMan(50, 50);
  turnRight = true;
}

function draw() {
  background(0);
  pacMan.display();
}

// **** Click on canvas to start **** //
function keyPressed(event) {
  if (keyCode === LEFT_ARROW) {
    turnLeft = true;
    turnRight = false;
    turnDown = false;
    turnUp = false;
  } else if (keyCode === RIGHT_ARROW) {
    turnRight = true;
    turnLeft = false;
    turnDown = false;
    turnUp = false;
  } else if (keyCode === UP_ARROW) {
    turnUp = true;
    turnRight = false;
    turnLeft = false;
    turnDown = false;
  } else if (keyCode === DOWN_ARROW) {
    turnDown = true;
    turnRight = false;
    turnLeft = false;
    turnUp = false;
  }
}

Alternate technique using Array to store direction changes;

// **** Click on canvas to start **** //
var dir = [1, 0, 0, 0];
var startAngle, endAngle;
var biteSize = 0;

class PacMan {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    fill(255, 255, 0);
    biteSize = PI / 16;
  }

  display() {
    if (dir[0] == 1) {
      this.x += 1;
      startAngle = radians(12) + biteSize * sin(frameCount * 0.1);
      endAngle = radians(348) - biteSize * sin(frameCount * 0.1);
    }
    if (dir[1] == 1) {
      this.x -= 1;
      startAngle = radians(-168.75) + biteSize * sin(frameCount * 0.1);
      endAngle = radians(168.75) - biteSize * sin(frameCount * 0.1);
    }
    if (dir[2] == 1) {
      this.y -= 1;
      startAngle = radians(-78.75) + biteSize * sin(frameCount * 0.1);
      endAngle = radians(258.75) - biteSize * sin(frameCount * 0.1);
    }
    if (dir[3] == 1) {
      this.y += 1;
      startAngle = radians(-258.75) + biteSize * sin(frameCount * 0.1);
      endAngle = radians(78.75) - biteSize * sin(frameCount * 0.1);
    }
    arc(this.x, this.y, 80, 80, startAngle, endAngle, PIE);
  }
}

function setup() {
  createCanvas(1200, 800);
  pacMan = new PacMan(50, 150);
}

function draw() {
  background(0);
  pacMan.display();
}

function keyPressed(event) {
  if (keyCode === RIGHT_ARROW) {
    dir = [1,0,0,0];
  } else if (keyCode === LEFT_ARROW) {
    dir = [0,1,0,0];
  } else if (keyCode === UP_ARROW) {
    dir = [0,0,1,0];
  } else if (keyCode === DOWN_ARROW) {
    dir = [0,0,0,1];
  }
}

This is my scratch and “better code” :laughing:

The problem is i dont understand quite well the startAngle and endAngle and somehow i stopped the waka waka
hope someone could fix these errors


class PacMan {
  constructor(x, y) {
    this.speed=10;
    this.x = x;
    this.y = y;
    this.direction = 1; //ClockWise 1 Left, 2 up, 3 left, 4 down arbitrary numbers
    fill(255, 255, 0);
    this.biteSize = PI / 16;
    this.startAngle = { 
      Left: radians(-168.75) + this.biteSize*sin(frameCount*0.1), 
      Right:this.biteSize * sin(frameCount * 0.1) + this.biteSize , 
      Up: radians(-78.75) + this.biteSize * sin(frameCount * 0.1) , 
      Down:radians(-258.75) + this.biteSize * sin(frameCount * 0.1),
    };
    this.endAngle = { 
        Left: radians(-168.75), 
        Right:TWO_PI - this.startAngle["Right"] , 
        Up: radians(258.75) - this.biteSize * sin(frameCount * 0.1), 
        Down:radians(78.75) - this.biteSize * sin(frameCount * 0.1),
       }
    }
 stay() {
      let startAngle = this.biteSize * sin(frameCount * 0.1) + this.biteSize;
      let endAngle = TWO_PI - startAngle;
      arc(this.x, this.y, 80, 80, this.startAngle.Right, this.endAngle.Right, PIE);
    }   
  moveRight() {
    console.log(this.x);
    if (this.direction != 3) {
      //Pacman has changed its direction
      //animation of rotation from previous direction to the new direction
      rotate(radians(90));
    } 
   this.direction = 3;
    this.x += this.speed;
   
   arc(this.x, this.y, 80, 80, this.startAngle.Up, this.endAngle.Up, PIE);

  }
    moveLeft() {
       if (this.direction != 1) {
         //Pacman has changed its direction
     //Pacman has changed its direction
      //animation of rotation from previous direction to the new direction
        // arc(this.x, this.y, 80, 80, 0, 90, PIE);
    } 
       this.direction = 1;
      this.x -= this.speed;
      //startAngle = radians(-168.75) + biteSize*sin(frameCount*0.1);
      //endAngle = radians(168.75) - biteSize*sin(frameCount*0.1);
      arc(this.x, this.y, 80, 80, this.startAngle.Left, this.endAngle.Left, PIE);
    }
  
   moveDown() {
      this.y += this.speed;
     this.direction = 4;
      arc(this.x, this.y, 80, 80, this.startAngle.Down, this.endAngle.Down, PIE);

    }
    moveUp(){
      this.y -= this.speed;
       this.direction = 2;
      //startAngle = radians(-78.75) + biteSize * sin(frameCount * 0.1);
     // endAngle = radians(258.75) - biteSize * sin(frameCount * 0.1);
       arc(this.x, this.y, 80, 80, this.startAngle.Up, this.endAngle.Up, PIE);
    }
   // arc(this.x, this.y, 80, 80, startAngle, endAngle, PIE);
  
}

function setup() {
  createCanvas(1200, 800);
  pacMan = new PacMan(50, 50);
  pacMan.direction = 1;
}

function draw() {
  background(0);
  pacMan.stay();
}

// **** Click on canvas to start **** //
function keyPressed(event) {
  if (keyCode === LEFT_ARROW) {
    pacMan.moveLeft();
  } else if (keyCode === RIGHT_ARROW) {
   pacMan.moveRight();
  } else if (keyCode === UP_ARROW) {
         pacMan.moveUp();
  } else if (keyCode === DOWN_ARROW) {
    pacMan.moveDown();
  }
}

Stay would be the animation when no key is pressed, and pacman is alone without any movement.
I need waka waka back and some kind of rotation from original direction to new direction ( if pacaman was moving to the right, and then user pres arrow up → pacman changes it direction from right to up → waka waka has to be pointing up → some kind of 90 rotation is needed

If it was moving to the right and user decides to go to the left
then some kind of 180 of waka waka
pointing now to the left inside right


Dont know why waka waka stopped


Any help would be apreciated

What is ‘waka waka’?

1 Like