Determine "Facing" for a 2D Sprite relative to 3D Isometric Camera

I’m working on a tactical RPG where the Player Characters are 2D billboard sprites set in a 3D world (think Final Fantasy Tactics). The view is isometric and the camera can be rotated the full 360 degrees (albeit in 90 degree chunks). The “facing” of our characters is done by using different textures based upon where the characters is currently facing.

I’m having issues trying to figure out how to calculate this mathematically based on the camera. I’ve done some research online but unfortunately I haven’t found any solutions that have a camera that can be rotated - every example of facing I’ve found has been fully 2D or uses a fixed camera position.

A few pictures (with some sweet MS Paint Art) to hopefully help illustrate what I’m trying to calculate:

In this first instance, when the character is facing north, we would use the sprite which is looking to the top right of the screen.

In this second instance, the character facing north would be rendered using a sprite which is looking to the bottom right of the screen.

I don’t think it should matter much, but just as an FYI I’m currently using cut-out Ramza sprites from FF Tactics as temp art in my test while I try to get the code working:

I’m trying to figure out a good way of mapping the texture to use based on both the facing of the unit and the camera direction. I’m currently mapping each cardinal direction to an angle value (North = 0, East = 90, etc) but I can’t figure out how to calculate that value with relation to the camera AND facing vectors.

I’m hoping someone has had some experience doing this before that could provide an idea of how to go about it. Either that or someone can point out an extremely simple solution that I’m just overlooking because I’ve been staring at this for a couple days now, which is often the case :grin:

Sounds like a kinda fun problem. Totally off top my head, stab in the dark really. But I guess I’d start by thinking about what do I know. Which would be your two vectors, the direction the character is pointing and the direction the camera is pointing. Then should just be able to use a bit of vector math to work which texture to use relative to the two vectors.

Did you try doing the cross product?

1 Like

Your camera forward direction is most likely pointing downwards a bit, so first discard the vertical component and renormalized the direction, so you have 2 normalized vectors in the ground plane. Now you’re interested in the angle between them. Use the dot product to get the cosine of the angle. That’ll tell you how far apart the angles are, but not which vector is on what side. For that you can use the 2D ‘cross product’ (v1.x * v2.y - v1.y * v2.x). The sign will tell you which way is the shortest turn from v1 to v2 (clockwise or counter clockwise). (Side note: it’s technically not a cross product)

So first check the sign dot product to see if your character is facing away from or towards the camera (>0 is away, <0 is towards) and then the sign of the cross product to see if they’re facing left or right.

Edit: actually, you don’t even need to renormalize the camera direction.

1 Like

Thanks so much for the help you two - this looks like exactly what I need.

I was trying to use the cross product last night but couldn’t quite get it working correctly. I believe the method Jjagg posted will probably fix that for me.

I will definitely be using this method as it is more performant (and likely more flexible). However, I figured I’d post up another idea I was toying around with because as Oven_Owl mentioned it’s an interesting problem. I realized that minus the 45 degree rotation I’m applying to the camera to give it that isometric view, my camera forward vector is essentially pointing along one of the cardinal directions (N, E, S, W). So using that and the unit facing vector, I can determine the angle between those two (to the nearest 90 degrees) and that would essentially give me my render facing.

I’ll take your suggestions and implement them, and then post up the code I come up with in case anyone else is ever looking for the solution to this.

1 Like

So here’s the code I’m using, which seems to work well:

Vector2 camForward = new Vector2( camera.Forward.X, camera.Forward.Z );
Vector2.Normalize(camForward );

Vector2 facingVec = Vector2.Zero;
switch (_Facing )
{
	case CharacterFacing.NORTH:
		facingVec = new Vector2( 0.0f, -1.0f );
		break;
	case CharacterFacing.EAST:
		facingVec = new Vector2( 1.0f, 0.0f );
		break;
	case CharacterFacing.SOUTH:
		facingVec = new Vector2( 0.0f, 1.0f );
		break;
	case CharacterFacing.WEST:
		facingVec = new Vector2( -1.0f, 0.0f );
		break;
	default:
		// Shouldn't get here - default value to avoid warning
		Debug.Fail( "ERROR - Character _Facing variable is set to an unknown value!" );
		facingVec = new Vector2( 0.0f, -1.0f );
		break;
};

bool facingAway;
bool facingLeft;

float cosAngle = Vector2.Dot( camForward, facingVec );
if (cosAngle > 0.0f )
{
	facingAway = true;
}
else if (cosAngle< 0.0f )
{
	facingAway = false;
}
else
{
	// Shouldn't get here - default value to avoid warning
	Debug.Fail( "ERROR - Unit Facing calculation was 0 for cosAngle!" );
	facingAway = true;
}

float cross = (camForward.X * facingVec.Y - camForward.Y * facingVec.X);
if (cross< 0.0f )
{
	facingLeft = true;
}
else if (cross > 0.0f )
{
	facingLeft = false;
}
else
{
	// Shouldn't get here - default value to avoid warning
	Debug.Fail( "ERROR - Unit Facing calculation was 0 for cross!" );
	facingLeft = false;
}

