I’m new to Unity, and am looking at making the switch from Quest (or at least using both). Here is a link to the functionality I am trying to achieve:
Before I can consider making this change, I am trying to determine if I can create an object inspection camera that can replace what I do in Quest. I think I can get comfortable enough with scripting to be fairly successful in Unity, but I am just surprised that the scripts I have found relating to camera rotation all seem to have one major limitation or another. I have been working with several scripts that I have found on the forums, and the closest I can seem to get is with the MaxCamera.cs that someone so graciously provided. I have stripped out some of the functionality, but this is the framework.
//
//
// original: [url]http://www.unifycommunity.com/wiki/index.php?title=MouseOrbitZoom[/url]
//
// --01-18-2010 - create temporary target, if none supplied at start
using UnityEngine;
using System.Collections;
[AddComponentMenu("Camera-Control/3dsMax Camera Style")]
public class maxCamera : MonoBehaviour
{
public Transform target;
public Vector3 targetOffset;
public float distance = 5.0f;
public float maxDistance = 20;
public float minDistance = .6f;
public float xSpeed = 200.0f;
public float ySpeed = 200.0f;
public int yMinLimit = -80;
public int yMaxLimit = 80;
public int zoomRate = 40;
public float panSpeed = 0.3f;
public float zoomDampening = 5.0f;
private float xDeg = 0.0f;
private float yDeg = 0.0f;
private float currentDistance;
private float desiredDistance;
private Quaternion currentRotation;
private Quaternion desiredRotation;
private Quaternion rotation;
private Vector3 position;
void Start() { Init(); }
void OnEnable() { Init(); }
public void Init()
{
//If there is no target, create a temporary target at 'distance' from the cameras current viewpoint
if (!target)
{
GameObject go = new GameObject("Cam Target");
go.transform.position = transform.position + (transform.forward * distance);
target = go.transform;
}
distance = Vector3.Distance(transform.position, target.position);
currentDistance = distance;
desiredDistance = distance;
//be sure to grab the current rotations as starting points.
position = transform.position;
rotation = transform.rotation;
currentRotation = transform.rotation;
desiredRotation = transform.rotation;
xDeg = Vector3.Angle(Vector3.right, transform.right );
yDeg = Vector3.Angle(Vector3.up, transform.up );
}
/*
* Camera logic on LateUpdate to only update after all character movement logic has been handled.
*/
void LateUpdate()
{
// If right mouse is selected? ORBIT
if (Input.GetMouseButton(1) )
{
xDeg += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
yDeg -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
////////OrbitAngle
//Clamp the vertical axis for the orbit
yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
// set camera rotation
desiredRotation = Quaternion.Euler(yDeg, xDeg, 0);
currentRotation = transform.rotation;
rotation = Quaternion.Lerp(currentRotation, desiredRotation, Time.deltaTime * zoomDampening);
transform.rotation = rotation;
}
////////Orbit Position
// affect the desired Zoom distance if we roll the scrollwheel
desiredDistance -= Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs(desiredDistance);
//clamp the zoom min/max
desiredDistance = Mathf.Clamp(desiredDistance, minDistance, maxDistance);
// For smoothing of the zoom, lerp distance
currentDistance = Mathf.Lerp(currentDistance, desiredDistance, Time.deltaTime * zoomDampening);
// calculate position based on the new currentDistance
position = target.position - (rotation * Vector3.forward * currentDistance + targetOffset);
transform.position = position;
}
private static float ClampAngle(float angle, float min, float max)
{
if (angle < -180)
angle += 180;
if (angle > 180)
angle -= 180;
return Mathf.Clamp(angle, min, max);
}
}
The problems I currently have are:
The rotation gets off kilter somewhere between the x and y axis. It starts to bank, I guess, and becomes disorienting.
If you’ll notice on the Quest file, when I let go of the mouse, the rotation damping still works. In this Unity script, dampening only works if the mouse is still down.
I would just appreciate any suggestions or at least an acknowledgement that such things are possible, so that I can feel like I’m not spinning my wheels (so to speak).
The short answer is, yes, you can do that. The slightly longer answer is that because you have access to a game object’s transform in Unity, any type of motion or camera control is possible; you just have to write the appropriate code.
Unless you get lucky and find an existing script that implements exactly the type of camera control you’re looking for, you’ll probably have to write the code yourself. But, it shouldn’t be too difficult.
Based on the demo you provided, it looks like the camera is always looking at the target object (which can be achieved using the Transform.LookAt() function), and is able to rotate 360 degrees around the object and pitch up and down within prescribed limits. In other words, you’re looking for a basic orbital camera with damping.
I didn’t look carefully at the code you posted, but if the code is intended to implement an orbital camera of the type in question and the camera is banking, then the code is doing something wrong.
So in summary, yes, it’s possible, but you might have to write the code yourself (or get someone to write it for you).
The link you posted seems to be to a Windows executable, which I can’t run on my Mac… That said, you can script pretty much any camera behaviour you want, so the answer to ‘is it possible?’ is yes.
If you can describe what behaviour you want, more people may be able to offer help. Otherwise you’ll have to wait for a Windows user to wander into the thread
@Laurie Yeah, sorry for the Windows exe. One of the reasons why I want to add Unity to the arsenal is that Quest standalone files are limited to Windows. I may try to post a web link that requires a plugin.
@Jesse And thanks for steering me in the right direction. I will look into the Transform.LookAt() function, and see what I can come up with.
Well, and I forgot that even the webviewer plugin only works in Windows. Apparently, it’s because the platform is DirectX. In case any Windows users would rather not download the exe, here is the webviewer:
Well, I figured out the damping issue. Just removed the damping code outside of the mouse event if/then.
But, I am still having trouble with the camera banking. I don’t want any Z rotation at all. Should I using localRotation instead of just Rotation. Is this a function of the Euler calculation? Any thoughts?
If I were implementing something like this from scratch, I’d probably either work directly with azimuth/elevation angles and rebuild the orientation from scratch each update (which would eliminate the possibility of any unwanted rolling or banking), or apply a corrective rotation each update to ensure that the camera remains aligned with the up axis.
I’d have to study the code you posted in order to tell you where the behavior you’re describing is coming from, but I can tell you that in principle, there’s absolutely no reason that a properly implemented orbital camera should exhibit unwanted banking or rolling.
Thanks for the response. Well, here is the code as it stands now. Almost exact the maxCamera script, but with the dampening placement moved outside of the mouse event.
I have tried a few things to limit the z rotation. I tried to clamp it, I tried switching from vector3 to vector2, but I realize I am probably grasping at straws. Its frustrating to be this close to what I need.
//
//Filename: maxCamera.cs
//
// original: [url]http://www.unifycommunity.com/wiki/index.php?title=MouseOrbitZoom[/url]
//
// --01-18-2010 - create temporary target, if none supplied at start
using UnityEngine;
using System.Collections;
[AddComponentMenu("Camera-Control/3dsMax Camera Style")]
public class maxCamera : MonoBehaviour
{
public Transform target;
public Vector3 targetOffset;
public float distance = 5.0f;
public float maxDistance = 20;
public float minDistance = .6f;
public float xSpeed = 200.0f;
public float ySpeed = 200.0f;
public int yMinLimit = -80;
public int yMaxLimit = 80;
public int zMinLimit = 0;
public int zMaxLimit = 0;
public int zoomRate = 40;
public float panSpeed = 0.3f;
public float zoomDampening = 5.0f;
private float xDeg = 0.0f;
private float yDeg = 0.0f;
private float zDeg = 0.0f;
private float currentDistance;
private float desiredDistance;
private Quaternion currentRotation;
private Quaternion desiredRotation;
private Quaternion rotation;
private Vector3 position;
void Start() { Init(); }
void OnEnable() { Init(); }
public void Init()
{
//If there is no target, create a temporary target at 'distance' from the cameras current viewpoint
if (!target)
{
GameObject go = new GameObject("Cam Target");
go.transform.position = transform.position + (transform.forward * distance);
target = go.transform;
}
distance = Vector3.Distance(transform.position, target.position);
currentDistance = distance;
desiredDistance = distance;
//be sure to grab the current rotations as starting points.
position = transform.position;
rotation = transform.rotation;
currentRotation = transform.localRotation;
desiredRotation = transform.localRotation;
xDeg = Vector3.Angle(Vector3.right, transform.right );
yDeg = Vector3.Angle(Vector3.up, transform.up );
//zDeg = Vector2.Distance(Vector2.forward, transform.forward );
}
/*
* Camera logic on LateUpdate to only update after all character movement logic has been handled.
*/
void LateUpdate()
{
// If right mouse is selected? ORBIT
if (Input.GetMouseButton(1) )
{
xDeg += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
yDeg -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
}
////////OrbitAngle
//Clamp the vertical axis for the orbit
//xDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
zDeg = ClampAngle(zDeg, zMinLimit, zMaxLimit);
// set camera rotation
desiredRotation = Quaternion.Euler(yDeg, xDeg, 0);
currentRotation = transform.rotation;
rotation = Quaternion.Lerp(currentRotation, desiredRotation, Time.deltaTime * zoomDampening);
transform.rotation = rotation;
////////Orbit Position
// affect the desired Zoom distance if we roll the scrollwheel
desiredDistance -= Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs(desiredDistance);
//clamp the zoom min/max
desiredDistance = Mathf.Clamp(desiredDistance, minDistance, maxDistance);
// For smoothing of the zoom, lerp distance
currentDistance = Mathf.Lerp(currentDistance, desiredDistance, Time.deltaTime * zoomDampening);
// calculate position based on the new currentDistance
position = target.position - (rotation * Vector3.forward * currentDistance + targetOffset);
transform.position = position;
}
private static float ClampAngle(float angle, float min, float max)
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp(angle, min, max);
}
}