Head Look Controller

Just a heads up that I made a head look controller that you can use to make characters look at any point in space, turning their head and upper bodies etc.

Blog entry (with video!):
http://blogs.unity3d.com/2009/07/10/just-looking-around/

Example project:
http://unity3d.com/support/resources/example-projects/head-look-controller

If you want the script itself only, you can get it on the Unify Community Wiki:
http://www.unifycommunity.com/wiki/index.php?title=HeadLookController

Looking forward to see what you people can do with it. :slight_smile:

Rune

Very nice.

Wow this is very cool, it just makes me think of all the ways this could be used.

Thanks abunch

I tried a million times to write some code like yours, but I never really got it… Thanks, that code will save much of my time! :slight_smile:

(I need it for characters who should be able to shoot in different angles, and not only forward…)

very cool !

just a small suggestion:

adding a “rootNode” public variable holding the root node transform, allows the script to be attached anywhere ( thus without breaking a Prefab ) - eg. to a global gamehandler…

i made the following 3 changes to the class

public class HeadLookController : MonoBehaviour {

// CHANGE 1 -> this declaration will hold the root transform to use
    public Transform rootNode;  

    public BendingSegment[] segments;

    public NonAffectedJoints[] nonAffectedJoints;
    public Vector3 headLookVector = Vector3.forward;

    public Vector3 headUpVector = Vector3.up;

    public Vector3 target = Vector3.zero;

   

