Archived

This topic is now archived and is closed to further replies.

Getting the right sprite direction like in Xenogears

This topic is 5380 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I''m trying to recreate the Xenogears engine, which I really like, mainly because of the interesting way they did the characters. So I have a billboarded sprite in space at position x1,y1,z1, and a camera at position x2,y2,z2, rotated around that position''s axis at theta degrees, pointed at the sprite. Having numerous sprites, how could I choose the right ANGLE frame (one of 8 directions) to display on the character, based on the direction of the character (0 through 7, 8 possible directions... 0, 45, 90, 135, 180, 225, 270, 315 degrees), the x/z (y is generally fixed and I don''t think it would matter) and the angle of the camera? ... for every sprite. What my first guess was is almost laughable: fabs(Camera.ang - Object.ang) / 45 (div by 45 so we can choose between the 8 direction frames). This works only if the Object.ang is straight ahead (I think it only works if Object.ang is 0, because it seems to act strangely otherwise), and I don''t think this would pick the accurate direction if the camera moved around freely, instead of just rotating around the character. If anyone can give any tips or ideas I would really appreciate it... I really want to get this to work and I know little math to think up an angle-relationship algorithm thingy on my own. Thanks =)

Share this post


Link to post
Share on other sites
Argh, I solved this problem last year, but I don't have the code for it anymore >_<. Let's see what I can recall from memory... (I hope you have a basic understanding of trig - can't explain it otherwise)

First, I'm drawing a diagram because it will help explain things. This is a bird's eye view:


z
| D
| /
|/
----B-----O---F---K-- x
|
|
C |


x,z are axis.
F is the front direction (0 degrees)
B is the back direction (180 degrees)
C is the camera's position
D is the direction the sprite is currently facing (degrees)
K is the base camera position (for explanation purposes)
In this diagram, the user is looking at the sprite's left shoulder from the back.


By making this resemble a trig problem as much as possible, I find it makes it easier to solve. We'll start with the facing direction F. This is where your character is facing at angle 0. Let's say this is in front. If you want to make front a different angle, just add/subtract 90 degrees from the answer.

If the camera were at position K looking at the sprite, then this would be easy - it's a regular trig problem of finding the angle between DOF (note: DOF should form a right-angled triangle). In this case, it would be Acos(OF, OD). From the looks of this diagram, the answer should be around 45 or 90 degrees.

Now the tough part is accounting for the camera at any position C. You need to get the angle between COF, possibly using B since it's in one of the back quadrants. You'll have to account for the different quadrants and their effect on Acos here. You may also need your camera direction angle here.

Once you have the angle for DOF and COF, now you can take the absolute value of the subtraction between the two, ie. Abs(DOF - COF). Now is where you should restrict the angle to one of your 45 degree numbers. So in the diagram, if DOF is 70 degrees and COF is 210 degrees, then the angle to display to the user would be 210-70 = 140, which, when divided by 45 and rounded, returns 3. 3 represents 135, which would be the angle of the sprite to display to the user.

I hope this helps, but I can't guarantee complete correctness (it has been at least a year after all). Still, it's a starting point. Play with it, and you'll get it eventually.


Latest project: Ariene Game Engine (.NET/DX9)

[edited by - Tri on March 24, 2003 4:15:30 PM]

Share this post


Link to post
Share on other sites
Thank you so much for your explanation! I am having a bit of trouble though. So, to get it, it would be (using your diagram):

fabs (acos (DOF) - acos (COF)) / 45.0f;

What I am having trouble is relating your diagram to world space. DOF and COF... what are they?

D, O, and F are points? C is a point, I know that heh... D is an angle, so how do I find the angle between an angle, a point (assuming O is the sprite position), and something else (F is a point? or an angle?)

I think I understand the method. Finding the angles using inverse cosine, then the absolute difference. But could you explain what D,O, and F are a bit more?

Once again thanks. This problem looks very solvable now. Hopefully I'll have it working soon.

[edited by - rm3 on March 24, 2003 8:08:44 PM]

Share this post


Link to post
Share on other sites
Okay, I can see how D and F aren't very clearly explained. oops :o So...

DOF is the angle between lines DO and OF. COF is the angle between lines CO and OF. The formula you gave is correct, the only difficulty now is in determining DOF and COF (and I would round the final result to an integer).

O is the position of the sprite.

F is a actually a point in front of O when the sprite is facing direction 0 (zero). So if O is (0,0,0) and the sprite is facing 0 (zero), then F is (1,0,0). F could also be (2,0,0) or (3,0,0), or whatever, but it should be of the form (x,0,0). The y and z coords are the same as O's.

D is the point that is in front of O when the sprite is facing angle theta. To get this point, take the unit vector of O and add it on to O. So D = O + UnitVector(O). Here's how you calculate the unit vector:

Public Function UnitVector(ByVal A As Vector) As Vector
Dim Result As Vector
Dim Magnitude As Single = 1 / Math.Sqrt(A.X ^ 2 + A.Y ^ 2 + A.Z ^ 2)
Result.X = Magnitude * A.X
Result.Y = Magnitude * A.Y
Result.Z = Magnitude * A.Z
Return Result
End Function


To calculate F from D, set F = (Dx, Oy, Oz), ie. the x-coord from D, and the Y and Z coords from O.

Sorry, I should've explained that part a bit more. Hope that helps.

[edited by - Tri on March 24, 2003 9:15:57 PM]

Share this post


Link to post
Share on other sites
Alright. I feel we''re almost there! =) Sorry for not grasping it completely yet. I appreciate the help though.

