Camera Flythrough Script Movement Limitations

I have a flycam script I am using to record trailer footage, and I am having difficulty getting full range of movement from it as it uses the mouses movement for camera movement, so when the mouse cursor reaches the edge of the monitor, it locks up, preventing me from moving the camera further.

As you can see from line 25 in the code below, I have tried to lock the cursor, but as the script uses that for movement, it just jitters around as it fights the system.

I am sure this has a very simple fix, but I am at a loss (coders block from staring at it for the last 2 hours).

Apologies for any spelling errors in the post, I am English but am on strong pain killers and accurately typing is a little difficul;t…

Any ideas on how I can fix this issuue?

using UnityEngine;
using System.Collections;

public class FlyCam : MonoBehaviour {
   
   
    public float speed = 50.0f;        // max speed of camera
    public float sensitivity = 0.25f;         // keep it from 0..1
    public bool inverted = false;
   
   
    private Vector3 lastMouse = new Vector3(255, 255, 255);
   
   
    // smoothing
    public bool smooth = true;
    public float acceleration = 0.1f;
    private float actSpeed = 0.0f;            // keep it from 0 to 1
    private Vector3 lastDir = new Vector3();
   
   
    // Use this for initialization
    void Start () {
        Cursor.visible = false;
        //Cursor.lockState = CursorLockMode.Locked;
    }
   
    // Update is called once per frame
    void FixedUpdate () {
       
       
        // Mouse Look
        lastMouse = Input.mousePosition - lastMouse;
        if ( ! inverted ) lastMouse.y = -lastMouse.y;
        lastMouse *= sensitivity;
        lastMouse = new Vector3( transform.eulerAngles.x + lastMouse.y, transform.eulerAngles.y + lastMouse.x, 0);
        transform.eulerAngles = lastMouse;
        lastMouse = Input.mousePosition;
       
       
       
       
       
        // Movement of the camera
       
        Vector3 dir = new Vector3();            // create (0,0,0)
       
        if (Input.GetKey(KeyCode.W)) dir.z += 1.0f;
        if (Input.GetKey(KeyCode.S)) dir.z -= 1.0f;
        if (Input.GetKey(KeyCode.A)) dir.x -= 1.0f;
        if (Input.GetKey(KeyCode.D)) dir.x += 1.0f;
        dir.Normalize();
       
       
        if (dir != Vector3.zero) {
            // some movement
            if (actSpeed < 1)
                actSpeed += acceleration * Time.deltaTime * 40;
            else
                actSpeed = 1.0f;
           
            lastDir = dir;
        } else {
            // should stop
            if (actSpeed > 0)
                actSpeed -= acceleration * Time.deltaTime * 20;
            else
                actSpeed = 0.0f;
        }
       
       
       
       
        if (smooth)
            transform.Translate( lastDir * actSpeed * speed * Time.deltaTime );
       
        else
            transform.Translate ( dir * speed * Time.deltaTime );
       
       
    }
   
    void OnGUI() {
        //GUILayout.Box ("actSpeed: " + actSpeed.ToString());
    }
}

Have you already considered taking a look at Timeline / Cinemachine? Or isn’t that an option? If not, you could also try to use the usual animation tools. Either way it will look much smoother and likely more professional, since you do not rely on “live controls” of the camera (this tends to look wonky / shaky even when you smooth it - it’s just not perfect).

These are options however, the way I have set up my scenes, I would need to fundamentally change each scene to encorporate such a system, which is completely my fault, but we don’t really have the time to do so, so we have altered things like triggers in duplicate scenes to respond to the camera instead and our plan was to use simple controls such as straffing and moving forward/backward as to not do anything too jittery such as diagonal movement.

An animation system would have been better but we don’t really have the time for that at the moment, perhaps later.

We just need to get the current system we have working properly, then once we have A trailer, we can worry about making a better one.

Certainly not the best way of doing so, but we have run out of time to implement a better system for this.

AFAIK, Unity’s current system won’t ever return positions that are outside of your physical monitor(s).