    void Start () {

        // Setup segments

        foreach (BendingSegment segment in segments) {

            Quaternion parentRot = segment.firstTransform.parent.rotation;

            Quaternion parentRotInv = Quaternion.Inverse(parentRot);

            segment.referenceLookDir =

// CHANGE 2 -> here we use rootNode's transform 
                parentRotInv * rootNode.transform.rotation * headLookVector.normalized; 

            segment.referenceUpDir =
// CHANGE 3 -> again here we use rootNode's transform
                parentRotInv * rootNode.transform.rotation * headUpVector.normalized;  
.
.
.
.[continued]

this small change works for me - now i can use the script anywhere, not just attached to the animated root bone…

Thanks, good point!

I’ll integrate those changes on the wiki next week (or you’re welcome to do it if you want, as long as the changed script is fully tested and known to work).

Rune

This has now been added.

Rune

Hi,

I tried and use your HeadLookController script and it works very good. So first of all thanks for the great script!

I have a question though. I’m using the script on some npc characters that should look at the player when he gets close to it. When I get out of range I disable the script. All fine but the bone rotations are locked in the latest position when I disable the script. Shouldn’t the animation take control over the bones when the script stops overriding it? It should at least snap back?

//ohm

Yes it should. If the head doesn’t snap back when you disable the HeadLookController component, it must be because you don’t have any animations playing that have animation curves for the bones in question.

Edit: Also see below for how to avoid snapping completely.

Rune

I added an effect variable to the script on the Wiki that can be used to gradually turn the head look controller on and off. When effect is 1 the controller is fully turned on; when it’s 0 it has no effect.

Rune

Yes it was animation problems that I had before. It now works fine. I did a similar change as you did with the effect variable and interpolates from and to that before I turn on and off the script and it now works as intended. Thanks again!

//ohm

This script is awesome, thank you Rune :slight_smile:

I made a simple example project:

http://unity3d.com/support/resources/example-projects/head-look-controller

(It’s a simplified version of the one shown in the video.)

The script is the same as the one on the Wiki; the example project just provides a character to test the script on. :slight_smile:

Rune

Rune, this is really impressive, as is all your work. In fact, right now my player character is controlled by the Normal Character Motor and Platform Character Controller scripts, the Head Look Controller, and the Locomotion System! The character holds a flashlight that points at whatever surface is behind the cursor with the help of the Head Look Controller, and I’m using a modified version of the Follower script that changes only the desiredFacingDirection to get the body to point toward that same cursor-projected object while the actual movement is controlled with WASD.

But I’m having a somewhat tangential problem. It’s not a problem with the Head Look Controller per se, more a problem with the Normal Character Motor, but I figured it fit better here than anywhere else.

Because the character motor’s facing direction changes with the slightest angle change from the cursor object and at a constant speed, it looks rather stiff and nullifies most of the nice natural effect the Head Look Controller gave to controlling the flashlight. Worse, if I do move the cursor far and fast enough to see the character’s arm significantly bend before the motor’s facing direction catches up, when it does catch up the arm’s gone too far and has to “snap back” to center on the cursor object which looks quite ugly and jarring. I thought I might be able to solve the first problem (the sudden, linear-feeling movement) by incorporating some ideas from the Head Look Controller into the character motor’s UpdateFacingDirection function, such as a threshold angle difference and max angle difference. But solving the second problem (the “snap-back” on the Head Look Controller’s actions) would seem to require making the Head Look Controller take the character motor’s facing direction into account as if it were a segment in the Head Look Controller script itself, and though I’ve studied the scripts for the last day and a half I’m at a loss as to how to make it do so. Does anyone have any ideas? I’m not expecting anyone to do the work for me, just someone with more experience setting me on a track that might be right. Let me know if I’m not making sense and I’ll try to clarify.

I have a vague understanding of the problem from your description, but seeing a video might help me better understand it.

Rune

hopefully this helps:

http://www.vimeo.com/6268716

i was a bit lazy/creative with the test scene textures, heh. i have someone else working on the real model and animation, which will be much more suited to holding a flashlight than the stock Hero model, but you should get the idea. it’s worth noting that before i posted i tried many different combinations of settings in the head look controller and character motor rotations and the problems persist. actually without the follower script activated there is still a small amount of snap-back in the character’s arm movements, but it’s not nearly as jarring and exagerrated. but this may still be a problem that fine-tuning the head look controller might alleviate, rather than having to program something custom. as for the stiffness, i can’t make the max rotation speed too small or it makes the character’s reactions too sluggish and has a detrimental effect on gameplay.

ideally the character itself should only start rotating once the angle of the target is a specified amount different enough, and it should only move about as far as it needs to for the flashlight to smoothly center on the target with the head look controller’s help.

It sounds like you could make a check in the character motor if the current forward direction is less than a certain angle away from the desired direction, and if so, don’t rotate further or only rotate very slowly. The threshold angle should match how far you have specified the arm to be able to point to the sides.

Rune

this is the first thing i tried:

private void UpdateFacingDirection() {
		
		if (Vector3.Angle(desiredFacingDirection,transform.forward) < thresholdAngleDifference) return;

which did stop the motor from turning instantly, but when it did move it was very choppy.

i ended up adapting the features of the head look controller into UpdateFacingDirection, and it seems to work very nicely for my purposes. here’s the changed code:

	public float thresholdAngleDifference;
	public float turningMultiplier;
	public float maxAngleDifference;
	public float responsiveness;

	private void UpdateFacingDirection() {

		float angleDifference = Vector3.Angle(desiredFacingDirection,transform.forward);

		//handling threshold angle difference
		float hfacingThr = Mathf.Max(
			0, Mathf.Abs(angleDifference) - thresholdAngleDifference
		) * Mathf.Sign(angleDifference);

		//handling bending multiplier and max angle difference
		angleDifference = Mathf.Max(
			Mathf.Abs(hfacingThr) * Mathf.Abs(turningMultiplier),
			Mathf.Abs(angleDifference) - maxAngleDifference
		) * Mathf.Sign(angleDifference) * Mathf.Sign(turningMultiplier);

		maxRotationSpeed = Mathf.Lerp(
			maxRotationSpeed, angleDifference, Time.deltaTime * responsiveness
		);

		// Calculate which way character should be facing
		float facingWeight = desiredFacingDirection.magnitude;
		Vector3 combinedFacingDirection = (
			transform.rotation * desiredMovementDirection * (1-facingWeight)
			+ desiredFacingDirection * facingWeight
		);
		combinedFacingDirection = Util.ProjectOntoPlane(combinedFacingDirection, transform.up);
		combinedFacingDirection = alignCorrection * combinedFacingDirection;
		
		if (combinedFacingDirection.sqrMagnitude > 0.01f) {
			Vector3 newForward = Util.ConstantSlerp(
				transform.forward,
				combinedFacingDirection,
				maxRotationSpeed*Time.deltaTime
			);
			newForward = Util.ProjectOntoPlane(newForward, transform.up);
			//Debug.DrawLine(transform.position, transform.position+newForward, Color.yellow)
			Quaternion q = new Quaternion();
			q.SetLookRotation(newForward, transform.up);
			transform.rotation = q;
		}
	}

smoothing out the rotation like this probably isn’t the right choice for many situations, but i thought posting it might be helpful to someone else using the Normal Character Motor down the road.

Cool. I’m looking forward to following the project / see it when it’s finished. :slight_smile:

Rune

hmm, every so often, most commonly when waving the flashlight around slowly in front of the character, the character jerks violently for a split second and i get this warning:

Look rotation viewing vector is zero

supposedly due to the following line:

			q.SetLookRotation(newForward, transform.up);

it looks like changing

		maxRotationSpeed = Mathf.Lerp(
			maxRotationSpeed, angleDifference, Time.deltaTime * responsiveness
		);

to LerpAngle prevents that problem, but makes the rest of the movement choppy again. any idea what’s up with this?