Help with "look at" ( using vector ops? )
by Phil Shenk · in Torque Game Builder · 02/27/2005 (9:05 pm) · 23 replies
I'm trying to take a sprite and set it's orientation to look at a point on the screen (such as the mouse cursor). Ideally ( I think ), I'd have a way to get a positive or negative angular offset from the sprite's current angle. Then I could apply set the sprite's rotation towards that new direction.
I'm pretty light on the vector maths, so any help is appreciated.
I'm pretty light on the vector maths, so any help is appreciated.
About the author
#2
Here's the code we used in the new "Grav" demo featuring "Flegg". His head is mounted onto neck segments which stretch as he moves and eventually, his head catches up! Anyway, when you move the mouse cursor, Flegg watches it. The script code we used to achieve this is...
The bizarre inversion (%flegY - %mouseY) is because the coordinate system has the origin at the top-left of the screen.
Look out for those neat helper functions like "mRadToDeg()" and "mDegToRad()"; soooo helpful. :)
I'm thinking at "rotate-to" utility function should be added so I'll put that on my list.
- Melv.
02/28/2005 (12:43 am)
There's also some shortcuts you can take but this all depends on certain constraints of what you're trying to achieve. Here's the code we used in the new "Grav" demo featuring "Flegg". His head is mounted onto neck segments which stretch as he moves and eventually, his head catches up! Anyway, when you move the mouse cursor, Flegg watches it. The script code we used to achieve this is...
// Yes, so get Fleggs Head Position. %flegHeadPos = flegHead.getPosition(); // Seperate the X/Y components of the head position. %flegX = getWord( %flegHeadPos, 0 ); %flegY = getWord( %flegHeadPos, 1 ); // Seperate the X/Y components of the mouse position. %mouseX = getWord( %worldPosition, 0 ); %mouseY = getWord( %worldPosition, 1 ); // Calculate head-angle. %headAngle = mRadToDeg(mAtan(%mouseX - %flegX, %flegY - %mouseY));... or...
function Angle(%x1, %y1, %x2, %y2)
{
return mRadToDeg(mAtan(%x2- %x1, %y1 - %y2));
}The bizarre inversion (%flegY - %mouseY) is because the coordinate system has the origin at the top-left of the screen.
Look out for those neat helper functions like "mRadToDeg()" and "mDegToRad()"; soooo helpful. :)
I'm thinking at "rotate-to" utility function should be added so I'll put that on my list.
- Melv.
#3
Melv, by "rotate-to" you mean rotate to face a point? If so, then yeah, that would be great!
03/01/2005 (12:09 am)
Thanks guys, that looks like that will do the trick.Melv, by "rotate-to" you mean rotate to face a point? If so, then yeah, that would be great!
#4
- Melv.
03/01/2005 (12:16 am)
@Phil: Yes, I'd think carefully what to call it but yes, an optimised version for this is on my TODO list. :)- Melv.
#5
Right now, I am just heavy-handedly seting the player rotation to be the angle of the mouse curor. I'd like it eventually to detect whether the mouse angle is to the left or right of the player angle, then set the player rotation towards it. I probably want to do something so the player rotation slows down then stops as it approaches the target angle, to avoid constant re-corrections.
So I was thinking of actually using vectors for this... to avoid the problem of the angles wrapping around at 180 to -180. That way I could just pass in two vectors, and I guess do a dot product to get the half point between them? I'll have to go back and read about that stuff, cause I forget what exactly I need to do there.
I've done this beforethough... with trig, in Flash about a year ago. I should look and see how I handled the wrap around with the degrees...
Ideas?
03/02/2005 (5:45 pm)
Ok, paste from the other thread...Right now, I am just heavy-handedly seting the player rotation to be the angle of the mouse curor. I'd like it eventually to detect whether the mouse angle is to the left or right of the player angle, then set the player rotation towards it. I probably want to do something so the player rotation slows down then stops as it approaches the target angle, to avoid constant re-corrections.
So I was thinking of actually using vectors for this... to avoid the problem of the angles wrapping around at 180 to -180. That way I could just pass in two vectors, and I guess do a dot product to get the half point between them? I'll have to go back and read about that stuff, cause I forget what exactly I need to do there.
I've done this beforethough... with trig, in Flash about a year ago. I should look and see how I handled the wrap around with the degrees...
Ideas?
#6
Yes, that's almost exactly the code I'm using right now. The issue is... what I want to do is set the player rotation to rotate *towards* the target rotation, not just snap right to it. Here's some reasons why:
1) It's different gameplay... limited turning speed restricts maneuverability, which is good, I think. Also, eventually when I have bad guys, I don't want them just whipping around instantly to face their target.
2) With code like what we've got now, it's fighting the physics system. As is, when I run into a wall, the ship bounces off, and it vibrates like crazy as the physics and the mouselook fight it out. By setting velocities(angular), I'm hoping to let more of the physics do its thing.
ANd then here's the issue I'm having:
when the mouse moves across the horizontal axis defined by the ship, the angle "wraps" from 180 to -180. In other words, if we start at the x axis going right, and rotate clockwise and counter clockwise, we get two ranges, 0 to 180 and 0 to -180. When we are snapping the angle of the ship to equal the angle towards the mouse, this is fine. But for detecting if the mouse is on the left or right side of the axis defined by the *ships* direction, it doesn't work.
What I want is to have the ships facing be the "X axis", and get the mouse position angle offset from that. So with the axis bisecting the ship, going CW is 0 to -180 and CCW is 0 to 180 (or is it the other way around?)
I think that there is a way to do this with trig... some function to take the mouse "world" angle, and convert it into the ship's "local" reference system. I also think that it could be done, maybe more efficiently, with vectors?
03/02/2005 (7:16 pm)
Hey Robert, Yes, that's almost exactly the code I'm using right now. The issue is... what I want to do is set the player rotation to rotate *towards* the target rotation, not just snap right to it. Here's some reasons why:
1) It's different gameplay... limited turning speed restricts maneuverability, which is good, I think. Also, eventually when I have bad guys, I don't want them just whipping around instantly to face their target.
2) With code like what we've got now, it's fighting the physics system. As is, when I run into a wall, the ship bounces off, and it vibrates like crazy as the physics and the mouselook fight it out. By setting velocities(angular), I'm hoping to let more of the physics do its thing.
ANd then here's the issue I'm having:
when the mouse moves across the horizontal axis defined by the ship, the angle "wraps" from 180 to -180. In other words, if we start at the x axis going right, and rotate clockwise and counter clockwise, we get two ranges, 0 to 180 and 0 to -180. When we are snapping the angle of the ship to equal the angle towards the mouse, this is fine. But for detecting if the mouse is on the left or right side of the axis defined by the *ships* direction, it doesn't work.
What I want is to have the ships facing be the "X axis", and get the mouse position angle offset from that. So with the axis bisecting the ship, going CW is 0 to -180 and CCW is 0 to 180 (or is it the other way around?)
I think that there is a way to do this with trig... some function to take the mouse "world" angle, and convert it into the ship's "local" reference system. I also think that it could be done, maybe more efficiently, with vectors?
#7
I will see what can be done to address these nice, almost utility functions as soon as possible.
- Melv.
03/03/2005 (1:32 am)
@Phil: I've been watching some of the stuff people have been doing with interest, particularly yours. Having the ability to issue some target position/rotation seems to be what's needed here, probably at specified linear/angular velocities. These would, of course, override existing velocities and would have to take into account things like autoRotation that may be being used.I will see what can be done to address these nice, almost utility functions as soon as possible.
- Melv.
#8
03/03/2005 (10:28 pm)
@Melv: That would be great! I think it would be tres useful to have such a thing... for pathfinding, for tracking, for, well, for look at. Add in some iterative feedback and you're not too far away from IK ;)
#10
looking at the %ang I notice that you switch the order inwhich events occur (%py-%mypos) instead of (%mypos-%py). Now wouldn't you want to keep them the same order. Like... y-y1/x-x1 both times the x comes first. This out of order I think may cause some wierd problems with the way your player is pointing.
EDIT: I see why it becomes inversed if you keep them the same. Now I think I found the problem with the pointing issue. I am using a default image so I do not know about you but my character is in the image facing right. If I use your code as a base then a problem of the character not facing the right direction comes up. For my I have to take the ang and subtract 90 from it if I want the character to point to the mouse.
Anyone know if its possible to have this done automatically. I dont think there is considering the program would have to realise which direction the image is facing to being with.
03/08/2005 (10:08 pm)
Robert:function sceneWindow2D::onMouseMove( %This, %Modifier, %WorldPosition, %MouseClicks )
{
//get mouse positions
%mxpos = getWord(%WorldPosition,0);
%mypos = getWord(%WorldPosition,1);
//get player positions
%playerpos = $player.getPosition();
%px = getWord(%playerpos,0);
%py = getWord(%playerpos,1);
%ang = mRadToDeg( mAtan(%mxpos-%px , %py-%mypos) );
$player.setRotation(%ang);
}looking at the %ang I notice that you switch the order inwhich events occur (%py-%mypos) instead of (%mypos-%py). Now wouldn't you want to keep them the same order. Like... y-y1/x-x1 both times the x comes first. This out of order I think may cause some wierd problems with the way your player is pointing.
EDIT: I see why it becomes inversed if you keep them the same. Now I think I found the problem with the pointing issue. I am using a default image so I do not know about you but my character is in the image facing right. If I use your code as a base then a problem of the character not facing the right direction comes up. For my I have to take the ang and subtract 90 from it if I want the character to point to the mouse.
Anyone know if its possible to have this done automatically. I dont think there is considering the program would have to realise which direction the image is facing to being with.
#11
It'll just cut-down on script-bulk on your side.
Expect it in the next update. :)
- Melv.
03/09/2005 (2:07 am)
@Christopher: As I mention above, I'll be adding a utility function in C++ to do this for you. I'll probably add an offset parameter but this doesn't necessarily make it any more "automatic" I guess.It'll just cut-down on script-bulk on your side.
Expect it in the next update. :)
- Melv.
#12
03/09/2005 (5:12 am)
@Phil Shenk: To detect whether the mouse point is to the left or right of the players point you can use the cross product. If you convert the players position to a vector and the mouse point to a vector, take the cross product of the two. By checking the direction of the Z component you can determine whehter the point is to the left or right.
#13
03/09/2005 (8:48 am)
I'm having a similar problem: thanks to you guys I've got the rotation working, but I need the sprite to extend or contract, only along its x-axis, depending on how far away the mouse is from the sprite center(in this case also the world center). I know almost no vector math. Any help would be appreciated.
#14
- Melv.
03/09/2005 (8:55 am)
Here yer' go...// Assuming that (0,0) is the center of the view.
// Assuming that your sprite is stored in "$sprite" and is at the center of the view.// Assuming the window is called "sceneWindow2D"
function sceneWindow2D::onMouseMove( %this, %modifier, %worldPosition, %mouseClicks )
{
// New X Size.
%sizeX = mAbs(getWord(%worldPosition,0)) * 2;
// Existing Y Size.
%sizeY = getWord($sprite.getSize(), 1);
// Set New Size.
$sprite.setSize( %sizeX SPC %sizeY );
}- Melv.
#15
I think all I need is a^2 + b^2 = c^2 and just use the squareroot of c^2 * 2
Where can I find all the math functions in Torquescript? I need the squareroot function.
EDIT:
msqrt seems to work, but my code doesn't:
I am attempting to create a lenseflare by mounting several flares along an invisible sprite that changes x-size based on mouse position(to represent flare origin).
03/09/2005 (9:06 am)
Hmmm... that didn't seem to work... probably a problem on my end, but it turns up the same as what I was doing.I think all I need is a^2 + b^2 = c^2 and just use the squareroot of c^2 * 2
Where can I find all the math functions in Torquescript? I need the squareroot function.
EDIT:
msqrt seems to work, but my code doesn't:
function sceneWindow2D::onMouseMove( %this, %modifier, %worldPosition, %mouseClicks )
{
//get mouse positions
%mxpos = getWord(%WorldPosition,0);
%mypos = getWord(%WorldPosition,1);
//get player positions
%flareBasepos = $flareBase.getPosition();
%fbx = getWord(%flareBasepos,0);
%fby = getWord(%flareBasepos,1);
%ang = mRadToDeg( mAtan(%mxpos-%fbx , %fby-%mypos) );
$flareBase.setRotation(%ang - 90);
// -------------- this code is the problem ----------------------
%flareLength = msqRt(mAbs(%mxpos^2) + mAbs(%mypos^2) );
echo( %flareLength );
$flareBase.setSize( %flareLength * 2 SPC "1" ); // adjust only
}This code returns strange numbers for the %flareLength.I am attempting to create a lenseflare by mounting several flares along an invisible sprite that changes x-size based on mouse position(to represent flare origin).
#16
Is the sprite rotating when you do this? If you describe exactly what you're doing I can help.
"mSqrt()" (from "mConsoleFunctions.cc")
- Melv.
03/09/2005 (9:18 am)
That most certainly does work, I just tried it. Maybe you didn't describe what you wanted clearly enough or I misunderstood?Is the sprite rotating when you do this? If you describe exactly what you're doing I can help.
"mSqrt()" (from "mConsoleFunctions.cc")
- Melv.
#17
03/09/2005 (9:25 am)
And yes, the sprite is rotating. What you gave me works on the x-axis so long as the mouse doesn't wander too far along the y-axis.
#18
The "^" operator is wrong here. You can just multiple the vars together to square or you can use "mPow(%mxpos,2)" to raise for arbitrary exponents.
Also, be careful when using commands to form strings...
Try this then...
This will size the sprite along its X-axii, not the screen basis.
Is this what you want?
- Melv.
03/09/2005 (9:34 am)
Okay, in your original post I thought you meant using the x position of the mouse only.The "^" operator is wrong here. You can just multiple the vars together to square or you can use "mPow(%mxpos,2)" to raise for arbitrary exponents.
Also, be careful when using commands to form strings...
$flareBase.setSize( %flareLength * 2 SPC "1" );... should be this ...
$flareBase.setSize( (%flareLength * 2) SPC "1" );... otherwise you can end up with bizarre precedence problems. In this case it works though.
Try this then...
function sceneWindow2D::onMouseMove( %this, %modifier, %worldPosition, %mouseClicks )
{
//get mouse positions
%mxpos = getWord(%WorldPosition,0);
%mypos = getWord(%WorldPosition,1);
%flareLength = mSqrt(mAbs(%mxpos*%mxpos) + mAbs(%mypos*%mypo) );
echo( %flareLength );
$flareBase.setSize( (%flareLength * 2) SPC "1" );
}This will size the sprite along its X-axii, not the screen basis.
Is this what you want?
- Melv.
#19
03/09/2005 (9:39 am)
Ah ha! That works. Thank you Melv and sorry to take up so much of your posting time.
#20
- Melv.
03/09/2005 (9:43 am)
No problem man. Glad to help. I can understand how frustrating it is to stall on something simple when you've got a game to write. :)- Melv.
Torque Owner Jason Cahill
Default Studio Name
function Angle(%x1, %y1, %x2, %y2) { %dx = %x2 - %x1; %dy = %y2 - %y1; %vLength = mSqrt(%dx * %dx + %dy * %dy); if (%vLength == 0) return 0; %ux = %dx / %vLength; %uy = %dy / %vLength; return (mAtan(%ux, %uy) * 180 / 3.141592654) - 90; }The math behind this is pretty simple (though I'm embarassed to admit it took me over an hour to figure it out in Excel + VBA, and then to get it ported to Torque using T2D's angle system... which assumes that 0 is the X-axis and what we'd call 10 degrees is actually -10).
Anyway, you pass is the coordinates of your object as (x1, y1) and the mouse in (x2, y2). The first thing we do is create a new vector "d" which is (x2 - x1, y2 - y1). From this we compute the length of the vector, then create a unit vector "u". In Torque, mAtan (for math ArcTan) returns the angle of the vector off of the Y-Axis. I then convert that to degrees and subtract 90 to get the angle relative to the X-axis.
Make sense?