- Home /
FlyCam and LookAt problem
This is my first question here, so please bear with me :). I also did some searches and seemed to find some issues similar to my problem, but they unfortunately weren't close enough to help me. Or perhaps I didn't know the correct terminology to look up.
I found a pretty good FlyCam script (found here: http://wiki.unity3d.com/index.php/FlyCam_Extended) that I use to control my camera, and it works great. I more or less pulled a "script kiddie with this, but I was able to tweak it a bit for my needs, but here is the code for it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
/*
EXTENDED FLYCAM
Desi Quintans (CowfaceGames.com), 17 August 2012.
Based on FlyThrough.js by Slin (http://wiki.unity3d.com/index.php/FlyThrough), 17 May 2011.
LICENSE
Free as in speech, and free as in beer.
FEATURES
WASD/Arrows: Movement
Q: Climb
E: Drop
Shift: Move faster
Control: Move slower
End: Toggle cursor locking to screen (you can also press Ctrl+P to toggle play mode on and off).
*/
public static PlayerController instance;
public float clickToSpeed = 0.5f;
public bool clickToLocation;
public Transform target;
public float cameraSensitivity = 200;
public float climbSpeed = 20;
public float normalMoveSpeed = 100;
public float slowMoveFactor = 0.25f;
public float fastMoveFactor = 3;
private float rotationX = 0.0f;
private float rotationY = 0.0f;
void Awake()
{
if (instance == null)
{
instance = this;
//DontDestroyOnLoad(gameObject);
}
else if (instance != this)
{
Destroy(gameObject);
}
}
void Start()
{
//Screen.lockCursor = true;
}
void Update()
{
if (Input.GetMouseButton(1))
{
rotationX += Input.GetAxis("Mouse X") * cameraSensitivity * Time.deltaTime;
rotationY += Input.GetAxis("Mouse Y") * cameraSensitivity * Time.deltaTime;
rotationY = Mathf.Clamp(rotationY, -90, 90);
transform.localRotation = Quaternion.AngleAxis(rotationX, Vector3.up);
transform.localRotation *= Quaternion.AngleAxis(rotationY, Vector3.left);
}
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
transform.position += transform.forward * (normalMoveSpeed * fastMoveFactor) * Input.GetAxis("Vertical") * Time.deltaTime;
transform.position += transform.right * (normalMoveSpeed * fastMoveFactor) * Input.GetAxis("Horizontal") * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
{
transform.position += transform.forward * (normalMoveSpeed * slowMoveFactor) * Input.GetAxis("Vertical") * Time.deltaTime;
transform.position += transform.right * (normalMoveSpeed * slowMoveFactor) * Input.GetAxis("Horizontal") * Time.deltaTime;
}
else
{
transform.position += transform.forward * normalMoveSpeed * Input.GetAxis("Vertical") * Time.deltaTime;
transform.position += transform.right * normalMoveSpeed * Input.GetAxis("Horizontal") * Time.deltaTime;
}
if (Input.GetKey(KeyCode.Q)) { transform.position += transform.up * climbSpeed * Time.deltaTime; }
if (Input.GetKey(KeyCode.E)) { transform.position -= transform.up * climbSpeed * Time.deltaTime; }
if (Input.GetKeyDown(KeyCode.End))
{
//Screen.lockCursor = (Screen.lockCursor == false) ? true : false;
}
GoToTarget();
}
private void GoToTarget()
{
if (clickToLocation)
{
transform.LookAt(target);
transform.position = Vector3.Lerp(transform.position, target.position, clickToSpeed);
}
// Check to see if the player has gotten to the location. If so, make clickToLocation false.
if (transform.position == target.position)
{
clickToLocation = false;
}
}
}
My issue comes in when I use transform.LookAt(target). I have an option for the user to click on an item from a panel and when they do, the camera looks towards the target, as it should, then Lerp is used to move them to it. All of that works great. The problem is that when I go to hold down the right-mouse button after this, the new direction I'm facing via LookAt isn't "known" by the variables connected to the mouse, so the camera quickly points back to the old direction that it "knows". So what I'd like to do is be able to update that information from what LookAt does. As I mentioned, I went very much "script kiddie" on this so I'm not entirely sure what is going on, but near as I can tell, I would think that if I can somehow get the info from LookAt into transform.localRotation, I'll be good to go:
transform.localRotation = Quaternion.AngleAxis(rotationX, Vector3.up);
transform.localRotation *= Quaternion.AngleAxis(rotationY, Vector3.left);
Am I in the ballpark on this? This feels right, but I just don't know how to get the information from the way the camera is facing from LookAt into transform.localRotation.
Thanks in advance!
Answer by Ady_M · Jan 08, 2019 at 03:52 PM
Does this fix your problem?:
if (clickToLocation)
{
transform.LookAt(target);
transform.position = Vector3.Lerp(transform.position, target.position, clickToSpeed);
// Calculating rotationX and rotationY after using LookAt
Vector3 vectorForwardAlongGround = transform.forward;
vectorForwardAlongGround.y = 0;
rotationX = Vector3.SignedAngle (vectorForwardAlongGround, transform.forward, transform.right);
rotationY = Vector3.SignedAngle (Vector3.forward, vectorForwardAlongGround, Vector3.up);
}
Updated on 10/1-2019:
It turns out you've been using rotationX to yaw and rotationY to pitch when it's the other way around in Unity. Rotating an object on its local X-axis should make it pitch up/down.
Use the following code for the input part.
Note that Mouse X now affects rotationY
and Mouse Y affects rotationX (also don't forget the minus sign here: rotationX -=).
This may feel unintuitive in the beginning, but you'll get used to it. It's important to respect the game engine's coordinate system otherwise it may confuse those who try to help and cause problems with the built-in functions.
if (Input.GetMouseButton(1))
{
rotationY += Input.GetAxis("Mouse X") * cameraSensitivity * Time.deltaTime;
rotationX -= Input.GetAxis("Mouse Y") * cameraSensitivity * Time.deltaTime;
rotationX = Mathf.Clamp(rotationX, -90, 90);
// A compact way of yawing (using rotationY) and then pitching (using rotationX)
transform.localRotation = Quaternion.Euler(rotationX, rotationY, 0);
}
In the GoToTarget method use the code from my first answer.
rotationX = Vector3.SignedAngle (vectorForwardAlongGround, transform.forward, transform.right);
rotationY = Vector3.SignedAngle (Vector3.forward, vectorForwardAlongGround, Vector3.up);
I've tested it and it works.
There are potentially two problems with your script besides the one you mentioned.
Problem 1: Vector3.Lerp
Are you intentionally using Lerp like that? It can make it difficult to control the speed. What it is actually doing is cutting the distance in half every frame because clickToSpeed is 0.5. If clickToSpeed were 0.1 then it would cut the distance by 10% very frame. But because the distance gets shorter after each frame (after each Lerp), 10% is not the same in units (cm, meters, feet, etc) as it was the frame before. In other words, your object slows down as it gets closer to the destination. If you did that on purpose then don't worry about it. But if it wasn't your intention then try using Vector3.$$anonymous$$oveTowards() ins$$anonymous$$d. Be aware, though, that it would make the movement linear.
Problem 2: Checking if the destination has been reached
Avoid doing if (float1 == float2) as it may yield unpredictable results (Vector3 consists of 3 floats: x, y and z). Ins$$anonymous$$d, you should check if the float value is less than. The float value to check in this case would be the distance between the object and the target.
// Check to see if the player has gotten to the location. If so, make clickToLocation false.
if (Vector3.Distance (transform.position, target.position) < 0.01f)
{
transform.position = target.position; // Unnecessary if you use Vector3.$$anonymous$$oveTowards above
clickToLocation = false;
}
Ok, now you've given me a conundrum LOL. Now I have to decide between slowing down when getting close (Lerp) or just going full speed using $$anonymous$$oveTowards (which I didn't know about so I learned something :) ).
As far as your advice to avoid if (float1 == float2), I see what you mean, but I wasn't sure of any other way to get my object to keep heading that way. When I did to turn it off was put the sphere colliders on them which are bigger than their float coordinates so the Lerp stops well before reaching them anyway and also makes clickToLocation = false; To be honest, I think your $$anonymous$$oveTowards() would allow me to avoid all of that hassle with colliders and triggers, messing with clickToLocation, etc.
And I wasn't sure if I could go with something like less than with those floats. I wasn't sure if something would act goofy with it depending on which direction you are co$$anonymous$$g from, you may end up with your own location already being negative in relation to where you're wanting to go. Does that make sense? Or is that something I wouldn't have had to worry about?
Unfortunately no. Once I right-click, it faces me in an entirely different direction than I was facing before I moved to the target. On the plus side, it managed to actually change where to face on the right-click (something I haven't been able to manage myself), so I think this is heading somewhere. It just didn't change it correctly to the way I was facing after the move.
I've been tinkering around with the code you gave. I "think" I have half of the problem solved. I seem to have rotationX figured out, but so far I haven't been able to get rotationY figured out. Here is my code so far (based on yours with rotationY commented out since it isn't working). Any advice on rotationY? I'm slowly grasping what is going on here, but it is VERY new to me so for now I am blindly making changes here and there hoping that they work. That is pretty much happened with my "fix" for rotationX.
Vector3 direction = target.position - transform.position;
direction.y = 0;
float angleX = Vector3.SignedAngle(direction, transform.forward, Vector3.up);
//float angleY = Vector3.SignedAngle(direction, transform.right, Vector3.left);
transform.rotation = Quaternion.LookRotation(direction);
rotationX -= angleX;
//rotationY -= angleY;
//rotationY = Vector3.SignedAngle(Vector3.forward, direction, Vector3.up);
Works now! Thank you!! The rotationX to yaw and rotationY to pitch came from the script I was using. Guess that's what happens when I "script kiddie" something like this...when something doesn't work, I don't know enough to effectively troubleshoot. Tell me, I was having a terrible time getting things to line up on x and y. Could this be due to the way the original script was written? For example, my tinkering after adding my latest comment gave me better results, but nowhere near as perfect as what you just gave me. The y was still janky, but just not as bad. But while I was poking around, I noticed that I was working with x in the inspector for rotationY and y in the inspector for rotationX. It was pretty confusing.
This is my first foray into 3D program$$anonymous$$g. Up until now I've been doing relatively simple 2D stuff in Unity. But one thing is clear...even though I learned a lot fumbling through this, I definitely need to find a good place to learn more about this. Any recommendations? I've been reading up on the documentation, but it isn't the same as a good tutorial.
Again, thank you VERY much!
Answer by badadam · Jan 10, 2019 at 08:05 PM
You can use Animation tab. You can add animation to your camera by pressing record button after you select your final time and change the rotation and position values of the camera without writing code in a few minutes.
Your answer
Follow this Question
Related Questions
How do I make an object in game face the direction it's traveling while viewing from above? 1 Answer
NavMesh Lookat causes jittery glitch in character 0 Answers
Transform.LookAt() when transform.forward is offset 1 Answer
Firing a projectile from orbit 1 Answer
Problems with using gravity and also MoveTowards/LookAt functions 2 Answers