How to scale a gameObject with pinch? - c#

How do I scale gameObject (cube) with a pinch (2 fingers) in Android/iOS?. If the pinch is expanding, then the cube's scale (x, y, z) gets bigger and if the pinch is closing down, then the cube's scale gets smaller.
I have a script that was for mouse drag but only scales for 2 axis (x, y).
public class Drag : MonoBehaviour
{
Vector3 lastMousePosition;
float scaleSensitivity = 2f;
void Update()
{
if (Input.GetMouseButtonDown(0)) {
lastMousePosition = Input.mousePosition;
}
if (Input.GetMouseButton(0)) {
transform.localScale += ((Input.mousePosition - lastMousePosition) * Time.deltaTime * scaleSensitivity);
lastMousePosition = Input.mousePosition;
}
}
}

Take a look at Input.Touches : Input.Touches and use difference (Input.Touches[0] - Input.Touches[1]).magnitude, fixing every frame that difference. If it's becoming bigger with every frame, that means user is scaling up, else if it's becoming smaller, that means user is scaling down
float? _prevFrameDiff;
void Update()
{
if (Input.touches.Length < 2)
{
_prevFrameDiff = null;
return;
}
var diff = (Input.touches[0].position - Input.touches[1].position).magnitude;
if (!_prevFrameDiff.HasValue)
{
_prevFrameDiff = diff;
return;
}
else
{
var scaleFactor = diff / _prevFrameDiff.Value;
this.transform.localScale = Vector3.one * scaleFactor;
}
}

Related

Why my Input touch code doesn't work on AR Foundation?

