Tune tutorial coin arcs and finish flow
This commit is contained in:
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
@@ -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<Sprite>(resourcePath);
|
||||
if(sprite == null)
|
||||
{
|
||||
Texture2D texture = Resources.Load<Texture2D>(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<Sprite>(resourcePath);
|
||||
if(sprite != null)
|
||||
{
|
||||
return sprite;
|
||||
}
|
||||
|
||||
Sprite[] sprites = Resources.LoadAll<Sprite>(resourcePath);
|
||||
if(sprites != null && sprites.Length > 0 && sprites[0] != null)
|
||||
{
|
||||
return sprites[0];
|
||||
}
|
||||
|
||||
Texture2D texture = Resources.Load<Texture2D>(resourcePath);
|
||||
if(texture != null)
|
||||
{
|
||||
return CreateRuntimeSprite(texture);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string assetPath = "Assets/Resources/" + resourcePath + ".png";
|
||||
sprite = AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);
|
||||
if(sprite != null)
|
||||
{
|
||||
return sprite;
|
||||
}
|
||||
|
||||
texture = AssetDatabase.LoadAssetAtPath<Texture2D>(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);
|
||||
|
||||
@@ -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<PlatformSpawner>();
|
||||
if(platformSpawner != null)
|
||||
activePlatformSpawner = FindFirstObjectByType<PlatformSpawner>();
|
||||
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<FinishGate>();
|
||||
activeFinishGate.Setup(currentMapDefinition.finishGateStyle);
|
||||
finishGateSpawned = true;
|
||||
|
||||
if(activePlatformSpawner != null)
|
||||
{
|
||||
activePlatformSpawner.StopCourseSpawningAfterFinishGate();
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFinishGateLanding(float gateGroundY) {
|
||||
if(activePlatformSpawner == null)
|
||||
{
|
||||
activePlatformSpawner = FindFirstObjectByType<PlatformSpawner>();
|
||||
}
|
||||
|
||||
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<Image>();
|
||||
itemImage.sprite = LoadTutorialSprite(itemAssetPaths[i]);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -84,6 +84,8 @@ public class PlatformSpawner : MonoBehaviour {
|
||||
private int courseStepIndex = 0; // 진행 중인 패턴 코스 순번
|
||||
private Platform.PlatformSkin mapPlatformSkin = Platform.PlatformSkin.Airport; // 현재 맵에서 사용할 발판 스킨
|
||||
private readonly List<PendingTutorialGuide> pendingTutorialGuides = new List<PendingTutorialGuide>(); // 실제 체험 직전에 띄울 설명들
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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; // 발판 이음새에서 접지가 아주 잠깐 끊겨도 유지하는 시간
|
||||
|
||||
@@ -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}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
+3
-4
@@ -96,7 +96,6 @@ Intro
|
||||
|
||||
## 다음 작업 우선순위
|
||||
|
||||
1. 변경된 도착 지점 이미지가 실제 게임에서 제대로 보이는지 확인
|
||||
2. 면세점 구매 아이템을 장착 슬롯과 연결
|
||||
3. 일본, 중국, 미국 맵의 실제 반복 패턴과 난이도 차별화
|
||||
4. 미국 도착 지점 파일명 정리
|
||||
1. 면세점 구매 아이템을 장착 슬롯과 연결
|
||||
2. 일본, 중국, 미국 맵의 실제 반복 패턴과 난이도 차별화
|
||||
3. 도착 지점 이미지의 실제 화면 크기를 플레이하면서 세부 조정
|
||||
|
||||
@@ -76,6 +76,7 @@ HUD:
|
||||
5. 아이템 사용
|
||||
|
||||
설명은 화면을 가리지 않도록 잠깐 멈춘 뒤 보여주고, 이후 실제 구간을 통과하게 합니다.
|
||||
아이템 설명창은 전체 아이템 목록을 한 번에 보여주지 않고, 현재 설명하는 아이템 이미지만 크게 보여줍니다.
|
||||
|
||||
## Result
|
||||
|
||||
|
||||
+20
-7
@@ -27,18 +27,31 @@
|
||||
- 하단 미니맵을 런타임 HUD에서 제거
|
||||
- 아이템 획득 시 짧은 링/반짝임/문구 이펙트 추가
|
||||
- 라멘 무적/방어막 방어 성공 시 피격 깜빡임 대신 `SAFE`/`BLOCK` 피드백 표시
|
||||
- 점프 마일리지 코인 경로를 5개 포물선으로 조정
|
||||
- 점프 조작성을 쉽게 맞추기 위해 플레이어 점프 힘을 `700`에서 `600`으로 낮춤
|
||||
- 속도가 붙을수록 점프 코인 경로의 가로 폭이 넓어지도록 보정
|
||||
- 점프 코인 아크의 기본 가로/세로 폭을 더 크게 조정
|
||||
- 출국 게이트 도착지는 만든 PNG 리소스를 우선 사용하도록 로딩 순서 수정
|
||||
- 점프 코인 기본 아크 폭을 `1.3`, 최대 속도 폭을 `2.0`으로 확대
|
||||
- 도착지점 스프라이트 로딩을 `Sprite`, `LoadAll<Sprite>`, `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. 컨셉 이펙트 이미지를 실제 개별 스프라이트로 분리할지 결정
|
||||
|
||||
@@ -54,6 +54,11 @@
|
||||
| 마일리지 카드 | 마일리지 획득량 증가 | 먹은 뒤 마일리지 수집 |
|
||||
|
||||
아이템을 먹으면 짧은 링, 반짝임, 문구 이펙트가 떠서 획득 여부를 확인할 수 있습니다.
|
||||
라멘 무적 아이템의 현재 지속 시간은 `2.2초`입니다.
|
||||
|
||||
점프 구간의 마일리지 코인은 플레이어가 뛰는 높이에 맞춰 넓고 높은 포물선으로 배치합니다.
|
||||
게임 속도가 빨라지면 코인 포물선의 가로 폭도 함께 넓혀 점프 타이밍과 맞춥니다.
|
||||
현재 점프 코인 가로 폭은 기본 속도에서 `1.8배`, 최대 속도에서 `2.5배`까지 보정합니다.
|
||||
|
||||
## 코스 생성
|
||||
|
||||
|
||||
+3
-3
@@ -97,9 +97,9 @@
|
||||
|
||||
도착 지점:
|
||||
|
||||
- `Assets/Resources/FinishGate_America_Highway.png`
|
||||
- 현재 파일명은 기존 하이웨이 이름이지만, 실제 이미지는 자유의 여신상/뉴욕 도착지입니다.
|
||||
- 추후 파일명을 `FinishGate_America_Liberty.png`로 바꾸는 것이 더 명확합니다.
|
||||
- `Assets/Resources/FinishGate_America_Liberty.png`
|
||||
- 도착 게이트가 나올 때 전용 착지 발판을 함께 깔아 이미지가 공중에 뜬 것처럼 보이지 않게 합니다.
|
||||
- 도착 게이트 뒤에는 추가 코스가 생성되지 않게 막아 클리어 지점이 끝처럼 보이게 합니다.
|
||||
|
||||
스킨:
|
||||
|
||||
|
||||
@@ -100,6 +100,8 @@ Intro
|
||||
- 미국: 자유의 여신상/뉴욕 도착지
|
||||
|
||||
플레이어가 도착 지점 트리거를 통과하면 클리어 처리합니다.
|
||||
도착 게이트가 생성될 때는 `PlatformSpawner`가 빈 착지 발판을 같은 위치에 함께 깔아, 게이트가 배경 위에 떠 보이지 않게 합니다.
|
||||
도착 게이트 뒤로는 새 발판, 장애물, 아이템 코스가 더 생성되지 않도록 스폰을 중지합니다.
|
||||
|
||||
## Minimap
|
||||
|
||||
|
||||
@@ -50,19 +50,19 @@ Intro
|
||||
- 튜토리얼 인천공항 맵
|
||||
- 패턴 기반 발판/장애물/아이템 배치
|
||||
- 점프, 2단 점프, 슬라이드 튜토리얼
|
||||
- 속도에 맞춰 가로 폭이 보정되는 점프 마일리지 코인 아크
|
||||
- 목숨, 속도 감소, 무적, 방어막, 마일리지 카드 등 아이템 효과
|
||||
- 아이템 획득/방어 성공 피드백 이펙트
|
||||
- 맵 데이터 구조: `Tutorial / Incheon`, `Japan`, `China`, `America`
|
||||
- 맵별 배경과 도착 지점 이미지 연결
|
||||
- 맵별 배경과 도착 지점 이미지, 도착용 착지 발판, 게이트 뒤 스폰 중지 연결
|
||||
- 저장/로드, 맵 해금, 결과 화면, 면세점 기본 흐름
|
||||
|
||||
## 다음 우선순위
|
||||
|
||||
현재 가장 가까운 작업은 다음입니다.
|
||||
|
||||
1. 도착 지점 이미지가 실제 게임에서 적절한 크기와 위치로 보이는지 확인
|
||||
2. 면세점 구매 아이템을 장착 슬롯과 실제 플레이 준비 구조에 연결
|
||||
3. 일본, 중국, 미국 맵의 실제 반복 패턴과 난이도 차별화
|
||||
4. 미국 도착 지점 파일명 정리
|
||||
1. 면세점 구매 아이템을 장착 슬롯과 실제 플레이 준비 구조에 연결
|
||||
2. 일본, 중국, 미국 맵의 실제 반복 패턴과 난이도 차별화
|
||||
3. 도착 지점 이미지의 실제 화면 크기를 플레이하면서 세부 조정
|
||||
|
||||
자세한 작업 기록과 다음 할 일은 [Docs/feedback.md](Docs/feedback.md)에 날짜별로 남깁니다.
|
||||
|
||||
Reference in New Issue
Block a user