- Home /
How can I make a character look in a direction that's halfway between its direction of 2D movement and facing the camera?
This is a bit of a specific one so bear with me.
I have a game with Rigidbody2D-based characters. It's side view, and characters can float/swim around X/Y axes freely.
My NPCs are 3D meshes, so they can be rotated to look in the direction of their movement. I have the mesh as a child of an empty gameobject which I can use for this purpose.
The problem is that I want their rotation to be limited to a narrower cone, so they're not facing absolutely in that direction, but kind of half-facing the camera so you can see their faces.
See this image above. The character can move along X and Y, but they should not have free rotation. It should be clamped to a circle kind of like the green cone (same angle in all directions).
The result should be that they always look kind of like one of the blue faces in the image on the right, always half-facing the camera and half-facing their direction of movement.
I'm struggling to figure out the maths required to get this effect. I've got something approximately working but it's not smooth and it's not accurate.
void LookTowardsTarget(Vector3 target)
{
Vector3 direction = (lastLightPosition - transform.position).normalized;
float clampedX = direction.x * maxPivotAngle;
float clampedY = direction.y * maxPivotAngle;
if (direction.x < 0 && clampedY < 0)
clampedY = -clampedY;
else if (direction.x > 0 && clampedY < 0)
clampedX = -clampedX;
else if (direction.x > 0 && clampedY > 0)
clampedY = -clampedY;
else if (direction.x < 0 && clampedY > 0)
clampedX = -clampedX;
lookPivot.rotation = Quaternion.Euler(clampedX, clampedY, 0f);
}
The 4 "if" statements are there to correct and negate certain axes being negative or positive in the code that precedes it. I'm happy to throw all this away though.
Uhh... help?! Rotational mathematics are really not my strong point!
Answer by LCStark · Oct 09, 2018 at 01:42 PM
Vector3.Slerp or Quaternion.Slerp should help you with this one. You can interpolate spherically between two vectors (or two rotations), so setting the t
parameter to 0.5f
will give you a half-way vector (or rotation).
Awesome, that works much better! Thank you! Here's my resulting code:
// Pivots a transform to look part-way between a) a position in world space and b) towards the camera .
void LookTowardsTarget(Vector3 target)
{
// Get the direction as a non-normalized vector.
Vector3 directionToTarget = transform.position - target;
// Calculate the rotation as a Quaternion.
// We do this by finding a lerp value between Quaternion.identity (straight forwards along Z) and a look rotation from above.
// We use a fixed lerp value as specified in the Inspector.
Quaternion rot = Quaternion.Lerp(
Quaternion.identity,
Quaternion.LookRotation(directionToTarget, Vector3.up),
lookPivotAmount);
// Set the transforms rotation.
lookPivot.rotation = rot;
}
There is a little snag whereby the 'move up' and 'move down' variants in my example image don't ever show, it's always snapped somewhere between the two adjacent ones (i.e. there is no 12 o'clock or 6 o'clock). Any ideas?
I can't be sure this is the source of the problem, since I haven't used LookRotation
a lot, but I think it is the cause here. If your target is above you, you're trying to find a rotation which looks in the up
direction, but also has its own relative up
direction as close to the global up
as possible. When the directionToTarget
is close to Vector3.up
, the resulting rotation will snap between up
being on the left and right.
I think it would be better if you first calculated your target vector and then used LookRotation
to get the rotation you need.
Vector3 directionToTarget = targetPosition - currentPosition;
Vector3 directionToCamera = cameraPosition - currentPosition;
Vector3 lookDirection = Vector3.Slerp(directionToTarget, directionToCamera, 0.5f);
Quaternion rot = Quaternion.LookRotation(lookDirection, up);
You can always use
Debug.DrawRay
to check whether the vectors you've generated are correct. (
Debug.DrawRay documentation).
That works perfectly! Thank you so much!
I ended up turning this into a static method so I can call it from any object. I just pass in the object's position, the 'look target' position, and a float that controls how strongly it pivots.
public static Quaternion LookTowardsTarget(Vector3 target, Vector3 origin, float lookPivotAmount)
{
Vector3 directionToTarget = origin - target;
Vector3 directionToCamera = origin - CameraController.mainCameraTransform.position;
Vector3 lookDirection = Vector3.Slerp(directionToTarget, directionToCamera, lookPivotAmount);
Quaternion rot = Quaternion.LookRotation(lookDirection, Vector3.up);
return rot;
}
I also found that your initial direction calculations were the wrong way round. :p
Your answer
Follow this Question
Related Questions
Rotating 2D sprite to match surface. 0 Answers
Quaternion reset rotations didnt smooth 0 Answers
Rotating an object in relation to its endpoints 1 Answer
(2D) rotate object to face movement direction 1 Answer
Strange rotation pattern. 0 Answers