I hope someone can help. Im trying to create a little script that zooms in to my player and back out - toggling.
The zoom in works fine, but when I try to zoom back out it doesn't work, it gets stuck. I've created a bool to ensure it only runs the code when it needs to and I'm wondering if that is what's causing the error.
using UnityEngine;
using System.Collections;
public class CameraZoom : MonoBehaviour
{
public float zoom = 10f;
public float normal = 3.471398f;
public float smooth = 5f;
private bool isZoomed = false;
public Camera cam;
public GameObject player;
// lock the camera settings
public float LockedX = 0f;
public float LockedY = 0f;
public float LockedZ = 0f;
private bool hasBeenZoomed = false;
Vector3 targetPos;
private Transform playerTransform;
// Use this for initialization
void Start()
{
targetPos = transform.position;
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("z")) { isZoomed = !isZoomed; }
if (isZoomed == true)
{
ZoomInToPlayer();
hasBeenZoomed = true;
}
else
{
if (hasBeenZoomed)
{
ZoomOutFromPlayer();
hasBeenZoomed = false;
}
}
}
void ZoomInToPlayer()
{
// By default the target x and y coordinates of the camera are it's current x and y coordinates.
float targetX = transform.position.x;
float targetY = transform.position.y;
// ... the target x coordinate should be a Lerp between the camera's current x position and the player's current x position.
targetX = Mathf.Lerp(transform.position.x, playerTransform.position.x, smooth * Time.deltaTime);
//Debug.Log("player x is " + playerTransform.position.x + " and TargetX is " + targetX);
// ... the target y coordinate should be a Lerp between the camera's current y position and the player's current y position.
targetY = Mathf.Lerp(transform.position.y, playerTransform.position.y, smooth * Time.deltaTime);
//Debug.Log("player y is " + playerTransform.position.y+ " and TargetY is " + targetY);
// Set the camera's position to the target position with the same z component.
cam.transform.position = new Vector3(targetX, targetY, transform.position.z);
// Change the size of the camera viewport
cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, zoom, Time.deltaTime * smooth);
}
void ZoomOutFromPlayer()
{
// By default the target x and y coordinates of the camera are it's current x and y coordinates.
float targetX;
float targetY;
// Change the size of the camera viewport
cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, normal, Time.deltaTime * smooth);
// ... the target x coordinate should be a Lerp between the camera's current x position and the original x position.
targetX = Mathf.Lerp(transform.position.x, LockedX, smooth * Time.deltaTime);
// ... the target y coordinate should be a Lerp between the camera's current y position and the original y position.
targetY = Mathf.Lerp(transform.position.y, LockedY, smooth * Time.deltaTime);
// Set the camera's position to the target position with the same z component.
cam.transform.position = new Vector3(targetX, targetY, transform.position.z);
}
}
Your methods ZoomInToPlayer and ZoomOutFromPlayer are written in a way that suggests that they should be called once per frame for the duration of the zoom in/out animation. However, ZoomOutFromPlayer will only be called once, because Update, when ZoomOutFromPlayer is called, the hasBeenZoomed is immediately set to false.
What you're trying to do here, essentially, is a simple Finite State Machine. I suggest researching this design pattern a little more — it will help you noticing the sources of such problems and structuring your code in a better way.
In this particular case, a good way to prevent this problem when designing your code would be to write something akin to "API documentation" for yourself, when writing your methods. For ZoomOutFromPlayer, it would read something like this:
Call every frame when you want to perform zoom-out animation, until the animation is complete.
After you written (and read) such a description, you should immediately notice a red flag — "until the animation is complete"? So, the code that calls this method should somehow take track of whether the animation is complete or not, during a separate mechanism? Wouldn't it make it really easy to use this method incorrectly? Well, that's exactly what happened here.
Instead, what you could've done, is to create two different methods, ZoomInUpdate and ZoomOutUpdate, with descriptions that would read something like this:
Call every frame when the camera should be zoomed out/zoomed in.
This way, using this methods is a lot easier, and you can safely throw out additional logic with hasBeenZoomed out. Just call these methods every frame, and ensure (inside these methods) that they change the camera settings with a certain speed, if these settings need to be changed, or otherwise do nothing.
Try this
using UnityEngine;
using System.Collections;
public class CameraZoom : MonoBehaviour
{
public float zoom = 10f;
public float normal = 3.471398f;
public float smooth = 5f;
private bool isZoomed = false;
private bool isZoomFinished = true; // the animation zoom is over ?
public Camera cam;
public GameObject player;
public float LockedX = 0f;
public float LockedY = 0f;
public float LockedZ = 0f;
private bool hasBeenZoomed = false;
Vector3 targetPos;
private Transform playerTransform;
void Start()
{
targetPos = transform.position;
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update()
{
if (Input.GetKeyDown("z") && isZoomFinished) {
isZoomed = !isZoomed;
isZoomFinished = false;
}
if (isZoomed && !isZoomFinished)
{
ZoomInToPlayer();
}
else if (!isZoomed && !isZoomFinished)
{
ZoomOutFromPlayer()
}
}
float delta = 0;
void ZoomInToPlayer()
{
delta += smooth * Time.deltaTime;
//Cam size
cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, zoom, delta);
//Cam pos
float targetX = transform.position.x;
float targetY = transform.position.y;
targetX = Mathf.Lerp(transform.position.x, playerTransform.position.x, delta);
targetY = Mathf.Lerp(transform.position.y, playerTransform.position.y, delta);
cam.transform.position = new Vector3(targetX, targetY, transform.position.z);
// is animation over ?
if(delta >= 1) {
isZoomFinished = true;
delta = 0;
}
}
void ZoomOutFromPlayer()
{
delta += smooth * Time.deltaTime;
//Cam size
cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, normal, delta);
//Cam pos
float targetX;
float targetY;
targetX = Mathf.Lerp(transform.position.x, LockedX, delta);
targetY = Mathf.Lerp(transform.position.y, LockedY, delta);
cam.transform.position = new Vector3(targetX, targetY, transform.position.z);
// is animation over ?
if(delta >= 1) {
isZoomFinished = true;
delta = 0;
}
}
}
Related
I made a Camera Script that follows the player using a lerp for making it look more smooth, but for some reason, it looks laggy when walking in some sudden areas.
The ground the player is walking on is tilemap, and at first, I thought there were tiny cell gaps that caused the problem, but it still happens even when I changed the ground to one solid block. So I concluded it must be something with Camera Script that cause this problem.
Here is a video clip of what I'm talking about: https://youtu.be/mmBMHWuHpxo
I think the problem stems from this part of my camera script:
void SetTargetPos()
{
// By default the target x and y coordinates of the camera are it's current x and y coordinates.
targetX = transform.position.x;
targetY = transform.position.y;
// If the player has moved beyond the x margin...
if (CheckXMargin())
{
// ... the target x coordinate should be a Lerp between the camera's current x position and the player's current x position.
targetX = Mathf.Lerp(transform.position.x, transformPlayer.position.x, xSmooth * Time.deltaTime);
}
// If the player has moved beyond the y margin...
if (CheckYMargin())
{
// ... the target y coordinate should be a Lerp between the camera's current y position and the player's current y position.
targetY = Mathf.Lerp(transform.position.y, transformPlayer.position.y, ySmooth * Time.deltaTime);
}
// The target x and y coordinates should not be larger than the maximum or smaller than the minimum.
targetX = Mathf.Clamp(targetX, currentMinBounds.x, currentMaxBounds.x);
targetY = Mathf.Clamp(targetY, currentMinBounds.y, currentMaxBounds.y);
// Set the camera's position to the target position with the same z component.
transform.position = new Vector3(targetX, targetY, transform.position.z);
TestOutOfCamBounds();
}
Btw I have the exact same script on different Unity Projects with the same variable inputs. There is a small difference, and that just the overall size of everything in the second project is a lot smaller than the first one. But it works completely fine on that project. I have also tried Smoothdamp instead of Lerp, still no success. Here is a video clip of the other project: https://youtu.be/baJmKehYfG0
Any help will be much appreciated.
If you want to look at the entire script here it is:
public class Camera_Controller : MonoBehaviour
{
private static Camera_Controller _instance;
public static Camera_Controller instance;
[Header("Put player here")]
public GameObject player;
public Transform transformPlayer;
[Space]
Vector2 currentMinBounds;
Vector2 currentMaxBounds;
public Vector3 targetPos;
[Space]
[Header("Camera Properties")]
public float xMargin = 1f;
public float yMargin = 1f;
public float xSmooth = 8f;
public float ySmooth = 8f;
public Vector3 velocity = Vector3.zero;
private float targetY;
private float targetX;
[Header("Smooth Transition")]
public Vector3 oldPosition;
public Vector3 targetPosition;
public float transitionsTime;
public bool switchingCamera;
private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
else
{
_instance = this;
}
instance = _instance;
}
void Start()
{
targetPos = transform.position;
}
private bool CheckXMargin()
{
// Returns true if the distance between the camera and the player in the x axis is greater than the x margin.
return Mathf.Abs(transform.position.x - transformPlayer.position.x) > xMargin;
}
private bool CheckYMargin()
{
// Returns true if the distance between the camera and the player in the y axis is greater than the y margin.
return Mathf.Abs(transform.position.y - transformPlayer.position.y) > yMargin;
}
void Update()
{
if (!switchingCamera)
{
SetTargetPos();
}
}
public void SetCamBounds(Vector2 minBounds, Vector2 maxBounds) //Called from Game Events Trough WarpController as trigger
{
currentMinBounds = minBounds;
currentMaxBounds = maxBounds;
}
//SetTargetPos() should be causing the problem
void SetTargetPos()
{
// By default the target x and y coordinates of the camera are it's current x and y coordinates.
targetX = transform.position.x;
targetY = transform.position.y;
// If the player has moved beyond the x margin...
if (CheckXMargin())
{
// ... the target x coordinate should be a Lerp between the camera's current x position and the player's current x position.
targetX = Mathf.Lerp(transform.position.x, transformPlayer.position.x, xSmooth * Time.deltaTime);
}
// If the player has moved beyond the y margin...
if (CheckYMargin())
{
// ... the target y coordinate should be a Lerp between the camera's current y position and the player's current y position.
targetY = Mathf.Lerp(transform.position.y, transformPlayer.position.y, ySmooth * Time.deltaTime);
}
// The target x and y coordinates should not be larger than the maximum or smaller than the minimum.
targetX = Mathf.Clamp(targetX, currentMinBounds.x, currentMaxBounds.x);
targetY = Mathf.Clamp(targetY, currentMinBounds.y, currentMaxBounds.y);
// Set the camera's position to the target position with the same z component.
transform.position = new Vector3(targetX, targetY, transform.position.z);
TestOutOfCamBounds();
}
void TestOutOfCamBounds() //Set Camera some boundaries.
{
if (targetPos.x <= currentMinBounds.x)
{
targetPos.x = currentMinBounds.x;
}
if (targetPos.x >= currentMaxBounds.x)
{
targetPos.x = currentMaxBounds.x;
}
if (targetPos.y <= currentMinBounds.y)
{
targetPos.y = currentMinBounds.y;
}
if (targetPos.y >= currentMaxBounds.y)
{
targetPos.y = currentMaxBounds.y;
}
}
}
Try doing the call for the cammer movement in Late update, Example bellow.
private void LateUpdate() {
SetTargetPos();
}
This is called after the update method and might help reduce the cammer jitter.
Your second video is 2d and your first is 3d. try lerping the z position also.
targetZ = Mathf.Lerp(transform.position.z, transformPlayer.position.z, zSmooth * Time.deltaTime);
I found a solution to it all!
I changed the update function to LateUpdate()... Sorry for wasting your time
What I am trying to do is have a pillar in the center of a scene, and what i think is happening is the quaternion.RotateTowards is receiving a different starting/initial quaternion than the action which is causing it to snap/teleport into a different location which then starts to move to the next. I thought it may be because i'm misunderstanding how quaternions are handled in unity, so i've tried messing with normalizing it, but i can't seem to get any change on the teleport.
The goal is to attach this scrip to a simple 3D cylinder and have it wobble basically, where there will be a player on top of it trying to stay on it. However I can't seem to figure out why it is teleporting and was hoping for a second set of eyes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformWobble : MonoBehaviour
{
public float timeDelay = 0;
public float rRange = 0.5f;
public float maxRotation = 20.0f;
public float rotRate = 0.05f;
private bool wobble = false;
private Vector3 randomRotation;
private Quaternion rotation;
private Quaternion destination;
private float x, y, z;
private bool inPlace;
private void Start()
{
randomRotation = new Vector3();
inPlace = true;
}
// Update is called once per frame
void FixedUpdate()
{
if (inPlace)
{
destination = RotateRandomly();
inPlace = false;
}
transform.localRotation = Quaternion.RotateTowards(transform.localRotation, destination, rotRate * Time.deltaTime);
if (transform.localRotation == destination)
{
inPlace = true;
}
}
//This will grab the rotation of the object and move it randomly, but won't exceed maxRotation;
Quaternion RotateRandomly()
{
randomRotation = new Vector3(Random.Range(-rRange, rRange), Random.Range(-rRange, rRange), Random.Range(-rRange, rRange));
rotation = transform.localRotation;
x = rotation.x + randomRotation.x;
if(x >= maxRotation && x <= 360 - maxRotation) { x = rotation.x; }
y = rotation.y + randomRotation.y;
if (y >= maxRotation && y <= 360 - maxRotation) { y = rotation.y; }
z = rotation.z + randomRotation.z;
if (z >= maxRotation && z <= 360-maxRotation) { z = rotation.z; }
return new Quaternion(x, y, z, transform.localRotation.w);
}
}
Your quaternion calculations are incorrect: quaternions do not hold angles, instead, they hold the representation of a rotation about an axis. I emphasized "representation" because it is a little complicated...
The x, y, z components of a quaternion hold the rotation axis unit vector scaled by the sine of the half angle of rotation. The w component holds the cosine of the half angle. That is...
// NOTE: rotationAngle is in radians rather than degrees
Quaternion MakeQuaternion (Vector3 rotationAxis, float rotationAngle)
{
float c = Mathf.Cos (rotationAngle / 2);
float s = Mathf.Sin (rotationAngle / 2);
Vector3 v = rotationAxis.normalized * s;
return new Quaternion (v.x, v.y, v.z, c);
}
Now, the tricky part for your problem is coming up with the rotation axis and angle for the desired effect.
One solution (if you wish to stick to Euler angles) is to compute quaternions for each Euler rotation and then combine them:
Quaternion EulerToQuat (float XAngle, float YAngle, float ZAngle)
{
Quaternion X = MakeQuaternion (Vector3.right, XAngle);
Quaternion Y = MakeQuaternion (Vector3.up, YAngle);
Quaternion Z = MakeQuaternion (Vector3.forward, ZAngle);
// combine the rotations such that the object is first rotated about the Z axis,
// then about the Y axis, then the X (ie, reverse order of multiplication).
// Reminder: quaternion multiplicate is not commutative: order matters, so if this
// is not the order you want, just siwtch things around
rotate X * Y * Z;
}
FixedUpdate doesn't get called every render frame, only on physics frames. You need a way to tell Unity to have each frame showing the rotation change, rather than only updating the rendering when the physics frame has just run.
That is what Rigidbody.MoveRotation is for. Cache a reference to the Rigidbody, calculate the new global rotation it should have, then call MoveRotation:
private Rigidbody rb;
private void Start()
{
randomRotation = new Vector3();
inPlace = true;
rb = GetComponent<Rigidbody>();
}
// ...
// Update is called once per frame
void FixedUpdate()
{
if (inPlace)
{
destination = RotateRandomly();
inPlace = false;
}
Quaternion newLocalRot = Quaternion.RotateTowards(transform.localRotation,
destination, rotRate * Time.deltaTime);
Quaternion newGlobalRot = transform.parent.rotation * newLocalRot;
rb.MoveRotation(newGlobalRot);
if (transform.localRotation == destination)
{
inPlace = true;
}
}
I am trying to rotate a GameObject along z-axis using Joystick, but its rotating y-axis. I might missed some math calculation. Any Help??
Reference Image
void FixedUpdate()
{
// get input from joystick
// get input from joystick
rightJoystickInput = rightJoystick.GetInputDirection();
float xMovementRightJoystick = rightJoystickInput.x; // The horizontal movement from joystick 02
float zMovementRightJoystick = rightJoystickInput.y; // The vertical movement from joystick 02
// if there is only input from the right joystick
if (rightJoystickInput != Vector3.zero)
{
// calculate the player's direction based on angle
float tempAngle = Mathf.Atan2(zMovementRightJoystick, xMovementRightJoystick);
xMovementRightJoystick *= Mathf.Abs(Mathf.Cos(tempAngle));
zMovementRightJoystick *= Mathf.Abs(Mathf.Sin(tempAngle));
// rotate the player to face the direction of input
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.z += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
if (lookDirection != Vector3.zero)
{
rotationTarget.localRotation = Quaternion.Slerp(rotationTarget.localRotation, Quaternion.LookRotation(lookDirection) * Quaternion.Euler(0, 45f, 0), rotationSpeed * Time.deltaTime);
}
}
}
You don't need most of the code in your question and this is really simple.
1.Find the angle with Mathf.Atan2 then multiple it with Mathf.Rad2Deg.
2.Use Quaternion.Euler(new Vector3(0, 0, angle)) to get the rotation then apply it to the Object.
This should be one in the Update function not FixedUpdate because FixedUpdate is used to move Rigidbody Objects.
public Transform rotationTarget;
public bool flipRot = true;
void Update()
{
rightJoystickInput = rightJoystick.GetInputDirection();
float horizontal = rightJoystickInput.x;
float vertical = rightJoystickInput.y;
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rotationTarget.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
}
If using Rigidbody2D then use Rigidbody2D.MoveRotation in the FixedUpdate function. The rest of the code stays the-same.
public Rigidbody2D rg2d;
public bool flipRot = true;
void FixedUpdate()
{
rightJoystickInput = rightJoystick.GetInputDirection();
float horizontal = rightJoystickInput.x;
float vertical = rightJoystickInput.y;
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rg2d.MoveRotation(angle);
}
EDIT:
But only the problem is when i leave joystick its rotation is setting
to 0 instantly which looks too odd. How can i fix it?
You have to detect when you release the joystick in OnPointerUp then slowly lerp the joystick thump back to the Zero position. You also have to lerp the current target object angle to zero or to its default value and this should be done in a coroutine function. When OnPointerDown is called, stop the current coroutine function. Prevent the code in FixedUpdate from running when finger is released so that it won't interfere with the coroutine function.
For the sake of completeness, below is the combination of a Joystick code and the Rigidbody answer above:
public class VirtualJoystickController : MonoBehaviour,
IDragHandler, IPointerUpHandler, IPointerDownHandler
{
private Image bgImg;
private Image joystickImg;
public float mas_distance = 7f;
void Start()
{
bgImg = GameObject.Find("JoystickBGImage").GetComponent<Image>(); // the joysticks background
joystickImg = GameObject.Find("Joystickthumb").GetComponent<Image>(); // the joystick object to use
}
private Vector3 _inputDirection = Vector3.zero;
//the movementDirection
public Vector3 joystickInputDirection
{
set
{
//Change only if value is different from old one
if (_inputDirection != value)
{
_inputDirection = value;
Debug.Log("Dir: " + _inputDirection);
}
}
get
{
return _inputDirection;
}
}
public void OnDrag(PointerEventData eventData)
{
dragJoyStick(eventData);
}
void dragJoyStick(PointerEventData eventData)
{
Vector3 tempDir = Vector3.zero;
Vector2 pos = Vector2.zero;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle
(bgImg.rectTransform,
eventData.position,
eventData.pressEventCamera,
out pos))
{
pos.x = (pos.x / bgImg.rectTransform.sizeDelta.x);
pos.y = (pos.y / bgImg.rectTransform.sizeDelta.y);
float x = (bgImg.rectTransform.pivot.x == 1) ? pos.x * 2 + 1 : pos.x * 2 - 1;
float y = (bgImg.rectTransform.pivot.y == 1) ? pos.y * 2 + 1 : pos.y * 2 - 1;
tempDir = new Vector3(x, y, 0);
if (tempDir.magnitude > 1)
{
tempDir = tempDir.normalized;
}
joystickImg.rectTransform.anchoredPosition = new Vector3(
tempDir.x * (bgImg.rectTransform.sizeDelta.x / mas_distance),
tempDir.y * (bgImg.rectTransform.sizeDelta.y / mas_distance));
joystickInputDirection = tempDir;
}
}
public void OnPointerDown(PointerEventData eventData)
{
released = false;
//Stop current coroutine
if (retCoroutine != null)
StopCoroutine(retCoroutine);
if (eventData.pointerCurrentRaycast.gameObject == bgImg.gameObject ||
eventData.pointerCurrentRaycast.gameObject == joystickImg.gameObject)
{
OnDrag(eventData);
}
}
public void OnPointerUp(PointerEventData eventData)
{
released = true;
//Stop current coroutine then start a new one
if (retCoroutine != null)
StopCoroutine(retCoroutine);
retCoroutine = StartCoroutine(SlowReturn(returnTime));
}
IEnumerator SlowReturn(float duration)
{
RectTransform thumbstickTransform = joystickImg.rectTransform;
Vector3 toPosition = Vector3.zero;
float counter = 0;
//Get the current position of the object to be moved
Vector2 currentThumb = thumbstickTransform.anchoredPosition;
while (counter < duration)
{
counter += Time.deltaTime;
//Slowly returns thumbstick
Vector2 tempThumbStickVal = Vector2.Lerp(currentThumb, toPosition, counter / duration);
joystickInputDirection = tempThumbStickVal;
thumbstickTransform.anchoredPosition = tempThumbStickVal;
//Slowly returns the target Object to original pos
float tempTargetObjAngle = Mathf.Lerp(angle, originalAngle, counter / duration);
rg2d.MoveRotation(tempTargetObjAngle);
yield return null;
}
}
public float returnTime = 1.0f;
public Rigidbody2D rg2d;
public bool flipRot = true;
const float originalAngle = 0;
bool released = true;
float angle;
Coroutine retCoroutine;
void FixedUpdate()
{
if (released)
return;
float horizontal = joystickInputDirection.x;
float vertical = joystickInputDirection.y;
angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rg2d.MoveRotation(angle);
}
}
your function calculates a point where you want to look at:
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.z += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
this point is on the XZ plane, that is why the car rotates on Y axis
if you want to rotate on Z axis, calculate a point on XY plane like this:
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.y += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
PS: i don't know why you multiply with Quaternion.Euler(0, 45f, 0) - that is a constant angle on Y axis, it just means that each lookDirection will be rotated for 45 degrees - i would have to see your scene to know why you need this...
I am trying to adjust the FreeLookCam script from the Standard Assets to work with my game, which has a different "world up" than standard unity projects. In my case, world up is -z, and I cannot seem to be able to adjust the script to fit this scenario.
Here is what it look like when I try it: https://www.youtube.com/watch?v=Ppb-tXvLZpI
and here is the code:
using System;
using UnityEngine;
//using UnityStandardAssets.CrossPlatformInput;
namespace UnityStandardAssets.Cameras
{
public class FreeLookCam : PivotBasedCameraRig
{
// This script is designed to be placed on the root object of a camera rig,
// comprising 3 gameobjects, each parented to the next:
// Camera Rig
// Pivot
// Camera
[SerializeField] private float m_MoveSpeed = 1f; // How fast the rig will move to keep up with the target's position.
[Range(0f, 10f)] [SerializeField] private float m_TurnSpeed = 1.5f; // How fast the rig will rotate from user input.
[SerializeField] private float m_TurnSmoothing = 0.0f; // How much smoothing to apply to the turn input, to reduce mouse-turn jerkiness
[SerializeField] private float m_TiltMax = 75f; // The maximum value of the x axis rotation of the pivot.
[SerializeField] private float m_TiltMin = 45f; // The minimum value of the x axis rotation of the pivot.
[SerializeField] private bool m_LockCursor = false; // Whether the cursor should be hidden and locked.
[SerializeField] private bool m_VerticalAutoReturn = false; // set wether or not the vertical axis should auto return
private float m_LookAngle; // The rig's y axis rotation.
private float m_TiltAngle; // The pivot's x axis rotation.
private const float k_LookDistance = 100f; // How far in front of the pivot the character's look target is.
private Vector3 m_PivotEulers;
private Quaternion m_PivotTargetRot;
private Quaternion m_TransformTargetRot;
protected override void Awake()
{
base.Awake();
// Lock or unlock the cursor.
Cursor.lockState = m_LockCursor ? CursorLockMode.Locked : CursorLockMode.None;
Cursor.visible = !m_LockCursor;
m_PivotEulers = m_Pivot.rotation.eulerAngles;
m_PivotTargetRot = m_Pivot.transform.localRotation;
m_TransformTargetRot = transform.localRotation;
}
protected void Update()
{
HandleRotationMovement();
if (m_LockCursor && Input.GetMouseButtonUp(0))
{
Cursor.lockState = m_LockCursor ? CursorLockMode.Locked : CursorLockMode.None;
Cursor.visible = !m_LockCursor;
}
}
private void OnDisable()
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
protected override void FollowTarget(float deltaTime)
{
if (m_Target == null) return;
// Move the rig towards target position.
transform.position = Vector3.Lerp(transform.position, m_Target.position, deltaTime*m_MoveSpeed);
}
private void HandleRotationMovement()
{
if(Time.timeScale < float.Epsilon)
return;
// Read the user input
// var x = CrossPlatformInputManager.GetAxis("Mouse X");
// var y = CrossPlatformInputManager.GetAxis("Mouse Y");
var x = Input.GetAxis("Mouse X");
var y = Input.GetAxis("Mouse Y");
// Adjust the look angle by an amount proportional to the turn speed and horizontal input.
m_LookAngle += x*m_TurnSpeed;
// Rotate the rig (the root object) around Y axis only:
m_TransformTargetRot = Quaternion.Euler(0f, m_LookAngle, 0f);
if (m_VerticalAutoReturn)
{
// For tilt input, we need to behave differently depending on whether we're using mouse or touch input:
// on mobile, vertical input is directly mapped to tilt value, so it springs back automatically when the look input is released
// we have to test whether above or below zero because we want to auto-return to zero even if min and max are not symmetrical.
m_TiltAngle = y > 0 ? Mathf.Lerp(0, -m_TiltMin, y) : Mathf.Lerp(0, m_TiltMax, -y);
}
else
{
// on platforms with a mouse, we adjust the current angle based on Y mouse input and turn speed
m_TiltAngle -= y*m_TurnSpeed;
// and make sure the new value is within the tilt range
m_TiltAngle = Mathf.Clamp(m_TiltAngle, -m_TiltMin, m_TiltMax);
}
// Tilt input around X is applied to the pivot (the child of this object)
// m_PivotTargetRot = Quaternion.Euler(m_TiltAngle, m_PivotEulers.y , m_PivotEulers.z);
m_PivotTargetRot = Quaternion.Euler(m_TiltAngle, m_PivotEulers.y , m_PivotEulers.z);
if (m_TurnSmoothing > 0)
{
m_Pivot.localRotation = Quaternion.Slerp(m_Pivot.localRotation, m_PivotTargetRot, m_TurnSmoothing * Time.deltaTime);
transform.localRotation = Quaternion.Slerp(transform.localRotation, m_TransformTargetRot, m_TurnSmoothing * Time.deltaTime);
}
else
{
m_Pivot.localRotation = m_PivotTargetRot;
transform.localRotation = m_TransformTargetRot;
}
}
}
}
There are two lines you need to change, both in HandleRotationMovement, and they both have to do with swapping the angles for y and z. The first one is on line 83:
// Rotate the rig (the root object) around Y axis only:
m_TransformTargetRot = Quaternion.Euler(0f, 0f, m_LookAngle);
and the second one is on line 101:
// Tilt input around X is applied to the pivot (the child of this object)
m_PivotTargetRot = Quaternion.Euler(m_TiltAngle + 90, m_PivotEulers.z , m_PivotEulers.y);
On that second line I had to add 90 degrees to get the tilt limits in the expected range in addition to swapping the y and z angles.
I have a Controller which moves my object diagonally. The left arrow should move the player forward and to the left 45 degrees, and the right arrow the same to the right. I would like to move the player relatively to its current position. Right now it moves relatively to the point(0,0,0).
My code:
public class JollyJumper : MonoBehaviour {
protected CharacterController control;
public float fTime = 1.5f; // Hop time
public float fRange = 5.0f; // Max dist from origin
public float fHopHeight = 2.0f; // Height of hop
private Vector3 v3Dest;
private Vector3 v3Last;
private float fTimer = 0.0f;
private bool moving = false;
private int number= 0;
private Vector2 direction;
public virtual void Start () {
control = GetComponent<CharacterController>();
if(!control){
Debug.LogError("No Character Controller");
enabled=false;
}
}
void Update () {
if (fTimer >= fTime&& moving) {
var playerObject = GameObject.Find("Player");
v3Last = playerObject.transform.position;
Debug.Log(v3Last);
v3Dest = direction *fRange;
//v3Dest = newVector* fRange + v3Last;
v3Dest.z = v3Dest.y;
v3Dest.y = 0.0f;
fTimer = 0.0f;
moving = false;
}
if(Input.GetKeyDown(KeyCode.LeftArrow)){
moving = true;
direction = new Vector2(1.0f, 1.0f);
number++;
}
if(Input.GetKeyDown(KeyCode.RightArrow)){
moving = true;
direction = new Vector2(-1.0f, 1.0f);
number++;
}
if(moving){
Vector3 v3T = Vector3.Lerp (v3Last, v3Dest, fTimer / fTime);
v3T.y = Mathf.Sin (fTimer/fTime * Mathf.PI) * fHopHeight;
control.transform.position = v3T;
fTimer += Time.deltaTime;
}
}
}
How can resolve this? Any ideas? Thanks a lot!
The short answer is: you hard-coded two locations you want to jump to: points (1, 1) and (-1, 1). You should create new Vector each time you start jumping. Replace each
direction = new Vector2(1.0f, 1.0f);
with this line:
v3Dest = transform.position + new Vector3(1.0f, 0, 1) * fRange;
and it should work.
While I'm on it, there are some other things I want to point:
There is a lot of floating point error after each jump. Notice that in your code v3T will never be equal to v3Dest (you never actually reach your destination), because you switch the moving flag earlier. You should explicitly set your position to v3Dest when the jump is over.
You are checking jump timers etc. every frame. A more elegent solution is to start a coroutine.
You use a sinusoid as your jump curve, which looks ok, but using a parabola would be conceptually more correct.
Right now it is possible to start next jump mid-air (I'm not sure whether it is intended or not)
Here is some code you may use that avoids those problems:
using System.Collections;
using UnityEngine;
public class Jumper : MonoBehaviour
{
#region Set in editor;
public float jumpDuration = 0.5f;
public float jumpDistance = 3;
#endregion Set in editor;
private bool jumping = false;
private float jumpStartVelocityY;
private void Start()
{
// For a given distance and jump duration
// there is only one possible movement curve.
// We are executing Y axis movement separately,
// so we need to know a starting velocity.
jumpStartVelocityY = -jumpDuration * Physics.gravity.y / 2;
}
private void Update()
{
if (jumping)
{
return;
}
else if (Input.GetKeyDown(KeyCode.LeftArrow))
{
// Warning: this will actually move jumpDistance forward
// and jumpDistance to the side.
// If you want to move jumpDistance diagonally, use:
// Vector3 forwardAndLeft = (transform.forward - transform.right).normalized * jumpDistance;
Vector3 forwardAndLeft = (transform.forward - transform.right) * jumpDistance;
StartCoroutine(Jump(forwardAndLeft));
}
else if (Input.GetKeyDown(KeyCode.RightArrow))
{
Vector3 forwardAndRight = (transform.forward + transform.right) * jumpDistance;
StartCoroutine(Jump(forwardAndRight));
}
}
private IEnumerator Jump(Vector3 direction)
{
jumping = true;
Vector3 startPoint = transform.position;
Vector3 targetPoint = startPoint + direction;
float time = 0;
float jumpProgress = 0;
float velocityY = jumpStartVelocityY;
float height = startPoint.y;
while (jumping)
{
jumpProgress = time / jumpDuration;
if (jumpProgress > 1)
{
jumping = false;
jumpProgress = 1;
}
Vector3 currentPos = Vector3.Lerp(startPoint, targetPoint, jumpProgress);
currentPos.y = height;
transform.position = currentPos;
//Wait until next frame.
yield return null;
height += velocityY * Time.deltaTime;
velocityY += Time.deltaTime * Physics.gravity.y;
time += Time.deltaTime;
}
transform.position = targetPoint;
yield break;
}
}