Trying to build an AR app where I can have a number of input touch like drag, rotate, scale, double tap to event, hold on object to event etc.
Everything works fine in a test scene I have built [not AR].
Once included the code in my AR placedOnPlane prefab [template scene - place on plane], once I touch the object, it disappears, and I cannot figure out what I am doing wrong!
Finally, I took advantage of LeanTouch and everything works fine (why? because it's a badass asset), but I usually hate using assets when I have my code that is working equally good and I spent days on it! Some help please.
I tried commenting out the built in drag function in the PlacedOnPlane code that comes with the scene of ARfoundation but it didn't work.
using UnityEngine;
using System.Collections;
using UnityEngine.iOS;
public class InputTouchUnity : MonoBehaviour
{
private Vector3 position;
private float width;
private float height;
public float speedDrag= 0.1f;
float initialFingersDistance;
float speedTwist = -4000f;
private float baseAngle = 0.0f;
Vector3 initialScale;
// scale clamp
//public float scalingSpeed = 0.03f;
public Vector3 min = new Vector3(292f, 292f, 292f);
public Vector3 max = new Vector3(800f, 800f, 800f);
// int tapCount;
// float doubleTapTimer;
void Awake()
{
width = (float)Screen.width / 2.0f;
height = (float)Screen.height / 2.0f;
//Position used for the cube.
position = this.transform.position;
}
void OnGUI() // TO OBSERVE MOTION
{
// Compute a fontSize based on the size of the screen width.
GUI.skin.label.fontSize = (int)(Screen.width / 25.0f);
GUI.Label(new Rect(20, 20, width, height * 0.25f),
"x = " + position.x.ToString("f2") +
", y = " + position.z.ToString("f2"));
}
void Update()
{
// Handle screen touches.
if (Input.touchCount > 0)
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
initialScale = transform.localScale;
{
{
{
//DRAG - got rid of it because conflicting the AR drag
Touch touch = Input.GetTouch(0);
Move the cube if the screen has the finger moving.
if (Input.touchCount == 2)
{
if (touch.phase == TouchPhase.Moved)
{
Vector2 pos = touch.position;
pos.x = (pos.x - width) / width;
pos.y = (pos.y - height) / height;
position = new Vector3(transform.position.x + pos.x * speedDrag, 0, transform.position.y + pos.y * speedDrag);
// Position the cube.
transform.position = position;
}
}
//SCALE
if (Input.touchCount == 2)
{
Touch touch1 = Input.GetTouch(0);
if (touch1.phase == TouchPhase.Began)
{
initialFingersDistance = Vector2.Distance(Input.touches[0].position , Input.touches[1].position);
initialScale = transform.localScale;
}
else
{
var currentFingersDistance = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
var scaleFactor = (currentFingersDistance / initialFingersDistance );
transform.localScale = initialScale * scaleFactor;
Debug.Log(transform.localScale);
GameObject[] models = GameObject.FindGameObjectsWithTag ("ARobject");
newScale.x = Mathf.Clamp(model.localScale.x - scaleFactor, min.x, max.x);
newScale.y = Mathf.Clamp(model.localScale.y - scaleFactor, min.y, max.y);
newScale.z = Mathf.Clamp(model.localScale.z - scaleFactor, min.z, max.z);
model.localScale = newScale;
}
}
//TWIST
if (Input.touchCount == 2)
{
Touch touch2 = Input.GetTouch(0);
if (touch2.phase == TouchPhase.Began)
{
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
baseAngle = Mathf.Atan2(pos.y, pos.x) * Mathf.Deg2Rad;
baseAngle -= Mathf.Atan2(transform.right.y, transform.right.x) * Mathf.Rad2Deg;
}
if (touch2.phase == TouchPhase.Moved)
{
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
float ang = Mathf.Atan2(pos.y, pos.x) * Mathf.Deg2Rad - baseAngle;
transform.rotation = Quaternion.AngleAxis(ang * speedTwist, Vector3.up);
}
}
}
}
}
}
}
}
//}
This is because you are using Physics.Raycast which does't work on AR trackables(Planes) because they don't have any specific geometry associated with them.
So to interact with trackable data unity has provided separate Raycast method that is available in ARRaycastManager in ARFoundation.
In previous version of ARFoundation it was available in ARSessionOrigin. So check which version you are using of Ar Foundation.
You can use it like this
enter code here
`
[SerializeField] ARRaycast​Manager raycastManager;
void Update()
{
if (Input.touchCount == 0)
return;
Touch touch = Input.GetTouch(0);
if (raycastManager.Raycast(touch.position,s_Hits,TrackableType.PlaneWithinPolygon))
{
// Raycast hits are sorted by distance, so the first one
// will be the closest hit.
var hitPose = s_Hits[0].pose;
if (spawnedObject == null)
{
spawnedObject = Instantiate(cube, hitPose.position, hitPose.rotation);
}
else
{
spawnedObject.transform.position = hitPose.position;
}
}
}
`
You can also refer to SimpleAR scene from Ar Foundation sample scenes available here: https://github.com/Unity-Technologies/arfoundation-samples

Issue with smooth camera zoom in Unity