Okay so I''ve got

vector3d D = pos + pos.UnitVector();

pos being the sprite''s position vector. Now where does theta (the sprite''s angle, 0,45,90, etc) come into this equation of finding D? Or is it not needed (can''t understand that but maybe).

Once that''s cleared up, I''ll have three points: O (the sprite position), D (a point the sprite''s facing (right?)), and C (the position of the camera). Are those all the points I need to get the angles and the difference?

Thank you so much for your help!! :D

Share this post


Link to post
Share on other sites
You don''t need theta to calculate D. The unit vector is a vector in the same direction as the vector it''s calculated from. So adding it on to O gives us another vector in the sprite''s facing direction. It''s _almost_ a direction vector, and would be, if we didn''t add O to it.

You''ve got O, D, and C yes, but you''ll also need F, since it''s your starting point. Notice that all the angles have an F in them. F is easy to get, and so is D. I think calculating the angle involving C will be the toughest since you have to take into account all four quadrants (I remember that giving me a headache when I worked on this).

Share this post


Link to post
Share on other sites
It's too bad you don't have the orig code lol =) Anyway, here's what I've come up with:


vector3d O = pos; // position
vector3d D = O + O.UnitVector(); // facing
vector3d C = vector3d(CamX,CamY,CamZ); // camera
vector3d F = vector3d(D.x,O.x,O.z); // front point

const double cosDeg =
(Distance2d (O.x,O.z,F.x,F.z) / Distance2d(C.x,C.z,O.x,O.z));

const double theta = acos(DEG2RAD(cosDeg));

Direction = ABS((int)CameraAngle - (int)RAD2DEG(theta)) / 45;


Sorry if its sloppy. Now I'm not really sure about this code, and my general idea. Hopefully you or someone else can clear a few things up for me.

1. Why is the sprite's angle not included? The O (sprite position) vector is just a point in space, I don't see how it holds anything about the sprite's direction. Wouldn't the angle have to be used somewhere, so that when you PRESS left and the sprite angle goes from 0 to 180 the sprite changes.

2. Am I calculating the cosine of theta right (Distance between O and F in the xz plane divided by distance between C and O in the xz plane?) ?

Thanks once again Tri!

[edited by - rm3 on March 24, 2003 11:04:10 PM]

Share this post


Link to post
Share on other sites
Hmm...you know what? I think D is calculated wrong :/ It doesn't say anything about the facing angle; all it says is "in front." Change D to:

D = O + (O.x * sin(SpriteAngle), O.y, O.z * cos(SpriteAngle));

where SpriteAngle is the angle the sprite is facing (in radians). Then when the user presses left or right, all you have to do is change this angle.

F should be (D.x, O.y, O.z). I don't see why O.x was in the y spot, but I think that could be a problem.

