I guess you could use Quaternion.AngleAxis.
Vector3 direction = Vector3.Normalize(obj1.position - obj2.position);
Quaternion rotation = Quaternion.AngleAxis(speed * Time.deltaTime, direction);
transform.rotation *= rotation;
Not sure if it’ll produce the effect you want. You could also convert it to a Vector3 with Quaternion.ToAngleAxis.
Edit: I’ve made an example on how you can set your own axis rotation based on inclination to a central body called “orbiting”. I’ve actually wrestled with this far longer than I thought I would have to. It’s a bit long because it’s got ample comments about implementation decisions and problems I hit to give you an idea where to continue if you want to base anything off this.
The easy way to make some things orbit another object is just to make empty child game objects that act as a root for your satellites. The satellites are then moved in X for any distance you desire to define as a radius and the root objects rotate around their own pivot. The game object heirarchy could look something like this:
- Planet
- Satellite Rotator (centered on parent)
- Satellite (offset from parent)
- Satellite Rotator (centered on parent)
- Satellite (offset from parent)
- Satellite Rotator (centered on parent)
- Satellite (offset from parent)
If for any reason you don’t want to parent them together you have to use transform.RotateAround or implement rotation using Quaternions etc.
I’m pasting the example script below, but it’s not very flexible but it is something. It has decent authoring flow and it will visualize the orbit, updating in real time.

using UnityEngine;
public class RotateAroundObject : MonoBehaviour
{
public float angularSpeed = 90;
public Transform orbiting;
[Range(0, 360)]
public float inclination = 90;
public bool randomizeInclination;
[SerializeField]
[HideInInspector]
public Vector3 bakedAxis = Vector3.up;
[SerializeField]
[HideInInspector]
public Vector3 bakedPivot = Vector3.zero;
[SerializeField]
[HideInInspector]
public Vector3 position;
[SerializeField]
[HideInInspector]
public float bakedRadius;
void Start()
{
// Eh, consider move it to another script...
if (randomizeInclination)
{
// Set randomizeInclination to false to let gizmo know
// that inclination has been determined.
randomizeInclination = false;
inclination = Random.Range(0f, 360f);
}
// Bake happens during Start to give scripts a chance
// to modify settings during their Awake.
Bake();
}
void Update()
{
UpdatePosition();
// Prefer not to RotateAround because if user change position
// in editor while playing, the orbit axis get screwed up.
// Sorry, if you want to move object, be a better mathematician than me! :)
// Or just do it the easy way with child transforms.
// transform.RotateAround(bakedPivot, bakedAxis, speed * Time.deltaTime);
}
void UpdatePosition()
{
// Updates position to next point in orbit based on angular speed.
// Position is managed and not shared through transform because
// of problems with math, I rather prevent user/scripts to move
// object while playing. Read note in Update.
float angle = angularSpeed * Time.deltaTime;
Vector3 offset = position - bakedPivot;
Quaternion rotation = Quaternion.AngleAxis(angle, bakedAxis);
position = bakedPivot + rotation * offset;
transform.position = position; // Overwrite position.
}
public void Bake()
{
// "Bakes" or freezes information about the rotation setup.
// This should not be called during play because the axis
// will reorient depending on the offset to pivot.
// If you can figure out how to solve LookRotation, then
// the system could work more flexibly and does not need
// to be baked like this. The problem is that the rotation
// will be different for every position, so if you keep
// updating bakedAxis with my approach, you'll get strange
// behaviour.
position = transform.position;
bakedPivot = orbiting ? orbiting.position : Vector3.zero;
bakedRadius = Vector3.Distance(bakedPivot, position);
bakedAxis = Quaternion.LookRotation(position - bakedPivot)
* Quaternion.Euler(90, -90, 0)
* Quaternion.Euler(inclination, 0, 0)
* -Vector3.forward;
}
void OnDrawGizmos()
{
if (!Application.isPlaying)
{
// Sorry mate, I don't know of a good way to
// update the Axis/Pivot while it is animating
// because the position will change the axis.
Bake();
}
Gizmos.color = orbitColor * 0.5f;
DrawOrbitGizmo();
}
void OnDrawGizmosSelected()
{
Gizmos.color = Color.white;
Gizmos.DrawLine(bakedPivot, position);
Gizmos.DrawLine(bakedPivot, bakedPivot + bakedAxis);
Gizmos.color = orbitColor;
DrawOrbitGizmo();
}
Color orbitColor
{
// Green means OK.
// Red means you haven't set the orbiting object in inspector.
get { return orbiting ? Color.green : Color.red; }
}
void DrawOrbitGizmo(int lineCount = 20)
{
if (randomizeInclination)
{
Gizmos.DrawWireSphere(bakedPivot, bakedRadius);
}
else
{
for (int i = 0; i < lineCount; ++i)
{
Vector3 orbitI = OrbitPoint(i, lineCount);
Vector3 orbitJ = OrbitPoint(i + 1, lineCount);
Gizmos.DrawLine(orbitI, orbitJ);
}
}
}
Vector3 OrbitPoint(int i, int count)
{
float percent = i / (float)count;
float angle = percent * Mathf.PI * 2;
return bakedPivot
+ Quaternion.LookRotation(bakedAxis)
* new Vector3(Mathf.Sin(angle) * bakedRadius, Mathf.Cos(angle) * bakedRadius);
}
}