I am trying to make my camera smoothly zoom in and out whenever I use a mouse scroll wheel, but for some reason it zooms instantly and not smoothly.
This is how I update the zoom:
[SerializeField, Range(10, 100)] float scrollSpeed = 10f;
[SerializeField] Vector2 zoomAmount = Vector2.zero;
private float ScrollWheel
{
get { return Input.GetAxis("Mouse ScrollWheel"); }
}
private new Camera camera = null;
void Update()
{
if (camera == null) return;
UpdateZoom();
}
void UpdateZoom()
{
pos = camera.transform.position;
pos = Vector3.Lerp(pos, new Vector3(pos.x, pos.y - scrollSpeed * 10 * ScrollWheel, pos.z), Time.deltaTime * 2);
pos.y = Mathf.Clamp(pos.y, zoomAmount.x, zoomAmount.y);
camera.transform.position = pos;
}
I think you're a little confused about Lerp (I was confused in the same way too when I started using it). When you pass in a 0 for the time argument (the third argument), it will return your "starting" vector. When you pass a value of 1 or more for the time argument, it will return your "ending" vector. However, if you're passing Time.deltaTime * 2, then you'll be returning approximately the same interpolated vector every frame. Lerp doesn't "track" how far it's already interpolated, so by passing in the same time value every frame, Lerp will never actually return your ending vector. So rather than passing Time.deltaTime * 2, you'd need to do something like this
float interpolatedTime = 0;
void Update()
{
var myVector = Vector3.Lerp(vector1, vector2, this.interpolatedTime);
this.interpolatedTime += Time.deltaTime;
}
How about something like this for your camera:
float zoomTime;
float zoomTarget;
float lastScrollWheelDirection;
void Update()
{
// If this camera is currently zooming in and the player started zooming
// out (or vice versa), reset the amount that is remaining to be zoomed
if ((this.lastScrollWheelDirection > 0 && this.ScrollWheel < 0) ||
(this.lastScrollWheelDirection < 0 && this.ScrollWheel > 0))
{
this.zoomTarget = 0;
}
if (this.ScrollWheel != 0)
{
this.lastScrollWheelDirection = this.ScrollWheel;
}
// zoomTarget is the total distance that is remaining to be zoomed.
// Each frame that the scroll wheel is moved, we'll add a little more
// to the distance that we want to zoom
zoomTarget += this.ScrollWheel * this.scrollSpeed;
// zoomTime is used to do linear interpolation to create a smooth zoom.
// Each time the player moves the mouse wheel, we reset zoomTime so that
// we restart our linear interpolation
if (this.ScrollWheel != 0)
{
this.zoomTime = 0;
}
if (this.zoomTarget != 0)
{
this.zoomTime += Time.deltaTime;
// Calculate how much our camera will be moved this frame using linear
// interpolation. You can adjust how fast the camera zooms by
// changing the divisor for zoomTime
var translation = Vector3.Lerp(
new Vector3(0, 0, 0),
new Vector3(0, this.zoomTarget, 0),
zoomTime / 4f); // see comment above
// Zoom the camera by the amount that we calculated for this frame
this.transform.position -= translation;
// Decrease the amount that's remaining to be zoomed by the amount
// that we zoomed this frame
this.zoomTarget -= translation.y;
}
}

Unity3D - C# 360 Orbital Camera Controller (Gimbal Lock Issue)

