unity - camera movement - c#

I'm reviewing this piece of code about camera movement:
using UnityEngine;
using System.Collections;
public class CamMove : MonoBehaviour {
Vector2 mouseLook;
Vector2 smoothV;
public float sensitivity=5.0f;
public float smoothing = 2.0f;
GameObject character;
void Start()
{
//moving left and right
character = this.transform.parent.gameObject;
}
void Update()
{
var md = new Vector2 (Input.GetAxisRaw ("Mouse X"), Input.GetAxisRaw ("Mouse Y")); //mouse movement
md = Vector2.Scale (md, new Vector2 (sensitivity * smoothing, sensitivity * smoothing));
smoothV.x = Mathf.Lerp (smoothV.x, md.x, 1f / smoothing);
smoothV.y = Mathf.Lerp (smoothV.y, md.y, 1f / smoothing);
mouseLook += smoothV;
if(mouseLook.y>-40 && mouseLook.y<60)
transform.localRotation = Quaternion.AngleAxis (-mouseLook.y, Vector3.right);
character.GetComponent<CharacterController>().transform.localRotation = Quaternion.AngleAxis (mouseLook.x, character.transform.up);
}
}
how does he get every new locatiion ? With Math.Lerp interpolation ? Also I can't understand the part md = Vector2.Scale (md, new Vector2 (sensitivity * smoothing, sensitivity * smoothing));
Also the part :
if(mouseLook.y>-40 && mouseLook.y<60)
transform.localRotation = Quaternion.AngleAxis (-mouseLook.y, Vector3.right);

Well
var md = new Vector2 (Input.GetAxisRaw ("Mouse X"), Input.GetAxisRaw ("Mouse Y")); //mouse movement
is even commented mouse movement. As usual a better variable name would already explain it. It would be better called mouseDelta. So the code not using a fixed mouse position but rather the traveled dinstance since last frame. (See Input.GetRawAxis)
Then
md = Vector2.Scale (md, new Vector2 (sensitivity * smoothing, sensitivity * smoothing));
Vector2.Scale is scaling up or down this Vector. You could also write it as
md = new Vector2 (md.x * sensitivity * smoothing, md.y * sensitivity * smoothing);
Actually it is quite unnecessary because you could write it way simplier like this:
md *= sensitivity * smoothing;
Then Mathf.Lerp is a linear interpolation between the two given positions using a certain factor between 0 and 1, where 0 would be fully the first parameter, 1 fully the second, otherwise anything in between. E.g. a factor 0.5 would result in the center between both values so this depends on your given value for smoothing
smoothV.x = Mathf.Lerp (smoothV.x, md.x, 1f / smoothing);
smoothV.y = Mathf.Lerp (smoothV.y, md.y, 1f / smoothing);
Again this is written quite unnecessarily because it is better written directly using Vector2.Lerp
smoothV = Vector2.Lerp(smoothV, md, 1f/smoothing);
But actually I'm not quite sure this even does what you would expect since this is no absolute value but something you later on add every frame so using a Lerp on it makes not much sense to me anyway... I would probably directly use Lerp for moving the current mouseLoock towards the new target value.
Finally you do
mouseLook += smoothV;
if(mouseLook.y>-40 && mouseLook.y<60)
transform.localRotation = Quaternion.AngleAxis (-mouseLook.y, Vector3.right);
character.GetComponent<CharacterController>().transform.localRotation = Quaternion.AngleAxis (mouseLook.x, character.transform.up);
Which updates two rotations. There is nothing in your code assigning any new position at all ...
Besides that the frame wise usage of GetComponent is extremely inefficient, you should rather store that reference once (e.g. in Start) and reuse it later.

Related

Interpolate Between Quaternion.Slerp for rotate around player & Vector3.Lerp to keerp camera chasing the player

