角色能力,存档
This commit is contained in:
@@ -98,6 +98,9 @@ namespace BaseGames.Editor
|
||||
|
||||
if (GUILayout.Button("↺ 全部同步限位区域", EditorStyles.toolbarButton))
|
||||
SyncAllConfiners();
|
||||
|
||||
if (GUILayout.Button("✔ 批量创建专属 VCam", EditorStyles.toolbarButton))
|
||||
BatchCreateDedicatedVCams();
|
||||
}
|
||||
|
||||
// ── 创建 CameraArea 面板 ───────────────────────────────────────
|
||||
@@ -217,6 +220,40 @@ namespace BaseGames.Editor
|
||||
|
||||
Debug.Log($"[CameraAreaSetupTool] 已同步 {count} 个 CameraArea 的限位区域。");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为所有尚未配置专有 VCam 的 CameraArea 批量创建专有 CinemachineCamera。
|
||||
/// 已有 _dedicatedCamera 的区域将跳过。
|
||||
/// </summary>
|
||||
private void BatchCreateDedicatedVCams()
|
||||
{
|
||||
if (_cameraAreas.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[CameraAreaSetupTool] 场景中无 CameraArea,跳过批量创建。");
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
foreach (var area in _cameraAreas)
|
||||
{
|
||||
if (area == null) continue;
|
||||
if (area.DedicatedCamera != null) continue; // 已有专有 VCam,跳过
|
||||
|
||||
CameraAreaEditor.CreateDedicatedVCamForArea(area);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
RescanScene();
|
||||
Debug.Log($"[CameraAreaSetupTool] 已为 {count} 个 CameraArea 创建专有 VCam。");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[CameraAreaSetupTool] 所有 CameraArea 均已有专有 VCam,无需创建。");
|
||||
}
|
||||
}
|
||||
|
||||
// ── CameraStateController ──────────────────────────────────────────
|
||||
|
||||
private void DrawControllerSection()
|
||||
@@ -240,8 +277,6 @@ namespace BaseGames.Editor
|
||||
}
|
||||
|
||||
SerializedObject so = new SerializedObject(_controller);
|
||||
DrawFieldCheck(so, "_vcamA", "全局 VCam A (CinemachineCamera)");
|
||||
DrawFieldCheck(so, "_vcamB", "全局 VCam B (CinemachineCamera)");
|
||||
DrawFieldCheck(so, "_brain", "CinemachineBrain");
|
||||
DrawFieldCheck(so, "_onPlayerSpawned", "玩家生成事件 (EVT_PlayerSpawned) → VCam 自动绑 Follow");
|
||||
DrawFieldCheck(so, "_impulseSource", "CinemachineImpulseSource", optional: true);
|
||||
@@ -334,7 +369,8 @@ namespace BaseGames.Editor
|
||||
bool confinerOk = so.FindProperty("_confinerCollider").objectReferenceValue != null;
|
||||
var boundZones = FindTriggerZonesForArea(area);
|
||||
bool hasZone = boundZones.Count > 0;
|
||||
bool allOk = confinerOk && hasZone;
|
||||
bool hasVCam = so.FindProperty("_dedicatedCamera").objectReferenceValue != null;
|
||||
bool allOk = confinerOk && hasZone && hasVCam;
|
||||
|
||||
using (new EditorGUILayout.VerticalScope(_boxStyle))
|
||||
{
|
||||
@@ -353,6 +389,10 @@ namespace BaseGames.Editor
|
||||
GUI.color = hasZone ? kOk : kError;
|
||||
GUILayout.Label(hasZone ? $"[{boundZones.Count} 触发器]" : "[无触发器]",
|
||||
EditorStyles.miniLabel, GUILayout.Width(74f));
|
||||
|
||||
GUI.color = hasVCam ? kOk : kError;
|
||||
GUILayout.Label(hasVCam ? "[VCam ✔]" : "[VCam ✗]",
|
||||
EditorStyles.miniLabel, GUILayout.Width(54f));
|
||||
GUI.color = prevC;
|
||||
|
||||
if (GUILayout.Button("⊙", GUILayout.Width(24f)))
|
||||
@@ -366,12 +406,61 @@ namespace BaseGames.Editor
|
||||
EditorGUILayout.Space(3f);
|
||||
|
||||
// ── 绑定字段 ────────────────────────────────────────────────
|
||||
DrawCheckRow("_confinerCollider(可视边界 PolygonCollider2D)", confinerOk);
|
||||
DrawCheckRow("_dedicatedCamera(专有 VCam,可选)",
|
||||
so.FindProperty("_dedicatedCamera").objectReferenceValue != null, optional: true);
|
||||
DrawCheckRow("_confinerCollider(可视边界 BoxCollider)", confinerOk);
|
||||
|
||||
// ── 专有 VCam 状态行(创建 / Ping 按鈕)────────────────────────
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
Color prevC2 = GUI.color;
|
||||
GUI.color = hasVCam ? kOk : kError;
|
||||
GUILayout.Label(hasVCam ? "●" : "✗", GUILayout.Width(16f));
|
||||
GUI.color = prevC2;
|
||||
|
||||
if (hasVCam)
|
||||
{
|
||||
var vcamObj = so.FindProperty("_dedicatedCamera").objectReferenceValue;
|
||||
EditorGUILayout.LabelField($"_dedicatedCamera:{vcamObj.name}",
|
||||
GUILayout.ExpandWidth(true));
|
||||
if (GUILayout.Button("⊙", GUILayout.Width(24f)))
|
||||
EditorGUIUtility.PingObject(vcamObj);
|
||||
if (GUILayout.Button("选中", GUILayout.Width(36f)))
|
||||
Selection.activeObject = vcamObj;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("_dedicatedCamera(专有 VCam)未创建",
|
||||
GUILayout.ExpandWidth(true));
|
||||
if (GUILayout.Button("创建专有 VCam", GUILayout.Width(90f), GUILayout.Height(18f)))
|
||||
{
|
||||
CameraAreaEditor.CreateDedicatedVCamForArea(area);
|
||||
RescanScene();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawCheckRow("_blendProfile(可选,未设则用全局默认)",
|
||||
so.FindProperty("_blendProfile").objectReferenceValue != null, optional: true);
|
||||
|
||||
// ── VCam 扩展组件顺序检查 ────────────────────────────────────
|
||||
// AsymmetricDamping/FallBias/FacingBias 必须在 CinemachineConfiner3D 之前;
|
||||
// AxisLock 必须在之后。顺序错误会使相机逃出限位区域。
|
||||
if (hasVCam)
|
||||
{
|
||||
var vcam = so.FindProperty("_dedicatedCamera").objectReferenceValue as CinemachineCamera;
|
||||
string issue = CameraAreaEditor.CheckVCamExtensionOrderIssue(vcam);
|
||||
if (issue != null)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
$"VCam 扩展组件顺序错误!相机会逃出限位区域:\n{issue}",
|
||||
MessageType.Error);
|
||||
if (GUILayout.Button("⚙ 自动修正组件顺序", GUILayout.Height(22f)))
|
||||
{
|
||||
CameraAreaEditor.FixVCamExtensionOrder(vcam);
|
||||
RescanScene();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 触发区域列表 ─────────────────────────────────────────────
|
||||
EditorGUILayout.Space(3f);
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
@@ -412,16 +501,16 @@ namespace BaseGames.Editor
|
||||
EditorGUILayout.Space(3f);
|
||||
if (!confinerOk)
|
||||
{
|
||||
// 区分:有非 Trigger 的 PolygonCollider2D 可直接绑定 vs 完全没有 AreaBoundary
|
||||
var existingBoundary = FindBoundaryPoly(area);
|
||||
// 区分:有 BoxCollider 可直接绑定 vs 完全没有 AreaBoundary
|
||||
var existingBoundary = FindBoundaryBox(area);
|
||||
if (existingBoundary != null)
|
||||
{
|
||||
if (GUILayout.Button("修复:绑定子节点 PolygonCollider2D", GUILayout.Height(22f)))
|
||||
if (GUILayout.Button("修复:绑定子节点 BoxCollider", GUILayout.Height(22f)))
|
||||
FixConfinerBinding(area);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUILayout.Button("创建 AreaBoundary(限位多边形,默认 24 × 12)", GUILayout.Height(22f)))
|
||||
if (GUILayout.Button("创建 AreaBoundary(限位体积,默认 24 × 12)", GUILayout.Height(22f)))
|
||||
{
|
||||
CreateAreaBoundary(area);
|
||||
RescanScene();
|
||||
@@ -433,7 +522,7 @@ namespace BaseGames.Editor
|
||||
Color helpC = GUI.color;
|
||||
GUI.color = kMuted;
|
||||
EditorGUILayout.LabelField(
|
||||
"★ 可视边界:选中子节点的 PolygonCollider2D,在 Scene 视图中编辑顶点。",
|
||||
"★ 限位体积:选中子节点的 BoxCollider,在 Inspector 中编辑 Center / Size。",
|
||||
EditorStyles.miniLabel);
|
||||
GUI.color = helpC;
|
||||
}
|
||||
@@ -557,34 +646,34 @@ namespace BaseGames.Editor
|
||||
Debug.LogWarning("[CameraAreaSetupTool] _vcamA/_vcamB 均未绑定,无法赋值 Follow。请先在 Inspector 中绑定。");
|
||||
}
|
||||
|
||||
/// <summary>将子节点中找到的第一个不含 CameraTriggerZone 的 PolygonCollider2D 绑定到 CameraArea._confinerCollider。</summary>
|
||||
/// <summary>将子节点中找到的第一个不含 CameraTriggerZone 的 BoxCollider 绑定到 CameraArea._confinerCollider。</summary>
|
||||
private static void FixConfinerBinding(CameraArea area)
|
||||
{
|
||||
PolygonCollider2D poly = FindBoundaryPoly(area)
|
||||
?? area.GetComponentInChildren<PolygonCollider2D>(true);
|
||||
if (poly == null)
|
||||
BoxCollider box = FindBoundaryBox(area)
|
||||
?? area.GetComponentInChildren<BoxCollider>(true);
|
||||
if (box == null)
|
||||
{
|
||||
Debug.LogWarning($"[CameraAreaSetupTool] {area.name}:子节点中未找到 PolygonCollider2D。");
|
||||
Debug.LogWarning($"[CameraAreaSetupTool] {area.name}:子节点中未找到 BoxCollider。");
|
||||
return;
|
||||
}
|
||||
|
||||
SerializedObject so = new SerializedObject(area);
|
||||
so.FindProperty("_confinerCollider").objectReferenceValue = poly;
|
||||
so.FindProperty("_confinerCollider").objectReferenceValue = box;
|
||||
so.ApplyModifiedProperties();
|
||||
|
||||
Debug.Log($"[CameraAreaSetupTool] {area.name}:_confinerCollider → {poly.gameObject.name}");
|
||||
Debug.Log($"[CameraAreaSetupTool] {area.name}:_confinerCollider → {box.gameObject.name}");
|
||||
}
|
||||
|
||||
/// <summary>返回 area 子节点中第一个不含 CameraTriggerZone 的 PolygonCollider2D(即 AreaBoundary 限位体)。</summary>
|
||||
private static PolygonCollider2D FindBoundaryPoly(CameraArea area)
|
||||
/// <summary>返回 area 子节点中第一个不含 CameraTriggerZone 的 BoxCollider(即 AreaBoundary 限位体)。</summary>
|
||||
private static BoxCollider FindBoundaryBox(CameraArea area)
|
||||
{
|
||||
foreach (var p in area.GetComponentsInChildren<PolygonCollider2D>(true))
|
||||
if (p.GetComponent<CameraTriggerZone>() == null) return p;
|
||||
foreach (var b in area.GetComponentsInChildren<BoxCollider>(true))
|
||||
if (b.GetComponent<CameraTriggerZone>() == null) return b;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为指定 CameraArea 创建 AreaBoundary 子节点(默认矩形限位多边形,isTrigger = false)并绑定到 _confinerCollider。
|
||||
/// 为指定 CameraArea 创建 AreaBoundary 子节点(默认 BoxCollider 限位体)并绑定到 _confinerCollider。
|
||||
/// </summary>
|
||||
private static void CreateAreaBoundary(CameraArea area)
|
||||
{
|
||||
@@ -602,27 +691,20 @@ namespace BaseGames.Editor
|
||||
childGo.transform.localPosition = Vector3.zero;
|
||||
}
|
||||
|
||||
PolygonCollider2D poly = childGo.GetComponent<PolygonCollider2D>()
|
||||
?? childGo.AddComponent<PolygonCollider2D>();
|
||||
poly.isTrigger = true; // 限位多边形,仅作为相机约束边界,不产生物理碰撞
|
||||
poly.pathCount = 1;
|
||||
poly.SetPath(0, new Vector2[]
|
||||
{
|
||||
new Vector2(-12f, -6f),
|
||||
new Vector2(-12f, 6f),
|
||||
new Vector2( 12f, 6f),
|
||||
new Vector2( 12f, -6f),
|
||||
});
|
||||
BoxCollider box = childGo.GetComponent<BoxCollider>()
|
||||
?? childGo.AddComponent<BoxCollider>();
|
||||
box.center = new Vector3(0f, 0f, -10f); // Z 占位符,绑定 LensConfig 后点击「同步限位区域」更新
|
||||
box.size = new Vector3(24f, 12f, 1f); // 默认 24 × 12 占位符
|
||||
EditorUtility.SetDirty(childGo);
|
||||
|
||||
SerializedObject so = new SerializedObject(area);
|
||||
so.Update();
|
||||
so.FindProperty("_confinerCollider").objectReferenceValue = poly;
|
||||
so.FindProperty("_confinerCollider").objectReferenceValue = box;
|
||||
so.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(area);
|
||||
|
||||
EditorGUIUtility.PingObject(childGo);
|
||||
Debug.Log($"[CameraAreaSetupTool] 已为 {area.name} 创建 AreaBoundary(矩形 24 × 12)。");
|
||||
Debug.Log($"[CameraAreaSetupTool] 已为 {area.name} 创建 AreaBoundary(BoxCollider 默认 24 × 12)。");
|
||||
}
|
||||
|
||||
/// <summary>返回所有以此 area 为激活目标的 CameraTriggerZone。</summary>
|
||||
@@ -642,18 +724,10 @@ namespace BaseGames.Editor
|
||||
/// <summary>为指定 CameraArea 创建配对的 CameraTriggerZone,自动匹配 Confiner 范围。</summary>
|
||||
private static void CreateTriggerZoneForArea(CameraArea area)
|
||||
{
|
||||
// 用 PolygonCollider2D 包围盒作为放置中心和尺寸;没有则退回到 area 自身位置
|
||||
Vector3 center = area.transform.position;
|
||||
Vector2 size = new Vector2(4f, 4f);
|
||||
|
||||
var poly = area.GetComponentInChildren<PolygonCollider2D>(true);
|
||||
if (poly != null)
|
||||
{
|
||||
Bounds b = poly.bounds;
|
||||
center = b.center;
|
||||
center.z = area.transform.position.z;
|
||||
size = new Vector2(b.size.x, b.size.y);
|
||||
}
|
||||
// 用 VisibleBounds 作为放置中心和初始多边形范围
|
||||
Rect visible = area.VisibleBounds;
|
||||
Vector3 center = new Vector3(visible.center.x, visible.center.y, area.transform.position.z);
|
||||
Vector2 half = visible.size * 0.5f;
|
||||
|
||||
var go = new GameObject($"{area.gameObject.name}_TriggerZone");
|
||||
Undo.RegisterCreatedObjectUndo(go, "Create CameraTriggerZone");
|
||||
@@ -661,20 +735,21 @@ namespace BaseGames.Editor
|
||||
go.transform.SetParent(area.transform);
|
||||
go.transform.position = center;
|
||||
|
||||
var col = go.AddComponent<PolygonCollider2D>();
|
||||
// [RequireComponent] 会自动附加 PolygonCollider2D;先 AddComponent<CameraTriggerZone>
|
||||
// 再通过 GetComponent 引用,避免顺序依赖问题
|
||||
var zone = go.AddComponent<CameraTriggerZone>();
|
||||
var col = go.GetComponent<PolygonCollider2D>();
|
||||
col.isTrigger = true;
|
||||
float hw = size.x * 0.5f;
|
||||
float hh = size.y * 0.5f;
|
||||
// 以 VisibleBounds 矩形四角为默认路径(可在 Inspector 中进一步编辑顶点)
|
||||
col.SetPath(0, new Vector2[]
|
||||
{
|
||||
new Vector2(-hw, -hh),
|
||||
new Vector2(-hw, hh),
|
||||
new Vector2( hw, hh),
|
||||
new Vector2( hw, -hh),
|
||||
new Vector2(-half.x, -half.y),
|
||||
new Vector2(-half.x, half.y),
|
||||
new Vector2( half.x, half.y),
|
||||
new Vector2( half.x, -half.y),
|
||||
});
|
||||
|
||||
var zone = go.AddComponent<CameraTriggerZone>();
|
||||
var so = new SerializedObject(zone);
|
||||
var so = new SerializedObject(zone);
|
||||
so.Update();
|
||||
so.FindProperty("_targetArea").objectReferenceValue = area;
|
||||
so.ApplyModifiedProperties();
|
||||
|
||||
Reference in New Issue
Block a user