CDF
April 25, 2014, 12:49am
1
Hi, I’m trying to rotate my characters spine so that his weapon aims at a specific point.
I’m close, but something is off. Here’s an image of my problem:
Red line is weapons direction, blue line is from barrel to target
and here’s my code so far:
//root
Transform a = GameObject.Find("swat").transform;
//spine
Transform s = a.FindChild("Hips/Spine");
//weapon
Transform w = a.FindChild("Hips/Spine/Spine1/Spine2/RightShoulder/RightArm/RightForeArm/RightHand/ak47");
Vector3 aimOrigin = w.TransformPoint(weapon.attackPoint);
Vector3 aimForward = target - aimOrigin;
Vector3 weaponForward = w.up;
//find the spine required for weapon to point at the target
aimRotation = Quaternion.FromToRotation(weaponForward, aimForward) * s.rotation;
//apply the local rotation back to spine
s.rotation = aimRotation;
any ideas?
We don’t have all the code, so I’m guessing at this, but…
I think what you’re doing is taking the weapon’s needed rotation and applying it to the spine. But they rotate from different points in space, so that isn’t going to work.
I would try finding the distance from the spine’s rotation point to the target, then finding the spot that distance from the gun’s rotation along its forward axis. Then find the rotation needed from the gun’s target location to the desired target location, from the spine’s rotation point. It won’t be completely accurate because the gun is offset from the spine, but I think it’d be better than you have.
But I haven’t tried that, and there could be a flaw in my thinking.
jabez
April 25, 2014, 11:57am
3
Take a look at head look Controller http://u3d.as/content/unity-technologies/head-look-controller/1qN
you can use it not only for the head, but for the arms.
CDF
April 25, 2014, 6:36pm
4
thanks for the help.
Part of my problem is related to mecanim resetting animation rotations before the above code is run.
The head look controller is a little helpful, but it modifies the internal orientation of the human. I need my character to stay rigid.
Anyway, I’m gonna try using “MatchTarget” and see how that goes.
CDF
April 25, 2014, 7:19pm
5
ok so MatchTarget doesn’t work.
However, I’ve found that if I rotate the root object (character) like so:
transform.rotation = Quaternion.FromToRotation(weaponForward, aimForward) * transform.rotation;
then the weapon aligns perfectly, but the entire character rotates
CDF
April 25, 2014, 11:29pm
6
iteration to the rescue!
breaks down though when objects are too close
Transform s = spine;
Transform w = weapon.transform;
float distance = float.PositiveInfinity;
float minDistance = 0.01f;
int iteration = 30;
while (distance > minDistance iteration > 0) {
Vector3 aimOrigin = w.TransformPoint(weapon.attackPoint);
Quaternion rot = Quaternion.FromToRotation(w.forward, target - aimOrigin);
distance = rot.eulerAngles.sqrMagnitude;
iteration--;
s.rotation = rot * s.rotation;
}
CDF
April 26, 2014, 12:22am
7
fixed the close object issue. Project weapon onto spine plane and use that as the target delta:
Transform s = spine;
Transform w = weapon.transform;
float distance = float.PositiveInfinity;
float minDistance = 0.01f;
int iteration = 30;
while (distance > minDistance iteration > 0) {
Vector3 aimOrigin = w.TransformPoint(weapon.attackPoint);
Vector3 origin;
if (!LinePlaneIntersection(out origin, aimOrigin, -w.forward, s.forward, s.position)) {
//if for some readon the weapon cannot intersect with spine forward plane, just use the aim origin
//hopefully this never happens as weapon should always be in front of spine
origin = aimOrigin;
}
Vector3 weaponForward = w.forward;
Vector3 aimForward = target - origin;
Quaternion rot = Quaternion.FromToRotation(weaponForward, aimForward);
distance = rot.eulerAngles.sqrMagnitude;
iteration--;
s.rotation = rot * s.rotation;
}
public static bool LinePlaneIntersection(out Vector3 intersection, Vector3 linePoint, Vector3 lineVec, Vector3 planeNormal, Vector3 planePoint) {
float length;
float dotNumerator;
float dotDenominator;
intersection = Vector3.zero;
dotNumerator = Vector3.Dot((planePoint - linePoint), planeNormal);
dotDenominator = Vector3.Dot(lineVec, planeNormal);
if (dotDenominator != 0.0f) {
length = dotNumerator / dotDenominator;
intersection = linePoint + lineVec.normalized * length;
return true;
}
else {
return false;
}
}
working nicely now. Iterations don’t usually go above 4. 2 at long distance.
Now to constrain the angles
CDF
April 26, 2014, 1:18am
8
ok I’m done. Clamping and smoothing added:
Transform s = spine;
Transform w = weapon.transform;
float distance = float.PositiveInfinity;
float minDistance = 0.01f;
int iteration = 30;
Quaternion oldRotation = lastAimRotation;
while (distance > minDistance iteration > 0) {
Vector3 aimOrigin = w.TransformPoint(weapon.attackPoint);
Vector3 origin;
if (!LinePlaneIntersection(out origin, aimOrigin, -w.forward, s.forward, s.position)) {
//if for some readon the weapon cannot intersect with spine forward plane, just use the aim origin
//hopefully this never happens as weapon should always be in front of spine
origin = aimOrigin;
}
Vector3 weaponForward = w.forward;
Vector3 aimForward = target - origin;
Quaternion rot = Quaternion.FromToRotation(weaponForward, aimForward);
distance = rot.eulerAngles.sqrMagnitude;
iteration--;
s.rotation = rot * s.rotation;
}
//clamp angles
Vector3 angles = s.localEulerAngles;
//offset the clamped angles by any upper body offset
//we may need this if characters aren't aligned forward when rotation is identity
angles.x = ClampAngle(angles.x, minUpperBodyAngles.x + upperBodyAngleOffset.x, maxUpperBodyAngles.x + upperBodyAngleOffset.x);
angles.y = ClampAngle(angles.y, minUpperBodyAngles.y + upperBodyAngleOffset.y, maxUpperBodyAngles.y + upperBodyAngleOffset.y);
angles.z = ClampAngle(angles.z, minUpperBodyAngles.z + upperBodyAngleOffset.z, maxUpperBodyAngles.z + upperBodyAngleOffset.z);
s.localEulerAngles = angles;
//smooth if we have easing
if (aimRotationEasing > 0) {
s.rotation = Quaternion.Slerp(lastAimRotation, s.rotation, Time.deltaTime * aimRotationEasing);
lastAimRotation = s.rotation;
}
private static float ClampAngle(float ang, float min, float max) {
if (ang > 180) ang -= 360;
if (max > 180) max -= 360;
if (min > 180) min -= 360;
ang = Mathf.Clamp(ang, min, max);
return ang < 0 ? ang + 360 : ang;
}
private static bool LinePlaneIntersection(out Vector3 intersection, Vector3 linePoint, Vector3 lineVec, Vector3 planeNormal, Vector3 planePoint) {
float dotNumerator = Vector3.Dot(planePoint - linePoint, planeNormal);
float dotDenominator = Vector3.Dot(lineVec, planeNormal);
if (dotDenominator != 0.0f) {
intersection = linePoint + lineVec.normalized * (dotNumerator / dotDenominator);
return true;
}
else {
intersection = Vector3.zero;
return false;
}
}
Hopefully this can help out anyone else wanting a character to aim at a target.
2 Likes
daeuk
May 16, 2014, 3:17am
9
thanks for this…
one question though, is the weapon.attackPoint
the target being aimed at?