自定义房间区域功能
效果:
文章中MultiMeshAreaCalculator的具体功能参考物体占用的区域及放置点自动化
功能:
- 能够自定义房间的大小
- 一键生成放置区域
- 可控的放置网格点
- 当物体放置到区域内可自动吸附
- 物体是否可放置,放置时如果与其他物体交叉则不可放置(纯算法计算)
- 管理房间内的物体,能够添加或删除房间内的物体
- 直观可调整的视觉效果
核心功能——RoomReferenceFrame
管理房间的边界信息和相关操作:
1.1 属性和字段
这些字段定义了房间的显示属性和调整手柄的参数,比如网格颜色、手柄大小和网格间隔等。
public bool showWorldArea = true;
public bool showForbidArea = true;
public Color gizmosColor = Color.black;
public Color gizmosXColor = new Color(1 , 0 , 0 , 0.2f);
public Color gizmosYColor = new Color(0 , 1 , 0 , 0.2f);
public Color gizmosZColor = new Color(0 , 0 , 1 , 0.2f);
public float HandlesSize = 0.5f;
public Vector2Int LeftAndRightInterval = Vector2Int.one*5;
public Vector2Int TopAndBottomInterval = Vector2Int.one*5;
public Vector2Int FrontAndBackInterval = Vector2Int.one*5;
1.2 初始位置定义
Vector3 worldLeftLocation, worldRightLocation, worldTopLocation, worldBottomLocation, worldFrontLocation, worldBackLocation;
[HideInInspector]
public Vector3 leftLocation = new Vector3(-5 , 0 , 0);
[HideInInspector]
public Vector3 rightLocation = new Vector3(5 , 0 , 0);
[HideInInspector]
public Vector3 topLocation = new Vector3(0 , 5 , 0);
[HideInInspector]
public Vector3 bottomLocation = new Vector3(0 , -5 , 0);
[HideInInspector]
public Vector3 frontLocation = new Vector3(0 , 0 , 5);
[HideInInspector]
public Vector3 backLocation = new Vector3(0 , 0 , -5);
public int CornerContagion = 1;
这些字段定义了房间各个边界的位置,使用HideInInspector属性隐藏在Inspector中,不直接显示给用户。
1.3 网格对齐方法
public Vector3 SnapToGrid(Vector3 point , SnapDirection snapDirection)
{
Vector3 localPoint = transform.InverseTransformPoint(point);
Vector3 localStart;
Vector3 localEnd;
int totalIntervals;
switch(snapDirection)
{
case SnapDirection.Front:
case SnapDirection.Back:
localStart=transform.InverseTransformPoint(worldLeftLocation);
localEnd=transform.InverseTransformPoint(worldRightLocation);
totalIntervals=FrontAndBackInterval.x-1;
localPoint.x=SnapCoordinate(localPoint.x , localStart.x , localEnd.x , totalIntervals);
localStart=transform.InverseTransformPoint(worldBottomLocation);
localEnd=transform.InverseTransformPoint(worldTopLocation);
totalIntervals=FrontAndBackInterval.y-1;
localPoint.y=SnapCoordinate(localPoint.y , localStart.y , localEnd.y , totalIntervals);
// Z轴坐标保持不变
break;
case SnapDirection.Left:
case SnapDirection.Right:
localStart=transform.InverseTransformPoint(worldFrontLocation);
localEnd=transform.InverseTransformPoint(worldBackLocation);
totalIntervals=LeftAndRightInterval.x-1;
localPoint.z=SnapCoordinate(localPoint.z , localStart.z , localEnd.z , totalIntervals);
// 与Front/Back情况相同的Y轴处理
localStart=transform.InverseTransformPoint(worldBottomLocation);
localEnd=transform.InverseTransformPoint(worldTopLocation);
totalIntervals=LeftAndRightInterval.y-1;
localPoint.y=SnapCoordinate(localPoint.y , localStart.y , localEnd.y , totalIntervals);
// X轴坐标保持不变
break;
case SnapDirection.Top:
case SnapDirection.Bottom:
localStart=transform.InverseTransformPoint(worldLeftLocation);
localEnd=transform.InverseTransformPoint(worldRightLocation);
totalIntervals=TopAndBottomInterval.x-1;
localPoint.x=SnapCoordinate(localPoint.x , localStart.x , localEnd.x , totalIntervals);
localStart=transform.InverseTransformPoint(worldFrontLocation);
localEnd=transform.InverseTransformPoint(worldBackLocation);
totalIntervals=TopAndBottomInterval.y-1;
localPoint.z=SnapCoordinate(localPoint.z , localStart.z , localEnd.z , totalIntervals);
// Y轴坐标保持不变
break;
}
return transform.TransformPoint(localPoint);
}
private float SnapCoordinate(float coordinate , float start , float end , int intervals)
{
float relativePos = (coordinate-start)/(end-start);
int intervalIndex = Mathf.RoundToInt(relativePos*intervals);
if(intervalIndex<CornerContagion)
intervalIndex=CornerContagion;
if(intervalIndex>intervals-(CornerContagion))
intervalIndex=intervals-(CornerContagion);
float lerpValue = (float)intervalIndex/intervals;
return Mathf.Lerp(start , end , lerpValue);
}
SnapToGrid方法用于将一个点对齐到网格节点上,使得物体在特定的方向上沿网格对齐。这在房间布局和物体摆放中非常有用,有助于保持场景中的物体排列整齐。
1.坐标转换:将输入点point转换到局部坐标系localPoint。
2.根据对齐方向处理:
- Front/Back方向:对局部x和y坐标进行对齐。
- Left/Right方向:对局部z和y坐标进行对齐。
- Top/Bottom方向:对局部x和z坐标进行对齐。
3.调用SnapCoordinate方法:计算并返回对齐后的坐标。
4.坐标还原:将对齐后的局部坐标转换回世界坐标。
SnapCoordinate方法用于将单个坐标值对齐到最近的网格节点,具体步骤如下:
1.计算相对位置:将坐标coordinate标准化到0到1范围内。
2.确定区间索引:根据相对位置和总区间数计算所在区间索引。
3.调整区间索引:确保区间索引不超出范围,避免靠近边界的物体超出区域。
4.计算对齐坐标:使用线性插值计算最终的对齐坐标。
优点
- 可维护性强:将对齐逻辑封装在SnapToGrid和SnapCoordinate方法中,便于代 码的维护和扩展。
- 灵活性高:通过SnapDirection参数指定对齐方向,适应不同场景需求。
- 防止超出边界:CornerContagion参数控制边界区域,确保对齐后的坐标不会超出预设范围。
- 简化复杂计算:使用线性插值和标准化简化坐标计算,保证精度和效率。
这些好处和技巧使得该方法在实现房间物体的网格对齐时既简洁高效,又具备高度的灵活性和可控性。
1.4 其他辅助方法
ResetArea(): 重置房间边界位置。
public void ResetArea()
{
leftLocation=new Vector3(-1 , 0 , 0);
rightLocation=new Vector3(1 , 0 , 0);
topLocation=new Vector3(0 , 1 , 0);
bottomLocation=new Vector3(0 , -1 , 0);
frontLocation=new Vector3(0 , 0 , 1);
backLocation=new Vector3(0 , 0 , -1)