- Home /
Rotate a globe with force using latitude and longitude
So after gathering info from people and experimenting and reading i am standing to a point where i can't finish my main task which seems relatively simple, but in fact it's a total nightmare. So i am working with my friend who wants a rotating globe with two rotation functionalities.
When a player swipes with the finger or moves the mouse in some direction the globe needs to rotate to that direction with some force - smoothly.
When the player only clicks or taps ones over the globe, the globe must rotate so the clicked point faces the center of the camera.
So here i go.For the first functionality i added rigidbody to the globe and started rotating the globe adding torque in direction towards the swiped position.Then just clamped the Y axis to a certain angle so it wont flip the globe around.Pretty easy and it works OK.
For the second functionality what i did is get the tapped or clicked point from the globe and create a rotation from it to Vector3.forward which stands in my scene as the camera's center.Now here is where i found out that when i rotate the globe arbitrary like that at certain point the continents started tilting which does not look appealing and it's not acceptable for the project.Look at the picture:
Next thing i found out is how to rotate the globe by latitude and longitude which was exactly the thing i was looking for and replaced the last method with this one. So now when the player taps over that location it angles the globe to align perfectly to the camera's center or the world forward vector.Great!
Then i created a method which gets my current latitude and longitude from the center of the globe so even if i spin the globe with torque i will always know the current latitude and longitude from the globes center and i will interpolate the angles without any problems.Now here is where i got it wrong.
When spinning the globe with torque of course, it does not angle the globe by the lat/lon angles.Here when the player spins the globe with the first functionality and after that taps over the planet the interpolation starts with the correct lat/lon, but it seems that this is not enough to get the correct current rotation.The result goes like:
Globe rotates instantly from the tilted angle from the torque to the correct lat/lon angles(bad)
After that it interpolates from that rotation to the tapped one.(its ok)
Look at the video to see exactly how the rotation happens.I first swipe then i click on a location.
Is it possible to combine the two functionalities: rotate the rigid body with torque + rotate to latitude/longitude to work together with no conflicts between them and interpolating smoothly?If so how?If not is there any similar way to produce such a thing?
I looked at the video. It seems to me this is not a pure rotation issue since you get to the a right final rotation, but you are setting something up incorrectly. The only way to know would be to exa$$anonymous$$e the code. But in looking at the code from a user/usability standpoint, I'm bothered by the askew nature of the zoom in. If it were me, I'd re-implement the solution with two, separate rotations...one on the 'x' axis, and one on the 'y'. It could be done using an empty parent object with a visible child. The empty parent would tilt up and down on the world 'x' axis, and the child would spin on the local 'y' axis. Or it would be done as a single visible object spinning on the 'y' axis, and the camera rotating around the visible object on the world 'y' object. In both cases, from the user's point of view, the poles of the planet would remain vertical.
I'd be glad to work up some sample code when I am back at my desktop and have a bit more time if you like.
Answer by robertbu · Aug 12, 2014 at 03:54 AM
As mentioned in my comment above, from a usability perspective I think maintaining the 'y' axis of the globe so that is appears vertical from the camera point of view is a better implementation than an arbitrary Quaternion.FromToRotation() rotation. And as mentioned this can be implemented by pulling the tilting up and down, and the 'y' rotation apart as two separate rotations...either on separate game objects, or between the earth and the camera. I was uncertain enough of the geometry and rotations, that I had to do a quick prototype of the whole behavior. Below is a script that implements the two-game-object-rotation concept. I setup as follows:
The earth is a child of an empty game object with the script on the empty game object
Both earth and the empty game object are at Vector3.zero
The camera is perspective and positioned at (0,0,-10) with rotation (0,0,0).
The child, visible earth object needs to have the name 'Earth'.
It is likely that if you move beyond this setup, you'll find some things that need to be fixed. I wasn't trying to build a general solution, but a framework to test the rotation code to make sure I had it right. You'll find the "magic" in the 'Animate()' method. After playing with it a bit, I found that, with the now vertical poles, I did not like the straightening of the globe during a spin. I left it in because it was one of the main points of the question, but find the comment about this feature and disable the line of code to see what it feel like without straightening. In addition, the code distinguishes between a move-rotate and a swipe-rotate. If the "swipe" results in a low velocity calculation for the spin, the spin and straighten are not done. This allows the user to rotate the globe to look around without a resulting spin.
You'll find a package to demonstrate here:
http://www.laughingloops.com/RotateEarth.unitypackage
And here is the script:
using UnityEngine;
using System.Collections;
public class RotateEarth : MonoBehaviour {
public float spinFactor = 10.0f; // Maps finger movement to rotation
public float tapTimeThreshold = 0.2f; // Tap vs swipe determiner
public float tapDistThreshold = 0.2f; // Tap vs swipe determiner
public float spinSpeedFactor = 5.0f; // Maps velocity to free spin speed
public float frictionFactor = 0.98f; // Lower numbers mean faster stoping on free spin
public float straightenSpeed = 0.5f; // How fast the axis straightens
public float noSpinThreshold = 75.0f; // Below this velocity threshold, no free spin
private Transform world;
private RaycastHit hit;
private bool moving = false;
private bool freeRotate = false;
private bool animating = false;
private Vector3 startPoint = Vector3.zero;
private float startTime = 0.0f;
private Quaternion startRotation;
private float spinRate = 0.0f;
void Start() {
world = transform.Find("Earth");
}
void Update () {
if (!animating && Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (world.collider.Raycast(ray, out hit, 100f)) {
freeRotate = false;
moving = true;
startPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10);
startPoint = Camera.main.ScreenToWorldPoint(startPoint);
startTime = Time.time;
startRotation = world.localRotation;
}
}
if (Input.GetMouseButton (0)) {
if (moving) {
Vector3 pt = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10);
pt = Camera.main.ScreenToWorldPoint(pt);
world.localRotation = Quaternion.AngleAxis((startPoint.x - pt.x) * spinFactor, Vector3.up) * startRotation;
}
}
if (Input.GetMouseButtonUp(0)) {
if (moving) {
Vector3 pt = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10);
pt = Camera.main.ScreenToWorldPoint(pt);
moving = false;
if (((Time.time - startTime) < tapTimeThreshold) && ((startPoint - pt).magnitude < tapDistThreshold)) {
// Do the tap
Debug.Log ("Do the tap");
StartCoroutine(Animate(hit.point, 1.2f));
}
else {
spinRate = (startPoint.x - pt.x) / (Time.time - startTime) * spinSpeedFactor;
Debug.Log (spinRate);
if (Mathf.Abs (spinRate) > noSpinThreshold) {
freeRotate = true;
}
}
}
}
if (freeRotate) {
world.localRotation = Quaternion.AngleAxis (spinRate * Time.deltaTime, Vector3.up) * world.localRotation;
spinRate *= frictionFactor;
// Comment out the following line to disable straightening of the axis during spin
transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.identity, Time.deltaTime * straightenSpeed);
if (Mathf.Abs (spinRate) < 0.5f) {
freeRotate = false;
}
}
}
IEnumerator Animate(Vector3 hitPoint, float time) {
animating = true;
Quaternion qFromWorld = world.localRotation;
Vector3 h = world.InverseTransformPoint(hitPoint);
h.y = 0.0f;
Quaternion qToWorld = Quaternion.FromToRotation(h, Vector3.back);
Quaternion qFrom = transform.rotation;
h = hitPoint;
h = world.InverseTransformPoint (hitPoint);
float up = h.y;
h.y = 0;
Vector3 v = Vector3.back * h.magnitude + Vector3.up * up;
Quaternion qTo = Quaternion.FromToRotation (v, Vector3.back);
float timer = 0.0f;
while (timer <= time) {
float t = Mathf.Sin (timer / time * Mathf.PI * 0.5f);
transform.rotation = Quaternion.Slerp(qFrom, qTo, t);
world.localRotation = Quaternion.Slerp(qFromWorld, qToWorld, t);
timer += Time.deltaTime;
yield return null;
}
animating = false;
}
}
Yes this is exactly the thing i was looking for!Thanks a lot for your time really..i can say truly you are a master of rotations and spending your time to work out a solution for me is just very honorable and respectful.
Answer by frogsbo · Aug 11, 2014 at 06:33 PM
if you are using physics, addtorque. if you are using transform position, quaternion rotate around axis going through centre and Vector3Cross of swipe direction.
Can you be more specific what is your suggestion.I am not really sure that i can understand the logic behind your sentences.
Your answer

Follow this Question
Related Questions
Trouble Rotating Full-Circle 0 Answers
How do you manually override mecanim's interpolation with rotations? 2 Answers
Quaternion Rotation On Specific Axis Issue 0 Answers
Calculate rotation angle for two side of cube ( like dice ) from Quaternion.identity 0 Answers
Look at like rotating around y axis 1 Answer