Unity camera problem - Moving FPS/TPS camera to look at a target and resuming player control - unity3d

I am having trouble with aligning my player controlled camera (I use the same class for FPS and TPS) to look at a target (when ResetCamera() is called by another script) and then getting the player to resume control. The main reason I am doing this is so I can switch between FPS and TPS cameras and remain looking at the same target.
I can look at the target fine, but ONLY if I stop setting the rotation based on yaw and pitch (from "Mouse X" and "Mouse Y" inputs) in LateUpdate() after I set the lookAtTarget in ResetCamera(), but that means the player can no longer look around.
However, I cannot figure out how to get the correct yaw and pitch values after this so the player can continue looking around from the new look at target. How could I do this so the player could continue looking around?
public class PlayerCamera : MonoBehaviour {
public float mouseSensitivity = 10f;
public Transform target;
public float dstFromTarget = 2f;
public Vector2 pitchConstraints = new Vector2(-20f, 85f);
public float rotSmoothTime = .12f;
Vector3 rotSmoothVel;
Vector3 currRot;
float yaw;
float pitch;
void LateUpdate() {
yaw += Input.GetAxis("Mouse X") * mouseSensitivity;
pitch -= Input.GetAxis("Mouse Y") * mouseSensitivity;
pitch = Mathf.Clamp(pitch, pitchConstraints.x, pitchConstraints.y);
currRot = Vector3.SmoothDamp(currRot, new Vector3(pitch, yaw), ref rotSmoothVel, rotSmoothTime);
transform.eulerAngles = currRot;
transform.position = target.position - transform.forward * dstFromTarget;
}
public void ResetCamera(Transform lookAtTarget) {
transform.LookAt(lookAtTarget);
// below gets yaw and pitch values that move the camera to look at the
// wrong location
// yaw = transform.eulerAngles.x;
// pitch = transform.eulerAngles.y;
// pitch = Mathf.Clamp(pitch, pitchConstraints.x, pitchConstraints.y);
// currRot = new Vector3(pitch, yaw);
}
}

So, I figured out how to do what I wanted. Below is a script for a third or first person (just adjust the dstFromTarget variable to zero) camera that can be rotated to look at a target by another script with ResetCamera() and the player can resume moving around from that point.
public class PlayerCamera : MonoBehaviour {
public bool isFirstPerson = false;
public float mouseSensitivity = 10f;
public Transform target;
public float dstFromTarget = 2f;
public bool invertedPitch = false;
public float smoothTime = .12f;
public float minimumX = -85f;
public float maximumX = 85f;
// LateUpdate, so the Camera Target will definitely have been set
void LateUpdate () {
// first person updating is handled by Inverse kinematics stuff in my case
if (isFirstPerson) return;
UpdateStuff();
}
public void UpdateStuff() {
float yaw = Input.GetAxisRaw("Mouse X");
float pitch = -Input.GetAxisRaw("Mouse Y");
if (invertedPitch) {
pitch *= -1f;
}
Vector3 yRot = new Vector3(0f, yaw, 0f) * mouseSensitivity;
Vector3 xRot = new Vector3(pitch, 0f, 0f) * mouseSensitivity;
xRot = ClampRotationAroundXAxis(transform.rotation, xRot);
Transform newTrans = transform;
newTrans.Rotate(xRot);
newTrans.Rotate(yRot, Space.World);
transform.rotation = Quaternion.Slerp(transform.rotation, newTrans.rotation, smoothTime * Time.deltaTime);
transform.position = target.position - transform.forward * dstFromTarget;
}
public void ResetCamera(Transform lookAtTarget) {
transform.LookAt(lookAtTarget);
}
Vector3 ClampRotationAroundXAxis(Quaternion q, Vector3 xrot) {
q.x /= q.w;
q.y /= q.w;
q.z /= q.w;
q.w = 1.0f;
float angleX = 2.0f * Mathf.Rad2Deg * Mathf.Atan (q.x);
if (angleX < minimumX && xrot.x < 0f) {
xrot = Vector3.zero;
}
if (angleX > maximumX && xrot.x > 0f) {
xrot = Vector3.zero;
}
return xrot;
}
}

Related

Unity2D - Enemy shoot at player in a cone shaped pattern

