[Solved] How to make FPC turn instead of strafe?

[I have moved this here from the Support forum]

Hi,

I’m trying to modify the standard asset First Person Controller so that navigation and camera movement work a bit differently (for example, turning instead of strafing). Here’s what I’m aiming for:

  • W and S keys (and up and down arrows) move the player forward and back
  • A and D keys (and left and right arrows) turn the player to the left and right (pivoting around the Y axis)
  • the mouse does not affect the camera or the position of the player unless the right mouse button is down
  • with the right mouse button down, the camera can look up and down, and the player can turn to the left and right.

I have searched this forum and found some code that I’ve been able to use to achieve most of what I need it to do. I have taken the following script (written by Paintbrush and posted HERE), [edit: pared it down extensively to removed functions I did not need], and attached it to the First Person Controller in place of the standard assets FPSwalker script:

[edit: the following code block contains the script that does not work right. See the later post for a script that does work.]

private var gravity:float = 20.0; 
private var walkSpeed:float = 5.0; 
private var rotateSpeed:float = 100.0; 
private var grounded:boolean = false; 
private var moveDirection:Vector3 = Vector3.zero; 

function Update () 
{ 
   // Allow movement while grounded 
   if(grounded)
   { 
      moveDirection = new Vector3((Input.GetMouseButton(1) ? Input.GetAxis("Horizontal") : 0),0,Input.GetAxis("Vertical")); 
      moveDirection = transform.TransformDirection(moveDirection) * walkSpeed; 
	  transform.Rotate(0,Input.GetAxis("Horizontal") * rotateSpeed * Time.deltaTime, 0); 
	} 
    
   //Apply gravity 
   moveDirection.y -= gravity * Time.deltaTime; 
    
   //Move controller 
   var controller:CharacterController = GetComponent(CharacterController); 
   var flags = controller.Move(moveDirection * Time.deltaTime); 
   grounded = (flags  CollisionFlags.Below) != 0; 
} 

@script RequireComponent(CharacterController)

For the mouselook script, I have just added “if(Input.GetMouseButton(1))” to the script that comes with the FPS standard asset, to produce this:

using UnityEngine;
using System.Collections;

[AddComponentMenu("Camera-Control/Mouse Look")]
public class MouseLook : MonoBehaviour {

	public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 }
	public RotationAxes axes = RotationAxes.MouseXAndY;
	public float sensitivityX = 15F;
	public float sensitivityY = 15F;

	public float minimumX = -360F;
	public float maximumX = 360F;

	public float minimumY = -60F;
	public float maximumY = 60F;

	float rotationX = 0F;
	float rotationY = 0F;
	
	Quaternion originalRotation;

	void Update ()
	{
	if (Input.GetMouseButton(1))
	{
		if (axes == RotationAxes.MouseXAndY)
		{
			// Read the mouse input axis
			rotationX += Input.GetAxis("Mouse X") * sensitivityX;
			rotationY += Input.GetAxis("Mouse Y") * sensitivityY;

			rotationX = ClampAngle (rotationX, minimumX, maximumX);
			rotationY = ClampAngle (rotationY, minimumY, maximumY);
			
			Quaternion xQuaternion = Quaternion.AngleAxis (rotationX, Vector3.up);
			Quaternion yQuaternion = Quaternion.AngleAxis (rotationY, Vector3.left);
			
			transform.localRotation = originalRotation * xQuaternion * yQuaternion;
		}
		else if (axes == RotationAxes.MouseX)
		{
			rotationX += Input.GetAxis("Mouse X") * sensitivityX;
			rotationX = ClampAngle (rotationX, minimumX, maximumX);

			Quaternion xQuaternion = Quaternion.AngleAxis (rotationX, Vector3.up);
			transform.localRotation = originalRotation * xQuaternion;
		}
		else
		{
			rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
			rotationY = ClampAngle (rotationY, minimumY, maximumY);

			Quaternion yQuaternion = Quaternion.AngleAxis (rotationY, Vector3.left);

			transform.localRotation = originalRotation * yQuaternion;
		}
	}
	}
	
	void Start ()
	{
		// Make the rigid body not change rotation
		if (rigidbody)
			rigidbody.freezeRotation = true;
		originalRotation = transform.localRotation;
	}
	
	public static float ClampAngle (float angle, float min, float max)
	{
		if (angle < -360F)
			angle += 360F;
		if (angle > 360F)
			angle -= 360F;
		return Mathf.Clamp (angle, min, max);
	}
}

This combination works pretty well. You can try it out here:

http://www.medievalist.net/unityworlds/gsugallery.htm

However, there are some problems.

If I use the mouse (right button) to look around, and then hit the W key to move forward, I go in the direction I’m looking, which is good. However, if I use the arrow keys to turn from side to side, and then hit the right mouse button to look around, the view instantly reverts to the direction the player was facing before I used the left/right arrows. [edit: removed references to problems that have been solved]