So the first thing you might wanna try is using GetAxis and do the corresponding math yourself. This can be a little inaccurate though. You probably need some information about the resolution and combine it with the value returned by GetAxis().
You also need to make sure that the WASD keys are not mapped to the axis input in the InputManager in order to avoid conflicts with the other controls.

Next limitation will be the physical surface that you’re moving the mouse on. :stuck_out_tongue:

As the game is PC only with no controller support (currently), we don’t need to tie it into the inputmanager, for now anyway.

We just need to fix this issue. Our player character has a similar setup but the input doesn’t seem to work for this.

Perhaps you could have “edge zones”, that while the cursor is inside a small margin around your screen it will continue to move the camera.

What would be the best way to implement such a system? I am unable to find any examples of this, perhaps I am using the wrong search terms.

From a quick search: Move the camera then the cursor is at the screen edge - Questions & Answers - Unity Discussions

My search was “move camera mouse edge screen unity” lol

I think that’s a good solution if you’re able to use constant speed. From what I’ve read, he might still want to be able to control the speed using the mouse.

Anyway, I’m still curious why locking the cursor doesn’t work for you. Have you only tested this in editor or have you actually built the application? You might wanna try it in a build.

I’m not going to test this to see, but perhaps the axis of Mouse X / Y would work better in locked mode than the code. I use code like that for a camera, with a locked cursor and don’t experience any “fighting for position”, even in the editor.

1 Like

That’s what I meant in the post when I spoke about GetAxis… There’s no point in using the mouse position when the cursor is locked, because there won’t be any change in mouse position.

Oops. I read that the first time I was responding, but had forgotten (and not re-read it) when I replied last :slight_smile:

No problem, perhaps it helps to understand what I actually meant, because the response (quoted below) kinda reads as if he refused to try it out, because he “just needs a fix”… but that would actually be the best fix imo.

I have made a few modifications, using Windexglows FlyCam script from 2010, which seems to work well, but has the same problems that the previous version did, the rotation is locked to the cursor MOVEMENT and not it’s axis movement, in a complex mathematical way, difficult to change.

I have tried hiding and locking the cursor several times in different ways but always get the same problem, as the cursor isn’t allowed to move, neither does the camera.

I have tried using the axis for movement instead, but it still doesn’t work.

using UnityEngine;
using System.Collections;

public class FlyCam : MonoBehaviour {


    /*
    Writen by Windexglow 11-13-10.  Use it, edit it, steal it I don't care. 
    Converted to C# 27-02-13 - no credit wanted.
    Simple flycam I made, since I couldn't find any others made public. 
    Made simple to use (drag and drop, done) for regular keyboard layout 
    wasd : basic movement
    shift : Makes camera accelerate
    space : Moves camera on X and Z axis only.  So camera doesn't gain any height*/


    public float mainSpeed = 2.0f; //regular speed
    public float shiftAdd = 0.2f; //multiplied by how long shift is held.  Basically running
    public float maxShift = 1000.0f; //Maximum speed when holdin gshift
    public float camSens = 0.25f; //How sensitive it with mouse
    public bool rotateOnlyIfMousedown = true;
    public bool movementStaysFlat = true;
    public bool hideCursor = true;

    public Texture2D blankCursor;

    private Vector3 lastMouse = new Vector3(255, 255, 255); //kind of in the middle of the screen, rather than at the top (play)
    private float totalRun = 1.0f;

    void Awake()
    {
        Debug.Log("FlyCamera Awake() - RESETTING CAMERA POSITION"); // nop?
                                                                    // nop:
                                                                    //transform.position.Set(0,8,-32);
                                                                    //transform.rotation.Set(15,0,0,1);
        transform.position = new Vector3(0, 8, -32);
        transform.rotation = Quaternion.Euler(25, 0, 0);

        blankCursor = Resources.Load("Cursor_Blank") as Texture2D;
        Cursor.SetCursor(blankCursor, Vector2.zero, CursorMode.Auto);

        Cursor.lockState = CursorLockMode.Locked;
    }


    void Update()
    {
        //This doesn't work!

        /*
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            hideCursor = !hideCursor;
            if(hideCursor)
            {
                Cursor.SetCursor(blankCursor, Vector2.zero, CursorMode.Auto);
            }
            else
            {
                Cursor.SetCursor(Resources.Load("Cursor_Arrow") as Texture2D, Vector2.zero, CursorMode.Auto);
            }
        }
        */


        if (Input.GetMouseButtonDown(1))
        {
            //lastMouse = Input.mousePosition; // $CTK reset when we begin
            lastMouse = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")); //Still not working well
        }

        if (!rotateOnlyIfMousedown ||
            (rotateOnlyIfMousedown && Input.GetMouseButton(1)))
        {
            lastMouse = Input.mousePosition - lastMouse;
            lastMouse = new Vector3(-lastMouse.y * camSens, lastMouse.x * camSens, 0);
            lastMouse = new Vector3(transform.eulerAngles.x + lastMouse.x, transform.eulerAngles.y + lastMouse.y, 0);
            transform.eulerAngles = lastMouse;
            lastMouse = Input.mousePosition;
            //Mouse  camera angle done. 
        }

        //Keyboard commands
        float f = 0.0f;
        Vector3 p = GetBaseInput();
        if (Input.GetKey(KeyCode.LeftShift))
        {
            totalRun += Time.deltaTime;
            p = p * totalRun * shiftAdd;
            p.x = Mathf.Clamp(p.x, -maxShift, maxShift);
            p.y = Mathf.Clamp(p.y, -maxShift, maxShift);
            p.z = Mathf.Clamp(p.z, -maxShift, maxShift);
        }
        else
        {
            totalRun = Mathf.Clamp(totalRun * 0.5f, 1f, 1000f);
            p = p * mainSpeed;
        }

        p = p * Time.deltaTime;
        Vector3 newPosition = transform.position;
        if (Input.GetKey(KeyCode.Space)
            || (movementStaysFlat && !(rotateOnlyIfMousedown && Input.GetMouseButton(1))))
        { //If player wants to move on X and Z axis only
            transform.Translate(p);
            newPosition.x = transform.position.x;
            newPosition.z = transform.position.z;
            transform.position = newPosition;
        }
        else
        {
            transform.Translate(p);
        }

    }

    private Vector3 GetBaseInput()
    { //returns the basic values, if it's 0 than it's not active.
        Vector3 p_Velocity = new Vector3();
        if (Input.GetKey(KeyCode.W))
        {
            p_Velocity += new Vector3(0, 0, 1);
        }
        if (Input.GetKey(KeyCode.S))
        {
            p_Velocity += new Vector3(0, 0, -1);
        }
        if (Input.GetKey(KeyCode.A))
        {
            p_Velocity += new Vector3(-1, 0, 0);
        }
        if (Input.GetKey(KeyCode.D))
        {
            p_Velocity += new Vector3(1, 0, 0);
        }
        return p_Velocity;
    }
}

That’s because it does still rely on mouse position. Like mentioned earlier, mousePosition does only return positions that are contained by the physical screen (locked : wherever mouse was locked, confined: game view /window, none: physical screen, i.e. also outside of the game view).

You should really use GetAxis, that’s how first person controllers are usually made

… which does make sense, as you only rely on mouse position. Cursor locked => mouse position stays the same.

Can you post the code you were using for that?

Stop doing all of your own silly calculations involving Input.mousePosition and just use GetAxis(“Mouse X”) and GetAxis(“Mouse Y”). They already calculate the delta (difference) from last frame.

pitch += Input.GetAxisRaw("Mouse Y") * sensitivity * Time.deltaTime;
yaw += Input.GetAxisRaw("Mouse X") * sensitivity * Time.deltaTime;

pitch = Mathf.Clamp(pitch, -85f, 85f);

camera.transform.rotation = Quaternion.Euler(-pitch, yaw, 0f);
1 Like