I have a stationary cube in my scene that I'm orbiting a camera around. I have my MainCamera nested under a GameObject that I'm calling 'OrbitalCamera'.
I setup the script so that a click (or tap) and drag will rotate the camera around the object in space so it feels like you're rotating the cube (ie: if I click the top of a face on the cube, and pull down, I'm rotating the X value) but you'll actually be rotating the camera.
For the most part, my script works. However, after rotating the Y so much, the camera is upside down and the X gets inverted. Here's my script:
public class OrbitalCamera : MonoBehaviour {
public bool cameraEnabled;
[SerializeField] private float touchSensitivity;
[SerializeField] private float scrollSensitivity;
[SerializeField] private float orbitDampening;
protected Transform xFormCamera;
protected Transform xFormParent;
protected Vector3 localRotation;
protected float cameraDistance;
void Start () {
cameraEnabled = true;
xFormCamera = transform;
xFormParent = transform.parent;
cameraDistance = transform.position.z * -1;
}
void LateUpdate () {
if (cameraEnabled) {
// TODO:: FIX PROBLEM WHERE WHEN CAMERA IS ROTATED TO BE UPSIDEDOWN, CONTROLS GET INVERSED
if (Input.GetMouseButton(0)) {
if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0) {
localRotation.x += Input.GetAxis("Mouse X") * touchSensitivity;
localRotation.y -= Input.GetAxis("Mouse Y") * touchSensitivity;
}
}
}
Quaternion qt = Quaternion.Euler(localRotation.y, localRotation.x, 0);
xFormParent.rotation = Quaternion.Lerp(xFormParent.rotation, qt, Time.deltaTime * orbitDampening);
}
}
Is there a good method to achieve this type of 360 camera? I'd like dragging from right to left to always move the camera left and dragging left to right to always move the camera right -- no matter how the camera is oriented.
Perhaps you could clamp the above/below pan at 89 degrees?
I recently helped a friend make a mouse gimbal, and found allowing freedom beyond 89 degrees was problematic and unnecessary. It seems like your application is the same, at least for one of the two planes.
In your LateUpdate() call, you could perhaps add:
localRotation.x += Input.GetAxis("Mouse X") * touchSensitivity;
localRotation.x = Clamp(localRotation.x);
Then, of course, create your clamp function, which should be fairly straight forward:
float Clamp(float val) // prevent values from ~90 - ~270
{
int lowVal = 89;
int highVal = 271;
int midVal = 180;
if (val > lowVal & val < highVal)
{
if (val > midVal) val = highVal;
else val = lowVal;
}
return val;
}
A slightly different application, but I'm sure you can see how I've set this up: I apply rotation in two steps. Step 1 - a simple Rotate() call, Step 2 - clamping some/all of the rotation:
using UnityEngine;
public class MouseGimbal : MonoBehaviour
{
[SerializeField] [Range(0,89)] float maxRotationDegrees = 10.0f; // At 90+ gimbal oddities must be dealt with.
[SerializeField] bool ClampToMaxRotationDegrees = true; // Disable for free rotation.
[SerializeField] float rotationSpeed = 10.0f;
const float fullArc = 360.0f;
const float halfArc = 180.0f;
const float nullArc = 0.0f;
void Update () { Tilt(); }
void Tilt()
{
// Apply the 'pre-clamp' rotation (rotation-Z and rotation-X from X & Y of mouse, respectively).
if (maxRotationDegrees > 0) { SimpleRotation(GetMouseInput()); }
// Clamp rotation to maxRotationDegrees.
if (ClampToMaxRotationDegrees) { ClampRotation(transform.rotation.eulerAngles); }
}
void ClampRotation(Vector3 tempEulers)
{
tempEulers.x = ClampPlane(tempEulers.x);
tempEulers.z = ClampPlane(tempEulers.z);
tempEulers.y = nullArc; // ClampPlane(tempEulers.y); // *See GIST note below...
transform.rotation = Quaternion.Euler(tempEulers);
///Debug.Log(tempEulers);
}
float ClampPlane(float plane)
{
if (OkayLow(plane) || OkayHigh(plane)) DoNothing(); // Plane 'in range'.
else if (BadLow(plane)) plane = Mathf.Clamp(plane, nullArc, maxRotationDegrees);
else if (BadHigh(plane)) plane = Mathf.Clamp(plane, fullArc - maxRotationDegrees, fullArc);
else Debug.LogWarning("WARN: invalid plane condition");
return plane;
}
Vector2 GetMouseInput()
{
Vector2 mouseXY;
mouseXY.x = -Input.GetAxis("Mouse X"); // MouseX -> rotZ.
mouseXY.y = Input.GetAxis("Mouse Y"); // MouseY -> rotX.
return mouseXY;
}
void SimpleRotation(Vector2 mouseXY)
{
Vector3 rotation = Vector3.zero;
rotation.x = mouseXY.y * Time.deltaTime * rotationSpeed;
rotation.z = mouseXY.x * Time.deltaTime * rotationSpeed;
transform.Rotate(rotation, Space.Self);
}
void DoNothing() { }
bool OkayHigh(float test) { return (test >= fullArc - maxRotationDegrees && test <= fullArc); }
bool OkayLow(float test) { return (test >= nullArc && test <= maxRotationDegrees); }
bool BadHigh(float test) { return (test > halfArc && !OkayHigh(test)); }
bool BadLow(float test) { return (test < halfArc && !OkayLow(test)); }
}
When I developed an orbital camera, I created 3 objects:
- x_axis (rotate with vertical mouse moviment)
- y_axis (rotate with horizontal mouse moviment)
- camera (look_at object) (translated Vector3(0,0,-10))

How to move 2D Object within camera view boundary

