3D 8 directional sprite rotation based on facing direction relative to camera direction

Started by
5 comments, last by Juxxec 4 years, 4 months ago

Greetings everyone!

I am trying to create a simple DOOM clone. I got some 8 directional sprites, which I want to draw in 3D dimentional space. I have an enemy class which has a facing direction. I also have a player with a camera. I want for the sprite to choose the correct direction depending on its facing direction and relative to the camera's viewing direction. I.e if I start going around in circles around the sprite it should rotate as well.

I want to achieve the same thing as in this animated picture:

2DSpriteCloudGL.gif



This is what I have tried so far, but it doesn't work correctly all of the time. Only for certain angles.

// Inside the enemy class
vec3 playerToSprite = this - > position - player.position;
vec3 direction = this - > facing_direction - playerToSprite;
angle = radtodeg(arctan2(direction.x, direction.y));
if (angle > 90) {
    angle = 450 - angle;
} else {
    angle = 90 - angle;
}
// Choose the correct sprite. Sprites 0 is the East facing sprite, 1 is the NorthEast, 2 is North and it goes on clockwise
this - > sprite = sprites[angle / 45];

Can you help me to fix my code?


EDIT: I managed to solve my problem. Here is the complete solution:

// Camera facing direction (in 2D)
// Direction is the direction at which the camera is looking at ( in 2D )
vec2 camera = vec2(cos(degtorad(CCamera::GetMainCamera() - > transform.direction)), -sin(degtorad(CCamera::GetMainCamera() - > transform.direction)));
// Object facing direction (in 2D)
// direction is the facing direction of the sprite, i.e. the direction at which the sprite is Looking At. ( in 2D )
vec2 facing = vec2(cos(degtorad(self - > transform - > direction)), -sin(degtorad(self - > transform - > direction)));
// Dot product
double cosine = dot(camera, facing);
double angle = radtodeg(arctan2(facing.y, facing.x) - arctan2(camera.y, camera.x));
if (angle > 90.0) {
    angle = 450.0 - angle;
} else {
    angle = 90.0 - angle;
}
self - > sprite = sprite_set[floor(angle / 45.0)];
Advertisement

arctan parameter order is usually (y, x) -- not (x, y).

Apart from that, I'd suggest just printing out some debug values as you spin the camera to see what values you get and compare them to what you are expecting to get, noting/looking into cases where the two differ.

Hello to all my stalkers.

That trick you're doing might not help you correctly.The issue is a problem of trigonometry and analytical geometry.

You have two objects to concern yourself with, the character and the camera.

Let's say that the camera is looking directly at the character. The character is facing to the west. The camera is facing north.

The issue with the method you're using is that you can not easily discriminate in the facing of the character or the camera. You can't know that for sure. So you reduce this to vector math.

Recall an axis aligned Unit Cube.

If you take the DOT product of the camera's facing vector, and the surface normal's of each face of the cubes, you can find out which of the six sides is facing the camera the most by using the largest positive value. If a result returns negative or zero, you can ignore it because it means that the normal is facing away from the camera or it's perpendicular.

When we solve this problem with the aforementioned example, the result we will get is that the south cube face is the side that's currently facing the camera. Because the character is facing left, we see his left side.

Now, lets extend this to the eight sided problem.

Build an axis aligned octagon with unit normals. You can compute this and hard code this later. Just start with a normal of (1,0,0), and rotate it around the Z axis by 45 degrees eight times.

When you run your tests, you can compute this against the eight sides.

Your character's facing will simply be a flag that determines which one of the sides he's facing, and then can be used to logically choose the right sprite depending on which normal is most present to the camera.

Hope this helps!

I managed to solve my problem! I edited my original post with the solution. Thank you all for your support.

tangent is rise over run or y/x(sin/cos), not x/y(cotangent). That is not your issue though. Your setup is arbitrary and finite. If I were at home, I would post my implementation. I will later tonight after work if an appropriate answer has not been given.

You remind me of RPG Maker.

Tangletail said:

That trick you're doing might not help you correctly.The issue is a problem of trigonometry and analytical geometry.

You have two objects to concern yourself with, the character and the camera.

Let's say that the camera is looking directly at the character. The character is facing to the west. The camera is facing north.

The issue with the method you're using is that you can not easily discriminate in the facing of the character or the camera. You can't know that for sure. So you reduce this to vector math.

Recall an axis aligned Unit Cube.

If you take the DOT product of the camera's facing vector, and the surface normal's of each face of the cubes, you can find out which of the six sides is facing the camera the most by using the largest positive value. If a result returns negative or zero, you can ignore it because it means that the normal is facing away from the camera or it's perpendicular.

When we solve this problem with the aforementioned example, the result we will get is that the south cube face is the side that's currently facing the camera. Because the character is facing left, we see his left side.

Now, lets extend this to the eight sided problem.

Build an axis aligned octagon with unit normals. You can compute this and hard code this later. Just start with a normal of (1,0,0), and rotate it around the Z axis by 45 degrees eight times.

When you run your tests, you can compute this against the eight sides.

Your character's facing will simply be a flag that determines which one of the sides he's facing, and then can be used to logically choose the right sprite depending on which normal is most present to the camera.

Hope this helps!

Greetings,

Years later rereading your comment. I like your solution but I have a problem with it. It requires me to compute 8 dot products, which seems a bit of an overkill here. I am mindful of performance. This is why I wanted to have a simple solution that I use less calculation. Though looking at this now, I wonder if a dot product and two arctan2's would take more time that just simply 8 dot products.

This topic is closed to new replies.

Advertisement