convert game playing..

This commit is contained in:
2026-09-23 12:53:52 +09:00
parent 37c7942b71
commit 5d11288a6f
136 changed files with 15856 additions and 299 deletions
+214 -77
View File
@@ -53,13 +53,17 @@ namespace BumpCombat.UI
private Button[] debugSpawnButtons = System.Array.Empty<Button>();
private GameObject minimapPanel;
private RectTransform minimapMapRect;
private RectTransform minimapTerrainRoot;
private RectTransform minimapMarkerRoot;
private RectTransform minimapViewportRoot;
private Image minimapPlayerMarker;
private Image[] minimapViewportEdges = System.Array.Empty<Image>();
private readonly List<Image> minimapEnemyMarkers = new();
private readonly Dictionary<Vector2Int, Image> minimapTerrainCells = new();
private EnemyController[] minimapEnemies = System.Array.Empty<EnemyController>();
private float minimapEnemyRefreshElapsed;
private Stage.ZoneInstance minimapTerrainZone;
private int minimapTerrainVisibilityRevision = -1;
private ArenaBounds arenaBounds;
// The panel sprite has transparent source padding. At multiplier 1,
// 6px of canvas space keeps the map inside the visible frame.
@@ -68,22 +72,9 @@ namespace BumpCombat.UI
private Image[] artifactSlotIcons = System.Array.Empty<Image>();
private readonly Stack<Text> damagePopupPool = new();
private readonly HashSet<Text> activeDamagePopups = new();
private static readonly RunTimedEvent[] NextBossEvents =
{
RunTimedEvent.MidBoss,
RunTimedEvent.FinalBoss,
};
private static readonly string[] NextBossLabels =
{
"다음 중간보스",
"다음 보스",
};
private int activeDamagePopupCount;
private int damagePopupLifecycleVersion;
private float compactHudFadeElapsed;
private int lastNextEventSecond = -1;
private bool lastNextEventUsesDebugTimes;
private bool lastNextEventIsProductionRun;
private static readonly Color PlayerDamagePopupColor =
new(244f / 255f, 117f / 255f, 112f / 255f, 1f);
private static readonly Color GuardReadyColor = new Color32(255, 205, 92, 255);
@@ -741,61 +732,25 @@ namespace BumpCombat.UI
new Vector2(0f, -100f),
new Vector2(420f, 28f));
ConfigureCompactHudText(eventText, 14, 18);
eventText.gameObject.SetActive(true);
eventText.gameObject.SetActive(false);
UpdateNextEventHud();
}
private void UpdateNextEventHud()
{
if (eventText == null || runManager == null || showingTimedEventMessage)
if (eventText == null)
{
return;
}
int elapsedSecond = Mathf.FloorToInt(runManager.ElapsedTime);
if (elapsedSecond == lastNextEventSecond
&& runManager.UseDebugEventTimes == lastNextEventUsesDebugTimes
&& runManager.IsProductionRun == lastNextEventIsProductionRun)
if (!showingTimedEventMessage)
{
return;
}
lastNextEventSecond = elapsedSecond;
lastNextEventUsesDebugTimes = runManager.UseDebugEventTimes;
lastNextEventIsProductionRun = runManager.IsProductionRun;
float nextTime = float.PositiveInfinity;
string nextLabel = "다음 이벤트 없음";
for (int i = 0; i < runManager.CurrentEliteEventCount; i++)
{
float eliteTime = runManager.GetEliteEventTimeForCurrentRun(i);
if (eliteTime > runManager.ElapsedTime)
if (eventText.gameObject.activeSelf)
{
nextTime = eliteTime;
nextLabel = "다음 엘리트";
break;
eventText.gameObject.SetActive(false);
}
}
for (int i = 0; i < NextBossEvents.Length; i++)
{
float eventTime = runManager.GetEventTimeForCurrentRun(
NextBossEvents[i]);
if (eventTime > runManager.ElapsedTime && eventTime < nextTime)
{
nextTime = eventTime;
nextLabel = NextBossLabels[i];
}
}
if (float.IsPositiveInfinity(nextTime))
{
eventText.text = nextLabel;
return;
}
int seconds = Mathf.FloorToInt(nextTime);
eventText.text =
$"{nextLabel} {seconds / 60:00}:{seconds % 60:00}";
}
private void LayoutDebugText()
@@ -844,6 +799,20 @@ namespace BumpCombat.UI
{
popupText = $"LAUNCH {popupText}";
}
EnemyController popupEnemy = result.Target != null
? result.Target.GetComponentInParent<EnemyController>()
: null;
Stage.ZoneInstance popupZone = Stage.StageManager.Instance != null
? Stage.StageManager.Instance.CurrentZone
: null;
if (popupEnemy != null
&& popupZone != null
&& !popupZone.IsWorldPositionVisible(popupEnemy.GroundAnchorPosition))
{
return;
}
Color popupColor;
if (!TryGetArtifactPopupColor(result, out popupColor))
{
@@ -1271,7 +1240,7 @@ namespace BumpCombat.UI
RunTimedEvent.MidBoss,
RunTimedEvent.FinalBoss,
};
string[] labels = { "엘리트 출현", "중간보스 출현", "보스 출현" };
string[] labels = { "엘리트 출현", "중간보스 출현", "보스방 이동" };
debugSpawnButtons = new Button[events.Length];
for (int i = 0; i < events.Length; i++)
{
@@ -1296,16 +1265,30 @@ namespace BumpCombat.UI
Button button = buttonObject.GetComponent<Button>();
button.targetGraphic = image;
PresentationUiStyle.ApplyButton(button);
RunTimedEvent requestedEvent = events[i];
button.onClick.AddListener(() =>
if (i == 2)
{
if (spawnDirector != null
&& spawnDirector.TryDebugSpawnEventEnemy(requestedEvent))
button.onClick.AddListener(() =>
{
HandleTimedEvent(requestedEvent);
}
UpdateDebugSpawnHud();
});
if (Stage.StageManager.Instance != null)
{
Stage.StageManager.Instance.CheatTeleportToBossRoom();
}
UpdateDebugSpawnHud();
});
}
else
{
RunTimedEvent requestedEvent = events[i];
button.onClick.AddListener(() =>
{
if (spawnDirector != null
&& spawnDirector.TryDebugSpawnEventEnemy(requestedEvent))
{
HandleTimedEvent(requestedEvent);
}
UpdateDebugSpawnHud();
});
}
CreateRuntimeText(
buttonObject.transform,
"Label",
@@ -1327,7 +1310,7 @@ namespace BumpCombat.UI
bool visible = runManager != null && runManager.UseDebugArtifactSelection;
debugSpawnPanel.SetActive(visible);
if (!visible || spawnDirector == null)
if (!visible)
{
return;
}
@@ -1338,10 +1321,19 @@ namespace BumpCombat.UI
RunTimedEvent.MidBoss,
RunTimedEvent.FinalBoss,
};
bool inputEnabled = RunManager.GameplayInputEnabled;
for (int i = 0; i < debugSpawnButtons.Length; i++)
{
debugSpawnButtons[i].interactable =
spawnDirector.CanDebugSpawnEventEnemy(events[i]);
if (i == 2)
{
debugSpawnButtons[i].interactable =
inputEnabled && Stage.StageManager.Instance != null;
}
else
{
debugSpawnButtons[i].interactable =
spawnDirector != null && spawnDirector.CanDebugSpawnEventEnemy(events[i]);
}
}
}
@@ -1370,7 +1362,7 @@ namespace BumpCombat.UI
new Vector2(panelWidth, panelHeight));
Image minimapFrame = minimapPanel.GetComponent<Image>();
PresentationUiStyle.ApplyPanel(minimapFrame);
minimapFrame.pixelsPerUnitMultiplier = 1f;
minimapFrame.pixelsPerUnitMultiplier = PresentationUiStyle.ThreePixelBorderMultiplier;
minimapFrame.fillCenter = false;
GameObject map = CreateRuntimePanel(
@@ -1384,6 +1376,7 @@ namespace BumpCombat.UI
map.GetComponent<Image>().color = new Color(0.035f, 0.06f, 0.08f, 0.28f);
map.AddComponent<RectMask2D>();
minimapTerrainRoot = CreateMinimapLayer(map.transform, "Minimap Terrain");
minimapMarkerRoot = CreateMinimapLayer(map.transform, "Markers");
minimapViewportRoot = CreateMinimapLayer(map.transform, "Minimap Camera Viewport");
minimapMarkerRoot.SetAsLastSibling();
@@ -1453,6 +1446,8 @@ namespace BumpCombat.UI
return;
}
UpdateMinimapTerrain();
SetMinimapMarkerPosition(
minimapPlayerMarker,
playerHealth != null ? playerHealth.transform.position : Vector3.zero,
@@ -1469,6 +1464,9 @@ namespace BumpCombat.UI
}
int markerIndex = 0;
Stage.ZoneInstance visibilityZone = Stage.StageManager.Instance != null
? Stage.StageManager.Instance.CurrentZone
: null;
for (int i = 0; i < minimapEnemies.Length; i++)
{
EnemyController enemy = minimapEnemies[i];
@@ -1476,6 +1474,16 @@ namespace BumpCombat.UI
{
continue;
}
if (worldCamera == null
|| !arenaBounds.IsInsideCameraVisible(worldCamera, enemy.GroundAnchorPosition))
{
continue;
}
if (visibilityZone != null
&& !visibilityZone.IsWorldPositionVisible(enemy.GroundAnchorPosition))
{
continue;
}
Image marker = GetMinimapEnemyMarker(markerIndex++);
marker.gameObject.SetActive(true);
@@ -1499,6 +1507,132 @@ namespace BumpCombat.UI
UpdateMinimapViewport(extents);
}
private void UpdateMinimapTerrain()
{
Stage.ZoneInstance currentZone = Stage.StageManager.Instance != null
? Stage.StageManager.Instance.CurrentZone
: null;
if (currentZone == minimapTerrainZone)
{
if (currentZone != null
&& currentZone.VisibilityRevision != minimapTerrainVisibilityRevision)
{
UpdateMinimapTerrainVisibility(currentZone);
}
return;
}
minimapTerrainZone = currentZone;
minimapTerrainVisibilityRevision = -1;
minimapTerrainCells.Clear();
if (minimapTerrainRoot == null)
{
return;
}
for (int i = minimapTerrainRoot.childCount - 1; i >= 0; i--)
{
Destroy(minimapTerrainRoot.GetChild(i).gameObject);
}
Stage.RoomPreset preset = currentZone != null ? currentZone.Preset : null;
if (preset == null || !currentZone.HasRuntimeGrid ||
preset.GridWidth <= 0 || preset.GridHeight <= 0)
{
return;
}
Vector2 mapSize = minimapMapRect.rect.size;
float cellWidth = mapSize.x / preset.GridWidth;
float cellHeight = mapSize.y / preset.GridHeight;
float left = -mapSize.x * 0.5f;
float top = mapSize.y * 0.5f;
for (int y = 0; y < preset.GridHeight; y++)
{
for (int x = 0; x < preset.GridWidth; x++)
{
Stage.PresetCellType cellType = currentZone.GetRuntimeCell(x, y);
if (cellType == Stage.PresetCellType.Empty)
{
continue;
}
Image cell = CreateRuntimeImage(
minimapTerrainRoot,
$"Terrain {x},{y}",
Vector2.zero,
new Vector2(cellWidth + 0.2f, cellHeight + 0.2f));
cell.raycastTarget = false;
minimapTerrainCells[new Vector2Int(x, y)] = cell;
SetRectTransform(
cell.rectTransform,
new Vector2(0.5f, 0.5f),
new Vector2(0.5f, 0.5f),
new Vector2(
left + (x + 0.5f) * cellWidth,
top - (y + 0.5f) * cellHeight),
cell.rectTransform.sizeDelta);
}
}
UpdateMinimapTerrainVisibility(currentZone);
}
private void UpdateMinimapTerrainVisibility(Stage.ZoneInstance zone)
{
if (zone == null)
{
return;
}
foreach (KeyValuePair<Vector2Int, Image> entry in minimapTerrainCells)
{
Vector2Int cell = entry.Key;
if (entry.Value == null)
{
continue;
}
entry.Value.color = GetMinimapTerrainColor(
zone.GetRuntimeCell(cell.x, cell.y),
zone.IsCellVisible(cell.x, cell.y),
zone.IsCellExplored(cell.x, cell.y));
}
minimapTerrainVisibilityRevision = zone.VisibilityRevision;
}
private static Color GetMinimapTerrainColor(
Stage.PresetCellType cellType,
bool visible,
bool explored)
{
Color baseColor = cellType switch
{
Stage.PresetCellType.Obstacle => new Color32(12, 18, 22, 240),
Stage.PresetCellType.Socket => new Color32(54, 103, 112, 230),
Stage.PresetCellType.RewardAnchor => new Color32(73, 88, 70, 205),
_ => new Color32(41, 57, 62, 190),
};
if (visible)
{
return baseColor;
}
if (explored)
{
return new Color(
baseColor.r * 0.35f,
baseColor.g * 0.35f,
baseColor.b * 0.35f,
1f);
}
return new Color32(2, 4, 5, 255);
}
private Image GetMinimapEnemyMarker(int index)
{
while (minimapEnemyMarkers.Count <= index)
@@ -1544,9 +1678,11 @@ namespace BumpCombat.UI
return;
}
Vector2 center = arenaBounds != null ? arenaBounds.ZoneCenter : Vector2.zero;
Vector2 localPos = worldPosition - center;
Vector2 normalized = new(
Mathf.Clamp01((worldPosition.x + extents.x) / (extents.x * 2f)),
Mathf.Clamp01((worldPosition.y + extents.y) / (extents.y * 2f)));
Mathf.Clamp01((localPos.x + extents.x) / (extents.x * 2f)),
Mathf.Clamp01((localPos.y + extents.y) / (extents.y * 2f)));
Vector2 halfMarker = marker.rectTransform.rect.size * 0.5f;
float halfMapWidth = mapSize.x * 0.5f;
float halfMapHeight = mapSize.y * 0.5f;
@@ -1575,8 +1711,9 @@ namespace BumpCombat.UI
Vector2 mapSize = minimapMapRect.rect.size;
Bounds visible = arenaBounds.GetCameraVisibleBounds(worldCamera);
Vector2 min = WorldToMinimapPosition(visible.min, extents, mapSize);
Vector2 max = WorldToMinimapPosition(visible.max, extents, mapSize);
Vector2 zoneCenter = arenaBounds != null ? arenaBounds.ZoneCenter : Vector2.zero;
Vector2 min = WorldToMinimapPosition(visible.min, extents, mapSize, zoneCenter);
Vector2 max = WorldToMinimapPosition(visible.max, extents, mapSize, zoneCenter);
float width = Mathf.Max(2f, max.x - min.x);
float height = Mathf.Max(2f, max.y - min.y);
Vector2 center = (min + max) * 0.5f;
@@ -1617,11 +1754,13 @@ namespace BumpCombat.UI
private static Vector2 WorldToMinimapPosition(
Vector2 worldPosition,
Vector2 extents,
Vector2 mapSize)
Vector2 mapSize,
Vector2 center = default)
{
Vector2 localPos = worldPosition - center;
Vector2 normalized = new(
Mathf.Clamp01((worldPosition.x + extents.x) / (extents.x * 2f)),
Mathf.Clamp01((worldPosition.y + extents.y) / (extents.y * 2f)));
Mathf.Clamp01((localPos.x + extents.x) / (extents.x * 2f)),
Mathf.Clamp01((localPos.y + extents.y) / (extents.y * 2f)));
return new Vector2(
(normalized.x - 0.5f) * mapSize.x,
(normalized.y - 0.5f) * mapSize.y);
@@ -1705,12 +1844,10 @@ namespace BumpCombat.UI
private IEnumerator ShowEventMessage(string message)
{
showingTimedEventMessage = true;
lastNextEventSecond = -1;
eventText.text = message;
eventText.gameObject.SetActive(true);
yield return new WaitForSecondsRealtime(3f);
showingTimedEventMessage = false;
lastNextEventSecond = -1;
UpdateNextEventHud();
}
@@ -1760,7 +1897,7 @@ namespace BumpCombat.UI
"Presentation/Fonts/NanumGothic-Regular";
private const string NeoFontResource = "Presentation/Fonts/neodgm";
private const string GalmuriFontResource = "Presentation/Fonts/Galmuri9";
private const float ThreePixelBorderMultiplier = 1f / 3f;
internal const float ThreePixelBorderMultiplier = 1f / 3f;
private static readonly Color PanelFallback =
new(24f / 255f, 27f / 255f, 39f / 255f, 0.94f);