I have a scene that my camera doesn't follow my player. When player reaches the end of camera I want player to can't go further (out of camera view). How can I do this?
My codes for movement
public class PlayerBlueController : MonoBehaviour {
public float speed;
private float x;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void FixedUpdate () {
x = Input.GetAxis ("Horizontal") / 100 * speed;
transform.Translate (x,0,0);
}
}
As you can see from this. It gets out of camera's view.
I noticed you used a Collider2D. You should be using Rigidbody2D.MovePosition instead of transform.Translate or you'll likely run into issues when transform.Translate is used.
1.Take the final move position and convert it to new position in ViewPortPoint with Camera.main.WorldToViewportPoint
2.Apply a limit with Mathf.Clamp to the result in #1.
3.Convert the ViewPortPoint back to world point with Camera.main.ViewportToWorldPoint.
4.Finally, move it with Rigidbody2D.MovePosition.
The code below is modified from this answer to include restriction to screen boundary.
Move without Rigidbody:
Use only if collision and physics are NOT required:
public float speed = 100;
public Transform obj;
public void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
//Move only if we actually pressed something
if ((h > 0 || v > 0) || (h < 0 || v < 0))
{
Vector3 tempVect = new Vector3(h, v, 0);
tempVect = tempVect.normalized * speed * Time.deltaTime;
Vector3 newPos = obj.transform.position + tempVect;
checkBoundary(newPos);
}
}
void checkBoundary(Vector3 newPos)
{
//Convert to camera view point
Vector3 camViewPoint = Camera.main.WorldToViewportPoint(newPos);
//Apply limit
camViewPoint.x = Mathf.Clamp(camViewPoint.x, 0.04f, 0.96f);
camViewPoint.y = Mathf.Clamp(camViewPoint.y, 0.07f, 0.93f);
//Convert to world point then apply result to the target object
obj.position = Camera.main.ViewportToWorldPoint(camViewPoint);
}
Move Object with Rigidbody2D:
Use if collision and physics are required:
public float speed = 100;
public Rigidbody2D rb;
public void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
//Move only if we actually pressed something
if ((h > 0 || v > 0) || (h < 0 || v < 0))
{
Vector3 tempVect = new Vector3(h, v, 0);
tempVect = tempVect.normalized * speed * Time.deltaTime;
//rb.MovePosition(rb.transform.position + tempVect);
Vector3 newPos = rb.transform.position + tempVect;
checkBoundary(newPos);
}
}
void checkBoundary(Vector3 newPos)
{
//Convert to camera view point
Vector3 camViewPoint = Camera.main.WorldToViewportPoint(newPos);
//Apply limit
camViewPoint.x = Mathf.Clamp(camViewPoint.x, 0.04f, 0.96f);
camViewPoint.y = Mathf.Clamp(camViewPoint.y, 0.07f, 0.93f);
//Convert to world point then apply result to the target object
Vector3 finalPos = Camera.main.ViewportToWorldPoint(camViewPoint);
rb.MovePosition(finalPos);
}
image not respond .
but you can check player location
x = Input.GetAxis ("Horizontal") / 100 * speed;
if(gameobject.transform.x > someValue)
x=0
gameobject will be OBJECT in scene that u attach class to it.
another way is place 2 empty gameobject with collider as invisibleWall and get collider to player
Possible solution is:
1.Get the coordinates of your screen cornes (top left, top right, bottom left, bottom right). You can get this coordinates using Screen.height and Screen.width.
2.Convert this coordinates using the camera you need with Camera.ScreenToWorldPoint.
3.Get your player coordinates and check that they are inside the rect which is formed by 4 coordinates of the screen corners.
You need to limit your transform's position based on the edges of the camera. Here is an answer describing the different coordinate systems in unity
You're probably looking to do something like this:
float xMin = Camera.main.ViewportToWorldPoint(Vector3.zero).x;
float xMax = Camera.main.ViewportToWorldPoint(Vector3.one).x;
Vector3 currentPos = transform.position;
float dx = Input.GetAxis ("Horizontal") / 100 * speed;
Vector3 desiredPos = new Vector3(currentPos.x + dx, currentPos.y, currentPos.z);
Vector3 realPos = desiredPos;
if(desiredPos.x > xMax)
realPos.x = xMax;
else if(desiredPos.x < xMin)
realPos.x = xMin;
transform.position = realPos;
Read up here for more info on ViewportToWorldPoint(), it's extremely useful to become comfortable with the different coordinate spaces and how you can convert between them.

