From 54233822966d045e91e208def9bdac3ca864115d Mon Sep 17 00:00:00 2001 From: jongjae0305 Date: Mon, 6 Jul 2026 17:34:13 +0900 Subject: [PATCH] Tune tutorial coin arcs and finish flow --- ...way.png => FinishGate_America_Liberty.png} | Bin ...ta => FinishGate_America_Liberty.png.meta} | 0 Assets/Scripts/FinishGate.cs | 88 +++++++++++---- Assets/Scripts/GameManager.cs | 102 +++++++++++++++--- Assets/Scripts/Maps/MapDatabase.cs | 2 +- Assets/Scripts/Platform.cs | 26 ++++- Assets/Scripts/PlatformSpawner.cs | 49 +++++++++ Assets/Scripts/PlayerController.cs | 2 +- Assets/Sence/Main.unity | 2 +- Docs/assets.md | 2 +- Docs/blueprint.md | 7 +- Docs/design.md | 1 + Docs/feedback.md | 27 +++-- Docs/gameplay.md | 5 + Docs/maps.md | 6 +- Docs/systems.md | 2 + README.md | 10 +- 17 files changed, 266 insertions(+), 65 deletions(-) rename Assets/Resources/{FinishGate_America_Highway.png => FinishGate_America_Liberty.png} (100%) rename Assets/Resources/{FinishGate_America_Highway.png.meta => FinishGate_America_Liberty.png.meta} (100%) diff --git a/Assets/Resources/FinishGate_America_Highway.png b/Assets/Resources/FinishGate_America_Liberty.png similarity index 100% rename from Assets/Resources/FinishGate_America_Highway.png rename to Assets/Resources/FinishGate_America_Liberty.png diff --git a/Assets/Resources/FinishGate_America_Highway.png.meta b/Assets/Resources/FinishGate_America_Liberty.png.meta similarity index 100% rename from Assets/Resources/FinishGate_America_Highway.png.meta rename to Assets/Resources/FinishGate_America_Liberty.png.meta diff --git a/Assets/Scripts/FinishGate.cs b/Assets/Scripts/FinishGate.cs index 573f631..b740eff 100644 --- a/Assets/Scripts/FinishGate.cs +++ b/Assets/Scripts/FinishGate.cs @@ -1,12 +1,15 @@ using TMPro; using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif public enum FinishGateStyle { None, AirportDeparture, JapanTorii, ChinaPaifang, - AmericaRoadSign + AmericaLiberty } public class FinishGate : MonoBehaviour { @@ -75,8 +78,8 @@ public class FinishGate : MonoBehaviour { case FinishGateStyle.ChinaPaifang: BuildChinaPaifang(); break; - case FinishGateStyle.AmericaRoadSign: - BuildAmericaRoadSign(); + case FinishGateStyle.AmericaLiberty: + BuildAmericaLibertyFallback(); break; case FinishGateStyle.AirportDeparture: default: @@ -98,19 +101,7 @@ public class FinishGate : MonoBehaviour { return false; } - Sprite sprite = Resources.Load(resourcePath); - if(sprite == null) - { - Texture2D texture = Resources.Load(resourcePath); - if(texture != null) - { - sprite = Sprite.Create( - texture, - new Rect(0f, 0f, texture.width, texture.height), - new Vector2(0.5f, 0.5f), - RuntimeSpritePixelsPerUnit); - } - } + Sprite sprite = LoadGateSprite(resourcePath); if(sprite == null) { @@ -129,10 +120,55 @@ public class FinishGate : MonoBehaviour { float spriteHeight = Mathf.Max(0.01f, sprite.bounds.size.y); float scale = targetHeight / spriteHeight; spriteObject.transform.localScale = new Vector3(scale, scale, 1f); - spriteObject.transform.localPosition = new Vector3(0f, targetHeight * 0.5f, 0f); + spriteObject.transform.localPosition = new Vector3(0f, targetHeight * 0.5f - GetSpriteGateBottomInset(style), 0f); return true; } + private Sprite LoadGateSprite(string resourcePath) { + Sprite sprite = Resources.Load(resourcePath); + if(sprite != null) + { + return sprite; + } + + Sprite[] sprites = Resources.LoadAll(resourcePath); + if(sprites != null && sprites.Length > 0 && sprites[0] != null) + { + return sprites[0]; + } + + Texture2D texture = Resources.Load(resourcePath); + if(texture != null) + { + return CreateRuntimeSprite(texture); + } + +#if UNITY_EDITOR + string assetPath = "Assets/Resources/" + resourcePath + ".png"; + sprite = AssetDatabase.LoadAssetAtPath(assetPath); + if(sprite != null) + { + return sprite; + } + + texture = AssetDatabase.LoadAssetAtPath(assetPath); + if(texture != null) + { + return CreateRuntimeSprite(texture); + } +#endif + + return null; + } + + private Sprite CreateRuntimeSprite(Texture2D texture) { + return Sprite.Create( + texture, + new Rect(0f, 0f, texture.width, texture.height), + new Vector2(0.5f, 0.5f), + RuntimeSpritePixelsPerUnit); + } + private string GetSpriteResourcePath(FinishGateStyle style) { switch(style) { @@ -142,8 +178,8 @@ public class FinishGate : MonoBehaviour { return "FinishGate_Japan_Torii"; case FinishGateStyle.ChinaPaifang: return "FinishGate_China_Paifang"; - case FinishGateStyle.AmericaRoadSign: - return "FinishGate_America_Highway"; + case FinishGateStyle.AmericaLiberty: + return "FinishGate_America_Liberty"; default: return ""; } @@ -154,13 +190,23 @@ public class FinishGate : MonoBehaviour { { case FinishGateStyle.ChinaPaifang: return 4.65f; - case FinishGateStyle.AmericaRoadSign: + case FinishGateStyle.AmericaLiberty: return 4.45f; default: return 4.35f; } } + private float GetSpriteGateBottomInset(FinishGateStyle style) { + switch(style) + { + case FinishGateStyle.JapanTorii: + return 0.36f; + default: + return 0f; + } + } + private void BuildAirportDepartureGate() { Color navy = new Color(0.05f, 0.2f, 0.34f, 1f); Color blue = new Color(0.08f, 0.42f, 0.67f, 1f); @@ -212,7 +258,7 @@ public class FinishGate : MonoBehaviour { CreateBlock("Paifang Lantern Right", new Vector2(0.78f, 2.18f), new Vector2(0.26f, 0.34f), red, 7); } - private void BuildAmericaRoadSign() { + private void BuildAmericaLibertyFallback() { Color metal = new Color(0.48f, 0.56f, 0.58f, 1f); Color green = new Color(0.03f, 0.38f, 0.22f, 1f); Color yellow = new Color(1f, 0.8f, 0.18f, 1f); diff --git a/Assets/Scripts/GameManager.cs b/Assets/Scripts/GameManager.cs index 8753388..43ede27 100644 --- a/Assets/Scripts/GameManager.cs +++ b/Assets/Scripts/GameManager.cs @@ -62,6 +62,7 @@ public class GameManager : MonoBehaviour { private int tutorialGuideVersion = 0; // 오래된 자동 숨김 요청을 무시하기 위한 버전 private FinishGate activeFinishGate; // 목표 지점에 나타나는 맵별 클리어 게이트 private bool finishGateSpawned = false; // 현재 맵에서 도착 게이트를 이미 만들었는지 여부 + private PlatformSpawner activePlatformSpawner; // 현재 맵의 발판/장애물 생성기 public int maxLife = 3; // 시작 목숨 public int initialMileage = 500; // 첫 시작시 지급할 초기 마일리지 @@ -222,10 +223,10 @@ public class GameManager : MonoBehaviour { gameoverUI.SetActive(false); } - PlatformSpawner platformSpawner = FindFirstObjectByType(); - if(platformSpawner != null) + activePlatformSpawner = FindFirstObjectByType(); + if(activePlatformSpawner != null) { - platformSpawner.ApplyMap(currentMapDefinition); + activePlatformSpawner.ApplyMap(currentMapDefinition); } StartTutorialGuideSequence(); @@ -646,11 +647,31 @@ public class GameManager : MonoBehaviour { return; } + float gateGroundY = finishGateGroundY; + EnsureFinishGateLanding(gateGroundY); + GameObject gateObject = new GameObject("Finish Gate - " + currentMapDefinition.displayName); - gateObject.transform.position = new Vector3(finishGateSpawnX, finishGateGroundY, 0f); + gateObject.transform.position = new Vector3(finishGateSpawnX, gateGroundY, 0f); activeFinishGate = gateObject.AddComponent(); activeFinishGate.Setup(currentMapDefinition.finishGateStyle); finishGateSpawned = true; + + if(activePlatformSpawner != null) + { + activePlatformSpawner.StopCourseSpawningAfterFinishGate(); + } + } + + private void EnsureFinishGateLanding(float gateGroundY) { + if(activePlatformSpawner == null) + { + activePlatformSpawner = FindFirstObjectByType(); + } + + if(activePlatformSpawner != null) + { + activePlatformSpawner.SpawnFinishLandingPlatform(finishGateSpawnX, gateGroundY); + } } private bool ShouldUseFinishGate() { @@ -665,6 +686,11 @@ public class GameManager : MonoBehaviour { Destroy(activeFinishGate.gameObject); activeFinishGate = null; } + + if(activePlatformSpawner != null) + { + activePlatformSpawner.ClearFinishLandingPlatform(); + } } private void SyncTotalMileageToFlow() { @@ -739,14 +765,15 @@ public class GameManager : MonoBehaviour { tutorialBodyText.text = tutorialMessage; } - bool showItemRow = tutorialGuideType == TutorialGuideType.Items; - Sprite mainSprite = GetTutorialMainSprite(tutorialGuideType); - bool showFullText = !showItemRow && mainSprite == null; - UpdateTutorialTextLayout(showItemRow, showFullText); + int activeItemImageIndex = GetTutorialItemImageIndex(); + bool showItemImage = tutorialGuideType == TutorialGuideType.Items && HasTutorialItemImage(activeItemImageIndex); + Sprite mainSprite = showItemImage ? null : GetTutorialMainSprite(tutorialGuideType); + bool showFullText = !showItemImage && mainSprite == null; + UpdateTutorialTextLayout(showItemImage, showFullText); if(tutorialMainImage != null) { - tutorialMainImage.gameObject.SetActive(!showItemRow && mainSprite != null); + tutorialMainImage.gameObject.SetActive(!showItemImage && mainSprite != null); tutorialMainImage.sprite = mainSprite; } @@ -761,7 +788,7 @@ public class GameManager : MonoBehaviour { { if(tutorialItemImages[i] != null) { - tutorialItemImages[i].gameObject.SetActive(showItemRow && tutorialItemImages[i].sprite != null); + tutorialItemImages[i].gameObject.SetActive(showItemImage && i == activeItemImageIndex); } } } @@ -775,10 +802,10 @@ public class GameManager : MonoBehaviour { if(showItemRow) { - textPosition = new Vector2(154f, -8f); - textSize = new Vector2(178f, 17f); - bodyPosition = new Vector2(154f, -26f); - bodySize = new Vector2(178f, 31f); + textPosition = new Vector2(80f, -8f); + textSize = new Vector2(252f, 17f); + bodyPosition = new Vector2(80f, -26f); + bodySize = new Vector2(252f, 31f); } else if(showFullText) { @@ -801,6 +828,49 @@ public class GameManager : MonoBehaviour { } } + private bool HasTutorialItemImage(int itemImageIndex) { + return tutorialItemImages != null + && itemImageIndex >= 0 + && itemImageIndex < tutorialItemImages.Length + && tutorialItemImages[itemImageIndex] != null + && tutorialItemImages[itemImageIndex].sprite != null; + } + + private int GetTutorialItemImageIndex() { + if(tutorialGuideType != TutorialGuideType.Items) + { + return -1; + } + + string itemText = (tutorialTitle + " " + tutorialMessage).Trim(); + if(itemText.Contains("초밥")) + { + return 0; + } + + if(itemText.Contains("라멘")) + { + return 1; + } + + if(itemText.Contains("방어막")) + { + return 2; + } + + if(itemText.Contains("운동화")) + { + return 3; + } + + if(itemText.Contains("마일리지")) + { + return 4; + } + + return -1; + } + private Color GetActiveLifeIconColor(Image lifeIcon) { if(lifeIcon != null && lifeIcon.sprite == null) { @@ -1061,8 +1131,8 @@ public class GameManager : MonoBehaviour { itemRect.anchorMin = new Vector2(0f, 0.5f); itemRect.anchorMax = new Vector2(0f, 0.5f); itemRect.pivot = new Vector2(0f, 0.5f); - itemRect.anchoredPosition = new Vector2(11f + (i * 27f), 0f); - itemRect.sizeDelta = new Vector2(24f, 24f); + itemRect.anchoredPosition = new Vector2(14f, 0f); + itemRect.sizeDelta = new Vector2(54f, 54f); Image itemImage = itemObject.GetComponent(); itemImage.sprite = LoadTutorialSprite(itemAssetPaths[i]); diff --git a/Assets/Scripts/Maps/MapDatabase.cs b/Assets/Scripts/Maps/MapDatabase.cs index b4ee89e..429d6ab 100644 --- a/Assets/Scripts/Maps/MapDatabase.cs +++ b/Assets/Scripts/Maps/MapDatabase.cs @@ -193,7 +193,7 @@ public static class MapDatabase { isTutorial = false, platformSkin = Platform.PlatformSkin.USA, backgroundResourcePath = "Map_America_NewYork", - finishGateStyle = FinishGateStyle.AmericaRoadSign, + finishGateStyle = FinishGateStyle.AmericaLiberty, targetDistance = 1700f, timeLimit = 125f, clearMileageReward = 500, diff --git a/Assets/Scripts/Platform.cs b/Assets/Scripts/Platform.cs index 78d0701..e0cc855 100644 --- a/Assets/Scripts/Platform.cs +++ b/Assets/Scripts/Platform.cs @@ -60,6 +60,8 @@ public class Platform : MonoBehaviour { private const float MileageColliderRadius = 0.4f; private const float ItemTokenScale = 0.68f; private const float ItemColliderRadius = 0.46f; + private const float JumpMileageMinWidthScale = 1.8f; + private const float JumpMileageMaxWidthScale = 2.5f; public void ConfigureForTutorial( PlatformPattern pattern, @@ -619,15 +621,29 @@ public class Platform : MonoBehaviour { } private void PlaceJumpMileageArc(float obstacleX) { + float widthScale = GetJumpMileageArcWidthScale(); Vector2[] positions = { - new Vector2(obstacleX - 1.2f, 1.65f), - new Vector2(obstacleX, 2.25f), - new Vector2(obstacleX + 1.2f, 1.65f), - new Vector2(obstacleX + 1.9f, 1.35f) + new Vector2(obstacleX - 2.25f * widthScale, 1.88f), + new Vector2(obstacleX - 1.15f * widthScale, 2.88f), + new Vector2(obstacleX, 3.58f), + new Vector2(obstacleX + 1.15f * widthScale, 2.88f), + new Vector2(obstacleX + 2.25f * widthScale, 1.88f) }; PlaceMileageTokens(positions, smallMileageValue); - SetPickupValue(1, bonusMileageValue); + SetPickupValue(2, bonusMileageValue); + } + + private float GetJumpMileageArcWidthScale() { + if(GameManager.instance == null) + { + return JumpMileageMinWidthScale; + } + + float minSpeed = Mathf.Max(0.1f, GameManager.instance.minGameSpeed); + float maxSpeed = Mathf.Max(minSpeed + 0.1f, GameManager.instance.maxGameSpeed); + float speedProgress = Mathf.InverseLerp(minSpeed, maxSpeed, GameManager.instance.gameSpeed); + return Mathf.Lerp(JumpMileageMinWidthScale, JumpMileageMaxWidthScale, speedProgress); } private void PlaceSlideMileageLine() { diff --git a/Assets/Scripts/PlatformSpawner.cs b/Assets/Scripts/PlatformSpawner.cs index 139eca4..3a61667 100644 --- a/Assets/Scripts/PlatformSpawner.cs +++ b/Assets/Scripts/PlatformSpawner.cs @@ -84,6 +84,8 @@ public class PlatformSpawner : MonoBehaviour { private int courseStepIndex = 0; // 진행 중인 패턴 코스 순번 private Platform.PlatformSkin mapPlatformSkin = Platform.PlatformSkin.Airport; // 현재 맵에서 사용할 발판 스킨 private readonly List pendingTutorialGuides = new List(); // 실제 체험 직전에 띄울 설명들 + private GameObject activeFinishLandingPlatform; // 도착 게이트가 공중에 뜨지 않도록 깔아두는 전용 발판 + private bool courseSpawningStopped = false; // 도착 게이트 뒤로 새 발판/장애물이 나오지 않게 막는다. private Vector2 poolPosition = new Vector2(0, -25); // 초반에 생성된 발판들을 화면 밖에 숨겨둘 위치 private float lastSpawnTime; // 마지막 배치 시점 @@ -100,7 +102,9 @@ public class PlatformSpawner : MonoBehaviour { tutorialSteps = MapDatabase.CloneSteps(mapDefinition.openingSteps); stagePatternSteps = MapDatabase.CloneSteps(mapDefinition.loopSteps); courseStepIndex = 0; + courseSpawningStopped = false; pendingTutorialGuides.Clear(); + ClearFinishLandingPlatform(); ResetPoolIndices(); lastSpawnTime = Time.time; timeBetSpawn = 0f; @@ -109,6 +113,46 @@ public class PlatformSpawner : MonoBehaviour { DeactivateAllPlatformPools(); } + public void SpawnFinishLandingPlatform(float spawnX, float groundY) { + if(platformPrefab == null) + { + return; + } + + ClearFinishLandingPlatform(); + activeFinishLandingPlatform = Instantiate(platformPrefab, poolPosition, Quaternion.identity); + activeFinishLandingPlatform.SetActive(false); + + TutorialStep landingStep = CreateStep( + Platform.PlatformPattern.Empty, + Platform.MileagePattern.None, + SlideObstaclePattern.SlideLayout.Random, + GameManager.TutorialGuideType.None, + Mathf.Clamp(groundY, yMin, yMax), + FallbackLandingSpacing, + "", + "", + Platform.ItemPattern.None, + CoursePlatformType.Basic); + + ConfigurePlatform(activeFinishLandingPlatform, landingStep); + activeFinishLandingPlatform.transform.position = new Vector2(spawnX, landingStep.yPosition); + activeFinishLandingPlatform.SetActive(true); + } + + public void ClearFinishLandingPlatform() { + if(activeFinishLandingPlatform != null) + { + Destroy(activeFinishLandingPlatform); + activeFinishLandingPlatform = null; + } + } + + public void StopCourseSpawningAfterFinishGate() { + courseSpawningStopped = true; + pendingTutorialGuides.Clear(); + } + void Start() { EnsurePatternSteps(); @@ -205,6 +249,11 @@ public class PlatformSpawner : MonoBehaviour { return; } + if(courseSpawningStopped) + { + return; + } + UpdatePendingTutorialGuides(); if(GameManager.instance != null && GameManager.instance.IsTutorialGuidePaused) { diff --git a/Assets/Scripts/PlayerController.cs b/Assets/Scripts/PlayerController.cs index fa54652..220abf9 100644 --- a/Assets/Scripts/PlayerController.cs +++ b/Assets/Scripts/PlayerController.cs @@ -5,7 +5,7 @@ using UnityEngine; // PlayerController는 플레이어 캐릭터로서 Player 게임 오브젝트를 제어한다. public class PlayerController : MonoBehaviour { public AudioClip deathClip; // 사망시 재생할 오디오 클립 - public float jumpForce = 700f; // 점프 힘 + public float jumpForce = 600f; // 점프 힘 public int maxJumpCount = 2; // 최대 점프 횟수 public float invincibleDuration = 1.5f; // 피해를 입은 뒤 무적 시간 public float groundStickGraceTime = 0.12f; // 발판 이음새에서 접지가 아주 잠깐 끊겨도 유지하는 시간 diff --git a/Assets/Sence/Main.unity b/Assets/Sence/Main.unity index 82b630b..f7ca8df 100644 --- a/Assets/Sence/Main.unity +++ b/Assets/Sence/Main.unity @@ -977,7 +977,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Assembly-CSharp::PlayerController deathClip: {fileID: 8300000, guid: 668a107660fa54fe98e03b698a09aa49, type: 3} - jumpForce: 700 + jumpForce: 600 maxJumpCount: 2 invincibleDuration: 1.5 slidingColliderSize: {x: 1.2, y: 0.6} diff --git a/Docs/assets.md b/Docs/assets.md index 346c558..2fd4ef3 100644 --- a/Docs/assets.md +++ b/Docs/assets.md @@ -17,7 +17,7 @@ | `Assets/Resources/FinishGate_Tutorial_Incheon.png` | 튜토리얼 출국 게이트 | | `Assets/Resources/FinishGate_Japan_Torii.png` | 일본 토리이 도착 지점 | | `Assets/Resources/FinishGate_China_Paifang.png` | 중국 패방 도착 지점 | -| `Assets/Resources/FinishGate_America_Highway.png` | 미국 자유의 여신상 도착 지점 | +| `Assets/Resources/FinishGate_America_Liberty.png` | 미국 자유의 여신상 도착 지점 | ## Intro Assets diff --git a/Docs/blueprint.md b/Docs/blueprint.md index 7676b18..e9602a0 100644 --- a/Docs/blueprint.md +++ b/Docs/blueprint.md @@ -96,7 +96,6 @@ Intro ## 다음 작업 우선순위 -1. 변경된 도착 지점 이미지가 실제 게임에서 제대로 보이는지 확인 -2. 면세점 구매 아이템을 장착 슬롯과 연결 -3. 일본, 중국, 미국 맵의 실제 반복 패턴과 난이도 차별화 -4. 미국 도착 지점 파일명 정리 +1. 면세점 구매 아이템을 장착 슬롯과 연결 +2. 일본, 중국, 미국 맵의 실제 반복 패턴과 난이도 차별화 +3. 도착 지점 이미지의 실제 화면 크기를 플레이하면서 세부 조정 diff --git a/Docs/design.md b/Docs/design.md index 0565213..d892572 100644 --- a/Docs/design.md +++ b/Docs/design.md @@ -76,6 +76,7 @@ HUD: 5. 아이템 사용 설명은 화면을 가리지 않도록 잠깐 멈춘 뒤 보여주고, 이후 실제 구간을 통과하게 합니다. +아이템 설명창은 전체 아이템 목록을 한 번에 보여주지 않고, 현재 설명하는 아이템 이미지만 크게 보여줍니다. ## Result diff --git a/Docs/feedback.md b/Docs/feedback.md index dac2e8e..f8cbce4 100644 --- a/Docs/feedback.md +++ b/Docs/feedback.md @@ -27,18 +27,31 @@ - 하단 미니맵을 런타임 HUD에서 제거 - 아이템 획득 시 짧은 링/반짝임/문구 이펙트 추가 - 라멘 무적/방어막 방어 성공 시 피격 깜빡임 대신 `SAFE`/`BLOCK` 피드백 표시 +- 점프 마일리지 코인 경로를 5개 포물선으로 조정 +- 점프 조작성을 쉽게 맞추기 위해 플레이어 점프 힘을 `700`에서 `600`으로 낮춤 +- 속도가 붙을수록 점프 코인 경로의 가로 폭이 넓어지도록 보정 +- 점프 코인 아크의 기본 가로/세로 폭을 더 크게 조정 +- 출국 게이트 도착지는 만든 PNG 리소스를 우선 사용하도록 로딩 순서 수정 +- 점프 코인 기본 아크 폭을 `1.3`, 최대 속도 폭을 `2.0`으로 확대 +- 도착지점 스프라이트 로딩을 `Sprite`, `LoadAll`, `Texture2D`, 에디터 직접 경로 순서로 보강 +- 점프 코인 기본 아크 폭을 다시 `1.5`, 최대 속도 폭을 `2.15`로 확대 +- 점프 코인 기본 가로 폭만 `1.8`로 추가 확대 +- 점프 코인 최대 속도 가로 폭을 `2.5`로 확대 +- 도착 게이트가 나온 뒤에는 새 발판/장애물 코스가 더 스폰되지 않도록 수정 +- 아이템 설명창은 전체 아이템 줄 대신 해당 아이템 하나만 큰 이미지로 표시 +- 도착 게이트가 생성될 때 전용 착지 발판을 함께 생성하도록 수정 +- 미국 도착 지점 리소스 파일명을 `FinishGate_America_Liberty.png`로 정리 +- 라멘 무적 아이템 지속 시간이 현재 `2.2초`임을 문서에 기록 ### 현재 문제 - 면세점 구매 아이템이 아직 장착 슬롯과 연결되지 않음 -- 미국 도착 지점 파일명이 아직 `FinishGate_America_Highway.png`라 실제 내용과 이름이 다름 - 일본, 중국, 미국 맵의 실제 패턴과 난이도 차별화가 더 필요함 -- 도착 지점 이미지의 게임 내 크기와 위치를 실제 플레이로 확인해야 함 +- 도착 지점 이미지의 게임 내 세부 크기는 실제 플레이로 더 조정해야 함 ### 다음 할 일 -1. 도착 지점 이미지 크기와 위치 플레이 확인 -2. 면세점 구매 아이템을 장착 슬롯과 연결 -3. 일본, 중국, 미국 맵의 실제 패턴과 난이도 차별화 -4. 미국 도착 지점 파일명 정리 검토 -5. 컨셉 이펙트 이미지를 실제 개별 스프라이트로 분리할지 결정 +1. 면세점 구매 아이템을 장착 슬롯과 연결 +2. 일본, 중국, 미국 맵의 실제 패턴과 난이도 차별화 +3. 도착 지점 이미지의 실제 화면 크기를 플레이하면서 세부 조정 +4. 컨셉 이펙트 이미지를 실제 개별 스프라이트로 분리할지 결정 diff --git a/Docs/gameplay.md b/Docs/gameplay.md index c18049a..17c41c2 100644 --- a/Docs/gameplay.md +++ b/Docs/gameplay.md @@ -54,6 +54,11 @@ | 마일리지 카드 | 마일리지 획득량 증가 | 먹은 뒤 마일리지 수집 | 아이템을 먹으면 짧은 링, 반짝임, 문구 이펙트가 떠서 획득 여부를 확인할 수 있습니다. +라멘 무적 아이템의 현재 지속 시간은 `2.2초`입니다. + +점프 구간의 마일리지 코인은 플레이어가 뛰는 높이에 맞춰 넓고 높은 포물선으로 배치합니다. +게임 속도가 빨라지면 코인 포물선의 가로 폭도 함께 넓혀 점프 타이밍과 맞춥니다. +현재 점프 코인 가로 폭은 기본 속도에서 `1.8배`, 최대 속도에서 `2.5배`까지 보정합니다. ## 코스 생성 diff --git a/Docs/maps.md b/Docs/maps.md index 8d65f9c..b9b9f49 100644 --- a/Docs/maps.md +++ b/Docs/maps.md @@ -97,9 +97,9 @@ 도착 지점: -- `Assets/Resources/FinishGate_America_Highway.png` -- 현재 파일명은 기존 하이웨이 이름이지만, 실제 이미지는 자유의 여신상/뉴욕 도착지입니다. -- 추후 파일명을 `FinishGate_America_Liberty.png`로 바꾸는 것이 더 명확합니다. +- `Assets/Resources/FinishGate_America_Liberty.png` +- 도착 게이트가 나올 때 전용 착지 발판을 함께 깔아 이미지가 공중에 뜬 것처럼 보이지 않게 합니다. +- 도착 게이트 뒤에는 추가 코스가 생성되지 않게 막아 클리어 지점이 끝처럼 보이게 합니다. 스킨: diff --git a/Docs/systems.md b/Docs/systems.md index 54c68f0..3889486 100644 --- a/Docs/systems.md +++ b/Docs/systems.md @@ -100,6 +100,8 @@ Intro - 미국: 자유의 여신상/뉴욕 도착지 플레이어가 도착 지점 트리거를 통과하면 클리어 처리합니다. +도착 게이트가 생성될 때는 `PlatformSpawner`가 빈 착지 발판을 같은 위치에 함께 깔아, 게이트가 배경 위에 떠 보이지 않게 합니다. +도착 게이트 뒤로는 새 발판, 장애물, 아이템 코스가 더 생성되지 않도록 스폰을 중지합니다. ## Minimap diff --git a/README.md b/README.md index f4f6ed3..9c6a19f 100644 --- a/README.md +++ b/README.md @@ -50,19 +50,19 @@ Intro - 튜토리얼 인천공항 맵 - 패턴 기반 발판/장애물/아이템 배치 - 점프, 2단 점프, 슬라이드 튜토리얼 +- 속도에 맞춰 가로 폭이 보정되는 점프 마일리지 코인 아크 - 목숨, 속도 감소, 무적, 방어막, 마일리지 카드 등 아이템 효과 - 아이템 획득/방어 성공 피드백 이펙트 - 맵 데이터 구조: `Tutorial / Incheon`, `Japan`, `China`, `America` -- 맵별 배경과 도착 지점 이미지 연결 +- 맵별 배경과 도착 지점 이미지, 도착용 착지 발판, 게이트 뒤 스폰 중지 연결 - 저장/로드, 맵 해금, 결과 화면, 면세점 기본 흐름 ## 다음 우선순위 현재 가장 가까운 작업은 다음입니다. -1. 도착 지점 이미지가 실제 게임에서 적절한 크기와 위치로 보이는지 확인 -2. 면세점 구매 아이템을 장착 슬롯과 실제 플레이 준비 구조에 연결 -3. 일본, 중국, 미국 맵의 실제 반복 패턴과 난이도 차별화 -4. 미국 도착 지점 파일명 정리 +1. 면세점 구매 아이템을 장착 슬롯과 실제 플레이 준비 구조에 연결 +2. 일본, 중국, 미국 맵의 실제 반복 패턴과 난이도 차별화 +3. 도착 지점 이미지의 실제 화면 크기를 플레이하면서 세부 조정 자세한 작업 기록과 다음 할 일은 [Docs/feedback.md](Docs/feedback.md)에 날짜별로 남깁니다.