im having an issue with the rotation of the camera around player getting too much damping. i do try to manipulate those speed value but it doesnt work at all. i think thats need something combination between slerp and lerp together, and i just dont know how. Btw im trying to create a third person game and the camera scripts make me stuck, and also i try to create my own scripts rather than using cinemachine. Really hope u guys can help me out.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Camera : MonoBehaviour
{
public float MouseX, MouseY;
public Transform Player;
public float SmoothTime;
public float DefaultZoom = 5f;
public float ZoomSpeed;
Vector3 NewPosition;
Quaternion NewRotation;
//----------------------------------------------------------------------------------------------------
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
Player = GameObject.Find("Player").transform;
}
//----------------------------------------------------------------------------------------------------
void Update()
{
MouseX += Input.GetAxis("Mouse X") * 100f * Time.deltaTime;
MouseY -= Input.GetAxis("Mouse Y") * 100f * Time.deltaTime;
MouseY = Mathf.Clamp(MouseY, -75, 75);
DefaultZoom += Input.GetAxis("Mouse ScrollWheel") * 100f * Time.deltaTime;
DefaultZoom = Mathf.Clamp(DefaultZoom, 3f, 9f);
DefaultZoom = Mathf.Lerp(Vector3.Distance(transform.position, Player.position), DefaultZoom, ZoomSpeed * Time.deltaTime);
}
void LateUpdate()
{
NewRotation = Quaternion.Euler(MouseY, MouseX, 0f);
transform.rotation = Quaternion.Slerp(transform.rotation, NewRotation, Time.deltaTime * SmoothTime);
NewPosition = Player.position - (transform.forward * DefaultZoom);
transform.position = Vector3.Lerp(transform.position, NewPosition, Time.deltaTime * SmoothTime);
}
}
You are using Lerp wrong, both for rotation and position. Lerp isn't actually intended for smoothing, but is used for that purpose never the less, since it is very quick to program.
To fix your problem quickly (tho at the cost of being frame rate dependent), simply remove the Time.deltaTime and make sure that the smooth variable is kept between 0 and 1. The underlying reason is that Time.deltaTime is jumpy (or jittery) by nature and it passes that property directly to Lerp.

Tilt first person camera's z axis while turning

I am working on controls for a bipedal tank, that has a boost/dash feature.
When the boost/dash mode is activated by holding a button the tank changes from a traditional WASD strafing movement to something like a jet fighter but on ground.
Now when the player tries to turn while boosting the camera needs to tilt with the turning direction to make it feel smoother.
Here is a video clip showing my problem
This is the script added to the main camera
public class PlayerCameraTilt : MonoBehaviour
{
private Camera viewCam;
private CharacterController characterController;
public float tiltAmount = 10f;
private void Start()
{
viewCam = GetComponent<Camera>();
characterController = GetComponentInParent<CharacterController>();
}
private void Update()
{
if (characterController.GetComponent<PlayerController>().movementState == MovementModes.boost)
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 euler = transform.localEulerAngles;
euler.z = Mathf.Lerp(euler.z, -inputValue, 5f * Time.deltaTime);
transform.localEulerAngles = euler;
}
else
{
Vector3 euler = transform.localEulerAngles;
euler.z = Mathf.Lerp(euler.z, 0f, 5f * Time.deltaTime);
transform.localEulerAngles = euler;
}
}
}
It kind of works, BUT only in one rotation direction, namely the positive one. If i turn the other way the angle becomes negative and does a whole 360 degree rotation until it ends up at 0 degrees.
I tried around with the built in Quaternion methods like Quaternion.Rotate() or Quaternion.AngleAxis()
but they didn't work because Quaternion.Rotate() doesn't tilt the Camera but completely rotates it. Quaternion.AngleAxis() works with the whole tilt angle limit but also does something to the other axis which i don't want to modify.
Is there a way to prevent the camera from doing a complete 360° rotation when the axis input becomes negative and just tilt in a slight angle?
Wraparound issues aside, the z component is "applied" first among the euler components, so I wouldn't modify it directly like that in this application.
Instead, I would convert the input to an angle to lean, rotate the direction of the local up before localRotation is applied (Quaternion.Inverse(transform.localRotation) * transform.up) by that angle, then use Quaternion.LookRotation to find a rotation with the current forward and that up, then use Quaternion.Slerp to slerp toward that rotation:
private void Update()
{
if (characterController.GetComponent<PlayerController>().movementState == MovementModes.boost)
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 upOfParent = Quaternion.Inverse(transform.localRotation) * transform.up;
Vector3 upDir = Quaternion.AngleAxis(-inputValue, transform.forward) * upOfParent;
Quaternion goalRot = Quaternion.LookRotation(transform.forward, upDir);
transform.rotation = Quaternion.Slerp(transform.rotation, goalRot, 5f * Time.deltaTime);
}
else
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 upOfParent = Quaternion.Inverse(transform.localRotation) * transform.up;
Quaternion goalRot = Quaternion.LookRotation(transform.forward, upOfParent);
transform.rotation = Quaternion.Slerp(transform.rotation, goalRot, 5f * Time.deltaTime);
}
}
Just keep in mind that using t = 5f * Time.deltaTime for lerp/slerp operations does not always guarantee that the output will ever equal the to parameter.

Unity3D - Third Person Player movement in diagonal direction is slower than vertical and horizontal movement?

I'm trying to create a character controller that allows the movement speed to be affected by how far the player is pressing the left stick. When I can get that much to work, the other issue I encounter is the players speed decreasing while moving diagonally.
Most of the information I've found online deal with the opposite issue where diagonal movement is faster (movement vector goes above 1). The solution there would be to clamp the magnitude so pressing the stick into a corner doesn't go above 1. I've tried clamping to see if that would work, however, I don't believe that's the solution to my problem.
The code here allows the player to move in relation to the camera and the speed is influenced by input vector.
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 inputDir = input.normalized;
void Move(Vector3 inputDir, Vector3 input)
{
running = Input.GetKey(KeyCode.Joystick1Button1); // B button on xbox one controller
// Rotation stuff
if (inputDir != Vector3.zero) {
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
}
// Pretty sure this is where things are breaking
float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude;
currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
currentSpeed *= input.magnitude;
vel = transform.forward * currentSpeed + Vector3.up * velocityY;
currentSpeed = new Vector3(_controller.velocity.x, 0, _controller.velocity.z).magnitude;
}
void ExecuteMovement()
{
_controller.Move(vel * Time.deltaTime);
}

Unity: Add strafing to Player Controller

For a few weeks, I've been trying to add strafing to my Player. I now have a working Script but when I try using the Mouse to Rotate the Player and the Camera the script is reacting wierd. When the Player is facing towards the Z-direction the Player is walking and running normaly, but as soon as I turn him around he slows down and moves super slow. Here's the code:
void Update () {
// input
Vector2 input = new Vector2 (Input.GetAxisRaw ("Horizontal"), Input.GetAxisRaw ("Vertical"));
Vector2 inputDir = input.normalized;
bool running = Input.GetKey (KeyCode.LeftShift);
if (!Input.GetMouseButtonDown(0) || Input.GetKey(KeyCode.JoystickButton2)) {
Move (inputDir, running);
Time.timeScale = 1;
}
if (Input.GetKeyDown (KeyCode.Space) || Input.GetKeyDown (KeyCode.JoystickButton0)) {
Jump ();
}
// animator
float animationSpeedPercent = ((running) ? currentSpeed.magnitude / runSpeed : currentSpeed.magnitude / walkSpeed * .5f);
void Move(Vector2 inputDir, bool running) {
float targetRotation = cameraT.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
Vector2 targetSpeed = new Vector2(
((running) ? runSpeed : walkSpeed) * inputDir.normalized.x,
((running) ? runSpeed : walkSpeed) * inputDir.normalized.y);
currentSpeed = Vector2.SmoothDamp(currentSpeed, targetSpeed,
ref speedSmoothVelocity,
GetModifiedSmoothTime(speedSmoothTime));
velocityY += Time.deltaTime * gravity;
Vector3 velocity = (transform.forward * currentSpeed.y) +
(transform.right * currentSpeed.x) +
Vector3.up * velocityY;
controller.Move(velocity * Time.deltaTime);
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z);
if (controller.isGrounded)
{
velocityY = 0;
}
}
In order to allow a player to strafe in the default PlayerController, a couple edits must be made. I won't write the code for you, but here are the steps you need to take, they should be relatively easy to implement:
First, you need to comment out the code related to rotation
Then rather than obtaining the target speed as a scalar float, you should either calculate it as a Vector2 or as 2 separate floats (preferably the former)
Vector2 targetSpeed = new Vector2(
((running) ? runSpeed : walkSpeed) * inputDir.normalized.x,
((running) ? runSpeed : walkSpeed) * inputDir.normalized.y);
currentSpeed is a float, that relies on targetSpeed, but rather than~~ adjusting currentSpeed to also be a Vector2, you can use targetSpeed.magnitude when calculating the SmoothDamp
Since you need speed in both the Z context and the X context, you should make currentSpeed a Vector2 Thankfully there already exists a Vector2.SmoothDamp so that should be easy to refactor.
currentSpeed = Vector2.SmoothDamp(currentSpeed, targetSpeed,
ref speedSmoothVelocity,
GetModifiedSmoothTime(speedSmoothTime));
You need to include the X component in the velocity calculation. (remember that a Vector2's X and Y correspond to X and Z respectively on a Vector3)
Vector3 velocity = (transform.forward * currentSpeed.y) +
(transform.right * currentSpeed.x) +
Vector3.up * velocityY;
Finally, you want to adjust the currentSpeed to include the appropriate X, and Z velocities. This can be done simply by calculating the magnitude based on a Vector3 instead of a Vector2
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z);
By the way, welcome to StackOverflow - In the future you want to be sure to include a minimal, verifiable, and complete example, and not just the default unmodified code.
People are more willing to help if you've shown that you've made an effort yourself.