I’m afraid I can’t figure out what’s going wrong here. If you can, please let me know.

Thanks.

Edit:
I’ve solved a number of the issues reported in my original post (and edited them out), but the main one persists: if I turn to the left or right using A or D, or the arrow keys, and then click the right mouse button, the camera viewpoint jumps back to the position it was in before I used the left/right keys. Obviously my script is missing something. If you can tell me what it is I’d be grateful. Or perhaps there is a code snippet somewhere that would work better than this one. If you can point me to such a thing, I’d appreciate it.
Thanks.

It might be that your arrow keys affect the variables MoveDirection, but doesn’t update rotationX or rotationY.

So after turning with arrows, MoveDirection is changed, but rotationX Y still have the same values as last time you used them, so the view snaps back there.

Have you tried using lockCursor?

I had a similar problem where the camera would jump to where the mouse was pointing and this cured it for me.

Thanks for the suggestions. They inspired me to try some different things, and I finally got the FPC navigation working as I wanted.

I’ve made it so that the right mouse button rotates the character horizontally, and I’ve eliminated the MouseLook script (MouseX) that was previously attached to the FPC, leaving only the MouseLook script (MouseY) on the camera attached to the FPC.

So, the Mouselook script on the camera is the same as in my initial post, and the “walker” script now looks like this:

var gravity:float = 20.0; 
var walkSpeed:float = 5.0; 
var rotateSpeed:float = 100.0; 
var mouseTurnMultiplier = 0.5;

private var grounded:boolean = false; 
private var moveDirection:Vector3 = Vector3.zero; 

function Update () 
{ 
   // Allow movement while grounded 
   if(grounded) 
   { 
      moveDirection = new Vector3(0,0,Input.GetAxis("Vertical")); 
      moveDirection = transform.TransformDirection(moveDirection) * walkSpeed; 
      transform.Rotate(0,Input.GetAxis("Horizontal") * rotateSpeed * Time.deltaTime, 0); 
   
      // Check to see if the right mouse button is pressed, and, if so, get horizontal rotation 
			if (Input.GetMouseButton(1)) 
			{
			x = Input.GetAxis("Mouse X") * rotateSpeed * mouseTurnMultiplier; 
			 transform.Rotate(0, x, 0); 
			 } 
	}
    
   //Apply gravity 
   moveDirection.y -= gravity * Time.deltaTime; 
    
   //Move controller 
   var controller:CharacterController = GetComponent(CharacterController); 
   var flags = controller.Move(moveDirection * Time.deltaTime); 
   grounded = (flags  CollisionFlags.Below) != 0; 
} 

@script RequireComponent(CharacterController)

[Edit: of course, I had to disable the context menu as well, since I am using the right mouse button for looking.]

You can confirm that this works by visiting this test world: http://www.medievalist.net/unityworlds/catacomb.htm

Though my script works, I realize that it may not be proper code. If anyone has refinements to suggest, please let me know.

Thanks again.

Hey, thanks for putting this up, it seems to be a lot cleaner than the code I was using to turn my character via the keys. I like the script a bunch but I’m encountering a few problems:

  1. When I turn it seems to happen in hitches instead of smoothly. Granted this was happening before but I still can’t figure it out? Is my coding bad or is there a render setting I’m screw up with ? Moving foward and backwards is smooth as silk but it gets all mucked up with the turning.

2)It used to run fine before but now when I start my scene I don’t move forwards, but instead slowly crawl sideways. I’ve tried cutting and pasting your script in again but to no avail.

Thanks for you help, I think I might be in over my head here.

As I am still just learning about Unity, I’m not the best person to ask about this. I suspect that you have made a mistake somewhere along the line, either with the mouselook or the character controller. I have tried making a couple of deliberate errors myself, but have not been able to duplicate your experience. Perhaps someone else will have a suggestion. In the meantime, perhaps you could post a web version of what you are working on, so others can see if we experience the same problems as you. I assume that when you look at the Catacomb environment mentioned above that everything works fine? Does the side-to-side turning work smoothly in that environment, but not in yours?

Here’s a link to what I have so far:
http://www.lutherp.com/BellicoseTest/Test1.html

Here’s the code I’m using right now:

var gravity:float = 20.0;
var walkSpeed:float = 2000.0;
var rotateSpeed:float = 80.0;
var mouseTurnMultiplier = 0.5;

private var grounded:boolean = false;
private var moveDirection:Vector3 = Vector3.zero;

function Update ()
{
   // Allow movement while grounded
   if(grounded)
   {
      currentRotation = new Vector3(0, Input.GetAxis("Turning")* rotateSpeed *Time.smoothDeltaTime,0);
	  transform.Rotate(currentRotation);
	  moveDirection = new Vector3(Input.GetAxis("Forward/Back")* walkSpeed,0,0);
      moveDirection = transform.TransformDirection(moveDirection) * walkSpeed;
      
	  //transform.Rotate(0,Input.GetAxis("Turning") * rotateSpeed * Time.deltaTime, 0);
   
	}
   
   //Apply gravity
   moveDirection.y -= gravity * Time.deltaTime;
   
   //Move controller
   var controller:CharacterController = GetComponent(CharacterController);
   var flags = controller.Move(moveDirection * Time.deltaTime);
   grounded = (flags  CollisionFlags.Below) != 0;
}

