角色能力,存档
This commit is contained in:
@@ -17,8 +17,9 @@ namespace BaseGames.Editor
|
||||
///
|
||||
/// 新格式:
|
||||
/// [新 CameraArea GO](CameraArea 组件,_visibleBounds = 本地 Rect)
|
||||
/// ├─ AreaBoundary(PolygonCollider2D,isTrigger=true,对应旧 Confiner)
|
||||
/// └─ TriggerZone(CameraTriggerZone + PolygonCollider2D,对应旧 TriggerRegion)
|
||||
/// ├─ AreaBoundary(BoxCollider,对应旧 Confiner)
|
||||
/// ├─ TriggerZone(CameraTriggerZone + PolygonCollider2D,对应旧 TriggerRegion)
|
||||
/// └─ VCam_xxx(CinemachineCamera + 所有扩展组件,专属虚拟相机)
|
||||
///
|
||||
/// 菜单:BaseGames → Camera → 相机区域迁移工具
|
||||
/// </summary>
|
||||
@@ -32,9 +33,10 @@ namespace BaseGames.Editor
|
||||
}
|
||||
|
||||
// ── 设置字段 ──────────────────────────────────────────────────────────
|
||||
private Transform _sourcesParent; // 旧 Zone_xxx 的父节点(通常名为 Zones)
|
||||
private Transform _targetParent; // 新对象放置位置(留空 = 与旧区域同级)
|
||||
private CameraLensConfigSO _lensConfig; // 绑定到新 CameraArea._lensConfig
|
||||
private Transform _sourcesParent; // 旧 Zone_xxx 的父节点(通常名为 Zones)
|
||||
private Transform _targetParent; // 新对象放置位置(留空 = 与旧区域同级)
|
||||
private CameraLensConfigSO _lensConfig; // 绑定到新 CameraArea._lensConfig
|
||||
private bool _createDedicatedVCam = true; // 为每个区域创建专属 CinemachineCamera
|
||||
|
||||
|
||||
// ── 运行时状态 ────────────────────────────────────────────────────────
|
||||
@@ -86,6 +88,12 @@ namespace BaseGames.Editor
|
||||
new GUIContent("镜头配置 SO", "赋给所有新 CameraArea._lensConfig;留空则不赋值"),
|
||||
_lensConfig, typeof(CameraLensConfigSO), false);
|
||||
|
||||
_createDedicatedVCam = EditorGUILayout.Toggle(
|
||||
new GUIContent("创建专属 VCam",
|
||||
"为每个迁移区域创建子节点 CinemachineCamera(含所有扩展组件),\n" +
|
||||
"并绑定到 CameraArea._dedicatedCamera。"),
|
||||
_createDedicatedVCam);
|
||||
|
||||
|
||||
|
||||
EditorGUILayout.Space(8);
|
||||
@@ -220,17 +228,24 @@ namespace BaseGames.Editor
|
||||
|
||||
foreach (Transform child in _sourcesParent)
|
||||
{
|
||||
Debug.Log($"[root]:{child.name}");
|
||||
// 旧 Zone 的标识:子节点直属,且挂有 BoxCollider2D
|
||||
var box = child.GetComponent<BoxCollider2D>();
|
||||
if (box == null) continue;
|
||||
|
||||
var entry = new ZoneEntry { ZoneObj = child.gameObject, VisibleBox = box };
|
||||
|
||||
// 收集触发多边形顶点(TriggerRegion 子节点的各个点对象)
|
||||
Transform triggerRoot = FindChildContaining(child, "TriggerRegion");
|
||||
if (triggerRoot != null)
|
||||
foreach (Transform pt in triggerRoot)
|
||||
// 收集触发多边形顶点——xxx_TriggerRegion 下每个子节点的世界坐标即一个顶点,
|
||||
// 按子节点顺序依次连线围成多边形触发区域。
|
||||
Transform triggerRoot = FindChildContaining(child, "Trigger");
|
||||
|
||||
if (triggerRoot != null){
|
||||
Debug.Log($"[trigger]:{triggerRoot.name}");
|
||||
foreach (Transform pt in triggerRoot){
|
||||
Debug.Log($"{pt.name}");
|
||||
entry.TriggerWorldPts.Add((Vector2)pt.position);
|
||||
}
|
||||
}
|
||||
|
||||
// 读取限位碰撞体(Zone_xxx_Confiner 上的 Collider2D)
|
||||
Transform confinerT = FindChildContaining(child, "Confiner");
|
||||
@@ -298,20 +313,20 @@ namespace BaseGames.Editor
|
||||
soArea.FindProperty("_lensConfig").objectReferenceValue = _lensConfig;
|
||||
soArea.ApplyModifiedProperties();
|
||||
|
||||
// ── 3. 创建 AreaBoundary(限位多边形,isTrigger = true)──────────
|
||||
// ── 3. 创建 AreaBoundary(限位体积 BoxCollider)─────────────────────────────
|
||||
GameObject boundaryGO = new GameObject($"{zoneGO.name}_AreaBoundary");
|
||||
Undo.RegisterCreatedObjectUndo(boundaryGO, "Migrate Camera Zone");
|
||||
boundaryGO.transform.SetParent(areaGO.transform, worldPositionStays: false);
|
||||
boundaryGO.transform.localPosition = Vector3.zero;
|
||||
|
||||
PolygonCollider2D confinerPoly = boundaryGO.AddComponent<PolygonCollider2D>();
|
||||
confinerPoly.isTrigger = true;
|
||||
confinerPoly.pathCount = 1;
|
||||
confinerPoly.SetPath(0, BuildConfinerPath(entry, worldPos, localBounds));
|
||||
BoxCollider confinerBox = boundaryGO.AddComponent<BoxCollider>();
|
||||
confinerBox.isTrigger = true;
|
||||
confinerBox.center = new Vector3(0f, 0f, -10f); // Z 占位符,SyncConfiner 会立即重算
|
||||
confinerBox.size = new Vector3(localBounds.width, localBounds.height, 1f);
|
||||
|
||||
// 绑定 _confinerCollider
|
||||
soArea.Update();
|
||||
soArea.FindProperty("_confinerCollider").objectReferenceValue = confinerPoly;
|
||||
soArea.FindProperty("_confinerCollider").objectReferenceValue = confinerBox;
|
||||
soArea.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(area);
|
||||
|
||||
@@ -330,9 +345,10 @@ namespace BaseGames.Editor
|
||||
// AddComponent 会因 [RequireComponent] 自动添加 PolygonCollider2D
|
||||
CameraTriggerZone triggerComp = triggerGO.AddComponent<CameraTriggerZone>();
|
||||
PolygonCollider2D triggerPoly = triggerGO.GetComponent<PolygonCollider2D>();
|
||||
// 将旧触发多边形路径(本地坐标,相对于新 CameraArea 世界位置)直接赋给 PolygonCollider2D
|
||||
Vector2[] triggerPath = BuildTriggerPath(entry, worldPos, localBounds);
|
||||
triggerPoly.isTrigger = true;
|
||||
triggerPoly.pathCount = 1;
|
||||
triggerPoly.SetPath(0, BuildTriggerPath(entry, worldPos, localBounds));
|
||||
triggerPoly.SetPath(0, triggerPath);
|
||||
|
||||
// _targetArea → 指向刚创建的 CameraArea
|
||||
var soTrigger = new SerializedObject(triggerComp);
|
||||
@@ -340,7 +356,11 @@ namespace BaseGames.Editor
|
||||
soTrigger.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(triggerComp);
|
||||
|
||||
// ── 5. 处理旧对象 ──────────────────────────────────────────────
|
||||
// ── 5. 创建专属 VCam(每区域独立相机)─────────────────────────────
|
||||
if (_createDedicatedVCam)
|
||||
CameraAreaEditor.CreateDedicatedVCamForArea(area);
|
||||
|
||||
// ── 6. 处理旧对象 ──────────────────────────────────────────────
|
||||
// 先记录原始激活状态,再对旧对象做处理,避免 SetActive(false) 后误读
|
||||
bool wasActive = zoneGO.activeSelf;
|
||||
|
||||
@@ -356,8 +376,8 @@ namespace BaseGames.Editor
|
||||
|
||||
Debug.Log($"[迁移工具] {zoneGO.name} → {areaGO.name} " +
|
||||
$"可视 {localBounds.width:F0}×{localBounds.height:F0} " +
|
||||
$"触发 {triggerPoly.GetTotalPointCount()} pt " +
|
||||
$"限位 {confinerPoly.GetTotalPointCount()} pt");
|
||||
$"触发 PolygonCollider2D ({triggerPath.Length} 点) " +
|
||||
$"限位 BoxCollider ({confinerBox.size.x:F1}×{confinerBox.size.y:F1})");
|
||||
}
|
||||
|
||||
// ── 限位多边形路径 ────────────────────────────────────────────────────
|
||||
@@ -405,14 +425,40 @@ namespace BaseGames.Editor
|
||||
{
|
||||
if (entry.TriggerWorldPts.Count >= 3)
|
||||
{
|
||||
var path = new Vector2[entry.TriggerWorldPts.Count];
|
||||
for (int i = 0; i < path.Length; i++)
|
||||
path[i] = entry.TriggerWorldPts[i] - (Vector2)areaWorldPos;
|
||||
return path;
|
||||
// 将世界坐标转换为 areaGO 本地坐标
|
||||
var localPts = new Vector2[entry.TriggerWorldPts.Count];
|
||||
for (int i = 0; i < localPts.Length; i++)
|
||||
localPts[i] = entry.TriggerWorldPts[i] - (Vector2)areaWorldPos;
|
||||
|
||||
// 按照质心角度排序,确保顶点顺序能够围成合法多边形
|
||||
return SortPointsByAngle(localPts);
|
||||
}
|
||||
return RectToPolygon(fallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一组点按照围绕质心的角度(逆时针)排序,使其能够围成合法的简单多边形。
|
||||
/// 适用于凸多边形及质心在多边形内部的凹多边形。
|
||||
/// </summary>
|
||||
private static Vector2[] SortPointsByAngle(Vector2[] points)
|
||||
{
|
||||
// 计算质心
|
||||
Vector2 centroid = Vector2.zero;
|
||||
foreach (var p in points)
|
||||
centroid += p;
|
||||
centroid /= points.Length;
|
||||
|
||||
// 按照相对质心的极角升序排列(逆时针)
|
||||
var sorted = new System.Collections.Generic.List<Vector2>(points);
|
||||
sorted.Sort((a, b) =>
|
||||
{
|
||||
float angleA = Mathf.Atan2(a.y - centroid.y, a.x - centroid.x);
|
||||
float angleB = Mathf.Atan2(b.y - centroid.y, b.x - centroid.x);
|
||||
return angleA.CompareTo(angleB);
|
||||
});
|
||||
return sorted.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手动计算 BoxCollider2D 的世界 AABB,不依赖 .bounds(inactive 对象上 .bounds 无效)。
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user