How to rotate a sprite toward the direction of motion

I am trying to figure out how to align a sprite image to the angle of the hill that it “moves” along. I am using atan2 and the result is this (gif is multiplying the angle by 360 just to show the effect):

Peek 2020-05-19 10-12

Here is my draw code:

        //lookAtX and lookAtY are 200, and the Y coordinate of the hill at the snail's X, respectively.
        //the snail doesn't move on it's X axis.
	show() {
         push();
          imageMode(CENTER);
          translate(this.x,this.y);
          this.angle = atan2(this.lookAtY,this.lookAtX-this.x)
          rotate(this.angle); //multiply by a large number to make the effect more pronounced.
	  image(snailImg,0,0,this.w,this.h);
         pop();
	}

I of course don’t want the snail to roll, but only to rotate to match the angle of the hill.

1 Like

So you want the snail to always rotate clockwise, but rate of rotation changes depending on how deep the angle is. Based on shape of atan2 https://en.wikipedia.org/wiki/Atan2 taking an absolute value might do the trick.

this.angle = abs(atan2(this.lookAtY,this.lookAtX-this.x))

No, I want the bottom of the snail to stay on the ground no matter the angle. Which implies counter-clockwise rotation for going up hill and clockwise rotation for going down hill. What I don’t want is for the rotation of the snail to exceed the angle of the hill.

Are you saying that speed of rotation is too high when slope is steep? That atan2 function gives too large values?

No, I am saying that the rate of rotation needs to be 0 when the snail’s angle matches the hill’s angle. The snail should not roll. The snail should stay on the ground. It should traverse the ground like a normal creature might traverse the ground (its bottom is always parallel with the ground, regardless of the slope).

When it goes down hill it should rotate clockwise until its bottom is parallel with the hill surface. When it goes up hill it should rotate counter clockwise (naturally!) until its bottom is parallel with the hill surface.

Instead of that try

this.angle = atan2(this.lookAtY - this.y, this.lookAtX - this.x);

Just thought if lookAt vector is relative to snails position then try

this.angle = atan2(this.lookAtY, this.lookAtX);

I have tried those things and the behavior is the same. When I log the angle, it never seems to go below 0.9 or above 1.3. Which is very strange considering that in its current state it rotates more than 360 degrees.

Logically your code is incorrect

this.angle = atan2(this.lookAtY, this.lookAtX - this.x);

would suggest that the Y lookat is relative to the snail position and the X look at is the absolute world/screen position. So depending on how you calculate the look at values i.e. absolute / relative then one of my solutions should work. This assumes that lookAtX and lookAtY are calculated correctly.

Since the snail is moving left to right the angle should vary between -\pi/4 \to \pi/4 i.e. (-45 \to 45^o ) approximately.

The snail’s X is 64. It never changes. The snail’s Y position is set equal to the Hill’s Y position @ X=64. That’s why the snail moves with the hill.

I don’t think I am setting those values in a weird way, and they are accurate when I see them in the console.

Ideally, there is no distance between the snail’s position and the positions of the point I want the snail to look at (lookAtX,lookAtY) for calculating its angle. So

this.angle = atan2(this.lookAtY, this.lookAtX);

should work. But it doesn’t. Here is what I get:

Peek 2020-05-19 13-46

If I set lookAtX to 100 and then do this:

this.angle = atan2(this.lookAtY-this.y,this.lookAtX-this.x)

I get a constant angle that never changes:
Peek 2020-05-19 13-55

Hello,

You need the slope of the curve at that point to get the angle.
If you do not have an equation to the curve (for derivative) you will have to use two points on the curve close to your plot point (I used data in an array; an index higher an index lower) to approximate.
See below for example.

I got this to work nicely and animated it as well; the tangent moved across the curve.

This is a Processing (Java) snippet to get you thinking about this:

PVector [] data;

//Slope using atan()
  int x = 200; 
  int off = 10; //smaller the better!
  
  //Slope using atan()
  int i = x;
  float m = (data[i+off].y - data[i-off].y) / (data[i+off].x - data[i-off].x); 
  float angle = atan(m);

  stroke(0);
  strokeWeight(3);
  int i = x;
  line(data[i-off].x, data[i-off].y, data[i+off].x, data[i+off].y); 

To accomplish this at my end:

  1. I created a shape with sine waves (this could have been any data); the shape was built using curveVertex().
  2. I created an array of x, y data for the boundary of shape (stroke(0)) by checking pixel color to see if it was black.
    I had to use noSmooth() otherwise there was blending of colors and I could not get a pure color(0).
  3. Once I have the data for the curve I can use atan() or atan2() to get the angle of the slope of a line at a point on the curve (approximation).
  4. I then translated a line to the point where the tangent is and rotated it with the angle.

It was easier for me to do than explain.

image

Any data version:
frame-000001

:)

4 Likes

It is true that the snail’s look at direction is the gradient of the hill at the snails position but depending on how you generate the hill it may not be possible to calculate an analytic (exact) value for the gradient.
Since it is obvious that you can calculate the height of the hill for any horizontal position a good numerical approximation is possible.

There are more than one way of doing it but the approach I suggest here is probably the best solution in this case.

Start with some simple definitions.

  • The coordinates [snailX, snailY] represent the position of the snail on the display
  • height(x) is the height of the hill for a given x position.
  • since snailX = 64 then snailY = height(64)

So now we come to calculating the gradient and hence the lookat angle using a numerical approach by using the hill height behind and in front of the snail like this

Determine the height of the hill 3 pixels either side of the mouse i.e. height(61) and height(67) we can calculate the angle with

angle = atan2(height(67) - height(61), 6)

Using a position either side of the snail will give better results at local maxima (hill tops) and minima (valley bottoms)

1 Like