How to procedurally generate a continually-connected 3D tube?

I’m trying to create a simple flying game where you fly though a long tunnel which twists and turns as it goes. To do it I created one segment as a prefab and instantiate a new copy of it in front of the next tube once I’ve exited the previous one (so there are only ever two currently running).

My problem is when I generate the next tube I’m having trouble getting the start of the next one to line up with the end of the current one. I’ve been trying to recall my old trig classes and using Sin and Cos. I’ve gotten the first generated tube to work, but the second one won’t.

Here’s my generation code:

function Update () {
	
	if(nextTunnel == null) {
		xRotation = currentTunnel.transform.rotation.x+Random.Range(-0.3, 0.3);
		yRotation = currentTunnel.transform.rotation.y+Random.Range(-0.3, 0.3);
		xSin = Mathf.Sin(xRotation);
		ySin = Mathf.Sin(yRotation);
		xCos = Mathf.Cos(xRotation);
		yCos = Mathf.Cos(yRotation);
		xOffset = -1*tunnelLength*xSin;
		yOffset = tunnelLength*ySin;

		nextTunnel = Instantiate(tunnel, Vector3(currentTunnel.transform.position.x+yOffset, currentTunnel.transform.position.y+xOffset, currentTunnel.transform.position.z+tunnelLength),
					Quaternion(xRotation, yRotation, currentTunnel.transform.rotation.z, currentTunnel.transform.rotation.w));	
		
		// Adjust to better connect the tubes
		nextTunnel.transform.position.z -= (nextTunnel.renderer.bounds.min.z - currentTunnel.renderer.bounds.max.z);
	}

	if(plane.transform.position.z > currentTunnel.transform.position.z+(tunnelLength*0.5)) {
		Debug.Log("Shifting tunnel assignments");
		prevTunnel = currentTunnel;
		currentTunnel = nextTunnel;
		nextTunnel = null;

		Destroy(prevTunnel.gameObject);
	}
	
}

Any advice on how to do this would be much appreciated! I tried searching for awhile but couldn’t find 3D tunnel generation like this. Thanks!

I’ve always recommended creating a basic class to just store an offset position in the tunnel objects.

class TunnelObject : MonoBehaviour {
  public Vector3 attachPoint;

  void OnDrawGizmos() {
    Gizmos.DrawWireSphere(transform.position.TransformPoint(attachPoint),0.5f);
  }
}

...
tunnel = GetRandomTunnelPiece();
instantiate(tunnel, atPosition, withRotation);
nextPosition = tunnel.transform.TransformPoint(tunnel.attachPoint);

This is because I cannot do math to save my life :<