How to increase main-camera height for thirdpersoncontroller in Unity

I have an issue whereby I am unable to uplift camera height to head of third-person controller, always it is in the middle of third-person controller. Could anyone help me on this regard.
This is image description
Here is the code attatched to my thirdpersoncontroller:
public class ThirdPersonCam : NetworkBehaviour
{
public Transform cameraTransform;
private Transform _target;
// The distance in the x-z plane to the target
public float distance = 2.0f;
// the height we want the camera to be above the target
public float height = 0.0f;
public float angularSmoothLag = 0.0000001f;
public float angularMaxSpeed = 100000000.0f;
public float heightSmoothLag = 0.3f;
public float snapSmoothLag = 0.2f;
public float snapMaxSpeed = 720.0f;
public float clampHeadPositionScreenSpace = 0.75f;
public float lockCameraTimeout = 0.2f;
private Vector3 headOffset = Vector3.zero;
private Vector3 centerOffset = Vector3.zero;
private float heightVelocity = 0.0f;
private float angleVelocity = 0.0f;
private bool snap = false;
private ThirdPersonShooter controller;
private float targetHeight = 100000.0f;
void Awake()
{
if (!cameraTransform && Camera.main)
cameraTransform = Camera.main.transform;
if (!cameraTransform)
{
Debug.Log("Please assign a camera to the ThirdPersonCamera script.");
enabled = false;
}
_target = transform;
if (_target)
{
controller = _target.GetComponent<ThirdPersonShooter>();
}
if (controller)
{
CharacterController characterController = _target.GetComponent<CharacterController>();
centerOffset = characterController.bounds.center - _target.position;
headOffset = centerOffset;
headOffset.y = characterController.bounds.max.y - _target.position.y;
}
else
Debug.Log("Please assign a target to the camera that has a ThirdPersonController script attached.");
Cut(_target, centerOffset);
}
void DebugDrawStuff()
{
Debug.DrawLine(_target.position, _target.position + headOffset);
}
public float AngleDistance(float a, float b)
{
a = Mathf.Repeat(a, 360);
b = Mathf.Repeat(b, 360);
return Mathf.Abs(b - a);
}
void Apply(Transform dummyTarget, Vector3 dummyCenter)
{
// Early out if we don't have a target
if (!controller)
return;
Vector3 targetCenter = _target.position + centerOffset;
Vector3 targetHead = _target.position + headOffset;
// DebugDrawStuff();
// Calculate the current & target rotation angles
float originalTargetAngle = _target.eulerAngles.y;
float currentAngle = cameraTransform.eulerAngles.y;
// Adjust real target angle when camera is locked
float targetAngle = originalTargetAngle;
// When pressing Fire2 (alt) the camera will snap to the target direction real quick.
//// It will stop snapping when it reaches the target
//if (Input.GetButton("LockOn"))
// snap = true;
if (snap)
{
// We are close to the target, so we can stop snapping now!
if (AngleDistance(currentAngle, originalTargetAngle) < 3.0f)
snap = false;
currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, snapSmoothLag, snapMaxSpeed);
}
// Normal camera motion
else
{
if (controller.GetLockCameraTimer() < lockCameraTimeout)
{
targetAngle = currentAngle;
}
// Lock the camera when moving backwards!
// * It is really confusing to do 180 degree spins when turning around.
if (AngleDistance(currentAngle, targetAngle) > 160 && controller.IsMovingBackwards())
targetAngle += 180;
currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, angularSmoothLag, angularMaxSpeed);
}
// When jumping don't move camera upwards but only down!
if (controller.IsJumping())
{
// We'd be moving the camera upwards, do that only if it's really high
float newTargetHeight = targetCenter.y + height;
if (newTargetHeight < targetHeight || newTargetHeight - targetHeight > 5)
targetHeight = targetCenter.y + height;
}
// When walking always update the target height
else
{
targetHeight = targetCenter.y + height;
}
// Damp the height
float currentHeight = cameraTransform.position.y;
currentHeight = Mathf.SmoothDamp(currentHeight, targetHeight, ref heightVelocity, heightSmoothLag);
// Convert the angle into a rotation, by which we then reposition the camera
Quaternion currentRotation = Quaternion.Euler(0, currentAngle, 0);
// Set the position of the camera on the x-z plane to:
// distance meters behind the target
cameraTransform.position = targetCenter;
cameraTransform.position += currentRotation * Vector3.back * distance;
Vector3 tempCameraTransformPos = cameraTransform.position;
tempCameraTransformPos.y = currentHeight;
cameraTransform.position = tempCameraTransformPos;
// Always look at the target
SetUpRotation(targetCenter, targetHead);
}
void LateUpdate()
{
Apply(transform, Vector3.zero);
}
void Cut(Transform dummyTarget, Vector3 dummyCenter)
{
float oldHeightSmooth = heightSmoothLag;
float oldSnapMaxSpeed = snapMaxSpeed;
float oldSnapSmooth = snapSmoothLag;
snapMaxSpeed = 10000;
snapSmoothLag = 0.001f;
heightSmoothLag = 0.001f;
snap = true;
Apply(transform, Vector3.zero);
heightSmoothLag = oldHeightSmooth;
snapMaxSpeed = oldSnapMaxSpeed;
snapSmoothLag = oldSnapSmooth;
}
void SetUpRotation(Vector3 centerPos, Vector3 headPos)
{
// Now it's getting hairy. The devil is in the details here, the big issue is jumping of course.
// * When jumping up and down we don't want to center the guy in screen space.
// This is important to give a feel for how high you jump and avoiding large camera movements.
//
// * At the same time we dont want him to ever go out of screen and we want all rotations to be totally smooth.
//
// So here is what we will do:
//
// 1. We first find the rotation around the y axis. Thus he is always centered on the y-axis
// 2. When grounded we make him be centered
// 3. When jumping we keep the camera rotation but rotate the camera to get him back into view if his head is above some threshold
// 4. When landing we smoothly interpolate towards centering him on screen
Vector3 cameraPos = cameraTransform.position;
Vector3 offsetToCenter = centerPos - cameraPos;
// Generate base rotation only around y-axis
Quaternion yRotation = Quaternion.LookRotation(new Vector3(offsetToCenter.x, 0, offsetToCenter.z));
Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height;
cameraTransform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);
// Calculate the projected center position and top position in world space
Ray centerRay = cameraTransform.GetComponent<Camera>().ViewportPointToRay(new Vector3(.5f, 0.5f, 1f));
Ray topRay = cameraTransform.GetComponent<Camera>().ViewportPointToRay(new Vector3(.5f, clampHeadPositionScreenSpace, 1f));
Vector3 centerRayPos = centerRay.GetPoint(distance);
Vector3 topRayPos = topRay.GetPoint(distance);
float centerToTopAngle = Vector3.Angle(centerRay.direction, topRay.direction);
float heightToAngle = centerToTopAngle / (centerRayPos.y - topRayPos.y);
float extraLookAngle = heightToAngle * (centerRayPos.y - centerPos.y);
if (extraLookAngle < centerToTopAngle)
{
extraLookAngle = 0;
}
else
{
extraLookAngle = extraLookAngle - centerToTopAngle;
cameraTransform.rotation *= Quaternion.Euler(-extraLookAngle, 0, 0);
}
}
public Vector3 GetCenterOffset()
{
return centerOffset;
}
}

Categories

Resources