How can I rotate a sphere so the point is centered in front of the camera
Hello,
I'm working on a project where if the user clicks a specified point on a sphere, that point rotates to the center of the screen.
To elaborate further, I need a way to center the location clicked on the screen as one would with crosshairs.
Tried a bunch of things but can't seem to make it work it either rotates the camera(which is a big no no for me) or the sphere just goes crazy and starts spinning somewhere i don't want it to.
Can I just map the location of the mouse click to Screen.width/2 and screen.height/2 or is there a simpler way around this problem that I'm not seeing.
Thanks in advance.
Rüzgar
Answer by ThePersister · Nov 30, 2016 at 12:29 PM
Hi @milamber112,
I've created a little example scene for you. https://www.dropbox.com/s/yhoyy4yvrg33xwj/RotateAndCenterExample.unitypackage?dl=0
The package contains:
A selection script that centers objects to a target transform. (applying position and rotation)
Objects that are selected will await deselection and return to their original position / rotation.
Whilst we're centering an object, if another object is selected. The current object will be interrupted and immediately move back with the inverted current duration. (This was the tough bit) (This means, if it moved 0.2 seconds towards the center, it will move back in 0.2 seconds)
Supports multiple objects returning to their original position / rotation at the same time.
Only one object will be selected at a time.
.
Preview GIF (Looks jittery because it's a gif): https://gyazo.com/741fe47721efeb0ff5d8069accce352c
Here's the code for anyone directly interested:
using UnityEngine;
using System.Collections;
public class RotateAndCenterHandler : MonoBehaviour
{
public LayerMask m_onlySelectable;
public Transform m_targetPoint;
private RaycastHit m_rayOutput;
[Header( "Object Movement Settings" )]
public float m_centerDuration = .5f;
public AnimationCurve m_smoothCurve = new AnimationCurve( new Keyframe[] { new Keyframe( 0, 0 ), new Keyframe( 1, 1 ) } );
private WaitForSeconds m_skipFrame = new WaitForSeconds( 0.01f );
private GameObject m_selectedObject;
// Data for movement.
private class MoveData
{
public GameObject m_objectToCenter;
public float m_moveTimerCurrent;
public Vector3 m_startPos;
public Vector3 m_endPos;
public Quaternion m_startRotation;
public Quaternion m_endRotation;
public bool m_shouldCheckForInterruption;
}
void Update()
{
if( Input.GetMouseButtonDown( 0 ) )
{
// Define Ray.
Ray rayToMouse = Camera.main.ScreenPointToRay( Input.mousePosition );
// Optional
Debug.DrawRay( rayToMouse.origin, rayToMouse.direction * 1000f, Color.magenta, 3f );
// Shoot Ray.
if( Physics.Raycast( rayToMouse, out m_rayOutput, 1000f, m_onlySelectable ) )
{
GameObject hitObject = m_rayOutput.collider.gameObject;
Debug.Log( hitObject.name ); // Optional
// No need to move if already selected.
if( hitObject != m_selectedObject )
{
// Center hit object to screen. (without moving camera, as specifically asked for)
StartCoroutine( _centerObject( hitObject ) );
}
}
}
}
private IEnumerator _centerObject( GameObject objectToCenter )
{
m_selectedObject = objectToCenter;
// Store movedata.
MoveData md = new MoveData();
md.m_objectToCenter = objectToCenter;
md.m_startPos = objectToCenter.transform.position;
md.m_startRotation = objectToCenter.transform.rotation;
md.m_endPos = m_targetPoint.position;
md.m_endRotation = m_targetPoint.rotation;
md.m_shouldCheckForInterruption = true;
// Move to center.
yield return StartCoroutine( _moveObject( md ) );
_checkInterrupted( md );
// Wait until no longer selected.
while( objectToCenter == m_selectedObject )
{
yield return m_skipFrame;
}
// Invert start and end points.
md.m_endPos = md.m_startPos;
md.m_endRotation = md.m_startRotation;
md.m_startPos = m_targetPoint.position;
md.m_startRotation = m_targetPoint.rotation;
md.m_shouldCheckForInterruption = false; // Another object has been selected, if we keep checking this, our object won't move.
// Move back to original position and rotation.
yield return StartCoroutine( _moveObject( md ) );
}
private IEnumerator _moveObject( MoveData md )
{
Transform selected = md.m_objectToCenter.transform;
float current01 = 0f;
while( md.m_moveTimerCurrent <= m_centerDuration && !_isInterrupted(md) )
{
// Increment
md.m_moveTimerCurrent += Time.deltaTime;
// Update position and rotation.
current01 = m_smoothCurve.Evaluate( md.m_moveTimerCurrent / m_centerDuration );
selected.position = Vector3.Lerp( md.m_startPos, md.m_endPos, current01 );
selected.rotation = Quaternion.Lerp( md.m_startRotation, md.m_endRotation, current01 );
// Wait a frame.
yield return m_skipFrame;
}
}
// Returns whether the current movement is being interrupted (and only if it should be checking for it)
private bool _isInterrupted(MoveData md)
{
return md.m_objectToCenter != m_selectedObject && md.m_shouldCheckForInterruption;
}
private void _checkInterrupted( MoveData md )
{
if( md.m_objectToCenter != m_selectedObject )
{
// Start moving back starting partway there. (Happens when selecting another object before this one has reached the center)
md.m_moveTimerCurrent = m_centerDuration - md.m_moveTimerCurrent;
}
else
{
// Start moving back from center. (Happens when another object is selected only after this one has reached the center)
md.m_moveTimerCurrent = 0f;
}
}
}
If you have any questions regarding the code or setup, please let me know! It's important to note that I have set the Layer of the selectable objects to Selectable, and the layermask to Selectable as well. (Selectable is a custom Layer, you can use any other layer and/or name it anything you want)
I hope that helps! If it did, please accept my answer, it'd be much appreciated! :)
Best of luck!
Cheers,
ThePersister
Wow ok you actually pulled off many things that are also useful on the side too Thanks a lot, You are an awesome person :)
You're welcome! I'm glad I could help :) Thanks for the compliments!