Add duty free systems and travel stage updates

This commit is contained in:
jongjae0305
2026-07-09 02:50:48 +09:00
parent 445d74d462
commit 750e9ce0a6
340 changed files with 21825 additions and 1197 deletions
+370 -82
View File
@@ -22,8 +22,7 @@ public class GameManager : MonoBehaviour {
public enum TutorialItemType {
HealthSushi,
InvincibleRamen,
Shield,
SpeedShoes,
EnergyDrink,
MileageCard
}
@@ -43,6 +42,8 @@ public class GameManager : MonoBehaviour {
private Image rightInfoHudBackground; // 오른쪽 거리/마일리지 수하물 라벨 배경
private TextMeshProUGUI rightMileageText; // 오른쪽 HUD의 적립 마일리지 텍스트
private Image[] lifeIcons; // 왼쪽 HUD의 목숨 도장 표시
private Image[] itemSlotIcons; // 왼쪽 HUD의 장착 아이템 표시
private DutyFreeItemType[] preparedDutyFreeItems; // 면세점에서 장착한 스테이지 준비 아이템
private GameObject tutorialGuideObject; // 화면 상단 이미지 튜토리얼 패널
private Image tutorialMainImage; // 점프/슬라이드 대표 이미지
private TextMeshProUGUI tutorialMultiplierText; // 2단 점프 x2 표시
@@ -74,8 +75,8 @@ public class GameManager : MonoBehaviour {
public bool playTutorialGuideSequence = false; // 코스 데이터가 없을 때만 시간 기준 안내를 보여준다.
public bool pauseGameplayForTutorialGuide = true; // 안내가 뜨는 동안 게임을 잠깐 멈춘다.
public float tutorialGuidePauseDuration = 2f; // 이전 자동 숨김 시간 값입니다. 현재 안내는 확인 클릭으로만 닫습니다.
public float itemInvincibleDuration = 2.2f; // 라멘 아이템 무적 지속 시간
public float itemSpeedBoostAmount = 0.35f; // 운동화 아이템 즉시 속도 보정량
public float itemInvincibleDuration = 2.2f; // 무적 아이템 지속 시간
public float itemSpeedBoostAmount = 0.35f; // 에너지 드링크 즉시 속도 보정량
public float mileageBonusDuration = 7f; // 마일리지 카드 보너스 지속 시간
public int mileageBonusMultiplier = 2; // 마일리지 카드 획득 배율
public float finishGateSpawnX = 20f; // 도착 게이트가 처음 나타나는 x 위치
@@ -86,21 +87,29 @@ public class GameManager : MonoBehaviour {
private const string TotalMileageKey = "UniRun.TotalMileage";
private const string InitialMileageGrantedKey = "UniRun.InitialMileageGranted";
private const string CharacterFaceResourcePath = "UI_JJ_PassportPhoto";
private const string PassportPanelResourcePath = "UI_LeftHudPassportCompactPanel";
private const string PassportPanelResourcePath = "UI_LeftHudPassportHorizontalGridPanel";
private const string PassportPhotoFrameResourcePath = "UI_PassportHudPhotoFrame";
private const string PassportPhotoHeaderResourcePath = "UI_PassportHudPhotoHeader";
private const string LifeStampResourcePath = "UI_LifeStamp";
private const string PassportItemSlotResourcePath = "UI_PassportItemSlot";
private const string PassportLifeCircleFrameResourcePath = "UI_PassportHudLifeCircleFrame";
private const string PassportItemSlotResourcePath = "UI_PassportHudItemSlotFrame";
private const string PassportFlightMarkerResourcePath = "UI_PassportHudFlightMarker";
private const string PassportItemMarkerResourcePath = "UI_PassportHudItemMarker";
private const string TutorialGuideFrameResourcePath = "UI_TutorialAirportMonitorFrame";
private const string TutorialNextButtonResourcePath = "UI_TutorialNextButton";
private const string RightHudLuggageLabelKoreaResourcePath = "UI_RightHudLuggageLabel_Korea";
private const string RightHudLuggageLabelJapanResourcePath = "UI_RightHudLuggageLabel_Japan";
private const string RightHudLuggageLabelChinaResourcePath = "UI_RightHudLuggageLabel_China";
private const string RightHudLuggageLabelAmericaResourcePath = "UI_RightHudLuggageLabel_America";
private const int MaxSupplementPurchases = 4;
private const int MaxLifeWithSupplements = 5;
private const float PassportHudWidth = 261f;
private const float PassportHudHeight = 102f;
private const float PassportHudScale = 0.5f;
private const float RightInfoHudWidth = 260f;
private const float RightInfoHudHeight = 64f;
private const float RightInfoHudScale = 0.6f;
private const int PreparedItemSlotCount = 3;
private const float TutorialJumpGuideDuration = 3.5f;
private const float TutorialDoubleJumpGuideDuration = 3.5f;
private const float TutorialSlideGuideDuration = 3.5f;
@@ -115,9 +124,9 @@ public class GameManager : MonoBehaviour {
public float speedIncreaseRate = 0.05f;
public float maxGameSpeed = 2f;
public float hitSpeedPenalty = 0.3f;
private int shieldCharges = 0; // 방어막 아이템으로 막을 수 있는 남은 피격 횟수
private float itemInvincibleEndTime = 0f; // 라멘 무적 종료 시각
private float itemInvincibleEndTime = 0f; // 무적 아이템 종료 시각
private float mileageBonusEndTime = 0f; // 마일리지 카드 보너스 종료 시각
private int baseMaxLife;
public bool IsTutorialGuidePaused {
get { return tutorialGuidePauseActive || tutorialGuideDismissFrame == Time.frameCount; }
@@ -130,6 +139,7 @@ public class GameManager : MonoBehaviour {
{
// instance가 비어있다면(null) 그곳에 자기 자신을 할당
instance = this;
baseMaxLife = maxLife;
}
else
{
@@ -142,7 +152,21 @@ public class GameManager : MonoBehaviour {
}
}
private void ApplySupplementLifeBonus() {
if(baseMaxLife <= 0)
{
baseMaxLife = maxLife;
}
int supplementPurchases = GameFlowManager.Instance != null && GameFlowManager.Instance.SaveData != null
? Mathf.Clamp(GameFlowManager.Instance.SaveData.supplementPurchases, 0, MaxSupplementPurchases)
: 0;
int bonusLife = supplementPurchases / 2;
maxLife = Mathf.Min(MaxLifeWithSupplements, baseMaxLife + bonusLife);
}
private void Start() {
ApplySupplementLifeBonus();
currentLife = maxLife;
totalMileage = GameFlowManager.Instance != null
? GameFlowManager.Instance.SaveData.totalMileage
@@ -213,18 +237,21 @@ public class GameManager : MonoBehaviour {
public void BeginMap(MapDefinition mapDefinition) {
StopTutorialGuidePause(true);
Time.timeScale = 1f;
ApplySupplementLifeBonus();
currentMapDefinition = mapDefinition != null
? mapDefinition
: MapDatabase.GetMap(MapId.TutorialIncheon);
BackgroundLoop.ApplyResourceBackground(currentMapDefinition.backgroundResourcePath);
BackgroundLoop.ApplyResourceBackground(
currentMapDefinition.GetBackgroundResourcePaths(),
currentMapDefinition.backgroundFitScaleMultiplier,
currentMapDefinition.backgroundVerticalOffset);
UpdateRightInfoHudSkin();
score = 0;
stageMileage = 0;
currentLife = maxLife;
gameSpeed = minGameSpeed;
shieldCharges = 0;
itemInvincibleEndTime = 0f;
mileageBonusEndTime = 0f;
stageElapsedTime = 0f;
@@ -234,6 +261,7 @@ public class GameManager : MonoBehaviour {
isGameover = false;
tutorialGuideSequenceComplete = false;
currentTutorialGuideSequenceIndex = -1;
LoadPreparedDutyFreeItems();
SetTutorialGuide(TutorialGuideType.None, "", "");
RemoveActiveFinishGate();
@@ -249,6 +277,8 @@ public class GameManager : MonoBehaviour {
}
StartTutorialGuideSequence();
UpdateTutorialItemRowSprites();
UpdatePreparedItemSlotUI();
UpdateScoreUI();
}
@@ -370,20 +400,25 @@ public class GameManager : MonoBehaviour {
}
private bool ShouldUseTimeBasedTutorialGuide() {
return playTutorialGuideSequence && !HasCourseDrivenTutorialGuide();
return playTutorialGuideSequence
&& currentMapDefinition != null
&& currentMapDefinition.isTutorial
&& !HasCourseDrivenTutorialGuide();
}
private bool HasCourseDrivenTutorialGuide() {
if(currentMapDefinition == null
|| currentMapDefinition.openingSteps == null
|| currentMapDefinition.openingSteps.Length == 0)
CourseStep[] openingSteps = currentMapDefinition != null
? currentMapDefinition.GetOpeningSteps()
: null;
if(openingSteps == null || openingSteps.Length == 0)
{
return false;
}
for(int i = 0; i < currentMapDefinition.openingSteps.Length; i++)
for(int i = 0; i < openingSteps.Length; i++)
{
CourseStep step = currentMapDefinition.openingSteps[i];
CourseStep step = openingSteps[i];
if(step != null
&& (step.guideType != TutorialGuideType.None
|| !string.IsNullOrEmpty(step.title)
@@ -436,7 +471,14 @@ public class GameManager : MonoBehaviour {
SetTutorialGuide(TutorialGuideType.Slide, "슬라이드", "오른쪽 클릭을 누르고 있으면 낮게 지나갑니다.");
break;
case 3:
SetTutorialGuide(TutorialGuideType.Items, "아이템", "초밥 -> 라멘 -> 방어막 -> 마일리지 카드 -> 운동화 순서입니다.");
SetTutorialGuide(
TutorialGuideType.Items,
"아이템",
string.Format(
"{0} -> {1} -> {2} 순서입니다.",
GetGameplayItemDisplayName(TutorialItemType.HealthSushi),
GetGameplayItemDisplayName(TutorialItemType.InvincibleRamen),
GetGameplayItemDisplayName(TutorialItemType.EnergyDrink)));
break;
default:
tutorialGuideSequenceComplete = true;
@@ -452,6 +494,12 @@ public class GameManager : MonoBehaviour {
return;
}
if(!IsMileageBonusActive() && TryConsumePreparedDutyFreeItem(DutyFreeItemType.MileageCard))
{
mileageBonusEndTime = Time.time + mileageBonusDuration;
SpawnPreparedItemFeedback(DutyFreeItemType.MileageCard, false);
}
int finalAmount = IsMileageBonusActive()
? amount * Mathf.Max(1, mileageBonusMultiplier)
: amount;
@@ -483,6 +531,12 @@ public class GameManager : MonoBehaviour {
gameSpeed -= hitSpeedPenalty;
gameSpeed = Mathf.Max(gameSpeed, minGameSpeed);
if(currentLife < maxLife && TryConsumePreparedDutyFreeItem(DutyFreeItemType.Lunchbox))
{
currentLife = Mathf.Min(maxLife, currentLife + 1);
SpawnPreparedItemFeedback(DutyFreeItemType.Lunchbox, false);
}
UpdateScoreUI();
return currentLife <= 0;
@@ -501,35 +555,24 @@ public class GameManager : MonoBehaviour {
AddScore(3);
if(ShouldShowItemEffectGuide())
{
SetTutorialGuide(TutorialGuideType.Items, "초밥", "방금 잃은 목숨을 1 회복합니다.");
string itemName = GetGameplayItemDisplayName(itemType);
SetTutorialGuide(TutorialGuideType.Items, itemName, itemName + "을 먹어 목숨을 1 회복합니다.");
}
break;
case TutorialItemType.InvincibleRamen:
itemInvincibleEndTime = Time.time + itemInvincibleDuration;
if(ShouldShowItemEffectGuide())
{
SetTutorialGuide(TutorialGuideType.Items, "라멘", "짧은 시간 동안 다음 충돌 피해를 받지 않습니다.");
string itemName = GetGameplayItemDisplayName(itemType);
SetTutorialGuide(TutorialGuideType.Items, itemName, itemName + " 효과로 짧은 시간 동안 충돌 피해를 받지 않습니다.");
}
break;
case TutorialItemType.Shield:
shieldCharges = Mathf.Max(shieldCharges, 1);
if(ShouldShowItemEffectGuide())
{
SetTutorialGuide(TutorialGuideType.Items, "방어막", "다음 충돌 1회를 방어막이 대신 막습니다.");
}
break;
case TutorialItemType.SpeedShoes:
case TutorialItemType.EnergyDrink:
gameSpeed = Mathf.Min(maxGameSpeed, gameSpeed + itemSpeedBoostAmount);
if(ShouldShowItemEffectGuide())
{
SetTutorialGuide(TutorialGuideType.Items, "운동화", "속도를 즉시 회복해 늦어진 일정을 따라잡습니다.");
}
break;
case TutorialItemType.MileageCard:
mileageBonusEndTime = Time.time + mileageBonusDuration;
if(ShouldShowItemEffectGuide())
{
SetTutorialGuide(TutorialGuideType.Items, "마일리지 카드", "잠시 동안 먹는 마일리지가 두 배로 들어옵니다.");
string itemName = GetGameplayItemDisplayName(itemType);
SetTutorialGuide(TutorialGuideType.Items, itemName, itemName + "로 속도를 조금 회복합니다.");
}
break;
}
@@ -547,25 +590,14 @@ public class GameManager : MonoBehaviour {
{
if(ShouldShowItemEffectGuide())
{
SetTutorialGuide(TutorialGuideType.Items, "라멘", "무적 중이라 이번 충돌은 피해 없이 지나갑니다.");
string itemName = GetGameplayItemDisplayName(TutorialItemType.InvincibleRamen);
SetTutorialGuide(TutorialGuideType.Items, itemName, itemName + " 효과로 이번 충돌은 피해 없이 지나갑니다.");
}
SpawnItemBlockFeedback(TutorialItemType.InvincibleRamen);
return true;
}
if(shieldCharges > 0)
{
shieldCharges--;
SpawnItemBlockFeedback(TutorialItemType.Shield);
if(ShouldShowItemEffectGuide())
{
SetTutorialGuide(TutorialGuideType.Items, "방어막", "방어막이 충돌 피해를 한 번 막았습니다.");
}
UpdateScoreUI();
return true;
}
return false;
}
@@ -580,7 +612,21 @@ public class GameManager : MonoBehaviour {
}
private bool ShouldShowItemEffectGuide() {
return currentMapDefinition == null || !currentMapDefinition.isTutorial;
return currentMapDefinition != null && currentMapDefinition.isTutorial;
}
private Platform.PlatformSkin GetCurrentPlatformSkin() {
return currentMapDefinition != null
? currentMapDefinition.platformSkin
: Platform.PlatformSkin.Airport;
}
private string GetGameplayItemDisplayName(TutorialItemType itemType) {
return GameplayItemCatalog.GetDisplayName(itemType, GetCurrentPlatformSkin());
}
private string GetGameplayItemSpritePath(TutorialItemType itemType) {
return GameplayItemCatalog.GetSpriteAssetPath(itemType, GetCurrentPlatformSkin());
}
private bool IsItemInvincibleActive() {
@@ -843,34 +889,31 @@ public class GameManager : MonoBehaviour {
}
string itemText = (tutorialTitle + " " + tutorialMessage).Trim();
if(itemText.Contains("초밥"))
if(ContainsGameplayItemName(itemText, TutorialItemType.HealthSushi))
{
return 0;
}
if(itemText.Contains("라멘"))
if(ContainsGameplayItemName(itemText, TutorialItemType.InvincibleRamen))
{
return 1;
}
if(itemText.Contains("방어막"))
if(ContainsGameplayItemName(itemText, TutorialItemType.EnergyDrink))
{
return 2;
}
if(itemText.Contains("운동화"))
{
return 3;
}
if(itemText.Contains("마일리지"))
{
return 4;
}
return -1;
}
private bool ContainsGameplayItemName(string text, TutorialItemType itemType) {
return text.Contains(GameplayItemCatalog.GetDisplayName(itemType, Platform.PlatformSkin.Airport))
|| text.Contains(GameplayItemCatalog.GetDisplayName(itemType, Platform.PlatformSkin.Japan))
|| text.Contains(GameplayItemCatalog.GetDisplayName(itemType, Platform.PlatformSkin.China))
|| text.Contains(GameplayItemCatalog.GetDisplayName(itemType, Platform.PlatformSkin.USA));
}
private Color GetActiveLifeIconColor(Image lifeIcon) {
if(lifeIcon != null && lifeIcon.sprite == null)
{
@@ -990,15 +1033,54 @@ public class GameManager : MonoBehaviour {
hudBackground.color = hudBackground.sprite != null
? Color.white
: new Color(0.04f, 0.08f, 0.11f, 0.68f);
hudBackground.preserveAspect = true;
hudBackground.preserveAspect = false;
hudBackground.raycastTarget = false;
CreateFaceIcon(hudObject.transform);
CreatePassportHudMarkers(hudObject.transform);
CreateLifeIcons(hudObject.transform);
CreateItemSlots(hudObject.transform);
}
private void CreatePassportHudMarkers(Transform parent) {
CreatePassportHudImage(
parent,
"Flight Marker",
PassportFlightMarkerResourcePath,
new Vector2(132f, -12f),
new Vector2(118f, 17f),
Color.white,
false);
CreatePassportHudImage(
parent,
"Item Marker",
PassportItemMarkerResourcePath,
new Vector2(132f, -50f),
new Vector2(118f, 17f),
Color.white,
false);
}
private void CreateFaceIcon(Transform parent) {
CreatePassportHudImage(
parent,
"Photo Header",
PassportPhotoHeaderResourcePath,
new Vector2(45f, -8f),
new Vector2(72f, 14f),
Color.white,
false);
CreatePassportHudImage(
parent,
"Photo Frame",
PassportPhotoFrameResourcePath,
new Vector2(28f, -14f),
new Vector2(96f, 76f),
Color.white,
false);
GameObject faceFrame = new GameObject("Face Frame", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
faceFrame.transform.SetParent(parent, false);
@@ -1006,8 +1088,8 @@ public class GameManager : MonoBehaviour {
frameRect.anchorMin = new Vector2(0f, 1f);
frameRect.anchorMax = new Vector2(0f, 1f);
frameRect.pivot = new Vector2(0f, 1f);
frameRect.anchoredPosition = new Vector2(32f, -20f);
frameRect.sizeDelta = new Vector2(72f, 68f);
frameRect.anchoredPosition = new Vector2(33f, -22f);
frameRect.sizeDelta = new Vector2(86f, 64f);
Image frameImage = faceFrame.GetComponent<Image>();
frameImage.color = new Color(1f, 1f, 1f, 0f);
@@ -1032,12 +1114,22 @@ public class GameManager : MonoBehaviour {
}
private void CreateLifeIcons(Transform parent) {
Sprite frameSprite = LoadHudSprite(PassportLifeCircleFrameResourcePath);
Sprite lifeSprite = LoadHudSprite(LifeStampResourcePath);
int iconCount = Mathf.Max(maxLife, 1);
lifeIcons = new Image[iconCount];
for(int i = 0; i < iconCount; i++)
{
CreatePassportHudImage(
parent,
"Life Circle Frame " + (i + 1),
PassportLifeCircleFrameResourcePath,
new Vector2(134f + (i * 39f), -18f),
new Vector2(34f, 34f),
frameSprite != null ? Color.white : new Color(0.93f, 0.73f, 0.28f, 0.75f),
true);
GameObject lifeObject = new GameObject("Life Stamp " + (i + 1), typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
lifeObject.transform.SetParent(parent, false);
@@ -1045,8 +1137,8 @@ public class GameManager : MonoBehaviour {
lifeRect.anchorMin = new Vector2(0f, 1f);
lifeRect.anchorMax = new Vector2(0f, 1f);
lifeRect.pivot = new Vector2(0f, 1f);
lifeRect.anchoredPosition = new Vector2(125f + (i * 45f), -23f);
lifeRect.sizeDelta = new Vector2(28f, 28f);
lifeRect.anchoredPosition = new Vector2(139f + (i * 39f), -23f);
lifeRect.sizeDelta = new Vector2(24f, 24f);
Image lifeImage = lifeObject.GetComponent<Image>();
lifeImage.sprite = lifeSprite;
@@ -1061,8 +1153,9 @@ public class GameManager : MonoBehaviour {
private void CreateItemSlots(Transform parent) {
Sprite slotSprite = LoadHudSprite(PassportItemSlotResourcePath);
itemSlotIcons = new Image[PreparedItemSlotCount];
for(int i = 0; i < 3; i++)
for(int i = 0; i < PreparedItemSlotCount; i++)
{
GameObject slotObject = new GameObject("Item Slot " + (i + 1), typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
slotObject.transform.SetParent(parent, false);
@@ -1071,8 +1164,8 @@ public class GameManager : MonoBehaviour {
slotRect.anchorMin = new Vector2(0f, 1f);
slotRect.anchorMax = new Vector2(0f, 1f);
slotRect.pivot = new Vector2(0f, 1f);
slotRect.anchoredPosition = new Vector2(125f + (i * 45f), -62f);
slotRect.sizeDelta = new Vector2(30f, 30f);
slotRect.anchoredPosition = new Vector2(132f + (i * 40f), -65f);
slotRect.sizeDelta = new Vector2(36f, 24f);
Image slotImage = slotObject.GetComponent<Image>();
slotImage.sprite = slotSprite;
@@ -1081,9 +1174,189 @@ public class GameManager : MonoBehaviour {
: new Color(0.92f, 0.97f, 1f, 0.22f);
slotImage.preserveAspect = true;
slotImage.raycastTarget = false;
GameObject iconObject = new GameObject("Prepared Item Icon " + (i + 1), typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
iconObject.transform.SetParent(slotObject.transform, false);
RectTransform iconRect = iconObject.GetComponent<RectTransform>();
iconRect.anchorMin = Vector2.zero;
iconRect.anchorMax = Vector2.one;
iconRect.offsetMin = new Vector2(7f, 2f);
iconRect.offsetMax = new Vector2(-7f, -2f);
Image iconImage = iconObject.GetComponent<Image>();
iconImage.preserveAspect = true;
iconImage.raycastTarget = false;
iconImage.gameObject.SetActive(false);
itemSlotIcons[i] = iconImage;
}
}
private void LoadPreparedDutyFreeItems() {
if(preparedDutyFreeItems == null || preparedDutyFreeItems.Length != PreparedItemSlotCount)
{
preparedDutyFreeItems = new DutyFreeItemType[PreparedItemSlotCount];
}
for(int i = 0; i < preparedDutyFreeItems.Length; i++)
{
preparedDutyFreeItems[i] = DutyFreeItemType.None;
}
GameSaveData saveData = GameFlowManager.Instance != null
? GameFlowManager.Instance.SaveData
: null;
if(saveData == null)
{
return;
}
int slotCount = Mathf.Clamp(saveData.bagSlots, 1, PreparedItemSlotCount);
for(int i = 0; i < slotCount; i++)
{
preparedDutyFreeItems[i] = NormalizePreparedItem(GetPreparedItemFromSave(saveData, i));
}
}
private DutyFreeItemType GetPreparedItemFromSave(GameSaveData saveData, int slotIndex) {
switch(slotIndex)
{
case 0:
return saveData.equippedSlot1;
case 1:
return saveData.equippedSlot2;
case 2:
return saveData.equippedSlot3;
default:
return DutyFreeItemType.None;
}
}
private DutyFreeItemType NormalizePreparedItem(DutyFreeItemType itemType) {
switch(itemType)
{
case DutyFreeItemType.Lunchbox:
case DutyFreeItemType.MileageCard:
return itemType;
default:
return DutyFreeItemType.None;
}
}
private bool TryConsumePreparedDutyFreeItem(DutyFreeItemType itemType) {
if(preparedDutyFreeItems == null || itemType == DutyFreeItemType.None)
{
return false;
}
for(int i = 0; i < preparedDutyFreeItems.Length; i++)
{
if(preparedDutyFreeItems[i] != itemType)
{
continue;
}
preparedDutyFreeItems[i] = DutyFreeItemType.None;
if(GameFlowManager.Instance != null)
{
GameFlowManager.Instance.ConsumeEquippedDutyFreeItem(i, itemType);
}
UpdatePreparedItemSlotUI();
return true;
}
return false;
}
private void UpdatePreparedItemSlotUI() {
if(itemSlotIcons == null)
{
return;
}
for(int i = 0; i < itemSlotIcons.Length; i++)
{
Image iconImage = itemSlotIcons[i];
if(iconImage == null)
{
continue;
}
DutyFreeItemType itemType = preparedDutyFreeItems != null && i < preparedDutyFreeItems.Length
? preparedDutyFreeItems[i]
: DutyFreeItemType.None;
Sprite itemSprite = GetPreparedDutyFreeItemSprite(itemType);
iconImage.sprite = itemSprite;
iconImage.color = itemSprite != null ? Color.white : new Color(1f, 1f, 1f, 0f);
iconImage.gameObject.SetActive(itemSprite != null);
}
}
private Sprite GetPreparedDutyFreeItemSprite(DutyFreeItemType itemType) {
switch(itemType)
{
case DutyFreeItemType.Lunchbox:
return LoadTutorialSprite("Assets/Sprites/Items/Item_Lunchbox.png");
case DutyFreeItemType.MileageCard:
return LoadTutorialSprite("Assets/Sprites/Items/Item_Mileage_Card.png");
default:
return null;
}
}
private void SpawnPreparedItemFeedback(DutyFreeItemType itemType, bool blockedDamage) {
GameObject playerObject = GameObject.FindGameObjectWithTag("Player");
if(playerObject == null)
{
return;
}
ItemFeedbackEffect.Spawn(
playerObject.transform.position,
GetPreparedDutyFreeItemSprite(itemType),
GetPreparedItemFeedbackType(itemType),
blockedDamage);
}
private TutorialItemType GetPreparedItemFeedbackType(DutyFreeItemType itemType) {
switch(itemType)
{
case DutyFreeItemType.Lunchbox:
return TutorialItemType.HealthSushi;
case DutyFreeItemType.MileageCard:
return TutorialItemType.MileageCard;
default:
return TutorialItemType.HealthSushi;
}
}
private Image CreatePassportHudImage(
Transform parent,
string objectName,
string resourcePath,
Vector2 anchoredPosition,
Vector2 sizeDelta,
Color fallbackColor,
bool preserveAspect) {
GameObject imageObject = new GameObject(objectName, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
imageObject.transform.SetParent(parent, false);
RectTransform rectTransform = imageObject.GetComponent<RectTransform>();
rectTransform.anchorMin = new Vector2(0f, 1f);
rectTransform.anchorMax = new Vector2(0f, 1f);
rectTransform.pivot = new Vector2(0f, 1f);
rectTransform.anchoredPosition = anchoredPosition;
rectTransform.sizeDelta = sizeDelta;
Image image = imageObject.GetComponent<Image>();
image.sprite = LoadHudSprite(resourcePath);
image.color = image.sprite != null ? Color.white : fallbackColor;
image.preserveAspect = preserveAspect;
image.raycastTarget = false;
return image;
}
private Sprite LoadHudSprite(string resourcePath) {
Sprite sprite = Resources.Load<Sprite>(resourcePath);
if(sprite != null)
@@ -1189,11 +1462,9 @@ public class GameManager : MonoBehaviour {
private void CreateTutorialItemRow(Transform parent) {
string[] itemAssetPaths = {
"Assets/Sprites/Items/Item_Health_Sushi.png",
"Assets/Sprites/Items/Item_Invincible_Ramen.png",
"Assets/Sprites/Items/Item_Shield.png",
"Assets/Sprites/Items/Item_Speed_Shoes.png",
"Assets/Sprites/Items/Item_Mileage_Card.png"
GetGameplayItemSpritePath(TutorialItemType.HealthSushi),
GetGameplayItemSpritePath(TutorialItemType.InvincibleRamen),
GetGameplayItemSpritePath(TutorialItemType.EnergyDrink)
};
tutorialItemImages = new Image[itemAssetPaths.Length];
@@ -1219,6 +1490,27 @@ public class GameManager : MonoBehaviour {
}
}
private void UpdateTutorialItemRowSprites() {
if(tutorialItemImages == null || tutorialItemImages.Length < 3)
{
return;
}
TutorialItemType[] itemTypes = {
TutorialItemType.HealthSushi,
TutorialItemType.InvincibleRamen,
TutorialItemType.EnergyDrink
};
for(int i = 0; i < itemTypes.Length; i++)
{
if(tutorialItemImages[i] != null)
{
tutorialItemImages[i].sprite = LoadTutorialSprite(GetGameplayItemSpritePath(itemTypes[i]));
}
}
}
private void CreateTutorialNextButton(Transform parent) {
GameObject buttonObject = new GameObject("Tutorial Confirm Button", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
buttonObject.transform.SetParent(parent, false);
@@ -1519,9 +1811,9 @@ public class GameManager : MonoBehaviour {
ClearMarkers();
float cursor = 0f;
AddStepMarkers(mapDefinition.openingSteps, ref cursor, targetDistance);
AddStepMarkers(mapDefinition.GetOpeningSteps(), ref cursor, targetDistance);
CourseStep[] loopSteps = mapDefinition.loopSteps;
CourseStep[] loopSteps = mapDefinition.GetLoopSteps();
int loopGuard = 0;
while(cursor < targetDistance
&& loopSteps != null
@@ -1638,12 +1930,8 @@ public class GameManager : MonoBehaviour {
return "+";
case Platform.ItemPattern.InvincibleRamen:
return "R";
case Platform.ItemPattern.Shield:
return "G";
case Platform.ItemPattern.SpeedShoes:
case Platform.ItemPattern.EnergyDrink:
return "V";
case Platform.ItemPattern.MileageCard:
return "M";
default:
return "I";
}