cosDeg can't be calculated like that since the triangle formed by OFC isn't a right-angled triangle. But the triangle between OFD IS right-angled. Replace C.x,C.z with D.x,D.z in cosDeg's definition.

Finally, I don't think you need convert degrees to radians in the theta assignment since cosDeg isn't an angle, but a ratio. Just leave it as acos(cosDeg).

Is CameraAngle set to the same scale as the diagram? That is, with 0 degrees along the positive x-axis? If so, then that's fine. I think you've got this one externally, so you don't need C in the diagram.

I think that should do it. Yeah, I wish I had my code too

[edited by - Tri on March 24, 2003 11:44:38 PM]

[edited by - Tri on March 24, 2003 11:48:28 PM]

Share this post


Link to post
Share on other sites
"I don't see why O.x was in the y spot"

Heh typo

Anyway, the camera position isn't used at all?

OK, here's what I have. "xAng" is the angle of the character, CameraAngle is the angle of the camera between 0 and 359.


const float Angle = DEG2RAD(xAng);
vector3d O = pos; // position

vector3d D = O +
vector3d(O.x * sin(Angle), O.y,
O.z * cos(Angle));

vector3d C = vector3d(CamX,CamY,CamZ); // camera
vector3d F = vector3d(D.x,O.y,O.z); // front point

const double cosDeg =
(Distance2d (O.x,O.z,F.x,F.z) / Distance2d(D.x,D.z,O.x,O.z));

const double theta = acos(cosDeg);

Direction =
FABS((int)CameraAngle - (int)RAD2DEG(theta)) / 45;


In the controls of the character, I set the angle as follows (no movement right now):


if (Right) { xAng = 0; }
if (Up) { xAng = 90; }
if (Left) { xAng = 180; }
if (Down) { xAng = 270; }


Rotating around the character with the camera, I get the correct directions (looks good), but only when the player is at 90 or 270 degrees. When I press Left or Right, the rotation messes up... the wrong directions are chosen.

Now I can think of 2 reasons:

1. This has to do with arccos's quadrants... Something I haven't accounted for.

2. There's another error somewhere in my code... Something I haven't accounted for.

Here's what happens on each direction:

0 Doesn't rotate correctly (picks wrong frames)
90 Rotates perfectly
180 Doesn't rotate correctly (ditto)
270 Rotates perfectly

Another odd thing... if I'm facing the back of the character, the character facing 90o, pressing Left or Right turns him right, and if facing the front, pressing left or right turns him left... whew boy my head.

Yawn, it's getting late. I don't know if it's late where you are, if it is I don't want to keep you up or anything.

I really appreciate the help and time you're giving me and I'd like it if we could perhaps continue tomorrow. I'll try fiddling with it to see if I can get it working on all directions.



[edited by - rm3 on March 24, 2003 12:15:27 AM]

Share this post


Link to post
Share on other sites
The only thing we needed the camera position for was to get its direction. When I originally did this, I had camera position/target, but since you've already got its direction, then there's no need.

Try to work with the quadrants now. Here's a little kickstart for you, taken from another function I wrote:

If Not (Hypotenuse = 0) Then
If Not ((Adjacent / Hypotenuse) = 1) Then
THETA = ArcCos(Adjacent / Hypotenuse)

' Take cases
If (vDest.z > vSrc.z) And (vDest.X >= vSrc.X) Then ' Quadrant 1
' Don't do anything
End If

If (vDest.z >= vSrc.z) And (vDest.X < vSrc.X) Then ' Quadrant 2
THETA = THETA + PI / 2
End If

If (vDest.z < vSrc.z) And (vDest.X < vSrc.X) Then ' Quadrant 3
THETA = THETA + PI
End If

If (vDest.z <= vSrc.z) And (vDest.X >= vSrc.X) Then ' Quadrant 4
THETA = THETA + 3 * PI / 2
End If

' You may not need this line, I put it in for corrective measures originally
THETA = THETA + PI / 2

Else
THETA = srcAng
End If
Else
THETA = srcAng
End If

Hypotenuse would be Distance2d(D.x,D.z,O.x,O.z) and Adjacent would be the distance from O to F. srcAng would be xAng in your case. At the end, I reduced theta so that it was between 0 and 2 * PI. vSrc would be the point O, and vDest would be D.