if (facingAway )
{
	texture = _UnitTextures[(int)CharacterFacing.WEST];
}
else
{
	texture = _UnitTextures[(int)CharacterFacing.SOUTH];
}

// Our base textures face left, so if we're looking towards the right side of the screen, flip the texture
if (facingLeft )
{
	verts[0].TextureCoordinate.X = 0.0f;
	verts[1].TextureCoordinate.X = 0.0f;
	verts[2].TextureCoordinate.X = 1.0f;
	verts[3].TextureCoordinate.X = 1.0f;
}
else
{
	verts[0].TextureCoordinate.X = 1.0f;
	verts[1].TextureCoordinate.X = 1.0f;
	verts[2].TextureCoordinate.X = 0.0f;
	verts[3].TextureCoordinate.X = 0.0f;
}

Note that is just part of my overall function, which is why there are a few unseen variables such as camera or _UnitTextures being used) but I believe that should be enough to illustrate how to implement should it prove useful for anyone.

Another plus it that I believe this code should be fairly easy to extend should I want to handle 8 directions instead of 4. I believe FF Tactics does 8 directions solely in their idle animations to have smoother transitions when rotating the camera. Not sure if we will do the same, it depends on how things look once we plug in some actual assets instead of the temp stuff I’m using. However I think to handle this you would simply need to use cosAngle to calculate the actual angle, and then based off that and the left/right side using the cross calculation you can determine which of the 8 quadrants the unit should be facing.

Thanks again for the help on this @Jjagg and @Oven_Owl it is much appreciated!

2 Likes

The way i did it to index directions is to take that direction and turn it into a bering then basically turn that into a index based on the closest ranged value. You can use the dot or cross to get radians and do the below that way but im going to explain the idea with degrees.

Since you already figured out how to get the facing into a vector2 you can then take that vector2 and use Math.Atan2() on it to get a radian (you can use ToDegrees to change it to degrees), this is now in bearing form.

You should wrap the degrees you got to be within 0 to 360 to keep it straight forward.
if(degreeResult < 0)
degreeResult += 360;
if(degreeResult > 360)
degreeResult -= 360;

Then you calculate were that direction falls within a series of images in a spritesheet.
To do that you create a predefined orientation limit per sprite.
Or… which i recommend instead you calculate the index directly based on the above value.

to expound on both…

Say you have 8 sprite facings or 16 or what not.
If you wished to define per sprite just its own limits you would set up each sprite to have a range.
sprite0 is more then 315 degrees and less then 45 degrees then sprite index = 0
sprite1 is more then 45 degrees and less then 90 degrees then sprite index = 1
sprite2 is more then 90 degrees and less then 135 degrees then sprite index = 2
ect…
The above is just to show in relation to how you are doing it already.

However this above sort of switch case can lead to some restrictive extensibility problems down the road when you have variable sized animations per character with more then one character in more then one spritesheet.

You can just find the index by algorithm directly this requires that you format your sprite sheets to have a certain standardization or look to them if you like, or that you maintain a list of source rectangles that have a logical ordering. Which you may already have.

You organize your images on your sprite sheet so they appear in a specific order by there looks that match a characters apparent visual rotation (clockwise or counter clockwise) this is a preferential choice which direction a character is facing be the starting direction a character faces in a sheet.
Whichever choice you make, it should be standardized to all sprite sheets to keep everything simple and clean.

You could then use the direction your sprite is facing from the math.atan2 function like so to determine the source rectangle to draw.

int numberOfFacingsPerThisAnimationSet = 8; 
// it could be that for the above value all your character animations have 8 images then this is constant, or they don't and this changes depending on the character and animation in use.
int degreeFacingRange = 360 / numberOfFacingsPerThisAnimationSet;
int spriteIndexOffset = (degreeResult + (degreeFacingRange /2)) / degreeFacingRange;
if(spriteIndexOffset >= numberOfFacingsPerThisAnimationSet )
    spriteIndexOffset = 0;

int spriteIndex = charactersAnimationSetStartOffset + spriteIndexOffset;

SpriteIndex then just determines which sprite in the sheet is choosen from left to right in a row column or from some starting index were a series of animations begin.

To say for a spriteSheet you have a array of incrementing source rectangles and also have sets of animated sprites for each direction they can face. You have a offset to were each animation set begins and this value is added from the starting offset for a animation to determine which source rectangle index in the sheet (in concert with the current animations starting offset) is to be used to draw the sprite.

The code i just typed out is unchecked pseudo code but its so simple it’s probably right as is.
As you can see this is mostly a standardization of your spritesheets which makes the math part simpler structured and extensible to variable sized animations and more then one character with animation sets in a spritesheet.

Thanks for the write-up, this is great food for thought! I definitely prefer this more programmatic way of figuring out the offset you show.

Our characters actually use Spine for their animations, so we are investigating exactly how we want to handle facing using that system. Since the animations are skeleton based and not an actual spritesheet. However, your approach should be very easy to extend out to work with whatever we end up doing. So the UnitTextures code I’m showing above will certainly go away. We’re not 100% sure yet if we will be using different animations for each direction or a bone that the rest of the skeleton is parented to which controls rotation, but either way converting it to an offset value will likely be the first step and make everything easier. Thanks for the example code!