public enum MirrorType { Reflect, Offset }
public enum MirrorAxisFlags
public class MirrorOperation : MonoBehaviour
public Transform mirrorPlane; // defines the mirror’s local space
public Transform objectToMirror;
public Transform mirroredObject;
[Header("Which axes to chain")]
public MirrorAxisFlags axesToMirror = MirrorAxisFlags.Z;
[Header("Per-axis behavior")]
public MirrorType xType = MirrorType.Reflect;
public MirrorType yType = MirrorType.Reflect;
public MirrorType zType = MirrorType.Reflect;
[Header("Offset distances (only when type==Offset)")]
public float xOffset = 0f;
public float yOffset = 0f;
public float zOffset = 5f;
[Header("Bounds via BoxColliders (in world space)")]
public BoxCollider sourceBoundsCollider;
public BoxCollider targetBoundsCollider;
if (objectToMirror != null && mirroredObject == null)
mirroredObject = Instantiate(objectToMirror, transform.parent);
if (mirrorPlane == null || objectToMirror == null || mirroredObject == null
|| sourceBoundsCollider == null || targetBoundsCollider == null)
// 1) Transform the source position & rotation into mirror-local space
Vector3 lp = mirrorPlane.InverseTransformPoint(objectToMirror.position);
Quaternion lr = Quaternion.Inverse(mirrorPlane.rotation) * objectToMirror.rotation;
// 2) Chain per-axis reflect/offset
Vector3 reflectFactor = Vector3.one;
if (axesToMirror.HasFlag(MirrorAxisFlags.X))
if (xType == MirrorType.Reflect) { lp.x *= -1f; reflectFactor.x = -1f; }
else { lp.x -= xOffset; }
if (axesToMirror.HasFlag(MirrorAxisFlags.Y))
if (yType == MirrorType.Reflect) { lp.y *= -1f; reflectFactor.y = -1f; }
else { lp.y -= yOffset; }
if (axesToMirror.HasFlag(MirrorAxisFlags.Z))
if (zType == MirrorType.Reflect) { lp.z *= -1f; reflectFactor.z = -1f; }
else { lp.z -= zOffset; }
// 3) Compute source & target local-space AABBs from their BoxColliders
Vector3 srcMin, srcMax, tgtMin, tgtMax;
ComputeLocalAABB(sourceBoundsCollider, mirrorPlane, out srcMin, out srcMax);
ComputeLocalAABB(targetBoundsCollider, mirrorPlane, out tgtMin, out tgtMax);
// 4) Remap lp from [srcMin,srcMax] → [tgtMin,tgtMax]
Vector3 srcSize = srcMax - srcMin;
Vector3 tgtSize = tgtMax - tgtMin;
srcSize.x != 0f ? (lp.x - srcMin.x) / srcSize.x : 0f,
srcSize.y != 0f ? (lp.y - srcMin.y) / srcSize.y : 0f,
srcSize.z != 0f ? (lp.z - srcMin.z) / srcSize.z : 0f
tgtMin.x + t.x * tgtSize.x,
tgtMin.y + t.y * tgtSize.y,
tgtMin.z + t.z * tgtSize.z
// 5) Mirror rotation via R' = M·R·M
var M = Matrix4x4.Scale(reflectFactor);
var R = Matrix4x4.Rotate(lr);
Quaternion mirroredLR = (M * R * M).rotation;
// 6) Transform back to world
mirroredObject.position = mirrorPlane.TransformPoint(lp);
mirroredObject.rotation = mirrorPlane.rotation * mirroredLR;
// Helper: takes a BoxCollider, and returns its axis-aligned min/max
// in the coordinate space of 'mirrorPlane'.
static void ComputeLocalAABB(BoxCollider bc, Transform mirrorPlane,
out Vector3 outMin, out Vector3 outMax)
var ext = bc.size * 0.5f;
outMin = new Vector3( float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
outMax = new Vector3(-float.PositiveInfinity, -float.PositiveInfinity, -float.PositiveInfinity);
for (int i = 0; i < 8; i++)
Vector3 corner = new Vector3(
center.x + ((i & 1) == 0 ? -ext.x : ext.x),
center.y + ((i & 2) == 0 ? -ext.y : ext.y),
center.z + ((i & 4) == 0 ? -ext.z : ext.z)
// world-space, then into mirror-local
Vector3 worldPt = t.TransformPoint(corner);
Vector3 localPt = mirrorPlane.InverseTransformPoint(worldPt);
outMin = Vector3.Min(outMin, localPt);
outMax = Vector3.Max(outMax, localPt);