It took a lot of experimenting, but I got it to work. I’m going to post what I did for the convenience of anyone else attempting to accomplish this. Also for critiquing. I’m pretty sure I did this in probably the worst way possible, but I can’t think of anything better.
//Keep track of the camera head so that the reflection probe is always at the correct height
public GameObject cameraHead;
//Which axes do we want to track?
public bool lockX = false;
public bool lockY = true;
public bool lockZ = false;
//Reflection probe reference
private ReflectionProbe probe;
//The trigger used to determine when the user enters or exits the bounds of this reflection probe
public BoxCollider trigger;
//The last position the reflection probe was located at
private Vector3 lastPos;
//Is this probe active?
private bool isProbeActive;
//The original extents height of the probe
Vector3 originalExtents;
Vector3 originalProbeCenter;
Vector3 originalProbePosition;
public bool isPlayerWithinTrigger
{
get
{
return trigger.bounds.Contains(cameraHead.transform.position);
}
private set { }
}
// Use this for initialization
void Start () {
probe = this.gameObject.GetComponent<ReflectionProbe>();
originalExtents = probe.bounds.extents;
originalProbeCenter = probe.center;
originalProbePosition = this.gameObject.transform.position;
trigger.center = probe.center;
trigger.center += Vector3.up * probe.gameObject.transform.position.y;
trigger.size = probe.size;
lastPos = transform.position;
isProbeActive = false;
}
// Update is called once per frame
void Update () {
if (isPlayerWithinTrigger)
{
//If we just moved from being outside of the trigger to inside of it, set this reflection probe to
//realtime and make it follow the player
if (!isProbeActive)
{
probe.refreshMode = UnityEngine.Rendering.ReflectionProbeRefreshMode.EveryFrame;
isProbeActive = true;
}
float newX = this.gameObject.transform.position.x;
if (lockX) newX = cameraHead.transform.position.x;
float newY = this.gameObject.transform.position.y;
if (lockY) newY = cameraHead.transform.position.y;
float newZ = this.gameObject.transform.position.z;
if (lockZ) newZ = cameraHead.transform.position.z;
Vector3 newPos = new Vector3(newX, newY, newZ);
Vector3 newProbeCenter = trigger.bounds.center + (trigger.center - newPos) - originalProbeCenter - (Vector3.up*originalProbePosition.y);
probe.center = newProbeCenter;
this.gameObject.transform.position = newPos;
}
else
{
//If we just moved from inside of the probe to outside of it, deactivate the probe
if (isProbeActive)
{
//Move the probe back to its starting point and refresh it one more time
this.gameObject.transform.position = originalProbePosition - (Vector3.up * originalProbePosition.y);
probe.center = trigger.center;
probe.refreshMode = UnityEngine.Rendering.ReflectionProbeRefreshMode.OnAwake;
isProbeActive = false;
}
}
}
To set this up, attached to a reflection probe that is the child of a gameobject with nothing but a box collider marked as a trigger.
When the player enters the extents of the trigger, the reflection probe starts to follow the player, and offsets it’s center to keep the reflection area constant. When the player leaves the extents of the trigger, the reflection probe resets to its original position. While the reflection probe is active, it is refreshing every frame. Once the play leaves the trigger area, it goes back to not refreshing.
The cameraHead variable is your main camera,or, in the case of VR, the center eye camera. The trigger is the box collider of the parent object. Locking X, Y, and Z is for determining which axes you want to probe to follow in. This can be useful for creating mirror surfaces where the reflection probe will match the user’s height, and follow them along a flat surface.
I didn’t show it in that code snippit, but this is the body of a monobehavior script.
It’s not a perfect solution, and I’m definitely open to suggestions, but it achieves what I am looking for, or at least close to it.