I am trying to set up an enemy that will shoot at the player in a cone shape/shotgun shape. If I set the projectile script to transform.position += transform.right * m_Speed * Time.deltaTime; then the cone shape works as intended, just only to the right and not in the direction of the player. With the current setup (below), the projectile will shoot at the player, but all the bullet prefabs will be on top of each other and all going in the same direction, not in a cone shape.
How can I adjust this so the enemy will shoot at the player but retain the cone shape?
Enemy Script
aimAngle = 60f;
for (int i = 0; i < spreadShot; i++)
{
var shotRotation = gameObject.transform.rotation;
shotRotation *= Quaternion.Euler(0, 0, aimAngle);
GameObject clone = Instantiate(projectile, new Vector2(gameObject.transform.position.x, gameObject.transform.position.y), shotRotation);
aimAngle = aimAngle - 30f;
Vector3 direction = (Vector3)((player.transform.position - transform.position));
direction.Normalize();
clone.GetComponent<Projectile>().Setup(direction);
}
Projectile.cs
[SerializeField] float m_Speed;
Vector3 shootDir;
public void Setup(Vector3 shootDir)
{
this.shootDir = shootDir;
}
private void Update()
{
transform.position += shootDir * m_Speed * Time.deltaTime;
}
Not perfect yet but here is a working solution if anyone else has the same question.
Enemy Script
[SerializeField] public float attackSpread;
[SerializeField] private float startShotTime;
[SerializeField] private float delayShotTime;
[HideInInspector] public int spreadShot;
public void Attack() {
Vector2 targetPosition = target.transform.position - aimIndicator.transform.position;
Vector2 dirTowardsTarget = (targetPosition - (Vector2)transform.position);
transform.right = targetPosition.normalized;
Quaternion newRot;
for (int i = 0; i < spreadShot; i++)
{
float addedOffset = (i - (spreadShot / 2)) * attackSpread;
newRot = Quaternion.Euler(transform.localEulerAngles.x,
transform.localEulerAngles.y,
transform.localEulerAngles.z + addedOffset);
Instantiate(projectile).GetComponent<ButcherProjectile>().SpawnBullet(transform.position, newRot);
}
}
Projectile Script
[SerializeField] private float MovementSpeed = 10;
private Vector2 velocity;
public void SpawnBullet(Vector3 position, Quaternion rotation)
{
transform.position = position;
transform.rotation = rotation;
velocity = transform.right.normalized * MovementSpeed;
}
public void Update()
{
Vector2 nextPosition = (Vector2)transform.position + (velocity * Time.deltaTime);
transform.position = nextPosition;
Destroy(gameObject, 2f);
}
At the moment, your code rotates the bullet itself, not the vector of direction towards player. Shot rotation you computed doesn't affect your direction in any way.
To elaborate, if you rotate an object and just move it by transform.position, you changed its position by exactly how you specified, as if you just changed its x, y or z component by hand. Your rotation change to an object would produce desired results if you used transform.Translate(params) as this operation is dependant on object's current rotation and scale.
Apply your rotation to the direction, it should work just fine :)
More reference on how to do it: https://answers.unity.com/questions/46770/rotate-a-vector3-direction.html

How to fix Character Controller jumping up steep slopes?

I'm using a 3d character controller for my player but I can't figure out how to stop it from being able to jump up steep slopes. I've looked around and could only find one video that fixes the issue but after the character slides down the slope it stays locked in the slide movement function and I'm not really sure why. I've edited out the code that doesn't matter for clarity, pls lmk if u know how to fix this!
Heres the code:
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
public float speed = 12f;
public float gravity = -9.81f;
public float jumpHeight = 3f;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
//SLOPES
private float groundRayDistance = 1;
private RaycastHit slopeHit;
public float slopeSlideSpeed;
Vector3 velocity;
bool isGrounded;
// Update is called once per frame
void Update()
{
HandleMovement();
HandleAnimations();
if(OnSteepSlope())
{
SteepSlopeMovement();
}
}
private void HandleMovement()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
velocity = AdjustVelocityToSlope(velocity);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
private Vector3 AdjustVelocityToSlope(Vector3 velocity)
{
var ray = new Ray(transform.position, Vector3.down);
if(Physics.Raycast(ray, out RaycastHit hitInfo, 0.2f))
{
var slopeRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
var adjustedVelocity = slopeRotation * velocity;
if(adjustedVelocity.y < 0)
{
return adjustedVelocity;
}
}
return velocity;
}
private bool OnSteepSlope()
{
if (!isGrounded) return false;
if (Physics.Raycast(transform.position, Vector3.down, out slopeHit, (controller.height / 2) + groundRayDistance))
{
float slopeAngle = Vector3.Angle(slopeHit.normal, Vector3.up);
if (slopeAngle > controller.slopeLimit) return true;
}
return false;
}
private void SteepSlopeMovement()
{
Vector3 slopeDirection = Vector3.up - slopeHit.normal * Vector3.Dot(Vector3.up, slopeHit.normal);
float slideSpeed = speed + slopeSlideSpeed + Time.deltaTime;
velocity = slopeDirection * -slideSpeed;
velocity.y = velocity.y - slopeHit.point.y;
}
OnSteepSlope() wasnt returning false so I put the return false statement in an else loop, I also changed how I was moving the character in SteepSlopeMovement by using controller.Move() instead of directly changing the velocity. It works great now
I see you've got dot product check inside of SteepSlopeMovement might be useful to draw some rays with gizmos of the player's down and the normal of the surface. Then you can adjust the threshold value for when you can jump.

