In my project I currently have 1 script, 1 camera, and 1 object. I wrote a code (see below) that will rotate the camera around the object but X axis is not always left and right, and Y axis is not always up and down.
What I mean by this is, if for example, we load into unity with the camera axis being Y = up, X = left, Z being forward as the transform position and the object having the same orientation as the camera. I can click and drag left or right/ up or down and the camera will rotate with it correctly, but as soon as I rotate it 90 DEG any direction, the opposite axis will not rotate as I want it to.
I know there is a term for this "perfect rotation" but I do not know the term , thus it is very difficult for me to explain. Sorry. Please feel free to enter the code into a C# script and play around with it. add the script to the camera, make sure the camera is looking at the object, and the object needs to be added to the gameobject on the script.
can someone please help me so that when I swipe left, the object will always rotate left, right for right, up for up and down for down.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FocusObject : MonoBehaviour
{
public GameObject target;//the target object
private Vector3 point;//the coord to the point where the camera looks at
float f_lastX = 0.0f;
float f_difX = 0.0f;
float f_lastY = 0.0f;
float f_difY = 0.0f;
void Start()
{//Set up things on the start method
point = target.transform.position;//get target's coords
transform.LookAt(point);//makes the camera look to it
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
f_difX = 0.0f;
f_difY = 0.0f;
}
else if (Input.GetMouseButton(0))
{
f_difX = Mathf.Abs(f_lastX - Input.GetAxis("Mouse X"));
f_difY = Mathf.Abs(f_lastY - Input.GetAxis("Mouse Y"));
if (f_lastX < Input.GetAxis("Mouse X"))
{
transform.RotateAround(point, new Vector3(0.0f, 1.0f, 0.0f), f_difX);
}
if (f_lastX > Input.GetAxis("Mouse X"))
{
transform.RotateAround(point, new Vector3(0.0f, -1.0f, 0.0f), f_difX);
}
if (f_lastY < Input.GetAxis("Mouse Y"))
{
transform.RotateAround(point, new Vector3(1.0f, 0.0f, 0.0f), f_difY);
}
if (f_lastY > Input.GetAxis("Mouse Y"))
{
transform.RotateAround(point, new Vector3(-1.0f, 0.0f, 0.0f), f_difY);
}
f_lastX = -Input.GetAxis("Mouse X");
f_lastY = -Input.GetAxis("Mouse Y");
}
}
}
Related
Right now, I am working on the Unity camera for first person view using mouse.
Camera is suppose to move toward to direction of mouse.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
namespace input
{
public class CameraControl : MonoBehaviour
{
[SerializeField] private GameObject player;
[SerializeField] private float speed = 20f;
float xRotation;
float yRotation;
private InputAction cameraAction;
public void Initialize(InputAction cameraAction)
{
this.cameraAction = cameraAction;
this.cameraAction.Enable();
}
private void FixedUpdate()
{
Vector2 camera = cameraAction.ReadValue<Vector2>();
float mouseX = camera.x;
float mouseY = camera.y;
xRotation -= mouseY;
yRotation += mouseX;
Quaternion goalRotation = Quaternion.Euler(xRotation, yRotation, 0);
//Quaternion goalRotation = Quaternion.Euler(30, 30, 0);
//Debug.Log(goalRotation);
//Quaternion goalRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.RotateTowards(transform.rotation, goalRotation, speed * Time.deltaTime);
//transform.rotation = goalRotation;
player.transform.rotation = Quaternion.Euler(0, yRotation, 0);
}
}
}
In the code, I am trying to understand how Quaternion.RotateToward() is working. RotateToward is having tree parameters: target from, target to, and speed of rotation.
Target from is current roation of camera. Target to is rotation that camera should rotate.
So, in the code, from the inputAction, I am receiving the Vector2 x and y values from mouse.
For example, if I move the mouse only to the right value would be like (1.0, 0) (2.0) ...
and if I move the mouse to upward (0, 1.0) (0.3)... Then, when I move the mouse diagnal, (1.0 1.0)..
So, using the Quaternion.RotateToward() actually rotate to the point that I want. However, problem is that it is not rotating based on the speed that I set which is 20f.
I know using
transform.rotation = goalRotation;
is eaier way to solve rather than use RotateToward(), however, I want to learn how this function should be working.
Any idea about this?
speed * Time.deltaTime to speed ables to solve the problem
I am trying to recreate a "GTA-Style" third person camera in Unity. I created code that rotates the player in the direction the camera is pointing. However, the camera is a child of the player, so when I rotate the player, I also rotate the camera. So I have to reset the camera's Y rotation. which in "turn" causes a frame or two to have the camera looking in a different direction.
Hierarchy of Player:
Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThirdPersonCamera : MonoBehaviour
{
public float rotationSpeed = 2f;
public Transform target, player;
float mouseX;
float mouseY;
float h;
float v;
// Start is called before the first frame update
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void LateUpdate()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
mouseX += Input.GetAxis("Mouse X") * rotationSpeed;
mouseY -= Input.GetAxis("Mouse Y") * rotationSpeed;
mouseY = Mathf.Clamp(mouseY, -35, 60);
CamControl();
}
void CamControl()
{
transform.LookAt(target);
if (h != 0 || v != 0)
{
target.rotation = Quaternion.Euler(mouseY, mouseX, 0);
player.rotation = Quaternion.Euler(0, mouseX, 0);
}
else
{
target.rotation = Quaternion.Euler(mouseY, mouseX, 0);
while (h != 0 || v != 0)
{
target.rotation = Quaternion.Euler(target.rotation.x, 0, target.rotation.z);
player.rotation = Quaternion.Euler(player.rotation.x, player.rotation.y - target.rotation.y + 180, player.rotation.z);
}
}
}
}
How this script works:
How I have it set up on Main Camera. (Notice hierarchy setup)
Note: The "Player" and "Target" variables have the same name as the GameObjects assigned to them.
If there is no input on Horizontal and Vertical axes, the camera will rotate around the player like normal.
If there is input on the Horizontal and Vertical axes, the player will face the direction the camera is facing. Then it has to reset the camera's Y rotation.
What I have tried:
Using slerp to smooth out the camera angles.
Note: I have TRIED slerping, I'm not sure that I did it correctly.
Unchilding the Target (Camera)
Result: This just made the camera not follow the player, but it shows that my code to turn the player is somewhat functional.
Setting the Player (in script) to the graphics.
Result: This gave me an odd view of the player, and made the player rotate up and down as well.
Sorry for the long and complicated post!
If your code to rotate the player and the camera works when unchilding the target from the player, then don't child the target of camera in the player.
Instead, make the target follow the player.
For instance:
void CamControl()
{
target.position = player.position;
transform.LookAt(target);
...
}
You can then do a smooth follow using a SmoothDamp for example, instead of copying directly the player position.
Moreover, if your player is controlled by physics (using the rigidbody for the movement), you should update your camera in the fixed update.
Now I'm a beginner game developer and I'm working on a game so I have set the camera motion that when you move the mouse the player rotates however I want to make the WASD keys work to the direction the player is rotated. Basically when I rotate the camera to the left the W key still moves the player on the Z dimension. How can I fix this?
Here's my player rotation code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraMovement : MonoBehaviour
{
public float speedH = 2.0f;
public float speedV = 2.0f;
private float yaw = 0.0f;
private float pitch = 0.0f;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
yaw += speedH * Input.GetAxis("Mouse X");
pitch -= speedV * Input.GetAxis("Mouse Y");
transform.eulerAngles = new Vector3(pitch, yaw, 0.0f);
}
}
Moving in the direction the character is facing is a simple work for Transform.forward and Transform.right because you need to move the player based on local position and rotation and not the world one.
For example, you could have
void Move (float h, float v)
{
movement.Set (h, 0f, v);
movement = movement.normalized * speed * Time.deltaTime;
playerRigidbody.MovePosition (transform.position + (transform.forward * movement.z) + (transform.right * movement.x));
}
The Rigidbody is what you need to move it, if you want an object that is always moving in the direction is facing you could easily do a
transform.position += transform.forward * Time.deltaTime * movementSpeed;
In this case, to go back you should subtract instead of add.
But use my first suggestion for your player :D
EDIT
In your update you can add
Move(yaw, pitch);
I'm working on developing a game, and my current roadblock is Camera Rotation. I want the mouse to control the camera, and when the camera turns, I want to rotate the player as well. However, the code I am using rotates the player object COMPLETELY with the player, causing the player to turn like this:
The player rotating forward not only looks strange, but causes clipping problems with the terrain. How do I make my code only rotate the player along one axis, while allowing the camera to rotate along any axis (to allow the audience to look around, without the player object being turned upwards or down).
this is the code that is rotating the player:
Quaternion QT = Quaternion.Euler(_LocalRotation.y, _LocalRotation.x, 0);
this._XForm_Parent.rotation = Quaternion.Lerp(this._XForm_Parent.rotation, QT, Time.deltaTime * OrbitDampening);
if (this._XForm_Camera.localPosition.z != this._CameraDistance * -1f)
{
this._XForm_Camera.localPosition = new Vector3(0f, 0f, Mathf.Lerp(this._XForm_Camera.localPosition.z, this._CameraDistance * -1f, Time.deltaTime * ScrollDampening));
}
and this is the complete script I am using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraOrbit : MonoBehaviour
{
protected Transform _XForm_Camera;
protected Transform _XForm_Parent;
protected Vector3 _LocalRotation;
protected float _CameraDistance = 10f;
public float MouseSensitivity = 4f;
public float ScrollSensitvity = 2f;
public float OrbitDampening = 10f;
public float ScrollDampening = 6f;
public bool CameraDisabled = false;
// Use this for initialization
void Start()
{
this._XForm_Camera = this.transform;
this._XForm_Parent = this.transform.parent;
}
void LateUpdate()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
CameraDisabled = !CameraDisabled;
if (!CameraDisabled)
{
//Rotation of the Camera based on Mouse Coordinates
if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0)
{
_LocalRotation.x += Input.GetAxis("Mouse X") * MouseSensitivity;
_LocalRotation.y += Input.GetAxis("Mouse Y") * MouseSensitivity;
//Clamp the y Rotation to horizon and not flipping over at the top
if (_LocalRotation.y < 0f)
_LocalRotation.y = 0f;
else if (_LocalRotation.y > 90f)
_LocalRotation.y = 90f;
}
//Zooming Input from our Mouse Scroll Wheel
if (Input.GetAxis("Mouse ScrollWheel") != 0f)
{
float ScrollAmount = Input.GetAxis("Mouse ScrollWheel") * ScrollSensitvity;
ScrollAmount *= (this._CameraDistance * 0.3f);
this._CameraDistance += ScrollAmount * -1f;
this._CameraDistance = Mathf.Clamp(this._CameraDistance, 1.5f, 100f);
}
}
//Actual Camera Rig Transformations
Quaternion QT = Quaternion.Euler(_LocalRotation.y, _LocalRotation.x, 0);
this._XForm_Parent.rotation = Quaternion.Lerp(this._XForm_Parent.rotation, QT, Time.deltaTime * OrbitDampening);
if (this._XForm_Camera.localPosition.z != this._CameraDistance * -1f)
{
this._XForm_Camera.localPosition = new Vector3(0f, 0f, Mathf.Lerp(this._XForm_Camera.localPosition.z, this._CameraDistance * -1f, Time.deltaTime * ScrollDampening));
}
}
}
A picture of my heirarchy:
(Please note that Player is an empty object containing all of my scripts and physics components, whereas the GFXs component is simply the model, with an animator component clean of any physics components.)
It sounds like what you want is basically to rotate the player around its local Y axis until it is facing the same direction as the camera. You can find out if things are facing the same direction using Vector3.SignedAngle and passing in the axis that you care about. Here is some sample code that should get you going.
float turnThresholdDegrees = 15;
float playerTurnSpeed = 1;
// Get delta angle in degrees on the Y axis between the direction player is facing and the direction camera is facing
float angleDelta = Vector3.SignedAngle(player.transform.forward, camera.transform.forward, Vector3.up);
// Calculate a rotation speed, clamping it to 0 when the angle delta is < some threshold
float rotateSpeed = Mathf.Abs(angleDelta) > turnThresholdDegrees ? Mathf.Sign(angleDelta) * playerTurnSpeed : 0;
// Rotate the player around its local Y axis by the rotation speed
player.transform.Rotate(0, rotateSpeed * Time.deltaTime, 0, Space.Self);
I'm watching unity tutorials and in the control code of the ship I would like to make it rotate on its axis, that is, it can rotate 360 degrees continuously while I press the right key for example.
playerController:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Boundary
{
public float xMin, xMax, zMin, zMax;
}
public class PlayerController : MonoBehaviour {
[Header("Movement")]
public float speed;
public float tilt;
public Boundary boundary;
private Rigidbody rig;
[Header("Shooting")]
public GameObject shot;
public Transform shotSpawn;
public float fireRate;
private float nextFire;
void Awake () {
rig = GetComponent<Rigidbody>();
}
void Update () {
if (Input.GetButton ("Fire1") && Time.time > nextFire) {
nextFire = Time.time + fireRate;
Instantiate (shot, shotSpawn.position, Quaternion.identity);
}
}
void FixedUpdate () {
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0f, moveVertical);
rig.velocity = movement * speed;
rig.position = new Vector3 (Mathf.Clamp (rig.position.x, boundary.xMin, boundary.xMax), 0f, Mathf.Clamp (rig.position.z, boundary.zMin, boundary.zMax));
rig.rotation = Quaternion.Euler (0f, 0f, rig.velocity.x * -tilt);
}
}
How can I edit it to do what I want?
example:
You can use Transform.Rotate()
Your code would look like this, with the example you provided:
void FixedUpdate () {
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0f, moveVertical);
rig.velocity = movement * speed;
rig.position = new Vector3 (Mathf.Clamp (rig.position.x, boundary.xMin, boundary.xMax), 0f, Mathf.Clamp (rig.position.z, boundary.zMin, boundary.zMax));
if(moveHorizontal > 0){ //if your "right key" is pressed
// Rotate the object around its local X axis at 1 degree per second
transform.Rotate(Vector3.right * Time.deltaTime);
}
}
For faster Rotation you can simply multiply the Vector3.right with some value.
To Rotate around another Axis, use other Vector directions, like Vector3.up.
For example:
transform.Rotate(Vector3.Up * moveHorizontal * Time.deltaTime);
transform.Rotate(Vector3.Forward * moveHorizontal * Time.deltaTime);
When you multiply the Vector3.Right with your moveHorizontal it should also work when you press the "left key" and that should result in rotating in the other direction.
transform.Rotate(Vector3.right * moveHorizontal * Time.deltaTime);
Notes:
That only works if your PlayerController is attached to your ship gameobject. If it is not attached you have to use the transform of your ship of course.
World Space vs Local Space
When you click on your object in the scene view, you should see the transform and the 3 arrows (red, green, blue) pointing on different directions (the 3 axis). When you use the method with the parameters provided above, you are using these arrows as rotation axis.
You can also rotate arround the WorldSpace axis.
transform.Rotate(Vector3.up, Time.deltaTime, Space.World);
When to use transform.Rotate?
When you change position or rotation of a transform, using the transforms
methods, it will be applied at the end of the frame. These changes ignore physics.
-> Use transforms methods if you don't care about collisions
When to use rigidbody.MoveRotation?
When you change rigidbody.position or rigidbody.MoveRotation using rigidbodies methods, it will be applied at the end of the next physics step. These changes care about physics (collisions and stuff)
-> Use rigidbodies methods if you care about collisions
Thanks to Helium for that hint.
The third possibility:
Instead of directly calling transform.Rotate or rigidbody.MoveRotation, you can also rotate your Object, using an animation, which changes the transforms rotation.
Example of Transform.Rotate()
You can clearly see, that the collision checks are ignored on that object while it's rotating through the ground. (I packed that gif into a spoiler to reduce noise. You'll need to hover over it, if you want to see it)
This will be the speed and axis of your rotation.
public Vector3 eulerAngleVelocity = new Vector3(0f,0f,1000f);
Since you want to rotate a rigidbody so use MoveRotation
Quaternion deltaRotation = Quaternion.Euler(-moveHorizontal * eulerAngleVelocity * Time.deltaTime);
rig.MoveRotation(rig.rotation * deltaRotation);
Final code will look like this
// NEW CODE BEGIN------
public Vector3 eulerAngleVelocity = new Vector3(0f,0f,1000f);
// NEW CODE END------
void FixedUpdate () {
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
// NEW CODE BEGIN------
Vector3 movement = new Vector3(0f, 0f, moveVertical); // Notice I have removed moveHorizontal, this will make sure your gameobject doesnt go left and right. We will use move horizontal for rotating the gameobject.
// NEW CODE END------
rig.velocity = movement * speed;
rig.position = new Vector3 (Mathf.Clamp (rig.position.x, boundary.xMin, boundary.xMax), 0f, Mathf.Clamp (rig.position.z, boundary.zMin, boundary.zMax));
// NEW CODE BEGIN------
Quaternion deltaRotation = Quaternion.Euler(-moveHorizontal * eulerAngleVelocity * Time.deltaTime);
rig.MoveRotation(rig.rotation * deltaRotation);
// NEW CODE END------
}
You can play with eulerAngleVelocity to get the desired speed. Hope this helps. ;)