@script RequireComponent(CharacterController)

As you’ll notice forwards and backwards movement is smooth and easy, but turning is all hitched up. But if you turn while moving forward it’s smooth. I’ve read and re-read this code over 20 times now, it should work, it makes sense.

This data shed some light on the situation at all?

Thanks a bunch,

  • Luther

I fixed it!
Apparently the turn ‘hitching’ happens when you have a minimum move distance set on your character controller. Set it to 0 and the problem disappears.

Consider this problem solved (again).

I’ve modifed the walker script again so that character movement can be controlled by holding down the left mouse button and moving the mouse (as well as by arrow keys and WASD keys). This works well in the environments I create with Unity, which tend to be small interior spaces. Now it’s possible to move the character, pivot the camera, and click on things in the environment, all without removing your hand from the mouse. Here is the code (again, I don’t know that it is proper scripting, I just know that it works):

var gravity:float = 20.0; 
var walkSpeed:float = 3.0; 
var rotateSpeed:float = 100.0; 
var mouseTurnMultiplier = 0.1;
var mouseWalkMultiplier = 3;

private var grounded:boolean = false; 
private var moveDirection:Vector3 = Vector3.zero; 

function Update () 
{ 
  // Allow movement while grounded 
  if(grounded) 
  { 
        moveDirection = new Vector3(0,0,Input.GetAxis("Vertical")); 
        moveDirection = transform.TransformDirection(moveDirection) * walkSpeed; 
        transform.Rotate(0,Input.GetAxis("Horizontal") * rotateSpeed * Time.deltaTime, 0); 
            
     // Check to see if the right mouse button is pressed, and, if so, get horizontal rotation 
        if (Input.GetMouseButton(1) || Input.GetMouseButton(0)) 
        {
        x = Input.GetAxis("Mouse X") * rotateSpeed * mouseTurnMultiplier;
        transform.Rotate(0, x, 0); 
        } 
            
     // Check to see if the left mouse button is pressed, and, if so, get movement 
        if (Input.GetMouseButton(0)) 
        {
        moveDirection = new Vector3(0,0,Input.GetAxis("Mouse Y")); 
        moveDirection = transform.TransformDirection(moveDirection) * walkSpeed * mouseWalkMultiplier; 
        } 
    }
    
  //Apply gravity 
  moveDirection.y -= gravity * Time.deltaTime; 
   
  //Move controller 
  var controller:CharacterController = GetComponent(CharacterController); 
  var flags = controller.Move(moveDirection * Time.deltaTime); 
  grounded = (flags  CollisionFlags.Below) != 0; 
}

Try it out here:

http://www.medievalist.net/unityworlds/gsugallery.htm

Not bad, the only real issue now is the fact that if you’re holding down the mouse button to look around, you can’t move forward or backward until you release the mouse button. Gotta love scripting sometimes… if you figure out one problem, it opens up another almost every time, lol.

Actually, I tried setting it up so that if you held down both mouse buttons you could move and look around simultaneously using only the mouse, but the results were very disorienting. As it stands now, if you want to move and look around at the same time, you have to use a key to move and the mouse to look, which I think is an acceptable compromise.

I’ve read different parts in this script. I am using the Charactermotor and FPS Controller along with a Crouch&Run.

I just want to make it so the character turns/rotates instead of strafing.

Example: I hit “A” to turn left and like a person would turning a corner or what not the character actually faces that way. The camera is sitting where the head would be so it is like looking from the eyes.

How do I edit the script so it allows for the correct rotation.

I did a CMD + F in Monodevelop and there is so many parts that say rotation or rotate.

Since there have been a lot of changes to Unity over the intervening years, and this is the first result I see when I search for this issue, I wanted to update the solution with something that works with newer Unity versions (eg Unity 5, Unity 2017).

If you’re using the stock FPSController in your scene, you can change the key functions easily in the Edit>Project Settings>Input panel.

You can remove the “a” and “d” keys from the Strafing function by deleting them in the “Horizontal” entry. (By default, they’re under “Alt Negative Button”, “Alt Positive Button”.)

You can create a control for the rotation of the camera by right-clicking the “Mouse X” function and Duplicating it, then changing the new function to Type: Key or Mouse Button. Then put a and d in the Negative and Positive Button fields, respectively. You will want to turn Snap on and tweak the Gravity, Dead, and Sensitivity. I found Gravity of 1, Dead of .001, and Sensitivity of 0.5 worked well for my purposes.

Hope this helps future searchers!