I'm using Unity and trying to move my player object in a direction relative to where the camera is facing. The camera is currently able to rotate/orbit around the player object by using the mouse, however, it only moves in directions relative to the world, not the camera. In essence, I'm trying to replicate what the game Absolver does. There's a good clip in this youtube video at around 4:30 showing the camera/player movement: https://www.youtube.com/watch?v=_lBqCTeJwYw&t=1199s.
I've looked at youtube videos, unity answers and scripting manuals dealing with joysticks, quaternions, and Euler values, but I just can't seem to find a solution that fits my particular problem. Any help would be absolutely great. Thanks in advance!
Camera Rotation Code:
using UnityEngine;
public class FollowPlayer : MonoBehaviour
{
private const float Y_ANGLE_MIN = 0f;
private const float Y_ANGLE_MAX = 85f;
public Transform lookAt;
public Transform camTransform;
private Camera cam;
private float distance = 10f;
private float currentX = 0f;
private float currentY = 0f;
private float sensitivityX = 5f;
private float sensitivityY = 5f;
private void Start()
{
camTransform = transform;
cam = Camera.main;
}
private void Update()
{
currentX += Input.GetAxis("Mouse X") * sensitivityX;
currentY -= Input.GetAxis("Mouse Y") * sensitivityY;
currentY = Mathf.Clamp(currentY, Y_ANGLE_MIN, Y_ANGLE_MAX);
}
private void LateUpdate()
{
Vector3 dir = new Vector3(0, 0, -distance);
Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);
camTransform.position = lookAt.position + rotation * dir;
camTransform.LookAt(lookAt.position);
}
}
Player Movement Code:
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Rigidbody rb;
public Camera cam;
public float movementForce = 500f;
// Update is called once per frame
void FixedUpdate()
{
if (Input.GetKey("w"))
{
rb.AddForce(0, 0, movementForce * Time.deltaTime, ForceMode.VelocityChange);
}
if (Input.GetKey("a"))
{
rb.AddForce(-movementForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (Input.GetKey("s"))
{
rb.AddForce(0, 0, -movementForce * Time.deltaTime, ForceMode.VelocityChange);
}
if (Input.GetKey("d"))
{
rb.AddForce(movementForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (rb.position.y < -1f)
{
FindObjectOfType<GameManager>().EndGame();
}
}
}
You want to use the transform.forward property of your camera.
Something like this:
rb.AddForce(cam.transform.forward * movementForce * Time.deltaTime, ForceMode.VelocityChange);
You also have at your disposal:
transform.left
transform.right
There is an example of exactly this on the AddForce docs.
Related
I am new to programming and currently working on a little shooter game in unity. I just implemented recoil but my "PlayerCam" script (Line 31: transform.rotation = Quaternion.Euler(xRotation, yRotation, 0);) interrupts my "Recoil" script, the Euler function to be exact. Without this line recoil works, but I cant move obviously. With it the screen just shakes a bit (reset every frame and not continuing the recoil). What do i need to change?
PlayerCam.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCam : MonoBehaviour
{
public float sensX;
public float sensY;
public Transform orientation;
float xRotation;
float yRotation;
private void Start()
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
private void Update()
{
float mouseX = Input.GetAxis("Mouse X") * sensX;
float mouseY = Input.GetAxis("Mouse Y") * sensY;
yRotation += mouseX;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
transform.rotation = Quaternion.Euler(xRotation, yRotation, 0);
orientation.rotation = Quaternion.Euler(0, yRotation, 0);
}
}
Recoil.cs
using UnityEngine;
public class Recoil : MonoBehaviour
{
private Vector3 currentRotation;
private Vector3 targetRotation;
[SerializeField] private float recoilX;
[SerializeField] private float recoilY;
[SerializeField] private float recoilZ;
[SerializeField] private float snappiness;
[SerializeField] private float returnSpeed;
void Start()
{
}
void Update()
{
targetRotation = Vector3.Lerp(targetRotation, Vector3.zero, returnSpeed * Time.deltaTime);
currentRotation = Vector3.Slerp(currentRotation, targetRotation, snappiness * Time.fixedDeltaTime);
transform.localRotation = Quaternion.Euler(currentRotation);
}
public void RecoilFire()
{
targetRotation += new Vector3(recoilX, Random.Range(-recoilY, recoilY), Random.Range(-recoilZ, recoilZ));
}
}
Thank you for your help :)
I'm not sure where your Recoil class comes in as far as modifying your PlayerCam's orientation, but it sounds like your camera is being hard-reset to the value of the player's turn angles every frame, rather than combining them with the current recoil (sometimes called "punch") values.
To do a recoil/viewpunch setup correctly, you'll need to give your camera class access to an orientation that represents the current amount of rotation caused by the recoil, and combine that with the camera's current player angle setup.
Something like:
// get your current recoil offset here
Quaternion viewPunch = MyRecoil.transform.rotation;
// update the "base" rotation, aka where the player is looking
// (your existing code)
float mouseX = Input.GetAxis("Mouse X") * sensX;
float mouseY = Input.GetAxis("Mouse Y") * sensY;
yRotation += mouseX;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
// final camera rotation = viewPunch * player angles.
// to rotate an orientation, we always do rotation * current,
// not the other way around.
// if the strength of the recoil has gone back to 0 or hasn't
// happened yet, we'll end up with the normal player angles,
// as expected.
// if your camera object is a child of another object, you may need to set localRotation instead
transform.rotation = viewPunch * Quaternion.Euler(xRotation, yRotation, 0);
It is necessary to rotate the turret and muzzle so that the sight is always directed to the center of the screen. I have a camera that can be rotated separately, and the turret and muzzle should follow it slowly, like in the world of tanks.
I have this code. The tower does not keep up with the camera and stops where I took the camera.
public class Tower : MonoBehaviour
{
public Transform Towr;
public Transform Cannon;
public float TowerSpeed;
public float CannonSpeed;
float TowerAngle;
float CannonAngle;
private void Update()
{
RotateTower();
RotateCannon();
}
void RotateTower()
{
TowerAngle += Input.GetAxis("Mouse X") * TowerSpeed * Time.deltaTime;
TowerAngle = Mathf.Clamp(TowerAngle, -90, 90);
Towr.localRotation = Quaternion.AngleAxis(TowerAngle, Vector3.up);
}
void RotateCannon()
{
CannonAngle += Input.GetAxis("Mouse Y") * CannonSpeed * -Time.deltaTime;
CannonAngle = Mathf.Clamp(CannonAngle, -2, 2);
Cannon.localRotation = Quaternion.AngleAxis(CannonAngle, Vector3.right);
}
}
I already found a solution how to rotate the tower behind the camera along the "Y" axis
public Transform cam;
public float speed = 50f;
private void FixedUpdate()
{
transform.localRotation = Quaternion.RotateTowards(transform.localRotation, Quaternion.Euler(0, cam.eulerAngles.y , 0), speed * Time.deltaTime);
}
This script is on the character. When the character starts to move, his face turns towards the camera. This code needs to be redone so that the character always looks towards the camera, even if he is standing still.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TPM : MonoBehaviour
{
CharacterController controller;
public Transform cam;
public float speed = 6f;
public float turnSmoothTime = 0.1f;
float turnSmoothVelocity;
private void Start()
{
controller = GetComponent<CharacterController>();
}
private void Update()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 direction = new Vector3(horizontal, 0f, vertical).normalized;
if(direction.magnitude >= 0.1f)
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + cam.eulerAngles.y;
float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, turnSmoothTime);
transform.rotation = Quaternion.Euler(0f, angle, 0f);
Vector3 moveDir = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
controller.Move(moveDir.normalized * speed * Time.deltaTime);
}
}
}
use transform.LookAt(Camera.main.transform.position) in your Update.
Here is a suitable solution I found
public Transform cam;
public float speed = 5f;
private void FixedUpdate()
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0f, cam.rotation.eulerAngles.y, 0f), speed * Time.deltaTime);
}
I have a player in the game, which I can move using the keyboard and rotate only on the horizontal axis using the mouse. That means, I can aim only horizontally and I can not aim it up and down.
I have the Main Camera and another VM Camera from Cinemachine. The current state of the game is like this:
On the horizontal axis, I rotate the player, but on the vertical axis I only want the player's camera/FOV to be moved up and down.
My movement script attached to the player is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController characterController;
public float speed = 35f;
public Animator animator;
// camera and rotation
public Transform cameraHolder;
public float mouseSensitivity = 2f;
public float upLimit = 50;
public float downLimit = -50;
// gravity
private float gravity = 9.87f;
private float verticalSpeed = 0;
void Update()
{
Move();
Rotate();
}
public void Rotate()
{
float horizontalRotation = Input.GetAxis("Mouse X");
float verticalRotation = Input.GetAxis("Mouse Y");
transform.Rotate(0, horizontalRotation * mouseSensitivity, 0);
cameraHolder.Rotate(-verticalRotation * mouseSensitivity, 0, 0);
Vector3 currentRotation = cameraHolder.localEulerAngles;
if (currentRotation.x > 180) currentRotation.x -= 360;
currentRotation.x = Mathf.Clamp(currentRotation.x, upLimit, downLimit);
cameraHolder.localRotation = Quaternion.Euler(currentRotation);
}
private void Move()
{
float horizontalMove = Input.GetAxis("Horizontal");
float verticalMove = Input.GetAxis("Vertical");
if (characterController.isGrounded) verticalSpeed = 0;
else verticalSpeed -= gravity * Time.deltaTime;
Vector3 gravityMove = new Vector3(0, verticalSpeed, 0);
Vector3 move = transform.forward * verticalMove + transform.right * horizontalMove;
characterController.Move(speed * Time.deltaTime * move + gravityMove * Time.deltaTime);
}
}
This is the code i use, it works for me, it's super easy to implement, and stops moving the camera when you aren't focusing the game, is the script from one of Brackey's tutorials modified for this purpose:
using UnityEngine;
public class CameraController : MonoBehaviour
{
public PlayerController player;
public float sensitivity = 150f;
public float clampAngle = 85f;
public bool look = true;
private float verticalRotation;
private float horizontalRotation;
private void Start()
{
verticalRotation = transform.localEulerAngles.x;
horizontalRotation = player.transform.eulerAngles.y;
// Defines the state of the cursor
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
private void Update()
{
// Looks around if the user is in the window
if (look)
{
Look();
}
Debug.DrawRay(transform.position, transform.forward * 2, Color.red);
// If the player presses ESC while in the game, it unlocks the cursor
if (look && Input.GetKeyDown(KeyCode.Escape))
{
look = false;
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else if (Input.GetMouseButtonDown(0) && !look)
{
look = true;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
private void Look()
{
float _mouseVertical = -Input.GetAxis("Mouse Y");
float _mouseHorizontal = Input.GetAxis("Mouse X");
verticalRotation += _mouseVertical * sensitivity * Time.deltaTime;
horizontalRotation += _mouseHorizontal * sensitivity * Time.deltaTime;
verticalRotation = Mathf.Clamp(verticalRotation, -clampAngle, clampAngle);
transform.localRotation = Quaternion.Euler(verticalRotation, 0f, 0f);
player.transform.rotation = Quaternion.Euler(0f, horizontalRotation, 0f);
}
}
There is my script Rigidbody controller -
public float Speed = 5f;
public float JumpHeight = 2f;
public float GroundDistance = 0.2f;
public float DashDistance = 5f;
public LayerMask Ground;
private Rigidbody _body;
private Vector3 _inputs = Vector3.zero;
private bool _isGrounded = true;
private Transform _groundChecker;
void Start()
{
_body = GetComponent<Rigidbody>();
_groundChecker = transform.GetChild(0);
}
void Update()
{
_isGrounded = Physics.CheckSphere(_groundChecker.position, GroundDistance, Ground, QueryTriggerInteraction.Ignore);
_inputs = Vector3.zero;
_inputs.x = Input.GetAxis("Horizontal");
_inputs.z = Input.GetAxis("Vertical");
if (_inputs != Vector3.zero)
transform.forward = _inputs;
if (Input.GetButtonDown("Jump") && _isGrounded)
{
_body.AddForce(Vector3.up * Mathf.Sqrt(JumpHeight * -2f * Physics.gravity.y), ForceMode.VelocityChange);
}
if (Input.GetButtonDown("Sprint"))
{
Vector3 dashVelocity = Vector3.Scale(transform.forward, DashDistance * new Vector3((Mathf.Log(1f / (Time.deltaTime * _body.drag + 1)) / -Time.deltaTime), 0, (Mathf.Log(1f / (Time.deltaTime * _body.drag + 1)) / -Time.deltaTime)));
_body.AddForce(dashVelocity, ForceMode.VelocityChange);
}
}
void FixedUpdate()
{
_body.MovePosition(_body.position + _inputs * Speed * Time.fixedDeltaTime);
}
What the best way to make a turn on y in the direction of the camera ? That is,the player turns to the side where the mouse is turned? Is it in fixedUpdate or update?
This is the camera script:
public float Smoothness = 0.3F;
public Vector2 Sensitivity = new Vector2(4, 4);
public Vector2 LimitX = new Vector2(-70, 80);
private Vector2 NewCoord;
public Vector2 CurrentCoord;
private Vector2 vel;
public GameManager GameMangerS;
public Transform Target;
public float TransformSpeed;
public Animator CameraAnimator;
void Update()
{
NewCoord.x = Mathf.Clamp(NewCoord.x, LimitX.x, LimitX.y);
NewCoord.x -= Input.GetAxis("Mouse Y") * Sensitivity.x;
NewCoord.y += Input.GetAxis("Mouse X") * Sensitivity.y;
CurrentCoord.x = Mathf.SmoothDamp(CurrentCoord.x, NewCoord.x, ref vel.x, Smoothness / 2);
CurrentCoord.y = Mathf.SmoothDamp(CurrentCoord.y, NewCoord.y, ref vel.y, Smoothness / 2);
transform.rotation = Quaternion.Euler(CurrentCoord.x, CurrentCoord.y, 0);
}
And added this line to the controller script -
void FixedUpdate()
{
_body.MovePosition(_body.position + _inputs * Speed * Time.fixedDeltaTime);
transform.rotation = Quaternion.Euler(0, MainCamera.CurrentCoord.y, 0);
}
When I'm standing the player normally rotates, but when I start to move, all rotations are reset and the player is not moving.
Update
Simple Rotation can be achieved using transform.Rotate().
Example:
this.transform.Rotate(Vector3.up, 30);
This example is gonna rotate the transform by 30° around the Vector that points upwards.
LookAtMouse:
To make your object turn towards the mouse, you need the ScreenToWorldSpace() method from your camera. In order to convert the ScreenCoordinates into your WorldCoordinates.
Example:
Please note:
You have to set the camera instance! If you don't add that, you'll get a NullReferenceException.
This Snippets shall only show the steps needed to achieve the behavior you wish.
You will have to find out yourself how to integrate that lines of code into your code to make it work. Consider what Programmer told you in the comment when integrating that.
Vector3 mousePosition = Input.mousePosition; //get the screenSpaceMousePosition
Vector3 worldPosition = this.camera.ScreenToWorldPoint(mousePosition); //convert it into worldSpacePosition
Vector3 calculatedDirection = worldPosition - transform.position; //calucate the looking direction
calculatedDirection.y = 0;
Quaternion rotation = Quaternion.LookRotation(calculatedDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime);