BT5-Dev 최적화 B안: Debug.Log 가드 + Legacy 4 파일 정리

PD 직접 지시 2026-05-08 — 프로젝트 반응 속도 최적화. 옵션 B 채택 (#1 Debug.Log + #7 Legacy).

#1 Debug.Log 가드 (#if UNITY_EDITOR && ENEMY_DIAG_VERBOSE) — 7건:
- EnemyController.cs Start[Enemy@N] / MeasureSafeWalk[Enemy@N dir] / Update[BT17·BT20·EnemyDiag]
- GameOptimizer.cs SetupJumpThroughPlatforms[GameOptimizer]
- PlayerEnemyCollision.cs Resolve[PEC]

근본: 16 몬스터 × 60fps frame당 16~32 string boxing + I/O = 반응 속도 저하 주 원인 추정 (Profiler 미실측·C44 추정). Editor 영역 차기 진단 시 ENEMY_DIAG_VERBOSE Scripting Define 추가 시점만 활성.

#7 Legacy 4 파일 정리:
- PlatformDropThrough.cs (자동 Destroy 영역·Layer 8 옛 영역)
- PatrolPath.cs / PatrolPath.Mover.cs (자동 patrol 도입 후 미참조)
- PatrolPathEditor.cs (CustomEditor PatrolPath 의존)
- EnemyController.path / mover field 제거
- PlayerController.Awake PlatformDropThrough Destroy 영역 제거

검증:
- 미참조 grep 0건 (PlatformDropThrough·PatrolPath 전수 제거 정합)
- Scene yaml PatrolPath GameObject 11건 잔존 (Missing Component 상태·게임 영향 X·PD 시각 후속 영역)

산출물: 12 파일 (D 8 + M 4)
This commit is contained in:
깃 관리자 2026-05-08 17:24:51 +09:00
parent 8b54816433
commit 69b1b9197b
12 changed files with 14 additions and 243 deletions

View File

@ -1,40 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using Platformer.Mechanics;
using UnityEditor;
using UnityEngine;
namespace Platformer
{
[CustomEditor(typeof(PatrolPath))]
public class PatrolPathGizmo : Editor
{
public void OnSceneGUI()
{
var path = target as PatrolPath;
using (var cc = new EditorGUI.ChangeCheckScope())
{
var sp = path.transform.InverseTransformPoint(Handles.PositionHandle(path.transform.TransformPoint(path.startPosition), path.transform.rotation));
var ep = path.transform.InverseTransformPoint(Handles.PositionHandle(path.transform.TransformPoint(path.endPosition), path.transform.rotation));
if (cc.changed)
{
sp.y = 0;
ep.y = 0;
path.startPosition = sp;
path.endPosition = ep;
}
}
Handles.Label(path.transform.position, (path.startPosition - path.endPosition).magnitude.ToString());
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected)]
static void OnDrawGizmo(PatrolPath path, GizmoType gizmoType)
{
var start = path.transform.TransformPoint(path.startPosition);
var end = path.transform.TransformPoint(path.endPosition);
Handles.color = Color.yellow;
Handles.DrawDottedLine(start, end, 5);
Handles.DrawSolidDisc(start, path.transform.forward, 0.1f);
Handles.DrawSolidDisc(end, path.transform.forward, 0.1f);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 574b9f9d90f1a4e0180d882b20207004
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -41,7 +41,9 @@ namespace Platformer.Gameplay
bool inAir = !player.IsGrounded; bool inAir = !player.IsGrounded;
bool playerAbove = player.transform.position.y > enemy.transform.position.y; bool playerAbove = player.transform.position.y > enemy.transform.position.y;
#if UNITY_EDITOR && ENEMY_DIAG_VERBOSE
Debug.Log($"[PEC] inAir={inAir} above={playerAbove} delta={dyAtCollision:F2} pY={player.transform.position.y:F2} eY={enemy.transform.position.y:F2}"); Debug.Log($"[PEC] inAir={inAir} above={playerAbove} delta={dyAtCollision:F2} pY={player.transform.position.y:F2} eY={enemy.transform.position.y:F2}");
#endif
if (inAir && playerAbove) if (inAir && playerAbove)
{ {

View File

@ -12,7 +12,6 @@ namespace Platformer.Mechanics
[RequireComponent(typeof(AnimationController), typeof(Collider2D))] [RequireComponent(typeof(AnimationController), typeof(Collider2D))]
public class EnemyController : MonoBehaviour public class EnemyController : MonoBehaviour
{ {
public PatrolPath path; // legacy 호환·자동 patrol 영역 도입 후 미사용
public AudioClip ouch; public AudioClip ouch;
/// <summary>BT5-Dev #16 — Distance 기반 감지 영역 X 임계값 (전체 폭). 표준 platformer Enemy 옆 닿음 영역 0.6~0.8.</summary> /// <summary>BT5-Dev #16 — Distance 기반 감지 영역 X 임계값 (전체 폭). 표준 platformer Enemy 옆 닿음 영역 0.6~0.8.</summary>
@ -48,7 +47,6 @@ namespace Platformer.Mechanics
private float _maxLeftRange; // 시작 시 측정 — 좌측 안전 patrol 거리 private float _maxLeftRange; // 시작 시 측정 — 좌측 안전 patrol 거리
private bool _isInitialized; // BT97: Start 측정 완료 영역 (Awake 이후 활성) private bool _isInitialized; // BT97: Start 측정 완료 영역 (Awake 이후 활성)
internal PatrolPath.Mover mover; // legacy 호환·미사용
internal AnimationController control; internal AnimationController control;
internal Collider2D _collider; internal Collider2D _collider;
internal AudioSource _audio; internal AudioSource _audio;
@ -110,7 +108,9 @@ namespace Platformer.Mechanics
_maxRightRange = MeasureSafeWalkDistance(1f); _maxRightRange = MeasureSafeWalkDistance(1f);
_maxLeftRange = MeasureSafeWalkDistance(-1f); _maxLeftRange = MeasureSafeWalkDistance(-1f);
#if UNITY_EDITOR && ENEMY_DIAG_VERBOSE
Debug.Log($"[Enemy@{name}] startX={_startX:F2} startY={_startY:F2} maxR={_maxRightRange:F2} maxL={_maxLeftRange:F2}"); Debug.Log($"[Enemy@{name}] startX={_startX:F2} startY={_startY:F2} maxR={_maxRightRange:F2} maxL={_maxLeftRange:F2}");
#endif
SetNextPatrolTarget(); SetNextPatrolTarget();
_isInitialized = true; _isInitialized = true;
} }
@ -193,7 +193,9 @@ namespace Platformer.Mechanics
} }
if (startTm != null) break; if (startTm != null) break;
} }
#if UNITY_EDITOR && ENEMY_DIAG_VERBOSE
Debug.Log($"[Enemy@{name}] dir={dir} startY={transformY:F2} colliderFoot={colliderFootY:F2} spriteFoot={spriteFootY:F2} chosenFootY={chosenFootY:F2} startTm={(startTm!=null?startTm.name:"NULL")} startCell={startCell}"); Debug.Log($"[Enemy@{name}] dir={dir} startY={transformY:F2} colliderFoot={colliderFootY:F2} spriteFoot={spriteFootY:F2} chosenFootY={chosenFootY:F2} startTm={(startTm!=null?startTm.name:"NULL")} startCell={startCell}");
#endif
if (startTm == null) return 0f; if (startTm == null) return 0f;
// 좌·우 연속 Tile 영역 끝 영역 검색 // 좌·우 연속 Tile 영역 끝 영역 검색
@ -369,11 +371,13 @@ namespace Platformer.Mechanics
_cachedPlayer = Object.FindFirstObjectByType<PlayerController>(); _cachedPlayer = Object.FindFirstObjectByType<PlayerController>();
} }
#if UNITY_EDITOR && ENEMY_DIAG_VERBOSE
if (Time.frameCount % 60 == 0) if (Time.frameCount % 60 == 0)
{ {
int allCount = Object.FindObjectsByType<PlayerController>(FindObjectsSortMode.None).Length; int allCount = Object.FindObjectsByType<PlayerController>(FindObjectsSortMode.None).Length;
Debug.Log($"[BT17-Update@{name}] f={Time.frameCount} cached={(_cachedPlayer != null ? _cachedPlayer.name : "NULL")} pgoTag={(pgo != null ? pgo.name : "NULL")} allPCcount={allCount}"); Debug.Log($"[BT17-Update@{name}] f={Time.frameCount} cached={(_cachedPlayer != null ? _cachedPlayer.name : "NULL")} pgoTag={(pgo != null ? pgo.name : "NULL")} allPCcount={allCount}");
} }
#endif
} }
// BT20 — IgnoreCollision 영역 Awake 시점 Player 발견 X 영역 fallback. Update 영역에서 발견 직후 1회 영역 호출. // BT20 — IgnoreCollision 영역 Awake 시점 Player 발견 X 영역 fallback. Update 영역에서 발견 직후 1회 영역 호출.
@ -384,7 +388,9 @@ namespace Platformer.Mechanics
{ {
Physics2D.IgnoreCollision(_collider, pc, true); Physics2D.IgnoreCollision(_collider, pc, true);
_ignoreCollisionApplied = true; _ignoreCollisionApplied = true;
#if UNITY_EDITOR && ENEMY_DIAG_VERBOSE
Debug.Log($"[BT20-Ignore@{name}] Player↔Enemy IgnoreCollision applied"); Debug.Log($"[BT20-Ignore@{name}] Player↔Enemy IgnoreCollision applied");
#endif
} }
} }
if (_cachedPlayer != null && _cachedPlayer.health != null && _cachedPlayer.health.IsAlive) if (_cachedPlayer != null && _cachedPlayer.health != null && _cachedPlayer.health.IsAlive)
@ -400,7 +406,9 @@ namespace Platformer.Mechanics
if (inRange != _diagWasIntersecting) if (inRange != _diagWasIntersecting)
{ {
#if UNITY_EDITOR && ENEMY_DIAG_VERBOSE
Debug.Log($"[EnemyDiag@{name}] inRange={inRange} footHeadDelta={footHeadDelta:F2} VB={VisualBounds.size} PB={_cachedPlayer.Bounds.size}"); Debug.Log($"[EnemyDiag@{name}] inRange={inRange} footHeadDelta={footHeadDelta:F2} VB={VisualBounds.size} PB={_cachedPlayer.Bounds.size}");
#endif
_diagWasIntersecting = inRange; _diagWasIntersecting = inRange;
} }

View File

@ -149,7 +149,9 @@ namespace Platformer.Mechanics
if (fgTc != null) fgTc.ProcessTilemapChanges(); if (fgTc != null) fgTc.ProcessTilemapChanges();
var lvlTc = levelGo.GetComponent<UnityEngine.Tilemaps.TilemapCollider2D>(); var lvlTc = levelGo.GetComponent<UnityEngine.Tilemaps.TilemapCollider2D>();
if (lvlTc != null) lvlTc.ProcessTilemapChanges(); if (lvlTc != null) lvlTc.ProcessTilemapChanges();
#if UNITY_EDITOR && ENEMY_DIAG_VERBOSE
Debug.Log($"[GameOptimizer] AutoForeground moved={movedHigh + movedSmall} (high={movedHigh} small={movedSmall} threshold={airThresholdY:F2}) / Layer16 applied={applied} levelKept0={levelKept} excluded={excluded}"); Debug.Log($"[GameOptimizer] AutoForeground moved={movedHigh + movedSmall} (high={movedHigh} small={movedSmall} threshold={airThresholdY:F2}) / Layer16 applied={applied} levelKept0={levelKept} excluded={excluded}");
#endif
} }
} }

View File

@ -1,38 +0,0 @@
using UnityEngine;
namespace Platformer.Mechanics
{
public partial class PatrolPath
{
/// <summary>
/// The Mover class oscillates between start and end points of a path at a defined speed.
/// </summary>
public class Mover
{
PatrolPath path;
float p = 0;
float duration;
float startTime;
public Mover(PatrolPath path, float speed)
{
this.path = path;
this.duration = (path.endPosition - path.startPosition).magnitude / speed;
this.startTime = Time.time;
}
/// <summary>
/// Get the position of the mover for the current frame.
/// </summary>
/// <value></value>
public Vector2 Position
{
get
{
p = Mathf.InverseLerp(0, duration, Mathf.PingPong(Time.time - startTime, duration));
return path.transform.TransformPoint(Vector2.Lerp(path.startPosition, path.endPosition, p));
}
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 91fcc8d06d79c4678aabe2abed234213
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
using UnityEngine;
namespace Platformer.Mechanics
{
/// <summary>
/// This component is used to create a patrol path, two points which enemies will move between.
/// </summary>
public partial class PatrolPath : MonoBehaviour
{
/// <summary>
/// One end of the patrol path.
/// </summary>
public Vector2 startPosition, endPosition;
/// <summary>
/// Create a Mover instance which is used to move an entity along the path at a certain speed.
/// </summary>
/// <param name="speed"></param>
/// <returns></returns>
public Mover CreateMover(float speed = 1) => new Mover(this, speed);
void Reset()
{
startPosition = Vector3.left;
endPosition = Vector3.right;
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 83b023ebe508a49c880fc00552f0dfc1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,97 +0,0 @@
using UnityEngine;
namespace Platformer.Mechanics
{
/// <summary>
/// BT5-Dev #27 — PD 제안 Layer + Raycast 동적 충돌.
/// Layer 8(JumpThrough) 발판은 기본 통과(IgnoreLayerCollision). Player 하강 + 발 Raycast 시점만 IgnoreCollision(false)로 충돌 활성 → 착지.
/// 점프(상승) 시 IgnoreCollision(true) 복구 → 다시 통과.
/// PlayerController.Awake에서 자동 부착.
/// </summary>
[RequireComponent(typeof(Collider2D))]
public class PlatformDropThrough : MonoBehaviour
{
const int JUMP_THROUGH_LAYER = 8;
[Tooltip("발 raycast 기본 거리. velocity 영역에 따라 동적 확대.")]
public float footRayDistance = 1.0f;
Collider2D _self;
KinematicObject _ko;
Collider2D _activePlatform;
int _jumpThroughMask;
void Awake()
{
_self = GetComponent<Collider2D>();
_ko = GetComponent<KinematicObject>();
_jumpThroughMask = 1 << JUMP_THROUGH_LAYER;
Debug.Log($"[BT32-DropThrough] Awake — self={(_self != null ? "OK" : "NULL")} ko={(_ko != null ? "OK" : "NULL")} mask={_jumpThroughMask}");
}
void Start()
{
Debug.Log($"[BT32-DropThrough] Start called");
if (_self == null) return;
Vector2 footPos = new Vector2(_self.bounds.center.x, _self.bounds.min.y + 0.02f);
// BT32 — 거리 무한대 = Player 시작 위치 영역 발판 영역 영역 식별
RaycastHit2D startHit = Physics2D.Raycast(footPos, Vector2.down, Mathf.Infinity, _jumpThroughMask);
if (startHit.collider != null)
{
Physics2D.IgnoreCollision(_self, startHit.collider, false);
_activePlatform = startHit.collider;
Debug.Log($"[BT32-StartHit] dist={startHit.distance:F2} platform='{startHit.collider.gameObject.name}' layer={startHit.collider.gameObject.layer}");
}
else
{
Debug.Log($"[BT32-StartHit] no Layer 8 platform below footPos={footPos} (mask={_jumpThroughMask})");
// 마지막 수단 — 모든 Layer raycast해서 가장 가까운 영역 식별
RaycastHit2D anyHit = Physics2D.Raycast(footPos, Vector2.down, Mathf.Infinity);
if (anyHit.collider != null)
Debug.Log($"[BT32-AnyHit] anyLayer dist={anyHit.distance:F2} platform='{anyHit.collider.gameObject.name}' layer={anyHit.collider.gameObject.layer}");
else
Debug.Log($"[BT32-AnyHit] NOTHING below player");
}
}
void Update()
{
if (_self == null || _ko == null) return;
// Player 발 위치 (collider 하단 중심)
Vector2 footPos = new Vector2(_self.bounds.center.x, _self.bounds.min.y + 0.02f);
// velocity 기반 동적 거리 (빠른 하강 시 raycast miss 차단)
float dist = Mathf.Max(footRayDistance, Mathf.Abs(_ko.velocity.y) * Time.deltaTime + 0.1f);
RaycastHit2D hit = Physics2D.Raycast(footPos, Vector2.down, dist, _jumpThroughMask);
bool falling = _ko.velocity.y <= 0.01f; // 하강 또는 정지
bool rising = _ko.velocity.y > 0.5f; // 점프 상승
if (rising)
{
// BT5-Dev #33 — 활성 해제 = 점프(상승) 시점만. 발판 떠남(낙하)은 활성 유지 (Player 거리 영역 벗어나도 충돌 유지·복구)
if (_activePlatform != null)
{
Physics2D.IgnoreCollision(_self, _activePlatform, true);
_activePlatform = null;
}
}
else if (falling && hit.collider != null && hit.collider != _activePlatform)
{
// 하강 + 발판 raycast hit → 충돌 활성 (착지)
Physics2D.IgnoreCollision(_self, hit.collider, false);
_activePlatform = hit.collider;
}
// BT33 — falling + hit X 영역 활성 해제 영역 폐기 (raycast 거리 일시 벗어남 영역 통과 복구 차단)
}
void OnDrawGizmosSelected()
{
if (_self == null) return;
Vector2 footPos = new Vector2(_self.bounds.center.x, _self.bounds.min.y + 0.02f);
Gizmos.color = Color.cyan;
Gizmos.DrawLine(footPos, footPos + Vector2.down * footRayDistance);
}
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: bc0df74503a03ee478bb504a5cd1b0c3

View File

@ -91,9 +91,6 @@ namespace Platformer.Mechanics
// 동반 컴포넌트 자동 부착 (Inspector 부착 불요) // 동반 컴포넌트 자동 부착 (Inspector 부착 불요)
if (GetComponent<PlayerInvulnerabilityFlash>() == null) gameObject.AddComponent<PlayerInvulnerabilityFlash>(); if (GetComponent<PlayerInvulnerabilityFlash>() == null) gameObject.AddComponent<PlayerInvulnerabilityFlash>();
if (GetComponent<Platformer.UI.ResurrectPromptUI>() == null) gameObject.AddComponent<Platformer.UI.ResurrectPromptUI>(); if (GetComponent<Platformer.UI.ResurrectPromptUI>() == null) gameObject.AddComponent<Platformer.UI.ResurrectPromptUI>();
// 구 PlatformDropThrough 컴포넌트 자동 제거 (Drop-Through는 ContactFilter mask 동적 갱신으로 처리)
var oldDrop = GetComponent<PlatformDropThrough>();
if (oldDrop != null) Destroy(oldDrop);
// 사망 시 입력 차단 / 부활 시 입력 복원 // 사망 시 입력 차단 / 부활 시 입력 복원
if (health != null) if (health != null)