Parameter values are not being sent from script to blend tree (Unity 2020)

(Parameter values are not being sent from script to blend tree resulting in not following animation with movement.) I have animations set up in a blend tree that has a 1d type blend tree. I have it set to custom thresholds with an idle animation as the base state then the blend tree has three motions: a strafe left, right, and a walk forward. When I move forward the player does not do the animations told in the tree and I am almost certain it is the script.
I am also using Unity 1212.1.2f1 and for my scripts I have been using Microsoft Visual Studio if it matters.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
public float speed = 12f;
public float gravity = -9.81f;
public float jumpHeight = 3f;
public float rotationSpeed = 75.0f;
public Animator anim;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
Vector3 velocity;
bool isGrounded;
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
float translation = Input.GetAxis("Vertical") * speed;
float rotation = Input.GetAxis("Horizontal") * rotationSpeed;
translation *= Time.deltaTime;
rotation *= Time.deltaTime;
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
if ((Input.GetButtonDown("Jump")) && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
{
anim.SetFloat("Vertical", Input.GetAxis("Vertical"));
anim.SetFloat("Horizontal", Input.GetAxis("Horizontal"));
}
}
}
first i think you could try to make a new var :
private Rigidbody2D rg;
then add this in Start ()
rg = Player.GetComponent<Rigidbody2D>();
and Update () with this
anim.SetFloat("Vertical", rg.velocity.x);
anim.SetFloat("Horizontal", rgvelocity.x);
and sure made 2 Vertical and Horizontal in Animator parameters

I'm unable to move the cursor in play mode in the Unity VR example scene

I'm using the VR Samples from Unity https://www.assetstore.unity3d.com/en/#!/content/51519 with the scene 'Shooter 360' (though I've tried the other scenes).
I'm on a Mac and in XR settings I have both 'Mock HMD - HTC' and 'Oculus SDK' supported.
When I press Play, I'm unable to move the cursor or interact with the GUI at all. Any recommendations?
By default it is the SDK you use that will rotate the camera at runtime (Cardboard, Oculus, HTC Vive, ...).
You can use a script that will rotate the camera while in edit mode. Here is an example, just assign this script to your camera and press Left Ctrl while moving the mouse in the editor window:
public class EditorCameraController : MonoBehaviour
{
public float sensitivityX = 15F;
public float sensitivityY = 15F;
public float minimumX = -360F;
public float maximumX = 360F;
public float minimumY = -60F;
public float maximumY = 60F;
private float rotationX = 0F;
private float rotationY = 0F;
private Quaternion originalRotation;
void Start()
{
originalRotation = transform.localRotation;
}
void Update ()
{
if (Input.GetKey(KeyCode.LeftControl))
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, -Vector3.right);
transform.localRotation = originalRotation * xQuaternion * yQuaternion;
}
}
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -360F)
angle += 360F;
if (angle > 360F)
angle -= 360F;
return Mathf.Clamp(angle, min, max);
}
}

How to jump smoothly in Unity3D

I add a CharacterController to Player.But when I test the jump function,I find that the Player will move upwards immediately.
if (Player.isGrounded)
{
if (jump)
{
Move.y = JumpSpeed;
jump = false;
Player.Move (Move * Time.deltaTime);
}
}
Move += Physics.gravity * Time.deltaTime * 4f;
Player.Move (Move * Time.fixedDeltaTime);`
You are calling Player.Move() twice in one frame. This might be an issue.
You are adding gravity to Move vector, which means it will always go upward when you call this code.
naming a variable like Move is not a good convention. It creates confusion while reading because there is already a method of same name. change it to moveDirection.
Here is sample code:
public class ExampleClass : MonoBehaviour {
public float speed = 6.0F;
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
private Vector3 moveDirection = Vector3.zero;
CharacterController controller;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update() {
if (controller.isGrounded) {
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
if (Input.GetButton("Jump"))
moveDirection.y = jumpSpeed;
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
}
hope this helps.