For the quadrants, you'll just have to play with it and see what works. You may also have to play around with quadrants when calculating D - adding the x-coord, but subtracting the z in on quadrant, while subtracting x and adding z in another, for example.

Have fun! :D

[edited by - Tri on March 25, 2003 9:27:32 AM]

Share this post


Link to post
Share on other sites
Fun... lol. Okay, I''ve struggled to piece it all together, and I''ve got this code chunk... (sorry it''s in C not in that language you post (VB?), but you seem to know C quite well! =D)


const float radAngle = DEG2RAD (xAng);

vector3d O = pos; // O is the sprite''s position

vector3d D (O + vector3d(O.x * sin(radAngle), O.y,
O.z * cos(radAngle)));

vector3d F = vector3d (D.x, O.y, O.z);

double theta;
const float hyp = Distance2d (D.x, D.z, O.x, O.z);
const float adj = Distance2d (O.x, O.z, F.x, F.z);

if (hyp != 0) {
if ((adj / hyp) != 1) {
theta = acos (adj / hyp);

if ((D.z > O.z) && (D.x >= O.x))
;

if ((D.z >= O.z) && (D.x < O.x))
theta = theta + (PI * 0.5);

if ((D.z < O.z) && (D.x < O.x))
theta = theta + PI;

if ((D.z <= O.z) && (D.x >= O.x))
theta = theta + 3 * PI * 0.5;

// theta = theta + (PI * 0.5);
}
else
theta = radAngle;
}
else
theta = radAngle;

unsigned int direction =
FABS((int)CameraAngle - (int)RAD2DEG(theta)) / 45;


Given pos, the position of the sprite, xAng, the angle in degrees the sprite is facing, and CameraAngle, an angle in degrees the camera is rotated, "direction" should hold a number between 0 and 7 that is the correct viewable direction of the character...

Unfortunately it does not. A lot of strange directions are chosen. When will I get this to work? Only the mystics know. But I think that I can do it.

Let me just get what the goal is straight. To find the correct direction, we need to find the angle between line OF (the line between the sprite''s position, and a point in front of it?) and line OD (the line between the sprite''s position, and a point its facing?), then find the difference between this and the camera angle. My head isn''t forming this too well. Why would this work? Why is the camera position not needed - it would seem that the farther away the camera is, the less "sensitive" the sprite is to switching angles when the camera rotates - but this may not be true.

The way I''ve coded it doesn''t seem to work at all - is it a flaw in what I''ve coded, or am I not getting the process and idea right?

Thank you so much for helping... sorry I''m not getting this, don''t give up on me. I''m going to figure this out by gum. =)

Share this post


Link to post
Share on other sites
Could you show a bunch of test data here? The values for pos, xAng, CameraAngle, hyp, adj, theta, and Direction? Include what the direction should be for this set of data. This solution is coming straight from memory so it's not perfect and requires a bit of tweaking to get it right. I suspect you still need to tweak quadrants, but I'll need to see some test data to be sure.

(And I'm actually quite terrible with C/C++, but I can at least read it )

[edited by - Tri on March 25, 2003 7:28:43 PM]

[edited by - Tri on March 25, 2003 8:07:40 PM]

Share this post


Link to post
Share on other sites
OK, here''s my test code, just so you know where it''s printing stuff (all of the vout << ):


void Test (vector3d pos, float xAng, float CameraAngle,
ostream &vout)
{
const float radAngle = DEG2RAD (xAng);

vector3d O = pos;// O is the sprite''s position

vector3d D (O + vector3d(O.x * sin(radAngle), O.y,
O.z * cos(radAngle)));

vector3d F = vector3d (D.x, O.y, O.z);

double theta;
const float hyp = Distance2d (D.x, D.z, O.x, O.z);
const float adj = Distance2d (O.x, O.z, F.x, F.z);

// report
vout << "Pos.x: " << pos.x << " Pos.z " << pos.z << endl;
vout << "Sprite Angle (degrees): " << xAng << endl;
vout << "HYP: " << hyp << endl;
vout << "ADJ: " << adj << endl;

if (hyp != 0) {
if ((adj / hyp) != 1) {
theta = acos (adj / hyp);

vout << "THETA (before quadrant checking): " << theta << endl;
vout << "THETA (in degrees): " << RAD2DEG(theta) << endl;

if ((D.z > O.z) && (D.x >= O.x))
;

if ((D.z >= O.z) && (D.x < O.x))
theta = theta + (PI * 0.5);

if ((D.z < O.z) && (D.x < O.x))
theta = theta + PI;

if ((D.z <= O.z) && (D.x >= O.x))
theta = theta + 3 * PI * 0.5;

//theta = theta + (PI * 0.5);
}
else
theta = radAngle;
}
else
theta = radAngle;

vout << "THETA (after q. check): " << theta << endl;
vout << "THETA (in degrees): " << RAD2DEG(theta) << endl;

unsigned int direction =
FABS((int)CameraAngle - (int)RAD2DEG(theta)) / 45;
}


Here''s some test outputs (a bunch of test outputs) Pos.y is always 0:

Pos.x: 32 Pos.z 32
Sprite Angle (degrees): 45
HYP: 32
ADJ: 22.6274
THETA (before quadrant checking): 0.785398
THETA (in degrees): 45
THETA (after q. check): 0.785398
THETA (in degrees): 45

Pos.x: 32 Pos.z 32
Sprite Angle (degrees): 0
HYP: 32
ADJ: 0
THETA (before quadrant checking): 1.5708
THETA (in degrees): 90
THETA (after q. check): 1.5708
THETA (in degrees): 90

Pos.x: 32 Pos.z 32
Sprite Angle (degrees): 135
HYP: 32
ADJ: 22.6274
THETA (before quadrant checking): 0.785398
THETA (in degrees): 45
THETA (after q. check): 5.49779
THETA (in degrees): 315

Pos.x: 32 Pos.z 32
Sprite Angle (degrees): 180
HYP: 32
ADJ: 1.90735e-006
THETA (before quadrant checking): 1.5708
THETA (in degrees): 90
THETA (after q. check): 4.71239
THETA (in degrees): 270

Pos.x: 16 Pos.z 14
Sprite Angle (degrees): 45
HYP: 15.0333
ADJ: 11.3137
THETA (before quadrant checking): 0.71883
THETA (in degrees): 41.1859
THETA (after q. check): 0.71883
THETA (in degrees): 41.1859

Pos.x: 16 Pos.z 14
Sprite Angle (degrees): 90
HYP: 16
ADJ: 16
THETA (after q. check): 1.5708
THETA (in degrees): 90

Pos.x: 16 Pos.z 14
Sprite Angle (degrees): 180
HYP: 14
ADJ: 9.53674e-007
THETA (before quadrant checking): 1.5708
THETA (in degrees): 90
THETA (after q. check): 4.71239
THETA (in degrees): 270

Pos.x: 16 Pos.z 14
Sprite Angle (degrees): 270
HYP: 16
ADJ: 16
THETA (after q. check): 4.71239
THETA (in degrees): 270

A terrible mess... some of the results really are weird. All of the results where there''s no "before quadrant checking" had hyp == 0 or adj/hyp == 1...

Any other things I should test?
~

Share this post


Link to post
Share on other sites
I dunno. I took this thing and tried it out over here, but I couldn't come up with anything. I can tell you that the calculation of theta before the quadrant check is correct. I also have the following test data with desired answers:

At xAng = 45, Pos = (32, 0, 32):
CameraAngle = 0, Direction should be 3
CameraAngle = 45, Direction should be 4
CameraAngle = 90, Direction should be 5
CameraAngle = 135, Direction should be 6
CameraAngle = 180, Direction should be 7
CameraAngle = 225, Direction should be 0
CameraAngle = 270, Direction should be 1
CameraAngle = 315, Direction should be 2
CameraAngle = 360, Direction should be 3

When I worked on this before, I had camera position and target. They need to be accounted for here, but I've forgotten how to do that, except for deriving the target from the camera direction. I thought direction was enough, but I guess not :/ I do remember it took me a few days to solve it. Sorry I can't be of much help right now. Perhaps in a few days I'll have thought of something, but for now you should try to figure out how to incorporate the camera's position into the calculation. Good luck!

[edited by - Tri on March 25, 2003 10:57:14 PM]

Share this post


Link to post
Share on other sites