I thought this was going to be an easy thing to do, but I am just having the most difficult time finding a way to do this.
In Unity 3D, I want to select an object by clicking on it with the mouse, and on the mouse click have the camera rotate around the object smoothly, always looking at the object, to a point 180° around a circle that is around the object.
Basically I want the camera to rotate around and look at the back side of that object. Then, on a similar click event, just move back along the same arc to the original position (and view the object from the front).
I see all kinds of things that will rotate a camera around an object, but they make the camera go in circles all the way around. I need to make the camera stop on the other side of the object.
LERP and MoveTo do straight lines and make the camera fly through objects. I need to go around it.
Does anyone know a good tutorial or good script to do this?
You were on the right track, this adds the code to start/stop at for example 180 deg, just run
GoFromTo(0.0f, 180.0f); //on object 1st click
GoFromTo(180.0f, 0.0f); //on object 2nd click
Add the script to the camera object, from the click-detection script you call the above, or do the click detection in update below…
public class camera_rotate_around: MonoBehaviour
{
public GameObject target;
public float speed = 50f; //deg per second
private float fromAngle, toAngle;
private bool bActive = false;
Vector3 originalPos;
public void GoFromTo(float from, float to)
{
fromAngle = from; toAngle = to;
bActive = true;
}
void Start()
{
originalPos = transform.position;
}
void Update()
{
if(bActive) {
if(fromAngle < toAngle) {
fromAngle += speed * Time.deltaTime;
if(fromAngle >= toAngle) {
fromAngle = toAngle;
bActive = false;
}
}
else if(fromAngle > toAngle) {
fromAngle -= speed * Time.deltaTime;
if(fromAngle <= toAngle) {
fromAngle = toAngle;
bActive = false;
}
}
transform.position = originalPos;
transform.RotateAround(target.transform.position, Vector3.up, fromAngle);
transform.LookAt(target.transform);
}
}
}
@rh_galaxy
Your answer was a tremendous help!
After a few days of playing around and trying some other things, I got it to work the way I had envisioned, but it uses a different approach. In fact it uses Slerp.
Two additional sources that helped me:
Here’s what I did. I have a total of 6 objects for the effect:
- The player has the main script on it, that uses the Slerp and moves the player through the arched path. The player also has a camera on it.
- The watchTarget, which is the central game object I want the player to rotate around and see the back side of.
- Two empty game objects with transforms set to where I want the player to start from and end up in. One of these is on the front side, and one on the back side of the central watchTarget.
- Two objects that serve as buttons. When I click either of these the player rotates around the arc and ends up on the other side of the central watchTarget.
Also, I’m using two scripts:
- slerpArcToPosition placed on the player
- btn_toggles_camera_rot placed on each of the two button game objects
Here is the code in btn_toggles_camera_rot
using UnityEngine;
public class btn_toggles_camera_rot : MonoBehaviour
{
public slerpArcToPosition cameraMoveScript;
void OnMouseDown() //OnMouseDown is called when the user has pressed the mouse button while over the Collider.
{
cameraMoveScript.goNow = true;
}
}
Here is the code from slerpArcToPosition:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class slerpArcToPosition : MonoBehaviour
{
//for Start and End Positions, add two empty game objects into the game to mark the start and end points of the travel path
public Transform startPosition;
public Transform endPosition;
//adjust journey time, speed, and repeat variables in the inspector to get the preferred performance
//set journey time to 1.0 and repeat to true for the ping pong action
public GameObject watchTarget;
public float journeyTime = 10.0f;
public float speed = 2.5f;
public bool objectIsInStartPosition = true;
public bool repeat = false;
public bool goNow = false;
public bool startTimeRecorded = false;
private float startTime;
private Vector3 centerPoint;
private Vector3 startRelativeCenter;
private Vector3 endRelativeCenter;
private Vector3 axisOfArc;
// Start is called before the first frame update
void Start()
{
// the axes are (x, y, z), place a 1 or -1 in the axis in which you want to arc, 0s in the other two
axisOfArc = new Vector3(1, 0, 0);
}
// Update is called once per frame
void Update()
{
GetCenter(axisOfArc);
//startTime = Time.time;
//one way trip version of the animation
if (!repeat)
{
if (goNow) {
if(!startTimeRecorded)
{
GetAnimationStartTime();
}
//new variable of scope that calculates the fraction of time spent in the journey so far
float fractionComplete = (Time.time - startTime) / journeyTime * speed;
//move the object in an arc from start to end at the speed calculated, adjust the center point accordingly
if (objectIsInStartPosition)
{
transform.position = Vector3.Slerp(startRelativeCenter, endRelativeCenter, fractionComplete * speed);
transform.position += centerPoint;
transform.LookAt(watchTarget.transform);
if (fractionComplete >= 1)
{
goNow = false;
startTimeRecorded = false;
objectIsInStartPosition = false;
}
}
else {
transform.position = Vector3.Slerp(endRelativeCenter, startRelativeCenter, fractionComplete * speed);
transform.position += centerPoint;
transform.LookAt(watchTarget.transform);
if (fractionComplete >= 1)
{
goNow = false;
startTimeRecorded = false;
objectIsInStartPosition = true;
}
}
}
}
//round trip version of the animation, works best with journeytime = 1 and speed = 1
if(repeat)
{
//new variable of scope using built-in ping pong function to calculate the fraction of the time spent in the journey so far
float fracComplete = Mathf.PingPong(Time.time - startTime, journeyTime / speed);
//move the object in an arc from start to end at the speed calculated, adjust the center point accordingly
transform.position = Vector3.Slerp(startRelativeCenter, endRelativeCenter, fracComplete * speed);
transform.position += centerPoint;
//reset the clock as needed
if (fracComplete >= 1)
{
startTime = Time.time;
}
}
}
public void GetCenter(Vector3 direction)
{
//centerPoint is the midpoint between the start and end positions
centerPoint = (startPosition.position + endPosition.position) * 0.5f;
//centerPoint uses the direction that was passed into this method, this is the axis about which to arc the motion
centerPoint -= direction;
startRelativeCenter = startPosition.position - centerPoint;
endRelativeCenter = endPosition.position - centerPoint;
}
public void GetAnimationStartTime()
{
startTime = Time.time;
startTimeRecorded = true;
}
}