Rotate towards the mouse

I want to rotate a cube towards the mouse position (in 2d) in unity (c#). I have this code, but i doesn't work:
Vector3 a = GameObject.FindGameObjectWithTag("GunTop").transform.position;
Vector3 b = GameObject.FindGameObjectWithTag("GunBottom").transform.position;
Vector3 c = new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x);
float aTob = Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, GameObject.FindGameObjectWithTag("GunBottom").transform.position);
float aToc = Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x));
float bToc = Vector3.Distance(GameObject.FindGameObjectWithTag("GunBottom").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x));
float winkel = Mathf.Acos(((Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, GameObject.FindGameObjectWithTag("GunBottom").transform.position) * Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, GameObject.FindGameObjectWithTag("GunBottom").transform.position)) + (Vector3.Distance(GameObject.FindGameObjectWithTag("GunBottom").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x)) * Vector3.Distance(GameObject.FindGameObjectWithTag("GunBottom").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x))) - (Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x)) * Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x)))) / (2 * Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, GameObject.FindGameObjectWithTag("GunBottom").transform.position) * Vector3.Distance(GameObject.FindGameObjectWithTag("GunBottom").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x))));
private void Update()
{
transform.Rotate(0, 0, winkel);
}
GunTop and GunBottom are the highest an lowest points in the middle of the cube, to create a triangle with the mouse.Does someone see the problem?
Edit1:
Okay, good news. Now I have to ways to solve the Problem. First one like memBrain sad(i changed it a bit):
` private void Update()
{
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - pos;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, 0, angle - 90);
}`
I had to change it a bit in order to work with unity. The second solution, i could optimize my code, so it works now:
` float winkel;
float echterWinkel;
float aTob;
float aToc;
float bToc;
private void Update()
{
Vector3 a =
GameObject.FindGameObjectWithTag("GunTop").transform.position;
Vector3 b =
GameObject.FindGameObjectWithTag("GunBottom").transform.position;
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - pos;
aTob = Vector3.Distance(a, b);
aToc = Vector3.Distance(a, dir);
bToc = Vector3.Distance(b, dir);
winkel = Mathf.Acos(((aTob * aTob) + (bToc * bToc) - (aToc * aToc)) / (2
* aTob * bToc));
echterWinkel = Mathf.Rad2Deg * winkel;
if (dir.x < 0)
{
transform.rotation = Quaternion.Euler(0, 0, echterWinkel);
}
else
{
transform.rotation = Quaternion.Euler(0, 0, -echterWinkel);
}
}
}`
Okay, so far so good. But I have to questions now. Obviously the first solution is the better one, so I want to know how it works, what does "Mathf.Atan2()"?
And now the seconds question: Why do I have to do this
`Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - pos;`
in order to get the real mouesposition?
Your code seems needlessly complicated to me. Is there a particular reason you need all those vectors? Nevertheless, the following code is an example from the Unity forum (http://answers.unity3d.com/questions/731922/rotate-object-to-face-mouse-2d-rotate-on-z-axis.html) that you can use to rotate an object around the z-axis.
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - pos;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
It's really as simple as that.
Edit: Keep in mind that the transform will be the transform of your target GameObject.
On a side note, the non-functioning code in your example is rather inefficient. You have declared vectors that you don't use, and floats you don't use. So you can replace a lot of your code using these stored values instead.
Vector3 a = GameObject.FindGameObjectWithTag("GunTop").transform.position;
Vector3 b = GameObject.FindGameObjectWithTag("GunBottom").transform.position;
Vector3 c = new Vector3 (
Input.mousePosition.x,
Input.mousePosition.y,
Input.mousePosition.x
);
float aTob = Vector3.Distance(a, b);
float aToc = Vector3.Distance(a, c);
float bToc = Vector3.Distance(b, c);
float winkel = Mathf.Acos (
(
(
aTob * aTob
) +
(
bToc * bToc
) -
(
aToc * aToc
)
) /
(
2 * aTob * bToc
)
);
private void Update()
{
transform.Rotate(0, 0, winkel);
}
Note how much simpler the code now looks after properly using your stored values. I also took the liberty of using the parenthesis like curly braces to make long lines of code more readable. You need to keep one thing in mind. Lookups are potentially expensive. Never do a lookup more than once if you don't have to.
I haven't bothered checking to see if the code is correct. I can't even verify that your lookups will return a value. In fact, that is potentially going to be a problem for this code. If either lookup fails, you will have null values stored in either a or b or both! You need some kind of conditional check to verify that a and b had successful lookups, and what to do if they didn't. As it now stands, you are going to have a failure the moment you try to perform an operation on either a or b the first time they are used. The objective is to fail gracefully, not forcefully.
Edit 2: Looking over everything again, I noticed one other problem. Your intention is to rotate around the z-axis, and you are setting the z-coordinate to Input.mousePosition.x. This is wrong. You should set it to a fixed value, like '0'. You don't want z tied to x.
Edit 3: Here is the information provided by the Unity Scripting API for Mathf.Atan2(...).
Yes. those two lines are related to obtaining the mouse position. Let me explain what is happening here.
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
This line converts the world coordinate (transform.position) of your "gun" object to a screen point coordinate and stores it in pos.
Vector3 dir = Input.mousePosition - pos;
This line takes the mousePosition vector and subtracts from it the position vector (pos) from before, and stores it in dir (presumably for "direction"). You could just as easily change your Vector3 c as follows:
Vector3 c = Input.mousePosition;
This is equivalent to what you did without needing to create the new Vector3 and assigning it the vector coordinates explicitly as you did.
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
This line replaces winkel. Consequently, your entire code can be condensed as follows:
GameObject gun;
void Start()
{
/*
initialization code for gun GameObject
*/
}
void Update()
{
Vector3 gunScreenPosition = Camera.main.WorldToScreenPoint(gun.transform.position);
Vector3 mouseDirection = Input.mousePosition - gunScreenPosition;
float angle = Mathf.Atan2(mouseDirection.y, mouseDirection.x) * Mathf.Rad2Deg;
gun.transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
You really don't need all the extra code you were using. Is there a particular reason you needed GunTop and GunBottom? If it's just to calculate the position for the gun, my example is all that you need.
I'd be remiss if I didn't share one more thing. Be careful when using Quaternion.Euler(...). It can suffer from a condition known as "gimbal lock"

Categories

Resources