I have the following script, but it rotates totally uncontrolled when I hold down the mouse. Can’t seem to figure out what’s wrong. Any help/input is appreciated.
if (isMouseDown)
{
if (castRay(Input.mousePosition))
{
Vector3 dir = hitInfo.point - transform.position;
dir.Normalize();
float angle = Vector3.Angle(transform.forward, dir);
Vector3 crossProd = Vector3.Cross(transform.forward, dir);
transform.Rotate(crossProd, angle);
}
}
bool castRay(Vector3 pos)
{
Ray ray = Camera.main.ScreenPointToRay(pos);
if (Physics.Raycast (ray, out hitInfo))
return true;
return false;
}
Ehh, well I am on the exact same problem. But more precisely I want to rotate my spherical object as it was a trackball.
So this means that independently of its axes it always should rotate left when you drag left and up when you drag up.
At the moment I am using this script to rotate it (I am just putting my code into yours):
if (isMouseDown)
{
if (castRay(Input.mousePosition))
{
whateverGameObj.transform.rotation *= Quaternion.Euler(Input.mousePosition.y, -Input.mousePosition.x, 0);
}
}
However this code rotates as expected only when spherical object is facing the user. Once it turns 180°, either vertical or horizontal axis rotation get inversed compared to drag direction. And this is not a trackball behaviour.
So if anyone else could achieve a true trackball rotation, it would be nice to hear about his/her solution.
Thanks a lot.
Greg
EDIT:
I made one mistake.
instead of using the Input.mousePosition in the Quaternion.Euler method you must use the Event.current.delta.x and Event.current.delta.y of the MouseDrag event.
So
public void OnGUI()
{
if (Event.current.type == EventType.MouseDrag)
{
rotate(Event.current.delta);
}
}
public void rotate(Vector2 rot)
{
whateverGameObj.transform.rotation *= Quaternion.Euler(rot.y, -rot.x, 0);
}
In case your axis are inverted, just change the sign of the rot.x or rot.y
The approach of Immortalis is the good one, but there are some stuff that is missing!
So here is the code that is almost perfect. There is a small lag when changing directions without releasing the mouse button. Please help me fine tune it!
//Variables that we will need
Vector3 center;
Vector3 axis;
float angle;
RaycastHit oldHitPoint;
RaycastHit newHitPoint;
public void OnGUI()
{
if (Event.current.type == EventType.mouseDown)
{
Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out oldHitPoint);
center = whateverGameObj.transform.position;
}
if (Event.current.type == EventType.MouseDrag)
{
rotate(Input.mousePosition);
}
}
public void rotate(Vector3 mCurrentPos)
{
Physics.Raycast(camera.ScreenPointToRay(mCurrentPos), out newHitPoint);
axis = Vector3.Cross(newHitPoint.point - center, oldHitPoint.point - center);
angle = Vector3.Dot(newHitPoint.point, oldHitPoint.point) / 0.04f; //this is the radius of the trackball. If you want that it turn faster, decrease this float
whateverGameObj.transform.rotation = Quaternion.AngleAxis(-angle, axis) * skeleton.transform.rotation;
}
OK, now it goes as a true trackball, but there is that lag when changing directions.
greg767: That’s awsome! Just got home and tried your simplified version out and as you said 100% trackball - exactly what I was aiming for. Though I’m still puzzled why my first script was all acting crazy…?
I’ve made a non-GUI version. Though I had trouble checking if two vectors were identical by value, so I made my own function, but there might be a builtin function in unity though… But this is the complete script without GUI.
Vector3 previousMousePosition;
bool isMouseDown;
// Update is called once per frame
void Update ()
{
if (isMouseDragged())
{
if (castRay(Input.mousePosition))
{
Vector3 mouseDelta = Input.mousePosition - previousMousePosition;
gameObject.transform.rotation = Quaternion.Euler(mouseDelta.y, -mouseDelta.x, 0) * gameObject.transform.rotation;
}
}
previousMousePosition = Input.mousePosition;
}
bool castRay(Vector3 pos)
{
Ray ray = Camera.main.ScreenPointToRay(pos);
if (Physics.Raycast (ray))
return true;
return false;
}
void OnMouseDown()
{
isMouseDown = true;
}
void OnMouseUp()
{
isMouseDown = false;
}
bool isMouseDragged()
{
if (isMouseDown !vector3Equals(previousMousePosition, Input.mousePosition))
return true;
return false;
}
bool vector3Equals(Vector3 vec1, Vector3 vec2)
{
if (vec1.x == vec2.x vec1.y == vec2.y vec1.z == vec2.z)
return true;
return false;
}
amcclay: I think it’s because your camera is placed so it looks down the negative z-axis. Try and rotate and move your camera so it looks down the positive z-axis instead.
Don’t know about what you’re referring to, but if you want to add spin (which was my goal as well) you can use the script I ended up doing. The factor variable can be adjusted for the amount of acceleration you want to add.
public class Rotation : MonoBehaviour
{
int factor = 30; //adjust as desired
Vector3 previousMousePosition;
bool isMouseDown;
Vector3 mouseDelta;
// Update is called once per frame
void Update ()
{
if (isMouseDown)
{
rigidbody.angularVelocity = Vector3.zero;
}
if (isMouseDragged())
{
if (castRay(Input.mousePosition))
{
mouseDelta = Input.mousePosition - previousMousePosition;
mouseDelta *= 0.1f; // reduction of rotation "speed".
transform.rotation = Quaternion.Euler(mouseDelta.y, -mouseDelta.x, 0) * transform.rotation;
}
}
else if (!isMouseDown)
{
rigidbody.AddTorque(factor* mouseDelta.y, -mouseDelta.x * factor, 0);
mouseDelta = Vector3.zero; // reset
}
previousMousePosition = Input.mousePosition;
}
bool castRay(Vector3 pos)
{
Ray ray = Camera.main.ScreenPointToRay(pos);
if (Physics.Raycast (ray))
return true;
return false;
}
void OnMouseDown()
{
isMouseDown = true;
}
void OnMouseUp()
{
isMouseDown = false;
}
bool isMouseDragged()
{
if (isMouseDown !vector3Equals(previousMousePosition, Input.mousePosition))
return true;
return false;
}
bool vector3Equals(Vector3 vec1, Vector3 vec2)
{
if (vec1.x == vec2.x vec1.y == vec2.y vec1.z == vec2.z)
return true;
return false;
}
}
My problem is that I am trying to do a similar thing to your trackball but with a flat disc that will rotate all the way around (x+y) with mouse drag. I have tried to edit the code to suit my needs, but it just wont work. What I am after is similar to if you had a dart board on the wall and being able to rotate it clockwise/anti-clockwise with the mouse dragging around the radius.
Any idea’s of what I would need to change would be a great help?
If you place a hinge joint at the centre of the disc, you might be able to use the DragRigidbody script (menu Component > Scripts > Drag Rigidbody) to rotate it with physics.