# How to keep an object that rotates around another at the same height when the radius changes?

In my game, I have one object that rotates around another using transform.RotateAround, and I need to change the radius of its orbit.

The behavior now is that the orbiting object moves to the larger radius, but it also changes its height.

Both when running this code the first time or when changing the radius value at runtime, I want it to move to the new radius like it does now, but also to keep the current height.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateAroundPoint : MonoBehaviour
{
public Transform objectToRotateAround;
public Vector3 axis;
public float rotationSpeed;

private void Start()
{
}

private void Update()
{
{
}

transform.RotateAround(objectToRotateAround.position, axis, rotationSpeed * Time.deltaTime);
}

{
Vector3 direction = (transform.position - objectToRotateAround.position).normalized;
Vector3 targetPosition = objectToRotateAround.position + direction * radius;

float step = moveToRadiusSpeed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, step);

{
transform.position = targetPosition;
}
}
}


I tried to change the code in the Update to this:

Vector3 direction = (transform.position - objectToRotateAround.position).normalized;

float currentDistance = Vector3.Distance(transform.position, objectToRotateAround.position);

float currentHeight = Mathf.Sqrt(Mathf.Pow(currentDistance, 2f) - Mathf.Pow(radius, 2f));

Vector3 newPosition = objectToRotateAround.position + direction * radius;

float step = moveToRadiusSpeed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, newPosition + Vector3.up * currentHeight, step);

{
transform.position = newPosition + Vector3.up * currentHeight;
}


But this makes the orbiting object just to move up quickly and without stopping. It does rotate, but not around the target (objectToRotateAround).

I would solve this by turning the one GameObject into two with a parent-child relationship.

1. Place the parent on the center of the orbit
2. Use the transform.rotation of the parent to control the direction where the child appears
3. Use the transform.localPosition of the child to control the radius/height.
4. Any renderers, colliders etc. would be on the child, of course.
• this is working but if I'm changing the child height then also the parent will start moving with the child. i also wanted to add some more features. Aug 8 at 18:22
• @OledNeduda "if I'm changing the child height then also the parent will start moving with the child" - that should not happen. If you have both the parent and the child selected, then it might appear that way in the Unity editor, because the gizmo shows the center of the selected objects. But if you look at the parent transform component, you will see that it didn't change its position. "i also wanted to add some more features." - a more detailed description of your requirements in your question would lead to more applicable answers. Aug 8 at 18:24

You need to change your ApplyRadius method to change the offset only in this horizontal plane.

private void ApplyRadius()
{
// Get offset between this object and its target.
Vector3 offset = transform.position - objectToRotateAround.position;

// Extract just the height, leaving the horizontal offset in the XZ plane.
float height = offset.y;
offset.y = 0f;

// Use Pythagorean Theorem to get the adjacent side that,
// combined with height as our opposite side,
// makes our hypotenuse match the desired total radius.

// Construct our new offset and apply it.
offset.y = height;

Vector3 targetPosition = objectToRotateAround.position + offset;

// ... the rest of your code


If your "height" can be non-vertical, you can do the same thing with an arbitrary axis like so (here I assume other code ensures axis is unit length, so we don't redundantly re-normalize it every frame)

private void ApplyRadius()
{
// Get offset between this object and its target.
Vector3 offset = transform.position - objectToRotateAround.position;

// Extract component parallel to axis, leaving offset in rotation plane.
float height = Vector3.Dot(offset, axis);
offset -= axis * height;

// Use Pythagorean Theorem to get the adjacent side that,
// combined with height as our opposite side,
// makes our hypotenuse match the desired total radius.

// Construct our new offset and apply it.
offset += axis * height;

Vector3 targetPosition = objectToRotateAround.position + offset;

// ... the rest of your code


However, note that you can accumulate errors using RotateAround. Myself, I would implement this in a way where I calculate the position with a little trig, so I know I'll maintain my target orbit exactly.

[SerializeField] Transform objectToRotateAround;
[SerializeField] Vector3 axis;
[SerializeField] float degreesPerSecond;

Quaternion _rotationSpace;

// Gets current displacement from the pivot object
// in a coordinate space where the rotation axis is +z.
Vector3 GetRotationSpaceOffset() {
return Quaternion.Inverse(_rotationSpace)
* (transform.position - objectToRotateAround.position);
}

// Update axis and angle on start-up / if axis is changed in Inspector.
void OnValidate() {
_rotationSpace = Quaternion.LookRotation(axis);
if (objectToRotateAround != null) {
var offset = GetRotationSpaceOffset();
}
}

void Start() {
// Initialize rotation coordinate space and angle.
OnValidate();
}

void Update() {
// Get our offset from the pivot in a coordinate space where our
// rotation axis is along the z+ direction.
var offset = GetRotationSpaceOffset();

// As before, extract the component parallel to the axis.
float height = offset.z;
offset.z = 0f;

// Blend our current radius toward the target.

// Advance our angle, and keep it wrapped close to zero for best precision.

// Construct a unit vector in our rotation plane at that angle.