2114 lines
83 KiB
C#
2114 lines
83 KiB
C#
using System.Collections;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.UI;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
public class GameFlowManager : MonoBehaviour {
|
|
public enum FlowScreen {
|
|
Intro,
|
|
OpeningStory,
|
|
Game,
|
|
Result,
|
|
DutyFreeShop,
|
|
MapPreview,
|
|
DebugMenu
|
|
}
|
|
|
|
public static GameFlowManager Instance { get; private set; }
|
|
|
|
public static bool IsGameplayActive {
|
|
get {
|
|
return Instance == null || Instance.gameplayActive;
|
|
}
|
|
}
|
|
|
|
public GameSaveData SaveData {
|
|
get { return saveData; }
|
|
}
|
|
|
|
public MapDefinition CurrentMap {
|
|
get { return currentMap; }
|
|
}
|
|
|
|
private const string IntroSceneName = "Intro";
|
|
private const string GameSceneName = "Main";
|
|
private const string ResultSceneName = "Result";
|
|
private const string DutyFreeShopSceneName = "DutyFreeShop";
|
|
private const string MapPreviewSceneName = "MapPreview";
|
|
private const string DepartureStampResourcePath = "UI_DepartureStamp_Red";
|
|
private const string ResultPassportPanelResourcePath = "UI_ResultBoardingPassPanel_New";
|
|
private const string ResultPassportPanelAssetPath = "Assets/Resources/UI_ResultBoardingPassPanel_New.png";
|
|
private const string ResultAirportBackgroundResourcePath = "Map_Tutorial_Incheon_Airport";
|
|
private const string ResultButtonResourcePath = "UI_TutorialNextButton";
|
|
private const string ResultButtonAssetPath = "Assets/Resources/UI_TutorialNextButton.png";
|
|
private const string MileageIconAssetPath = "Assets/Sprites/UIIcons/UI_Mileage.png";
|
|
private const string TimerIconAssetPath = "Assets/Sprites/UIIcons/UI_Timer.png";
|
|
private const string CheckpointIconAssetPath = "Assets/Sprites/UIIcons/UI_Checkpoint.png";
|
|
private const string ClearRankIconAssetPath = "Assets/Sprites/UIIcons/UI_ClearRank.png";
|
|
private const string DutyFreeIconAssetPath = "Assets/Sprites/UIIcons/UI_DutyFreeShop.png";
|
|
private const string AirplaneTravelIconAssetPath = "Assets/Sprites/UIIcons/UI_AirplaneTravel.png";
|
|
private const string DutyFreeBackgroundAssetPath = "Assets/ConceptArt/duty_free_shop_screen_concept_01.png";
|
|
private const string DutyFreeLunchboxAssetPath = "Assets/Sprites/DutyFree/DutyFree_Normal_Lunchbox.png";
|
|
private const string DutyFreeShieldAssetPath = "Assets/Sprites/DutyFree/DutyFree_Normal_ShieldCharm.png";
|
|
private const string DutyFreeShoesAssetPath = "Assets/Sprites/DutyFree/DutyFree_Normal_Shoes.png";
|
|
private const string DutyFreeMileageCardAssetPath = "Assets/Sprites/DutyFree/DutyFree_Normal_MileageCard.png";
|
|
private const string SouvenirCollectionIconAssetPath = "Assets/Sprites/UIIcons/UI_SouvenirCollection.png";
|
|
private const string AirportSouvenirAssetPath = "Assets/Sprites/Souvenirs/Souvenir_AirportMagnet.png";
|
|
private const string JapanSouvenirAssetPath = "Assets/Sprites/Souvenirs/Souvenir_Japan_Magnet.png";
|
|
private const string ChinaSouvenirAssetPath = "Assets/Sprites/Souvenirs/Souvenir_TempleCharm.png";
|
|
private const string AmericaSouvenirAssetPath = "Assets/Sprites/Souvenirs/Souvenir_SkylineMagnet.png";
|
|
private const int ShieldCost = 120;
|
|
private const int LunchboxCost = 80;
|
|
private const int SpeedShoesCost = 140;
|
|
private const int MileageCardCost = 160;
|
|
private static readonly string[] OpeningStoryPageAssetPaths = {
|
|
"Assets/ConceptArt/Opening/opening_cutscene_comic_jj_03_no_bubbles.png",
|
|
"Assets/ConceptArt/Opening/opening_cutscene_comic_jj_04_prepare_no_bubbles.png",
|
|
"Assets/ConceptArt/Opening/opening_cutscene_final_departure_jj_06_street_strict_stylematch.png"
|
|
};
|
|
|
|
private GameSaveData saveData;
|
|
private MapDefinition currentMap;
|
|
private MapDefinition completedMap;
|
|
private FlowScreen currentScreen = FlowScreen.Intro;
|
|
private bool gameplayActive = false;
|
|
private bool pendingStartGameplay = false;
|
|
private bool lastResultCleared = false;
|
|
private int lastResultScore = 0;
|
|
private int lastResultMileage = 0;
|
|
private float lastResultTime = 0f;
|
|
private float lastResultDistance = 0f;
|
|
private int openingStoryPageIndex = 0;
|
|
|
|
private Canvas overlayCanvas;
|
|
private RectTransform overlayRoot;
|
|
private Image overlayBackground;
|
|
private Camera flowCamera;
|
|
private TMP_FontAsset runtimeFont;
|
|
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
|
private static void Bootstrap() {
|
|
if(Instance != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GameObject flowObject = new GameObject("Game Flow Manager");
|
|
flowObject.AddComponent<GameFlowManager>();
|
|
DontDestroyOnLoad(flowObject);
|
|
}
|
|
|
|
private void Awake() {
|
|
if(Instance != null && Instance != this)
|
|
{
|
|
Destroy(gameObject);
|
|
return;
|
|
}
|
|
|
|
Instance = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
saveData = GameSaveManager.Load();
|
|
currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
|
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
}
|
|
|
|
private void Start() {
|
|
ShowIntro();
|
|
}
|
|
|
|
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
|
private void Update() {
|
|
if(Input.GetKeyDown(KeyCode.Escape))
|
|
{
|
|
ShowDebugMenu();
|
|
return;
|
|
}
|
|
|
|
HandleDebugMenuShortcutInput();
|
|
}
|
|
|
|
private void HandleDebugMenuShortcutInput() {
|
|
if(currentScreen != FlowScreen.DebugMenu)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(Input.GetKeyDown(KeyCode.Alpha1) || Input.GetKeyDown(KeyCode.Keypad1))
|
|
{
|
|
ShowDebugIntroPreview();
|
|
}
|
|
else if(Input.GetKeyDown(KeyCode.Alpha2) || Input.GetKeyDown(KeyCode.Keypad2))
|
|
{
|
|
ShowDebugGameStartPreview();
|
|
}
|
|
else if(Input.GetKeyDown(KeyCode.Alpha3) || Input.GetKeyDown(KeyCode.Keypad3))
|
|
{
|
|
ShowDebugResultPreview(false);
|
|
}
|
|
else if(Input.GetKeyDown(KeyCode.Alpha4) || Input.GetKeyDown(KeyCode.Keypad4))
|
|
{
|
|
ShowDebugResultPreview(true);
|
|
}
|
|
else if(Input.GetKeyDown(KeyCode.Alpha5) || Input.GetKeyDown(KeyCode.Keypad5))
|
|
{
|
|
ShowDebugDutyFreeShopPreview();
|
|
}
|
|
else if(Input.GetKeyDown(KeyCode.Alpha6) || Input.GetKeyDown(KeyCode.Keypad6))
|
|
{
|
|
ShowDebugMapPreview();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
private void OnDestroy() {
|
|
if(Instance == this)
|
|
{
|
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
Instance = null;
|
|
}
|
|
}
|
|
|
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
|
|
EnsureOverlay();
|
|
|
|
if(pendingStartGameplay)
|
|
{
|
|
StartCoroutine(StartGameplayAfterSceneLoad());
|
|
return;
|
|
}
|
|
|
|
if(currentScreen != FlowScreen.Game)
|
|
{
|
|
RefreshCurrentScreen();
|
|
}
|
|
}
|
|
|
|
public void SetTotalMileage(int totalMileage) {
|
|
if(saveData == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
saveData.totalMileage = Mathf.Max(0, totalMileage);
|
|
GameSaveManager.Save(saveData);
|
|
}
|
|
|
|
public void AddMileage(int amount) {
|
|
if(saveData == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GameSaveManager.AddMileage(saveData, amount);
|
|
}
|
|
|
|
public void CompleteMap(bool cleared, int score, int stageMileage, float elapsedTime, float distance) {
|
|
if(!gameplayActive && currentScreen == FlowScreen.Result)
|
|
{
|
|
return;
|
|
}
|
|
|
|
gameplayActive = false;
|
|
currentScreen = FlowScreen.Result;
|
|
completedMap = currentMap;
|
|
lastResultCleared = cleared;
|
|
lastResultScore = score;
|
|
lastResultMileage = stageMileage;
|
|
lastResultTime = elapsedTime;
|
|
lastResultDistance = distance;
|
|
|
|
if(cleared)
|
|
{
|
|
saveData.bestScore = Mathf.Max(saveData.bestScore, score);
|
|
GameSaveManager.AddMileage(saveData, completedMap.clearMileageReward);
|
|
|
|
if(completedMap.hasNextMap)
|
|
{
|
|
GameSaveManager.UnlockMap(saveData, completedMap.nextMapId);
|
|
}
|
|
else
|
|
{
|
|
GameSaveManager.Save(saveData);
|
|
}
|
|
}
|
|
|
|
ShowResult();
|
|
}
|
|
|
|
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
|
private void ShowDebugMenu() {
|
|
EnsureDebugPreviewData();
|
|
|
|
gameplayActive = false;
|
|
pendingStartGameplay = false;
|
|
currentScreen = FlowScreen.DebugMenu;
|
|
|
|
EnsureOverlay();
|
|
ClearOverlay();
|
|
SetOverlayVisible(true);
|
|
SetDefaultOverlayBackground();
|
|
|
|
AddTitle("디버그 메뉴");
|
|
AddBody("화면 조정용 바로 이동\n숫자키 1~6도 사용할 수 있습니다.");
|
|
AddButton("1. 인트로", ShowDebugIntroPreview);
|
|
AddButton("2. 게임 시작", ShowDebugGameStartPreview);
|
|
AddButton("3. 출국 실패", delegate { ShowDebugResultPreview(false); });
|
|
AddButton("4. 출국 성공", delegate { ShowDebugResultPreview(true); });
|
|
AddButton("5. 면세점", ShowDebugDutyFreeShopPreview);
|
|
AddButton("6. 지도", ShowDebugMapPreview);
|
|
}
|
|
|
|
private void ShowDebugIntroPreview() {
|
|
ShowIntro();
|
|
}
|
|
|
|
private void ShowDebugGameStartPreview() {
|
|
EnsureDebugPreviewData();
|
|
LoadGameSceneForCurrentMap();
|
|
}
|
|
|
|
private void ShowDebugResultPreview(bool cleared) {
|
|
EnsureDebugPreviewData();
|
|
|
|
gameplayActive = false;
|
|
pendingStartGameplay = false;
|
|
currentScreen = FlowScreen.Result;
|
|
completedMap = currentMap;
|
|
lastResultCleared = cleared;
|
|
lastResultScore = cleared ? 12800 : 7200;
|
|
lastResultMileage = cleared ? 263 : 84;
|
|
lastResultTime = cleared ? 23.9f : 38.4f;
|
|
lastResultDistance = completedMap != null
|
|
? (cleared ? completedMap.targetDistance : completedMap.targetDistance * 0.62f)
|
|
: 380f;
|
|
|
|
if(saveData != null)
|
|
{
|
|
saveData.totalMileage = Mathf.Max(saveData.totalMileage, 4120);
|
|
}
|
|
|
|
ShowResult();
|
|
}
|
|
|
|
private void ShowDebugDutyFreeShopPreview() {
|
|
EnsureDebugPreviewData();
|
|
|
|
if(saveData != null)
|
|
{
|
|
saveData.totalMileage = Mathf.Max(saveData.totalMileage, 4120);
|
|
}
|
|
|
|
ShowDutyFreeShop();
|
|
}
|
|
|
|
private void ShowDebugMapPreview() {
|
|
EnsureDebugPreviewData();
|
|
ShowMapPreview();
|
|
}
|
|
|
|
private void EnsureDebugPreviewData() {
|
|
if(saveData == null)
|
|
{
|
|
saveData = GameSaveManager.Load();
|
|
}
|
|
|
|
if(currentMap == null)
|
|
{
|
|
currentMap = MapDatabase.GetMapByIndex(saveData != null ? saveData.currentMapIndex : 0);
|
|
}
|
|
|
|
if(currentMap == null)
|
|
{
|
|
currentMap = MapDatabase.GetMapByIndex(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
private void StartNewJourney() {
|
|
saveData = GameSaveManager.ResetSave();
|
|
currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
|
|
completedMap = null;
|
|
openingStoryPageIndex = 0;
|
|
ShowOpeningStory();
|
|
}
|
|
|
|
private void ContinueJourney() {
|
|
saveData = GameSaveManager.Load();
|
|
currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
|
|
completedMap = null;
|
|
ShowMapPreview();
|
|
}
|
|
|
|
private void RetryCurrentMap() {
|
|
currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
|
|
LoadGameSceneForCurrentMap();
|
|
}
|
|
|
|
private void LoadGameSceneForCurrentMap() {
|
|
currentScreen = FlowScreen.Game;
|
|
gameplayActive = false;
|
|
pendingStartGameplay = true;
|
|
ShowLoading("탑승 준비 중", currentMap.displayName + " 코스를 불러오는 중입니다.");
|
|
|
|
if(Application.CanStreamedLevelBeLoaded(GameSceneName))
|
|
{
|
|
SceneManager.LoadScene(GameSceneName);
|
|
return;
|
|
}
|
|
|
|
Scene activeScene = SceneManager.GetActiveScene();
|
|
if(!string.IsNullOrEmpty(activeScene.name))
|
|
{
|
|
SceneManager.LoadScene(activeScene.name);
|
|
}
|
|
}
|
|
|
|
private IEnumerator StartGameplayAfterSceneLoad() {
|
|
yield return null;
|
|
|
|
pendingStartGameplay = false;
|
|
currentScreen = FlowScreen.Game;
|
|
gameplayActive = true;
|
|
|
|
SetOverlayVisible(false);
|
|
|
|
if(GameManager.instance != null)
|
|
{
|
|
GameManager.instance.BeginMap(currentMap);
|
|
}
|
|
}
|
|
|
|
private void ShowOpeningStory() {
|
|
currentScreen = FlowScreen.OpeningStory;
|
|
gameplayActive = false;
|
|
pendingStartGameplay = false;
|
|
|
|
if(TryLoadFlowScene(IntroSceneName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureOverlay();
|
|
ClearOverlay();
|
|
SetOverlayVisible(true);
|
|
|
|
BuildOpeningStoryScreen();
|
|
}
|
|
|
|
private void ShowIntro() {
|
|
currentScreen = FlowScreen.Intro;
|
|
gameplayActive = false;
|
|
pendingStartGameplay = false;
|
|
completedMap = null;
|
|
|
|
if(TryLoadFlowScene(IntroSceneName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureOverlay();
|
|
ClearOverlay();
|
|
SetOverlayVisible(true);
|
|
|
|
BuildIntroScreen();
|
|
}
|
|
|
|
private void ShowResult() {
|
|
if(TryLoadFlowScene(ResultSceneName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureOverlay();
|
|
ClearOverlay();
|
|
SetOverlayVisible(true);
|
|
BuildResultScreen();
|
|
}
|
|
|
|
private void BuildResultScreen() {
|
|
ApplyResultAirportBackground();
|
|
|
|
MapDefinition resultMap = completedMap != null ? completedMap : currentMap;
|
|
int rewardMileage = lastResultCleared && resultMap != null ? resultMap.clearMileageReward : 0;
|
|
int gainedMileage = Mathf.Max(0, lastResultMileage) + rewardMileage;
|
|
int targetDistance = resultMap != null ? Mathf.FloorToInt(resultMap.targetDistance) : 0;
|
|
int shownDistance = targetDistance > 0
|
|
? Mathf.Clamp(Mathf.FloorToInt(lastResultDistance), 0, targetDistance)
|
|
: Mathf.Max(0, Mathf.FloorToInt(lastResultDistance));
|
|
|
|
RectTransform passportRect = AddResultPassportPanel();
|
|
AddResultHeader(passportRect, resultMap);
|
|
AddResultStats(passportRect, gainedMileage, shownDistance, targetDistance);
|
|
AddResultTickets(passportRect, resultMap, rewardMileage);
|
|
|
|
if(lastResultCleared)
|
|
{
|
|
AddResultActionButton("면세점으로", ShowDutyFreeShop, new Vector2(-166f, -282f), new Vector2(250f, 48f), true, LoadUiSprite("", DutyFreeIconAssetPath));
|
|
AddResultActionButton("다음 여행지 가기", ShowMapPreview, new Vector2(160f, -282f), new Vector2(292f, 48f), false, LoadUiSprite("", AirplaneTravelIconAssetPath));
|
|
}
|
|
else
|
|
{
|
|
AddResultActionButton("다시 도전", RetryCurrentMap, new Vector2(-166f, -282f), new Vector2(250f, 48f), true, LoadUiSprite("", CheckpointIconAssetPath));
|
|
AddResultActionButton("다음 여행지 가기", ShowMapPreview, new Vector2(160f, -282f), new Vector2(292f, 48f), false, LoadUiSprite("", AirplaneTravelIconAssetPath));
|
|
}
|
|
}
|
|
|
|
private void ApplyResultAirportBackground() {
|
|
if(overlayBackground != null)
|
|
{
|
|
overlayBackground.sprite = LoadUiSprite(ResultAirportBackgroundResourcePath, "Assets/Resources/Map_Tutorial_Incheon_Airport.png");
|
|
overlayBackground.preserveAspect = false;
|
|
overlayBackground.color = overlayBackground.sprite != null
|
|
? Color.white
|
|
: new Color(0.008f, 0.024f, 0.034f, 0.96f);
|
|
}
|
|
|
|
if(flowCamera != null)
|
|
{
|
|
flowCamera.backgroundColor = new Color(0.006f, 0.022f, 0.035f, 1f);
|
|
}
|
|
|
|
GameObject scrimObject = CreateUiObject("Result Airport Background Scrim", overlayRoot, typeof(Image));
|
|
RectTransform scrimRect = scrimObject.GetComponent<RectTransform>();
|
|
scrimRect.anchorMin = Vector2.zero;
|
|
scrimRect.anchorMax = Vector2.one;
|
|
scrimRect.offsetMin = Vector2.zero;
|
|
scrimRect.offsetMax = Vector2.zero;
|
|
|
|
Image scrimImage = scrimObject.GetComponent<Image>();
|
|
scrimImage.color = new Color(0.005f, 0.015f, 0.020f, 0.30f);
|
|
scrimImage.raycastTarget = false;
|
|
}
|
|
|
|
private RectTransform AddResultPassportPanel() {
|
|
GameObject panelObject = CreateUiObject("Result Passport Page", overlayRoot, typeof(Image));
|
|
RectTransform panelRect = panelObject.GetComponent<RectTransform>();
|
|
panelRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
panelRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
panelRect.pivot = new Vector2(0.5f, 0.5f);
|
|
panelRect.anchoredPosition = new Vector2(0f, 38f);
|
|
panelRect.sizeDelta = new Vector2(1092f, 330f);
|
|
|
|
Image panelImage = panelObject.GetComponent<Image>();
|
|
panelImage.sprite = LoadUiSprite(ResultPassportPanelResourcePath, ResultPassportPanelAssetPath);
|
|
panelImage.color = panelImage.sprite != null
|
|
? Color.white
|
|
: new Color(0.95f, 0.91f, 0.80f, 0.98f);
|
|
panelImage.preserveAspect = true;
|
|
panelImage.raycastTarget = false;
|
|
|
|
return panelRect;
|
|
}
|
|
|
|
private void AddResultHeader(RectTransform parent, MapDefinition resultMap) {
|
|
Sprite rankSprite = LoadUiSprite("", ClearRankIconAssetPath);
|
|
|
|
AddResultImage(parent, "Result Rank Badge", rankSprite, new Vector2(150f, -140f), new Vector2(48f, 48f), true, Color.white);
|
|
|
|
string title = lastResultCleared ? "출국 성공!" : "일정 실패";
|
|
Color titleColor = lastResultCleared
|
|
? new Color(0.05f, 0.17f, 0.27f, 1f)
|
|
: new Color(0.52f, 0.08f, 0.07f, 1f);
|
|
AddResultCenteredText(parent, "Result Title", title, new Vector2(244f, -140f), new Vector2(220f, 50f), 27f, FontStyles.Bold, titleColor, TextAlignmentOptions.Center);
|
|
|
|
string routeText = GetResultOriginAirportCode(resultMap) + " -> " + GetResultDestinationAirportCode(resultMap);
|
|
AddResultCenteredText(parent, "Result Route Summary", routeText, new Vector2(244f, -181f), new Vector2(220f, 28f), 18f, FontStyles.Bold, new Color(0.17f, 0.34f, 0.43f, 0.98f), TextAlignmentOptions.Center);
|
|
AddResultCenteredText(parent, "Result Route Name", GetResultDestinationCityName(resultMap), new Vector2(244f, -207f), new Vector2(220f, 22f), 13f, FontStyles.Bold, new Color(0.43f, 0.58f, 0.64f, 0.96f), TextAlignmentOptions.Center);
|
|
|
|
if(lastResultCleared)
|
|
{
|
|
TextMeshProUGUI stampText = AddResultCenteredText(parent, "Result Stamp Text", "출국\n성공", new Vector2(238f, -260f), new Vector2(86f, 50f), 19f, FontStyles.Bold, new Color(0.70f, 0.04f, 0.03f, 0.96f), TextAlignmentOptions.Center);
|
|
stampText.lineSpacing = -16f;
|
|
}
|
|
else
|
|
{
|
|
AddResultCenteredText(parent, "Result Retry Note", "다시 준비해서\n출국 게이트까지", new Vector2(244f, -260f), new Vector2(210f, 58f), 17f, FontStyles.Bold, new Color(0.32f, 0.46f, 0.54f, 0.96f), TextAlignmentOptions.Center);
|
|
}
|
|
|
|
AddResultRouteBox(parent, "From", GetResultOriginAirportCode(resultMap), GetResultOriginCityName(resultMap), new Vector2(440f, -122f));
|
|
AddResultRouteBox(parent, "To", GetResultDestinationAirportCode(resultMap), GetResultDestinationCityName(resultMap), new Vector2(657f, -122f));
|
|
AddResultBoardingField(parent, "Flight", "항공편", GetResultFlightName(resultMap), new Vector2(402f, -253f));
|
|
AddResultBoardingField(parent, "Seat", "좌석", GetResultSeatName(resultMap), new Vector2(512f, -253f));
|
|
AddResultBoardingField(parent, "Boarding", "탑승", System.DateTime.Now.ToString("HH:mm"), new Vector2(622f, -253f));
|
|
AddResultBoardingField(parent, "Class", "클래스", lastResultCleared ? "일반석" : "대기", new Vector2(732f, -253f));
|
|
}
|
|
|
|
private void AddResultStats(RectTransform parent, int gainedMileage, int shownDistance, int targetDistance) {
|
|
AddResultStat(parent, "Mileage", LoadUiSprite("", MileageIconAssetPath), "마일리지", "+" + gainedMileage, new Vector2(812f, -140f));
|
|
AddResultStat(parent, "Time", LoadUiSprite("", TimerIconAssetPath), "도착 시간", lastResultTime.ToString("0.0") + "s", new Vector2(900f, -140f));
|
|
|
|
string distance = targetDistance > 0
|
|
? shownDistance + "/" + targetDistance + "m"
|
|
: shownDistance + "m";
|
|
AddResultStat(parent, "Distance", LoadUiSprite("", CheckpointIconAssetPath), "이동 거리", distance, new Vector2(986f, -140f));
|
|
}
|
|
|
|
private void AddResultTickets(RectTransform parent, MapDefinition resultMap, int rewardMileage) {
|
|
AddResultTicket(
|
|
parent,
|
|
"Destination",
|
|
LoadUiSprite("", CheckpointIconAssetPath),
|
|
"목적지",
|
|
GetResultDestinationCityName(resultMap),
|
|
new Vector2(812f, -251f));
|
|
|
|
AddResultTicket(
|
|
parent,
|
|
"Souvenir",
|
|
lastResultCleared ? GetResultSouvenirSprite(resultMap) : LoadUiSprite("", SouvenirCollectionIconAssetPath),
|
|
"보상",
|
|
lastResultCleared ? GetResultSouvenirName(resultMap) : "다음 도전",
|
|
new Vector2(900f, -251f));
|
|
|
|
AddResultTicket(
|
|
parent,
|
|
"Total Mileage",
|
|
LoadUiSprite("", MileageIconAssetPath),
|
|
"보유",
|
|
saveData.totalMileage + " M",
|
|
new Vector2(984f, -251f));
|
|
}
|
|
|
|
private void AddResultStat(RectTransform parent, string objectName, Sprite iconSprite, string label, string value, Vector2 centerPosition) {
|
|
AddResultImage(parent, "Result " + objectName + " Icon", iconSprite, centerPosition + new Vector2(0f, 23f), new Vector2(28f, 28f), true, Color.white);
|
|
AddResultCenteredText(parent, "Result " + objectName + " Value", value, centerPosition + new Vector2(0f, -2f), new Vector2(90f, 22f), 12f, FontStyles.Bold, new Color(0.05f, 0.17f, 0.25f, 1f), TextAlignmentOptions.Center);
|
|
AddResultCenteredText(parent, "Result " + objectName + " Label", label, centerPosition + new Vector2(0f, -25f), new Vector2(90f, 16f), 8f, FontStyles.Bold, new Color(0.42f, 0.58f, 0.64f, 0.92f), TextAlignmentOptions.Center);
|
|
}
|
|
|
|
private void AddResultTicket(RectTransform parent, string objectName, Sprite iconSprite, string label, string value, Vector2 centerPosition) {
|
|
AddResultImage(parent, "Result " + objectName + " Icon", iconSprite, centerPosition + new Vector2(0f, 27f), new Vector2(34f, 34f), true, Color.white);
|
|
AddResultCenteredText(parent, "Result " + objectName + " Label", label, centerPosition + new Vector2(0f, -9f), new Vector2(110f, 18f), 11f, FontStyles.Bold, new Color(0.43f, 0.58f, 0.64f, 0.96f), TextAlignmentOptions.Center);
|
|
AddResultCenteredText(parent, "Result " + objectName + " Value", value, centerPosition + new Vector2(0f, -31f), new Vector2(118f, 26f), 12f, FontStyles.Bold, new Color(0.05f, 0.18f, 0.27f, 1f), TextAlignmentOptions.Center);
|
|
}
|
|
|
|
private void AddResultRouteBox(RectTransform parent, string objectName, string code, string cityName, Vector2 centerPosition) {
|
|
AddResultCenteredText(parent, "Result " + objectName + " Airport Code", code, centerPosition + new Vector2(0f, 7f), new Vector2(150f, 48f), 34f, FontStyles.Bold, new Color(0.05f, 0.17f, 0.27f, 1f), TextAlignmentOptions.Center);
|
|
AddResultCenteredText(parent, "Result " + objectName + " City", cityName, centerPosition + new Vector2(0f, -30f), new Vector2(160f, 22f), 12f, FontStyles.Bold, new Color(0.43f, 0.58f, 0.64f, 0.96f), TextAlignmentOptions.Center);
|
|
}
|
|
|
|
private void AddResultBoardingField(RectTransform parent, string objectName, string label, string value, Vector2 centerPosition) {
|
|
AddResultCenteredText(parent, "Result " + objectName + " Label", label, centerPosition + new Vector2(0f, 13f), new Vector2(104f, 17f), 9f, FontStyles.Bold, new Color(0.43f, 0.58f, 0.64f, 0.96f), TextAlignmentOptions.Center);
|
|
AddResultCenteredText(parent, "Result " + objectName + " Value", value, centerPosition + new Vector2(0f, -12f), new Vector2(108f, 28f), 14f, FontStyles.Bold, new Color(0.05f, 0.18f, 0.27f, 1f), TextAlignmentOptions.Center);
|
|
}
|
|
|
|
private void AddResultActionButton(string label, UnityEngine.Events.UnityAction action, Vector2 position, Vector2 size, bool primary, Sprite iconSprite) {
|
|
GameObject buttonObject = CreateUiObject("Result Button " + label, overlayRoot, typeof(Image), typeof(Button));
|
|
RectTransform buttonRect = buttonObject.GetComponent<RectTransform>();
|
|
buttonRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
buttonRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
buttonRect.pivot = new Vector2(0.5f, 0.5f);
|
|
buttonRect.anchoredPosition = position;
|
|
buttonRect.sizeDelta = size;
|
|
|
|
Image buttonImage = buttonObject.GetComponent<Image>();
|
|
buttonImage.sprite = LoadUiSprite(ResultButtonResourcePath, ResultButtonAssetPath);
|
|
buttonImage.color = buttonImage.sprite != null
|
|
? (primary ? Color.white : new Color(0.86f, 0.94f, 1f, 0.94f))
|
|
: primary
|
|
? new Color(1f, 0.82f, 0.28f, 0.98f)
|
|
: new Color(0.82f, 0.91f, 0.96f, 0.90f);
|
|
buttonImage.preserveAspect = false;
|
|
buttonImage.raycastTarget = true;
|
|
|
|
Button button = buttonObject.GetComponent<Button>();
|
|
button.onClick.AddListener(action);
|
|
|
|
if(iconSprite != null)
|
|
{
|
|
AddResultImage(buttonRect, "Result Button Icon", iconSprite, new Vector2(34f, -(size.y * 0.5f)), new Vector2(24f, 24f), true, Color.white);
|
|
}
|
|
|
|
TextMeshProUGUI buttonText = AddResultText(buttonRect, "Result Button Label", label, new Vector2(iconSprite != null ? 54f : 0f, -1f), new Vector2(iconSprite != null ? size.x - 70f : size.x, size.y), 16f, FontStyles.Bold, new Color(0.94f, 0.99f, 1f, 1f), TextAlignmentOptions.Center);
|
|
RectTransform textRect = buttonText.rectTransform;
|
|
textRect.anchorMin = new Vector2(0f, 0.5f);
|
|
textRect.anchorMax = new Vector2(0f, 0.5f);
|
|
textRect.pivot = new Vector2(0f, 0.5f);
|
|
buttonText.outlineColor = new Color(0f, 0.04f, 0.07f, 0.98f);
|
|
buttonText.outlineWidth = 0.12f;
|
|
}
|
|
|
|
private Image AddResultImage(RectTransform parent, string objectName, Sprite sprite, Vector2 position, Vector2 size, bool preserveAspect, Color color) {
|
|
GameObject imageObject = CreateUiObject(objectName, parent, typeof(Image));
|
|
RectTransform imageRect = imageObject.GetComponent<RectTransform>();
|
|
imageRect.anchorMin = new Vector2(0f, 1f);
|
|
imageRect.anchorMax = new Vector2(0f, 1f);
|
|
imageRect.pivot = new Vector2(0.5f, 0.5f);
|
|
imageRect.anchoredPosition = position;
|
|
imageRect.sizeDelta = size;
|
|
|
|
Image image = imageObject.GetComponent<Image>();
|
|
image.sprite = sprite;
|
|
image.color = sprite != null ? color : new Color(0.30f, 0.46f, 0.54f, 0.28f);
|
|
image.preserveAspect = preserveAspect;
|
|
image.raycastTarget = false;
|
|
return image;
|
|
}
|
|
|
|
private TextMeshProUGUI AddResultText(RectTransform parent, string objectName, string text, Vector2 position, Vector2 size, float fontSize, FontStyles fontStyle, Color color, TextAlignmentOptions alignment) {
|
|
GameObject textObject = CreateUiObject(objectName, parent);
|
|
RectTransform textRect = textObject.GetComponent<RectTransform>();
|
|
textRect.anchorMin = new Vector2(0f, 1f);
|
|
textRect.anchorMax = new Vector2(0f, 1f);
|
|
textRect.pivot = new Vector2(0f, 1f);
|
|
textRect.anchoredPosition = position;
|
|
textRect.sizeDelta = size;
|
|
|
|
TextMeshProUGUI textComponent = textObject.AddComponent<TextMeshProUGUI>();
|
|
textComponent.text = text;
|
|
textComponent.font = runtimeFont != null ? runtimeFont : textComponent.font;
|
|
textComponent.fontSize = fontSize;
|
|
textComponent.fontStyle = fontStyle;
|
|
textComponent.alignment = alignment;
|
|
textComponent.color = color;
|
|
textComponent.raycastTarget = false;
|
|
textComponent.textWrappingMode = TextWrappingModes.Normal;
|
|
textComponent.overflowMode = TextOverflowModes.Ellipsis;
|
|
textComponent.enableAutoSizing = true;
|
|
textComponent.fontSizeMin = Mathf.Max(8f, fontSize * 0.66f);
|
|
textComponent.fontSizeMax = fontSize;
|
|
return textComponent;
|
|
}
|
|
|
|
private TextMeshProUGUI AddResultCenteredText(RectTransform parent, string objectName, string text, Vector2 centerPosition, Vector2 size, float fontSize, FontStyles fontStyle, Color color, TextAlignmentOptions alignment) {
|
|
Vector2 topLeftPosition = centerPosition + new Vector2(-size.x * 0.5f, size.y * 0.5f);
|
|
return AddResultText(parent, objectName, text, topLeftPosition, size, fontSize, fontStyle, color, alignment);
|
|
}
|
|
|
|
private string GetResultOriginAirportCode(MapDefinition resultMap) {
|
|
return GetAirportCode(GetResultOriginMapId(resultMap));
|
|
}
|
|
|
|
private string GetResultDestinationAirportCode(MapDefinition resultMap) {
|
|
return GetAirportCode(GetResultDestinationMapId(resultMap));
|
|
}
|
|
|
|
private string GetResultOriginCityName(MapDefinition resultMap) {
|
|
return GetAirportCityName(GetResultOriginMapId(resultMap));
|
|
}
|
|
|
|
private string GetResultDestinationCityName(MapDefinition resultMap) {
|
|
return GetAirportCityName(GetResultDestinationMapId(resultMap));
|
|
}
|
|
|
|
private MapId GetResultOriginMapId(MapDefinition resultMap) {
|
|
if(resultMap == null)
|
|
{
|
|
return MapId.TutorialIncheon;
|
|
}
|
|
|
|
return resultMap.mapId;
|
|
}
|
|
|
|
private MapId GetResultDestinationMapId(MapDefinition resultMap) {
|
|
if(resultMap == null)
|
|
{
|
|
return MapId.Japan;
|
|
}
|
|
|
|
if(resultMap.hasNextMap)
|
|
{
|
|
return resultMap.nextMapId;
|
|
}
|
|
|
|
return resultMap.mapId;
|
|
}
|
|
|
|
private string GetAirportCode(MapId mapId) {
|
|
switch(mapId)
|
|
{
|
|
case MapId.TutorialIncheon:
|
|
return "ICN";
|
|
case MapId.Japan:
|
|
return "FUK";
|
|
case MapId.China:
|
|
return "PVG";
|
|
case MapId.America:
|
|
return "LAX";
|
|
default:
|
|
return "TRP";
|
|
}
|
|
}
|
|
|
|
private string GetAirportCityName(MapId mapId) {
|
|
switch(mapId)
|
|
{
|
|
case MapId.TutorialIncheon:
|
|
return "인천공항";
|
|
case MapId.Japan:
|
|
return "후쿠오카";
|
|
case MapId.China:
|
|
return "상하이";
|
|
case MapId.America:
|
|
return "로스앤젤레스";
|
|
default:
|
|
return "여행지";
|
|
}
|
|
}
|
|
|
|
private string GetResultFlightName(MapDefinition resultMap) {
|
|
int mapNumber = resultMap != null ? (int)resultMap.mapId + 1 : 1;
|
|
return "UNR-" + mapNumber.ToString("000");
|
|
}
|
|
|
|
private string GetResultSeatName(MapDefinition resultMap) {
|
|
int mapNumber = resultMap != null ? (int)resultMap.mapId + 1 : 1;
|
|
return (10 + mapNumber).ToString() + "A";
|
|
}
|
|
|
|
private string GetResultSouvenirName(MapDefinition resultMap) {
|
|
if(resultMap == null)
|
|
{
|
|
return "여행 도장";
|
|
}
|
|
|
|
switch(resultMap.mapId)
|
|
{
|
|
case MapId.TutorialIncheon:
|
|
return "공항 마그넷";
|
|
case MapId.Japan:
|
|
return "일본 마그넷";
|
|
case MapId.China:
|
|
return "여행 부적";
|
|
case MapId.America:
|
|
return "스카이라인";
|
|
default:
|
|
return "여행 도장";
|
|
}
|
|
}
|
|
|
|
private Sprite GetResultSouvenirSprite(MapDefinition resultMap) {
|
|
if(resultMap == null)
|
|
{
|
|
return LoadUiSprite("", SouvenirCollectionIconAssetPath);
|
|
}
|
|
|
|
switch(resultMap.mapId)
|
|
{
|
|
case MapId.TutorialIncheon:
|
|
return LoadUiSprite("", AirportSouvenirAssetPath);
|
|
case MapId.Japan:
|
|
return LoadUiSprite("", JapanSouvenirAssetPath);
|
|
case MapId.China:
|
|
return LoadUiSprite("", ChinaSouvenirAssetPath);
|
|
case MapId.America:
|
|
return LoadUiSprite("", AmericaSouvenirAssetPath);
|
|
default:
|
|
return LoadUiSprite("", SouvenirCollectionIconAssetPath);
|
|
}
|
|
}
|
|
|
|
private void ShowDutyFreeShop() {
|
|
currentScreen = FlowScreen.DutyFreeShop;
|
|
gameplayActive = false;
|
|
pendingStartGameplay = false;
|
|
|
|
if(TryLoadFlowScene(DutyFreeShopSceneName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureOverlay();
|
|
ClearOverlay();
|
|
SetOverlayVisible(true);
|
|
|
|
BuildDutyFreeShopScreen();
|
|
}
|
|
|
|
private void BuildDutyFreeShopScreen() {
|
|
ApplyDutyFreeShopBackground();
|
|
|
|
RectTransform boardRect = AddDutyFreeBoard();
|
|
AddDutyFreeHeader(boardRect);
|
|
|
|
AddDutyFreeItemCard(
|
|
boardRect,
|
|
"도시락",
|
|
"HP 회복",
|
|
LunchboxCost,
|
|
saveData.lunchboxStock,
|
|
LoadUiSprite("", DutyFreeLunchboxAssetPath),
|
|
BuyLunchbox,
|
|
new Vector2(58f, -130f));
|
|
|
|
AddDutyFreeItemCard(
|
|
boardRect,
|
|
"방어막",
|
|
"1회 방어",
|
|
ShieldCost,
|
|
saveData.shieldStock,
|
|
LoadUiSprite("", DutyFreeShieldAssetPath),
|
|
BuyShield,
|
|
new Vector2(296f, -130f));
|
|
|
|
AddDutyFreeItemCard(
|
|
boardRect,
|
|
"운동화",
|
|
"속도 회복",
|
|
SpeedShoesCost,
|
|
saveData.speedShoesStock,
|
|
LoadUiSprite("", DutyFreeShoesAssetPath),
|
|
BuySpeedShoes,
|
|
new Vector2(534f, -130f));
|
|
|
|
AddDutyFreeItemCard(
|
|
boardRect,
|
|
"마일리지 카드",
|
|
"획득량 증가",
|
|
MileageCardCost,
|
|
saveData.mileageCardStock,
|
|
LoadUiSprite("", DutyFreeMileageCardAssetPath),
|
|
BuyMileageCard,
|
|
new Vector2(772f, -130f));
|
|
|
|
AddResultActionButton("다음 여행지 가기", ShowMapPreview, new Vector2(0f, -292f), new Vector2(292f, 48f), false, LoadUiSprite("", AirplaneTravelIconAssetPath));
|
|
}
|
|
|
|
private void ApplyDutyFreeShopBackground() {
|
|
if(overlayBackground != null)
|
|
{
|
|
overlayBackground.sprite = LoadUiSprite("", DutyFreeBackgroundAssetPath);
|
|
overlayBackground.preserveAspect = false;
|
|
overlayBackground.color = overlayBackground.sprite != null
|
|
? Color.white
|
|
: new Color(0.03f, 0.035f, 0.038f, 0.96f);
|
|
}
|
|
|
|
if(flowCamera != null)
|
|
{
|
|
flowCamera.backgroundColor = new Color(0.03f, 0.035f, 0.038f, 1f);
|
|
}
|
|
|
|
GameObject scrimObject = CreateUiObject("Duty Free Background Scrim", overlayRoot, typeof(Image));
|
|
RectTransform scrimRect = scrimObject.GetComponent<RectTransform>();
|
|
scrimRect.anchorMin = Vector2.zero;
|
|
scrimRect.anchorMax = Vector2.one;
|
|
scrimRect.offsetMin = Vector2.zero;
|
|
scrimRect.offsetMax = Vector2.zero;
|
|
|
|
Image scrimImage = scrimObject.GetComponent<Image>();
|
|
scrimImage.color = new Color(0.01f, 0.012f, 0.014f, 0.34f);
|
|
scrimImage.raycastTarget = false;
|
|
}
|
|
|
|
private RectTransform AddDutyFreeBoard() {
|
|
GameObject boardObject = CreateUiObject("Duty Free Product Board", overlayRoot, typeof(Image));
|
|
RectTransform boardRect = boardObject.GetComponent<RectTransform>();
|
|
boardRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
boardRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
boardRect.pivot = new Vector2(0.5f, 0.5f);
|
|
boardRect.anchoredPosition = new Vector2(0f, 28f);
|
|
boardRect.sizeDelta = new Vector2(1038f, 462f);
|
|
|
|
Image boardImage = boardObject.GetComponent<Image>();
|
|
boardImage.color = new Color(0.015f, 0.055f, 0.085f, 0.84f);
|
|
boardImage.raycastTarget = false;
|
|
|
|
return boardRect;
|
|
}
|
|
|
|
private void AddDutyFreeHeader(RectTransform parent) {
|
|
AddResultImage(parent, "Duty Free Header Icon", LoadUiSprite("", DutyFreeIconAssetPath), new Vector2(84f, -60f), new Vector2(48f, 48f), true, Color.white);
|
|
AddResultText(parent, "Duty Free Title", "면세점", new Vector2(120f, -38f), new Vector2(180f, 46f), 32f, FontStyles.Bold, new Color(0.96f, 0.99f, 1f, 1f), TextAlignmentOptions.MidlineLeft);
|
|
AddResultText(parent, "Duty Free Subtitle", "다음 여행 준비", new Vector2(122f, -82f), new Vector2(220f, 24f), 15f, FontStyles.Bold, new Color(0.64f, 0.82f, 0.92f, 0.96f), TextAlignmentOptions.MidlineLeft);
|
|
|
|
AddResultImage(parent, "Duty Free Mileage Icon", LoadUiSprite("", MileageIconAssetPath), new Vector2(760f, -61f), new Vector2(42f, 42f), true, Color.white);
|
|
AddResultText(parent, "Duty Free Mileage Label", "보유 마일리지", new Vector2(794f, -43f), new Vector2(140f, 22f), 13f, FontStyles.Bold, new Color(0.64f, 0.82f, 0.92f, 0.96f), TextAlignmentOptions.MidlineLeft);
|
|
AddResultText(parent, "Duty Free Mileage Value", saveData.totalMileage + " M", new Vector2(794f, -66f), new Vector2(160f, 32f), 24f, FontStyles.Bold, new Color(1f, 0.90f, 0.38f, 1f), TextAlignmentOptions.MidlineLeft);
|
|
}
|
|
|
|
private void AddDutyFreeItemCard(RectTransform parent, string itemName, string effectText, int cost, int stock, Sprite itemSprite, UnityEngine.Events.UnityAction buyAction, Vector2 topLeftPosition) {
|
|
bool canBuy = saveData.totalMileage >= cost;
|
|
GameObject cardObject = CreateUiObject("Duty Free Item " + itemName, parent, typeof(Image), typeof(Button));
|
|
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
|
|
cardRect.anchorMin = new Vector2(0f, 1f);
|
|
cardRect.anchorMax = new Vector2(0f, 1f);
|
|
cardRect.pivot = new Vector2(0f, 1f);
|
|
cardRect.anchoredPosition = topLeftPosition;
|
|
cardRect.sizeDelta = new Vector2(206f, 242f);
|
|
|
|
Image cardImage = cardObject.GetComponent<Image>();
|
|
cardImage.color = canBuy
|
|
? new Color(0.97f, 0.91f, 0.78f, 0.96f)
|
|
: new Color(0.40f, 0.45f, 0.48f, 0.88f);
|
|
cardImage.raycastTarget = true;
|
|
|
|
Button button = cardObject.GetComponent<Button>();
|
|
button.targetGraphic = cardImage;
|
|
button.interactable = canBuy;
|
|
button.onClick.AddListener(buyAction);
|
|
button.navigation = new Navigation {
|
|
mode = Navigation.Mode.None
|
|
};
|
|
|
|
Color textColor = canBuy
|
|
? new Color(0.06f, 0.16f, 0.22f, 1f)
|
|
: new Color(0.78f, 0.84f, 0.86f, 1f);
|
|
Color subColor = canBuy
|
|
? new Color(0.34f, 0.49f, 0.54f, 0.98f)
|
|
: new Color(0.66f, 0.72f, 0.74f, 0.98f);
|
|
|
|
AddResultImage(cardRect, "Duty Free " + itemName + " Image", itemSprite, new Vector2(103f, -70f), new Vector2(100f, 88f), true, canBuy ? Color.white : new Color(1f, 1f, 1f, 0.54f));
|
|
AddResultText(cardRect, "Duty Free " + itemName + " Name", itemName, new Vector2(16f, -116f), new Vector2(174f, 28f), 20f, FontStyles.Bold, textColor, TextAlignmentOptions.Center);
|
|
AddResultText(cardRect, "Duty Free " + itemName + " Effect", effectText, new Vector2(16f, -146f), new Vector2(174f, 22f), 13f, FontStyles.Bold, subColor, TextAlignmentOptions.Center);
|
|
AddResultText(cardRect, "Duty Free " + itemName + " Stock", "보유 " + stock, new Vector2(20f, -174f), new Vector2(72f, 22f), 13f, FontStyles.Bold, subColor, TextAlignmentOptions.MidlineLeft);
|
|
AddResultText(cardRect, "Duty Free " + itemName + " Cost", cost + " M", new Vector2(102f, -174f), new Vector2(84f, 22f), 15f, FontStyles.Bold, new Color(0.87f, 0.55f, 0.08f, 1f), TextAlignmentOptions.MidlineRight);
|
|
|
|
GameObject stripObject = CreateUiObject("Duty Free " + itemName + " Buy Strip", cardRect, typeof(Image));
|
|
RectTransform stripRect = stripObject.GetComponent<RectTransform>();
|
|
stripRect.anchorMin = new Vector2(0.5f, 0f);
|
|
stripRect.anchorMax = new Vector2(0.5f, 0f);
|
|
stripRect.pivot = new Vector2(0.5f, 0f);
|
|
stripRect.anchoredPosition = new Vector2(0f, 14f);
|
|
stripRect.sizeDelta = new Vector2(160f, 34f);
|
|
|
|
Image stripImage = stripObject.GetComponent<Image>();
|
|
stripImage.sprite = LoadUiSprite(ResultButtonResourcePath, ResultButtonAssetPath);
|
|
stripImage.color = canBuy ? Color.white : new Color(0.60f, 0.65f, 0.68f, 0.88f);
|
|
stripImage.preserveAspect = false;
|
|
stripImage.raycastTarget = false;
|
|
|
|
TextMeshProUGUI stripText = AddResultText(cardRect, "Duty Free " + itemName + " Buy Label", canBuy ? "구매" : "마일리지 부족", new Vector2(23f, -200f), new Vector2(160f, 32f), 14f, FontStyles.Bold, new Color(0.94f, 0.99f, 1f, 1f), TextAlignmentOptions.Center);
|
|
stripText.outlineColor = new Color(0f, 0.04f, 0.07f, 0.98f);
|
|
stripText.outlineWidth = 0.12f;
|
|
}
|
|
|
|
private void ShowMapPreview() {
|
|
currentScreen = FlowScreen.MapPreview;
|
|
gameplayActive = false;
|
|
pendingStartGameplay = false;
|
|
|
|
if(completedMap != null && lastResultCleared && completedMap.hasNextMap)
|
|
{
|
|
currentMap = MapDatabase.GetMap(completedMap.nextMapId);
|
|
}
|
|
else
|
|
{
|
|
currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
|
|
}
|
|
|
|
if(TryLoadFlowScene(MapPreviewSceneName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureOverlay();
|
|
ClearOverlay();
|
|
SetOverlayVisible(true);
|
|
SetDefaultOverlayBackground();
|
|
|
|
AddTitle("다음 경로");
|
|
AddBody(currentMap.displayName
|
|
+ "\n" + currentMap.routeName
|
|
+ "\n목표 거리: " + Mathf.FloorToInt(currentMap.targetDistance) + "m"
|
|
+ "\n제한 시간: " + currentMap.timeLimit.ToString("0") + "s");
|
|
AddButton("출발", ConfirmMapAndStart);
|
|
AddButton("인트로", ShowIntro);
|
|
}
|
|
|
|
private void ConfirmMapAndStart() {
|
|
saveData.currentMapIndex = MapDatabase.GetIndex(currentMap.mapId);
|
|
saveData.unlockedMapIndex = Mathf.Max(saveData.unlockedMapIndex, saveData.currentMapIndex);
|
|
GameSaveManager.Save(saveData);
|
|
LoadGameSceneForCurrentMap();
|
|
}
|
|
|
|
private void RefreshCurrentScreen() {
|
|
switch(currentScreen)
|
|
{
|
|
case FlowScreen.Result:
|
|
ShowResult();
|
|
break;
|
|
case FlowScreen.DutyFreeShop:
|
|
ShowDutyFreeShop();
|
|
break;
|
|
case FlowScreen.MapPreview:
|
|
ShowMapPreview();
|
|
break;
|
|
case FlowScreen.OpeningStory:
|
|
ShowOpeningStory();
|
|
break;
|
|
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
|
case FlowScreen.DebugMenu:
|
|
ShowDebugMenu();
|
|
break;
|
|
#endif
|
|
case FlowScreen.Intro:
|
|
default:
|
|
ShowIntro();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ResetAndStayIntro() {
|
|
saveData = GameSaveManager.ResetSave();
|
|
currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
|
|
ShowIntro();
|
|
}
|
|
|
|
private void BuyLunchbox() {
|
|
if(TrySpendMileage(LunchboxCost))
|
|
{
|
|
saveData.lunchboxStock++;
|
|
GameSaveManager.Save(saveData);
|
|
}
|
|
|
|
ShowDutyFreeShop();
|
|
}
|
|
|
|
private void BuyShield() {
|
|
if(TrySpendMileage(ShieldCost))
|
|
{
|
|
saveData.shieldStock++;
|
|
GameSaveManager.Save(saveData);
|
|
}
|
|
|
|
ShowDutyFreeShop();
|
|
}
|
|
|
|
private void BuySpeedShoes() {
|
|
if(TrySpendMileage(SpeedShoesCost))
|
|
{
|
|
saveData.speedShoesStock++;
|
|
GameSaveManager.Save(saveData);
|
|
}
|
|
|
|
ShowDutyFreeShop();
|
|
}
|
|
|
|
private void BuyMileageCard() {
|
|
if(TrySpendMileage(MileageCardCost))
|
|
{
|
|
saveData.mileageCardStock++;
|
|
GameSaveManager.Save(saveData);
|
|
}
|
|
|
|
ShowDutyFreeShop();
|
|
}
|
|
|
|
private bool TrySpendMileage(int cost) {
|
|
if(saveData.totalMileage < cost)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
saveData.totalMileage -= cost;
|
|
return true;
|
|
}
|
|
|
|
private void ShowLoading(string title, string body) {
|
|
EnsureOverlay();
|
|
ClearOverlay();
|
|
SetOverlayVisible(true);
|
|
SetDefaultOverlayBackground();
|
|
AddTitle(title);
|
|
AddBody(body);
|
|
}
|
|
|
|
private bool TryLoadFlowScene(string sceneName) {
|
|
Scene activeScene = SceneManager.GetActiveScene();
|
|
if(activeScene.name == sceneName)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!Application.CanStreamedLevelBeLoaded(sceneName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SceneManager.LoadScene(sceneName);
|
|
return true;
|
|
}
|
|
|
|
private void EnsureOverlay() {
|
|
EnsureEventSystem();
|
|
EnsureFlowCamera();
|
|
|
|
if(overlayCanvas != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GameObject canvasObject = new GameObject("Flow Overlay", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
|
DontDestroyOnLoad(canvasObject);
|
|
|
|
overlayCanvas = canvasObject.GetComponent<Canvas>();
|
|
overlayCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
overlayCanvas.worldCamera = null;
|
|
overlayCanvas.sortingOrder = 100;
|
|
overlayCanvas.targetDisplay = 0;
|
|
SetUiLayer(canvasObject);
|
|
|
|
CanvasScaler scaler = canvasObject.GetComponent<CanvasScaler>();
|
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|
scaler.referenceResolution = new Vector2(1280f, 720f);
|
|
scaler.matchWidthOrHeight = 0.5f;
|
|
|
|
GameObject rootObject = new GameObject("Flow Overlay Root", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
|
rootObject.transform.SetParent(canvasObject.transform, false);
|
|
SetUiLayer(rootObject);
|
|
|
|
overlayRoot = rootObject.GetComponent<RectTransform>();
|
|
overlayRoot.anchorMin = Vector2.zero;
|
|
overlayRoot.anchorMax = Vector2.one;
|
|
overlayRoot.offsetMin = Vector2.zero;
|
|
overlayRoot.offsetMax = Vector2.zero;
|
|
|
|
Image background = rootObject.GetComponent<Image>();
|
|
overlayBackground = background;
|
|
background.color = new Color(0.02f, 0.035f, 0.045f, 0.92f);
|
|
background.raycastTarget = true;
|
|
|
|
runtimeFont = CreateKoreanFontAsset();
|
|
}
|
|
|
|
private void SetDefaultOverlayBackground() {
|
|
if(overlayBackground != null)
|
|
{
|
|
overlayBackground.sprite = null;
|
|
overlayBackground.preserveAspect = false;
|
|
overlayBackground.color = new Color(0.02f, 0.035f, 0.045f, 0.92f);
|
|
}
|
|
|
|
if(flowCamera != null)
|
|
{
|
|
flowCamera.backgroundColor = new Color(0.02f, 0.035f, 0.045f, 1f);
|
|
}
|
|
}
|
|
|
|
private void BuildIntroScreen() {
|
|
if(overlayBackground != null)
|
|
{
|
|
overlayBackground.sprite = null;
|
|
overlayBackground.preserveAspect = false;
|
|
overlayBackground.color = Color.clear;
|
|
}
|
|
|
|
if(flowCamera != null)
|
|
{
|
|
flowCamera.backgroundColor = new Color(0.006f, 0.022f, 0.035f, 1f);
|
|
}
|
|
|
|
RectTransform mapRect = AddIntroWorldMap();
|
|
Vector2[] routePoints = GetIntroRoutePoints();
|
|
AddIntroRoute(mapRect, routePoints);
|
|
AddIntroPlane(mapRect, routePoints);
|
|
AddIntroPassportAndPhone();
|
|
AddIntroTitlePanel();
|
|
AddIntroButtonDock();
|
|
}
|
|
|
|
private void BuildOpeningStoryScreen() {
|
|
if(overlayBackground != null)
|
|
{
|
|
overlayBackground.sprite = null;
|
|
overlayBackground.preserveAspect = false;
|
|
overlayBackground.color = new Color(0.012f, 0.018f, 0.024f, 0.96f);
|
|
}
|
|
|
|
if(flowCamera != null)
|
|
{
|
|
flowCamera.backgroundColor = new Color(0.012f, 0.018f, 0.024f, 1f);
|
|
}
|
|
|
|
openingStoryPageIndex = Mathf.Clamp(openingStoryPageIndex, 0, OpeningStoryPageAssetPaths.Length - 1);
|
|
AddOpeningStoryPage();
|
|
AddOpeningStoryProgressDots();
|
|
AddOpeningStoryControls();
|
|
}
|
|
|
|
private void AddOpeningStoryPage() {
|
|
GameObject shadowObject = CreateUiObject("Opening Story Shadow", overlayRoot, typeof(Image));
|
|
RectTransform shadowRect = shadowObject.GetComponent<RectTransform>();
|
|
shadowRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
shadowRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
shadowRect.pivot = new Vector2(0.5f, 0.5f);
|
|
shadowRect.anchoredPosition = new Vector2(7f, 18f);
|
|
shadowRect.sizeDelta = new Vector2(1132f, 637f);
|
|
|
|
Image shadowImage = shadowObject.GetComponent<Image>();
|
|
shadowImage.color = new Color(0f, 0f, 0f, 0.34f);
|
|
shadowImage.raycastTarget = false;
|
|
|
|
GameObject pageObject = CreateUiObject("Opening Story Page", overlayRoot, typeof(Image), typeof(Button));
|
|
RectTransform pageRect = pageObject.GetComponent<RectTransform>();
|
|
pageRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
pageRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
pageRect.pivot = new Vector2(0.5f, 0.5f);
|
|
pageRect.anchoredPosition = new Vector2(0f, 24f);
|
|
pageRect.sizeDelta = new Vector2(1120f, 630f);
|
|
|
|
Image pageImage = pageObject.GetComponent<Image>();
|
|
pageImage.sprite = LoadUiTextureSprite(OpeningStoryPageAssetPaths[openingStoryPageIndex]);
|
|
pageImage.color = pageImage.sprite != null
|
|
? Color.white
|
|
: new Color(0.91f, 0.87f, 0.78f, 1f);
|
|
pageImage.preserveAspect = true;
|
|
|
|
Button pageButton = pageObject.GetComponent<Button>();
|
|
pageButton.transition = Selectable.Transition.None;
|
|
pageButton.onClick.AddListener(AdvanceOpeningStory);
|
|
}
|
|
|
|
private void AddOpeningStoryProgressDots() {
|
|
float startX = -((OpeningStoryPageAssetPaths.Length - 1) * 18f) * 0.5f;
|
|
|
|
for(int i = 0; i < OpeningStoryPageAssetPaths.Length; i++)
|
|
{
|
|
GameObject dotObject = CreateUiObject("Opening Story Dot " + (i + 1), overlayRoot, typeof(Image));
|
|
RectTransform dotRect = dotObject.GetComponent<RectTransform>();
|
|
dotRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
dotRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
dotRect.pivot = new Vector2(0.5f, 0.5f);
|
|
dotRect.anchoredPosition = new Vector2(startX + (i * 18f), -324f);
|
|
dotRect.sizeDelta = i == openingStoryPageIndex ? new Vector2(12f, 12f) : new Vector2(8f, 8f);
|
|
|
|
Image dotImage = dotObject.GetComponent<Image>();
|
|
dotImage.color = i == openingStoryPageIndex
|
|
? new Color(1f, 0.86f, 0.28f, 0.95f)
|
|
: new Color(0.72f, 0.84f, 0.88f, 0.5f);
|
|
dotImage.raycastTarget = false;
|
|
}
|
|
}
|
|
|
|
private void AddOpeningStoryControls() {
|
|
if(openingStoryPageIndex > 0)
|
|
{
|
|
AddOpeningStoryButton("이전", new Vector2(-450f, -324f), PreviousOpeningStoryPage);
|
|
}
|
|
|
|
string nextLabel = openingStoryPageIndex >= OpeningStoryPageAssetPaths.Length - 1 ? "게임 시작" : "다음";
|
|
AddOpeningStoryButton(nextLabel, new Vector2(450f, -324f), AdvanceOpeningStory);
|
|
}
|
|
|
|
private void AddOpeningStoryButton(string label, Vector2 position, UnityEngine.Events.UnityAction action) {
|
|
GameObject buttonObject = CreateUiObject("Opening Story Button " + label, overlayRoot, typeof(Image), typeof(Button));
|
|
RectTransform buttonRect = buttonObject.GetComponent<RectTransform>();
|
|
buttonRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
buttonRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
buttonRect.pivot = new Vector2(0.5f, 0.5f);
|
|
buttonRect.anchoredPosition = position;
|
|
buttonRect.sizeDelta = label == "게임 시작" ? new Vector2(178f, 44f) : new Vector2(126f, 42f);
|
|
|
|
Image buttonImage = buttonObject.GetComponent<Image>();
|
|
buttonImage.color = label == "게임 시작"
|
|
? new Color(1f, 0.86f, 0.34f, 0.96f)
|
|
: new Color(0.88f, 0.96f, 1f, 0.88f);
|
|
|
|
Button button = buttonObject.GetComponent<Button>();
|
|
button.onClick.AddListener(action);
|
|
|
|
TextMeshProUGUI buttonText = AddTextTo(buttonObject.transform, "Label", label, label == "게임 시작" ? 17f : 15f, FontStyles.Bold);
|
|
RectTransform textRect = buttonText.rectTransform;
|
|
textRect.anchorMin = Vector2.zero;
|
|
textRect.anchorMax = Vector2.one;
|
|
textRect.offsetMin = Vector2.zero;
|
|
textRect.offsetMax = Vector2.zero;
|
|
buttonText.color = new Color(0.035f, 0.065f, 0.08f, 1f);
|
|
}
|
|
|
|
private void AdvanceOpeningStory() {
|
|
if(openingStoryPageIndex >= OpeningStoryPageAssetPaths.Length - 1)
|
|
{
|
|
LoadGameSceneForCurrentMap();
|
|
return;
|
|
}
|
|
|
|
openingStoryPageIndex++;
|
|
ShowOpeningStory();
|
|
}
|
|
|
|
private void PreviousOpeningStoryPage() {
|
|
if(openingStoryPageIndex <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
openingStoryPageIndex--;
|
|
ShowOpeningStory();
|
|
}
|
|
|
|
private RectTransform AddIntroWorldMap() {
|
|
GameObject mapObject = CreateUiObject("Intro World Map", overlayRoot, typeof(Image));
|
|
RectTransform mapRect = mapObject.GetComponent<RectTransform>();
|
|
mapRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
mapRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
mapRect.pivot = new Vector2(0.5f, 0.5f);
|
|
mapRect.anchoredPosition = Vector2.zero;
|
|
mapRect.sizeDelta = new Vector2(1280f, 720f);
|
|
|
|
Image mapImage = mapObject.GetComponent<Image>();
|
|
mapImage.sprite = LoadUiSprite("", "Assets/ConceptArt/Intro/intro_world_map_clean_01.png");
|
|
mapImage.color = mapImage.sprite != null
|
|
? Color.white
|
|
: new Color(0.015f, 0.078f, 0.112f, 0.76f);
|
|
mapImage.preserveAspect = true;
|
|
mapImage.raycastTarget = false;
|
|
|
|
GameObject vignetteObject = CreateUiObject("Intro Map Vignette", overlayRoot, typeof(Image));
|
|
RectTransform vignetteRect = vignetteObject.GetComponent<RectTransform>();
|
|
vignetteRect.anchorMin = Vector2.zero;
|
|
vignetteRect.anchorMax = Vector2.one;
|
|
vignetteRect.offsetMin = Vector2.zero;
|
|
vignetteRect.offsetMax = Vector2.zero;
|
|
|
|
Image vignetteImage = vignetteObject.GetComponent<Image>();
|
|
vignetteImage.color = new Color(0f, 0.006f, 0.012f, 0.08f);
|
|
vignetteImage.raycastTarget = false;
|
|
|
|
return mapRect;
|
|
}
|
|
|
|
private void AddIntroMapGrid(RectTransform mapRect) {
|
|
Color gridColor = new Color(0.46f, 0.86f, 1f, 0.12f);
|
|
|
|
for(int i = -5; i <= 5; i++)
|
|
{
|
|
GameObject lineObject = CreateUiObject("Intro Map Longitude " + i, mapRect, typeof(Image));
|
|
RectTransform lineRect = lineObject.GetComponent<RectTransform>();
|
|
lineRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
lineRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
lineRect.pivot = new Vector2(0.5f, 0.5f);
|
|
lineRect.anchoredPosition = new Vector2(i * 92f, 0f);
|
|
lineRect.sizeDelta = new Vector2(2f, 610f);
|
|
|
|
Image lineImage = lineObject.GetComponent<Image>();
|
|
lineImage.color = gridColor;
|
|
lineImage.raycastTarget = false;
|
|
}
|
|
|
|
for(int i = -3; i <= 3; i++)
|
|
{
|
|
GameObject lineObject = CreateUiObject("Intro Map Latitude " + i, mapRect, typeof(Image));
|
|
RectTransform lineRect = lineObject.GetComponent<RectTransform>();
|
|
lineRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
lineRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
lineRect.pivot = new Vector2(0.5f, 0.5f);
|
|
lineRect.anchoredPosition = new Vector2(0f, i * 74f);
|
|
lineRect.sizeDelta = new Vector2(1110f, 2f);
|
|
|
|
Image lineImage = lineObject.GetComponent<Image>();
|
|
lineImage.color = gridColor;
|
|
lineImage.raycastTarget = false;
|
|
}
|
|
}
|
|
|
|
private void AddIntroLandMass(RectTransform mapRect, string objectName, Vector2 position, Vector2 size, float rotation) {
|
|
GameObject landObject = CreateUiObject(objectName, mapRect, typeof(Image));
|
|
RectTransform landRect = landObject.GetComponent<RectTransform>();
|
|
landRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
landRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
landRect.pivot = new Vector2(0.5f, 0.5f);
|
|
landRect.anchoredPosition = position;
|
|
landRect.sizeDelta = size;
|
|
landRect.localRotation = Quaternion.Euler(0f, 0f, rotation);
|
|
|
|
Image landImage = landObject.GetComponent<Image>();
|
|
landImage.color = new Color(0.24f, 0.72f, 0.59f, 0.22f);
|
|
landImage.raycastTarget = false;
|
|
}
|
|
|
|
private Vector2[] GetIntroRoutePoints() {
|
|
return new Vector2[] {
|
|
new Vector2(356f, 50f),
|
|
new Vector2(405f, 58f),
|
|
new Vector2(316f, 42f),
|
|
new Vector2(-450f, 94f)
|
|
};
|
|
}
|
|
|
|
private void AddIntroRoute(RectTransform mapRect, Vector2[] routePoints) {
|
|
Color routeColor = new Color(1f, 0.86f, 0.28f, 0.82f);
|
|
|
|
for(int i = 0; i < routePoints.Length - 1; i++)
|
|
{
|
|
AddIntroRouteSegment(mapRect, routePoints[i], routePoints[i + 1], routeColor, 4f);
|
|
}
|
|
|
|
AddIntroRouteMarker(mapRect, routePoints[0], "INCHEON", true, 0f, new Vector2(-4f, -25f));
|
|
AddIntroRouteMarker(mapRect, routePoints[1], "JAPAN", false, 0.4f, new Vector2(28f, -20f));
|
|
AddIntroRouteMarker(mapRect, routePoints[2], "CHINA", false, 0.8f, new Vector2(-18f, -24f));
|
|
AddIntroRouteMarker(mapRect, routePoints[3], "AMERICA", false, 1.2f, new Vector2(0f, -25f));
|
|
}
|
|
|
|
private void AddIntroRouteSegment(RectTransform mapRect, Vector2 from, Vector2 to, Color color, float thickness) {
|
|
GameObject segmentObject = CreateUiObject("Intro Route Segment", mapRect, typeof(Image));
|
|
RectTransform segmentRect = segmentObject.GetComponent<RectTransform>();
|
|
Vector2 delta = to - from;
|
|
segmentRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
segmentRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
segmentRect.pivot = new Vector2(0.5f, 0.5f);
|
|
segmentRect.anchoredPosition = from + (delta * 0.5f);
|
|
segmentRect.sizeDelta = new Vector2(delta.magnitude, thickness);
|
|
segmentRect.localRotation = Quaternion.Euler(0f, 0f, Mathf.Atan2(delta.y, delta.x) * Mathf.Rad2Deg);
|
|
|
|
Image segmentImage = segmentObject.GetComponent<Image>();
|
|
segmentImage.color = color;
|
|
segmentImage.raycastTarget = false;
|
|
}
|
|
|
|
private void AddIntroRouteMarker(RectTransform mapRect, Vector2 position, string label, bool current, float phase, Vector2 labelOffset) {
|
|
GameObject markerObject = CreateUiObject("Intro Route Marker " + label, mapRect, typeof(Image));
|
|
RectTransform markerRect = markerObject.GetComponent<RectTransform>();
|
|
markerRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
markerRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
markerRect.pivot = new Vector2(0.5f, 0.5f);
|
|
markerRect.anchoredPosition = position;
|
|
markerRect.sizeDelta = current ? new Vector2(23f, 23f) : new Vector2(17f, 17f);
|
|
|
|
Image markerImage = markerObject.GetComponent<Image>();
|
|
markerImage.color = current
|
|
? new Color(1f, 0.92f, 0.22f, 0.95f)
|
|
: new Color(0.78f, 0.96f, 1f, 0.86f);
|
|
markerImage.raycastTarget = false;
|
|
|
|
FlowIntroFloatMotion markerMotion = markerObject.AddComponent<FlowIntroFloatMotion>();
|
|
markerMotion.scalePulse = current ? 0.18f : 0.09f;
|
|
markerMotion.frequency = new Vector2(0f, 0.72f);
|
|
markerMotion.phase = phase;
|
|
|
|
TextMeshProUGUI labelText = AddIntroMapLabel(mapRect, "Intro Marker Label " + label, label, position + labelOffset, new Vector2(84f, 20f), 9.5f);
|
|
labelText.color = current
|
|
? new Color(1f, 0.94f, 0.44f, 0.92f)
|
|
: new Color(0.78f, 0.94f, 1f, 0.72f);
|
|
}
|
|
|
|
private TextMeshProUGUI AddIntroMapLabel(RectTransform parent, string objectName, string text, Vector2 position, Vector2 size, float fontSize) {
|
|
GameObject textObject = CreateUiObject(objectName, parent);
|
|
RectTransform textRect = textObject.GetComponent<RectTransform>();
|
|
textRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
textRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
textRect.pivot = new Vector2(0.5f, 0.5f);
|
|
textRect.anchoredPosition = position;
|
|
textRect.sizeDelta = size;
|
|
|
|
TextMeshProUGUI textComponent = textObject.AddComponent<TextMeshProUGUI>();
|
|
textComponent.text = text;
|
|
textComponent.font = runtimeFont != null ? runtimeFont : textComponent.font;
|
|
textComponent.fontSize = fontSize;
|
|
textComponent.fontStyle = FontStyles.Bold;
|
|
textComponent.alignment = TextAlignmentOptions.Center;
|
|
textComponent.raycastTarget = false;
|
|
textComponent.textWrappingMode = TextWrappingModes.NoWrap;
|
|
return textComponent;
|
|
}
|
|
|
|
private void AddIntroPlane(RectTransform mapRect, Vector2[] routePoints) {
|
|
Sprite planeSprite = LoadUiSprite("", "Assets/Sprites/UIIcons/UI_AirplaneTravel.png");
|
|
GameObject planeObject = CreateUiObject("Intro Airplane", overlayRoot, typeof(Image));
|
|
RectTransform rectTransform = planeObject.GetComponent<RectTransform>();
|
|
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
|
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
|
rectTransform.pivot = new Vector2(0.5f, 0.5f);
|
|
rectTransform.anchoredPosition = routePoints[0] + mapRect.anchoredPosition;
|
|
rectTransform.sizeDelta = new Vector2(58f, 58f);
|
|
|
|
Image image = planeObject.GetComponent<Image>();
|
|
image.sprite = planeSprite;
|
|
image.color = planeSprite != null
|
|
? new Color(1f, 1f, 1f, 0.95f)
|
|
: new Color(0.9f, 0.97f, 1f, 0.88f);
|
|
image.preserveAspect = true;
|
|
image.raycastTarget = false;
|
|
|
|
FlowIntroRouteMotion motion = planeObject.AddComponent<FlowIntroRouteMotion>();
|
|
motion.points = routePoints;
|
|
motion.offset = mapRect.anchoredPosition;
|
|
motion.duration = 7.5f;
|
|
}
|
|
|
|
private void AddIntroPassportAndPhone() {
|
|
AddIntroPassportCover();
|
|
AddIntroSmartPhone();
|
|
}
|
|
|
|
private void AddIntroPassportCover() {
|
|
GameObject passportObject = CreateUiObject("Intro Passport Cover", overlayRoot, typeof(Image));
|
|
RectTransform passportRect = passportObject.GetComponent<RectTransform>();
|
|
passportRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
passportRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
passportRect.pivot = new Vector2(0.5f, 0.5f);
|
|
passportRect.anchoredPosition = new Vector2(424f, -242f);
|
|
passportRect.sizeDelta = new Vector2(270f, 300f);
|
|
passportRect.localRotation = Quaternion.Euler(0f, 0f, -24f);
|
|
|
|
Image passportImage = passportObject.GetComponent<Image>();
|
|
passportImage.sprite = LoadUiTextureSprite("Assets/ConceptArt/Intro/UI/intro_passport_clean.png");
|
|
passportImage.color = passportImage.sprite != null
|
|
? new Color(1f, 1f, 1f, 0.98f)
|
|
: new Color(0.034f, 0.18f, 0.22f, 0.96f);
|
|
passportImage.preserveAspect = true;
|
|
passportImage.raycastTarget = false;
|
|
}
|
|
|
|
private void AddIntroSmartPhone() {
|
|
GameObject phoneObject = CreateUiObject("Intro Smart Phone", overlayRoot, typeof(Image));
|
|
RectTransform phoneRect = phoneObject.GetComponent<RectTransform>();
|
|
phoneRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
phoneRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
phoneRect.pivot = new Vector2(0.5f, 0.5f);
|
|
phoneRect.anchoredPosition = new Vector2(558f, -218f);
|
|
phoneRect.sizeDelta = new Vector2(132f, 252f);
|
|
phoneRect.localRotation = Quaternion.Euler(0f, 0f, 10f);
|
|
|
|
Image phoneImage = phoneObject.GetComponent<Image>();
|
|
phoneImage.sprite = LoadUiTextureSprite("Assets/ConceptArt/Intro/UI/intro_phone_clean.png");
|
|
phoneImage.color = phoneImage.sprite != null
|
|
? new Color(1f, 1f, 1f, 0.98f)
|
|
: new Color(0.015f, 0.018f, 0.022f, 0.98f);
|
|
phoneImage.preserveAspect = true;
|
|
phoneImage.raycastTarget = false;
|
|
|
|
FlowIntroFloatMotion phoneMotion = phoneObject.AddComponent<FlowIntroFloatMotion>();
|
|
phoneMotion.amplitude = new Vector2(0f, 5f);
|
|
phoneMotion.frequency = new Vector2(0f, 0.45f);
|
|
phoneMotion.phase = 0.6f;
|
|
}
|
|
|
|
private void AddIntroTitlePanel() {
|
|
GameObject titleObject = CreateUiObject("Intro Title Lockup", overlayRoot, typeof(Image));
|
|
RectTransform titleRect = titleObject.GetComponent<RectTransform>();
|
|
titleRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
titleRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
titleRect.pivot = new Vector2(0.5f, 0.5f);
|
|
titleRect.anchoredPosition = new Vector2(-364f, 196f);
|
|
titleRect.sizeDelta = new Vector2(448f, 166f);
|
|
|
|
Image titleImage = titleObject.GetComponent<Image>();
|
|
titleImage.sprite = LoadUiTextureSprite("Assets/ConceptArt/Intro/UI/intro_title_lockup.png");
|
|
titleImage.color = titleImage.sprite != null
|
|
? Color.white
|
|
: new Color(0.86f, 0.98f, 1f, 0.94f);
|
|
titleImage.preserveAspect = true;
|
|
titleImage.raycastTarget = false;
|
|
}
|
|
|
|
private void AddIntroButtonDock() {
|
|
GameObject dockObject = CreateUiObject("Intro Button Dock", overlayRoot);
|
|
RectTransform dockRect = dockObject.GetComponent<RectTransform>();
|
|
dockRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
dockRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
dockRect.pivot = new Vector2(0.5f, 0.5f);
|
|
dockRect.anchoredPosition = new Vector2(0f, -224f);
|
|
dockRect.sizeDelta = new Vector2(424f, 136f);
|
|
|
|
AddIntroButton(dockRect, "게임 시작", new Vector2(45f, 0f), StartNewJourney, true, 0);
|
|
AddIntroButton(dockRect, "이어하기", new Vector2(0f, -76f), ContinueJourney, false, 1);
|
|
AddIntroButton(dockRect, "설정", new Vector2(220f, -76f), ShowIntroSettingsPlaceholder, false, 2);
|
|
}
|
|
|
|
private void AddIntroButton(RectTransform parent, string label, Vector2 position, UnityEngine.Events.UnityAction action, bool primary, int buttonStyle) {
|
|
GameObject buttonObject = CreateUiObject("Button " + label, parent, typeof(Image), typeof(Button));
|
|
RectTransform buttonRect = buttonObject.GetComponent<RectTransform>();
|
|
buttonRect.anchorMin = new Vector2(0f, 1f);
|
|
buttonRect.anchorMax = new Vector2(0f, 1f);
|
|
buttonRect.pivot = new Vector2(0f, 1f);
|
|
buttonRect.anchoredPosition = position;
|
|
buttonRect.sizeDelta = primary ? new Vector2(334f, 74f) : new Vector2(204f, 58f);
|
|
|
|
Image buttonImage = buttonObject.GetComponent<Image>();
|
|
buttonImage.sprite = GetIntroButtonSprite(primary, buttonStyle);
|
|
buttonImage.color = buttonImage.sprite != null
|
|
? Color.white
|
|
: primary
|
|
? new Color(0.98f, 0.88f, 0.38f, 0.96f)
|
|
: new Color(0.88f, 0.96f, 1f, 0.9f);
|
|
buttonImage.preserveAspect = true;
|
|
buttonImage.raycastTarget = true;
|
|
|
|
Button button = buttonObject.GetComponent<Button>();
|
|
button.targetGraphic = buttonImage;
|
|
button.navigation = new Navigation {
|
|
mode = Navigation.Mode.None
|
|
};
|
|
|
|
GameObject stampObject = CreateUiObject("Departure Stamp", buttonObject.transform, typeof(Image));
|
|
RectTransform stampRect = stampObject.GetComponent<RectTransform>();
|
|
stampRect.anchorMin = new Vector2(1f, 0.5f);
|
|
stampRect.anchorMax = new Vector2(1f, 0.5f);
|
|
stampRect.pivot = new Vector2(0.5f, 0.5f);
|
|
stampRect.anchoredPosition = new Vector2(primary ? -47f : -28f, primary ? 1f : 0f);
|
|
stampRect.sizeDelta = primary ? new Vector2(58f, 58f) : new Vector2(44f, 44f);
|
|
stampRect.localRotation = Quaternion.Euler(0f, 0f, -12f);
|
|
|
|
Image stampImage = stampObject.GetComponent<Image>();
|
|
stampImage.sprite = LoadUiSprite(DepartureStampResourcePath, "Assets/Resources/UI_DepartureStamp_Red.png");
|
|
stampImage.color = stampImage.sprite != null
|
|
? new Color(1f, 1f, 1f, 0.96f)
|
|
: new Color(0.76f, 0.08f, 0.06f, 0.96f);
|
|
stampImage.preserveAspect = true;
|
|
stampImage.raycastTarget = false;
|
|
|
|
TextMeshProUGUI stampText = AddTextTo(stampObject.transform, "Stamp Label", "출국", primary ? 13f : 11f, FontStyles.Bold);
|
|
RectTransform stampTextRect = stampText.rectTransform;
|
|
stampTextRect.anchorMin = Vector2.zero;
|
|
stampTextRect.anchorMax = Vector2.one;
|
|
stampTextRect.offsetMin = Vector2.zero;
|
|
stampTextRect.offsetMax = Vector2.zero;
|
|
stampText.color = new Color(0.74f, 0.04f, 0.03f, 0.98f);
|
|
stampObject.SetActive(false);
|
|
|
|
button.onClick.AddListener(delegate {
|
|
button.interactable = false;
|
|
stampObject.SetActive(true);
|
|
FlowIntroStampMotion stampMotion = stampObject.GetComponent<FlowIntroStampMotion>();
|
|
if(stampMotion == null)
|
|
{
|
|
stampMotion = stampObject.AddComponent<FlowIntroStampMotion>();
|
|
}
|
|
|
|
stampMotion.Play();
|
|
StartCoroutine(InvokeIntroActionAfterStamp(action));
|
|
});
|
|
|
|
if(primary)
|
|
{
|
|
FlowIntroFloatMotion motion = buttonObject.AddComponent<FlowIntroFloatMotion>();
|
|
motion.scalePulse = 0.018f;
|
|
motion.frequency = new Vector2(0f, 0.55f);
|
|
}
|
|
}
|
|
|
|
private void AddIntroButtonIcon(Transform parent, int buttonStyle) {
|
|
GameObject iconObject = CreateUiObject("Button Icon", parent, typeof(Image));
|
|
RectTransform iconRect = iconObject.GetComponent<RectTransform>();
|
|
iconRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
iconRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
iconRect.pivot = new Vector2(0.5f, 0.5f);
|
|
iconRect.anchoredPosition = new Vector2(-38f, 0f);
|
|
iconRect.sizeDelta = new Vector2(30f, 30f);
|
|
|
|
Image iconImage = iconObject.GetComponent<Image>();
|
|
iconImage.sprite = GetIntroButtonIconSprite(buttonStyle);
|
|
iconImage.color = iconImage.sprite != null
|
|
? new Color(1f, 1f, 1f, 0.96f)
|
|
: new Color(0.94f, 0.98f, 1f, 0.92f);
|
|
iconImage.preserveAspect = true;
|
|
iconImage.raycastTarget = false;
|
|
}
|
|
|
|
private Sprite GetIntroButtonSprite(bool primary, int buttonStyle) {
|
|
if(primary)
|
|
{
|
|
return LoadUiTextureSprite("Assets/ConceptArt/Intro/UI/intro_button_start.png");
|
|
}
|
|
|
|
if(buttonStyle == 2)
|
|
{
|
|
return LoadUiTextureSprite("Assets/ConceptArt/Intro/UI/intro_button_settings.png");
|
|
}
|
|
|
|
return LoadUiTextureSprite("Assets/ConceptArt/Intro/UI/intro_button_continue.png");
|
|
}
|
|
|
|
private Sprite GetIntroButtonIconSprite(int buttonStyle) {
|
|
if(buttonStyle == 2)
|
|
{
|
|
return LoadUiTextureSprite("Assets/ConceptArt/Intro/Cutouts/intro_icon_settings.png");
|
|
}
|
|
|
|
return LoadUiTextureSprite("Assets/ConceptArt/Intro/Cutouts/intro_icon_continue.png");
|
|
}
|
|
|
|
private void ShowIntroSettingsPlaceholder() {
|
|
ShowIntro();
|
|
}
|
|
|
|
private IEnumerator InvokeIntroActionAfterStamp(UnityEngine.Events.UnityAction action) {
|
|
yield return new WaitForSecondsRealtime(0.32f);
|
|
|
|
if(action != null)
|
|
{
|
|
action.Invoke();
|
|
}
|
|
}
|
|
|
|
private TextMeshProUGUI AddTextToPanel(RectTransform parent, string objectName, string text, Vector2 position, Vector2 size, float fontSize, FontStyles fontStyle, TextAlignmentOptions alignment) {
|
|
GameObject textObject = CreateUiObject(objectName, parent);
|
|
RectTransform textRect = textObject.GetComponent<RectTransform>();
|
|
textRect.anchorMin = new Vector2(0f, 1f);
|
|
textRect.anchorMax = new Vector2(0f, 1f);
|
|
textRect.pivot = new Vector2(0f, 1f);
|
|
textRect.anchoredPosition = position;
|
|
textRect.sizeDelta = size;
|
|
|
|
TextMeshProUGUI textComponent = textObject.AddComponent<TextMeshProUGUI>();
|
|
textComponent.text = text;
|
|
textComponent.font = runtimeFont != null ? runtimeFont : textComponent.font;
|
|
textComponent.fontSize = fontSize;
|
|
textComponent.fontStyle = fontStyle;
|
|
textComponent.alignment = alignment;
|
|
textComponent.color = new Color(0.95f, 0.98f, 1f, 0.96f);
|
|
textComponent.raycastTarget = false;
|
|
textComponent.textWrappingMode = TextWrappingModes.Normal;
|
|
return textComponent;
|
|
}
|
|
|
|
private GameObject CreateUiObject(string objectName, Transform parent, params System.Type[] components) {
|
|
System.Type[] finalComponents;
|
|
if(components == null || components.Length == 0)
|
|
{
|
|
finalComponents = new System.Type[] {
|
|
typeof(RectTransform),
|
|
typeof(CanvasRenderer)
|
|
};
|
|
}
|
|
else
|
|
{
|
|
finalComponents = new System.Type[components.Length + 2];
|
|
finalComponents[0] = typeof(RectTransform);
|
|
finalComponents[1] = typeof(CanvasRenderer);
|
|
for(int i = 0; i < components.Length; i++)
|
|
{
|
|
finalComponents[i + 2] = components[i];
|
|
}
|
|
}
|
|
|
|
GameObject gameObject = new GameObject(objectName, finalComponents);
|
|
gameObject.transform.SetParent(parent, false);
|
|
SetUiLayer(gameObject);
|
|
return gameObject;
|
|
}
|
|
|
|
private void SetUiLayer(GameObject gameObject) {
|
|
if(gameObject != null)
|
|
{
|
|
gameObject.layer = GetUiLayer();
|
|
}
|
|
}
|
|
|
|
private int GetUiLayer() {
|
|
int uiLayer = LayerMask.NameToLayer("UI");
|
|
return uiLayer >= 0 ? uiLayer : 0;
|
|
}
|
|
|
|
private Sprite LoadUiSprite(string resourcePath, string assetPath) {
|
|
if(!string.IsNullOrEmpty(resourcePath))
|
|
{
|
|
Sprite resourceSprite = Resources.Load<Sprite>(resourcePath);
|
|
if(resourceSprite != null)
|
|
{
|
|
return resourceSprite;
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
if(!string.IsNullOrEmpty(assetPath))
|
|
{
|
|
Object[] assets = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath);
|
|
if(assets != null)
|
|
{
|
|
for(int i = 0; i < assets.Length; i++)
|
|
{
|
|
Sprite sprite = assets[i] as Sprite;
|
|
if(sprite != null)
|
|
{
|
|
return sprite;
|
|
}
|
|
}
|
|
}
|
|
|
|
Sprite directSprite = AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);
|
|
if(directSprite != null)
|
|
{
|
|
return directSprite;
|
|
}
|
|
|
|
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
|
if(texture == null)
|
|
{
|
|
AssetDatabase.Refresh();
|
|
texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
|
}
|
|
|
|
if(texture != null)
|
|
{
|
|
return Sprite.Create(texture, new Rect(0f, 0f, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return null;
|
|
}
|
|
|
|
private Sprite LoadUiTextureSprite(string assetPath) {
|
|
#if UNITY_EDITOR
|
|
if(string.IsNullOrEmpty(assetPath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
|
if(texture == null)
|
|
{
|
|
AssetDatabase.Refresh();
|
|
texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
|
}
|
|
|
|
if(texture == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return Sprite.Create(texture, new Rect(0f, 0f, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
|
|
#else
|
|
return null;
|
|
#endif
|
|
}
|
|
|
|
private Sprite LoadUiSpriteRegion(string assetPath, Rect pixelRect) {
|
|
#if UNITY_EDITOR
|
|
if(string.IsNullOrEmpty(assetPath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
|
if(texture == null)
|
|
{
|
|
AssetDatabase.Refresh();
|
|
texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
|
}
|
|
|
|
if(texture == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Rect safeRect = ClampSpriteRect(pixelRect, texture.width, texture.height);
|
|
if(safeRect.width <= 0f || safeRect.height <= 0f)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return Sprite.Create(texture, safeRect, new Vector2(0.5f, 0.5f), 100f);
|
|
#else
|
|
return null;
|
|
#endif
|
|
}
|
|
|
|
private Rect ClampSpriteRect(Rect pixelRect, int textureWidth, int textureHeight) {
|
|
float xMin = Mathf.Clamp(pixelRect.xMin, 0f, textureWidth);
|
|
float yMin = Mathf.Clamp(pixelRect.yMin, 0f, textureHeight);
|
|
float xMax = Mathf.Clamp(pixelRect.xMax, 0f, textureWidth);
|
|
float yMax = Mathf.Clamp(pixelRect.yMax, 0f, textureHeight);
|
|
|
|
if(xMax < xMin)
|
|
{
|
|
xMax = xMin;
|
|
}
|
|
|
|
if(yMax < yMin)
|
|
{
|
|
yMax = yMin;
|
|
}
|
|
|
|
return Rect.MinMaxRect(xMin, yMin, xMax, yMax);
|
|
}
|
|
|
|
private void EnsureEventSystem() {
|
|
if(EventSystem.current != null)
|
|
{
|
|
EventSystem.current.enabled = true;
|
|
EventSystem.current.gameObject.SetActive(true);
|
|
|
|
StandaloneInputModule inputModule = EventSystem.current.GetComponent<StandaloneInputModule>();
|
|
if(inputModule == null)
|
|
{
|
|
inputModule = EventSystem.current.gameObject.AddComponent<StandaloneInputModule>();
|
|
}
|
|
|
|
inputModule.enabled = true;
|
|
DontDestroyOnLoad(EventSystem.current.gameObject);
|
|
return;
|
|
}
|
|
|
|
GameObject eventSystemObject = new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
|
|
DontDestroyOnLoad(eventSystemObject);
|
|
}
|
|
|
|
private void EnsureFlowCamera() {
|
|
if(flowCamera != null)
|
|
{
|
|
flowCamera.enabled = true;
|
|
flowCamera.targetDisplay = 0;
|
|
return;
|
|
}
|
|
|
|
GameObject cameraObject = new GameObject("Flow UI Camera", typeof(Camera));
|
|
DontDestroyOnLoad(cameraObject);
|
|
|
|
flowCamera = cameraObject.GetComponent<Camera>();
|
|
flowCamera.transform.position = new Vector3(640f, 360f, -10f);
|
|
flowCamera.clearFlags = CameraClearFlags.SolidColor;
|
|
flowCamera.backgroundColor = new Color(0.006f, 0.022f, 0.035f, 1f);
|
|
flowCamera.cullingMask = 1 << GetUiLayer();
|
|
flowCamera.depth = 100f;
|
|
flowCamera.orthographic = true;
|
|
flowCamera.orthographicSize = 360f;
|
|
flowCamera.targetDisplay = 0;
|
|
}
|
|
|
|
private void ClearOverlay() {
|
|
if(overlayRoot == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for(int i = overlayRoot.childCount - 1; i >= 0; i--)
|
|
{
|
|
Destroy(overlayRoot.GetChild(i).gameObject);
|
|
}
|
|
}
|
|
|
|
private void SetOverlayVisible(bool visible) {
|
|
if(visible)
|
|
{
|
|
EnsureFlowCamera();
|
|
}
|
|
|
|
if(overlayCanvas != null)
|
|
{
|
|
overlayCanvas.gameObject.SetActive(visible);
|
|
}
|
|
|
|
if(flowCamera != null)
|
|
{
|
|
flowCamera.gameObject.SetActive(visible);
|
|
}
|
|
}
|
|
|
|
private void AddTitle(string text) {
|
|
TextMeshProUGUI title = AddText("Title", text, 34f, FontStyles.Bold);
|
|
RectTransform rectTransform = title.rectTransform;
|
|
rectTransform.anchoredPosition = new Vector2(0f, 160f);
|
|
rectTransform.sizeDelta = new Vector2(760f, 62f);
|
|
}
|
|
|
|
private void AddBody(string text) {
|
|
TextMeshProUGUI body = AddText("Body", text, 17f, FontStyles.Normal);
|
|
RectTransform rectTransform = body.rectTransform;
|
|
rectTransform.anchoredPosition = new Vector2(0f, 52f - (GetBodyCount() * 82f));
|
|
rectTransform.sizeDelta = new Vector2(760f, 96f);
|
|
}
|
|
|
|
private void AddButton(string label, UnityEngine.Events.UnityAction action) {
|
|
int buttonIndex = GetButtonCount();
|
|
|
|
GameObject buttonObject = new GameObject("Button " + label, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
|
|
buttonObject.transform.SetParent(overlayRoot, false);
|
|
SetUiLayer(buttonObject);
|
|
|
|
RectTransform buttonRect = buttonObject.GetComponent<RectTransform>();
|
|
buttonRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
buttonRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
buttonRect.pivot = new Vector2(0.5f, 0.5f);
|
|
buttonRect.anchoredPosition = new Vector2(0f, -82f - (buttonIndex * 46f));
|
|
buttonRect.sizeDelta = new Vector2(260f, 34f);
|
|
|
|
Image buttonImage = buttonObject.GetComponent<Image>();
|
|
buttonImage.color = new Color(0.92f, 0.96f, 1f, 0.95f);
|
|
|
|
Button button = buttonObject.GetComponent<Button>();
|
|
button.onClick.AddListener(action);
|
|
|
|
TextMeshProUGUI buttonText = AddTextTo(buttonObject.transform, "Label", label, 15f, FontStyles.Bold);
|
|
RectTransform textRect = buttonText.rectTransform;
|
|
textRect.anchorMin = Vector2.zero;
|
|
textRect.anchorMax = Vector2.one;
|
|
textRect.offsetMin = Vector2.zero;
|
|
textRect.offsetMax = Vector2.zero;
|
|
buttonText.color = new Color(0.04f, 0.07f, 0.09f, 1f);
|
|
}
|
|
|
|
private int GetBodyCount() {
|
|
int count = 0;
|
|
|
|
for(int i = 0; i < overlayRoot.childCount; i++)
|
|
{
|
|
if(overlayRoot.GetChild(i).name.StartsWith("Body"))
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private int GetButtonCount() {
|
|
int count = 0;
|
|
|
|
for(int i = 0; i < overlayRoot.childCount; i++)
|
|
{
|
|
if(overlayRoot.GetChild(i).name.StartsWith("Button"))
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private TextMeshProUGUI AddText(string objectName, string text, float fontSize, FontStyles fontStyle) {
|
|
GameObject textObject = new GameObject(objectName + " " + GetBodyCount(), typeof(RectTransform), typeof(CanvasRenderer));
|
|
textObject.transform.SetParent(overlayRoot, false);
|
|
SetUiLayer(textObject);
|
|
|
|
RectTransform rectTransform = textObject.GetComponent<RectTransform>();
|
|
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
|
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
|
rectTransform.pivot = new Vector2(0.5f, 0.5f);
|
|
|
|
TextMeshProUGUI label = textObject.AddComponent<TextMeshProUGUI>();
|
|
label.text = text;
|
|
label.font = runtimeFont != null ? runtimeFont : label.font;
|
|
label.fontSize = fontSize;
|
|
label.fontStyle = fontStyle;
|
|
label.alignment = TextAlignmentOptions.Center;
|
|
label.color = new Color(0.95f, 0.98f, 1f, 1f);
|
|
label.raycastTarget = false;
|
|
label.textWrappingMode = TextWrappingModes.Normal;
|
|
return label;
|
|
}
|
|
|
|
private TextMeshProUGUI AddTextTo(Transform parent, string objectName, string text, float fontSize, FontStyles fontStyle) {
|
|
GameObject textObject = new GameObject(objectName, typeof(RectTransform), typeof(CanvasRenderer));
|
|
textObject.transform.SetParent(parent, false);
|
|
SetUiLayer(textObject);
|
|
TextMeshProUGUI label = textObject.AddComponent<TextMeshProUGUI>();
|
|
label.text = text;
|
|
label.font = runtimeFont != null ? runtimeFont : label.font;
|
|
label.fontSize = fontSize;
|
|
label.fontStyle = fontStyle;
|
|
label.alignment = TextAlignmentOptions.Center;
|
|
label.raycastTarget = false;
|
|
label.textWrappingMode = TextWrappingModes.NoWrap;
|
|
return label;
|
|
}
|
|
|
|
private TMP_FontAsset CreateKoreanFontAsset() {
|
|
string[] familyNames = {
|
|
"Malgun Gothic",
|
|
"맑은 고딕",
|
|
"Arial Unicode MS"
|
|
};
|
|
|
|
for(int i = 0; i < familyNames.Length; i++)
|
|
{
|
|
TMP_FontAsset fontAsset = TMP_FontAsset.CreateFontAsset(familyNames[i], "Regular", 80);
|
|
if(fontAsset != null)
|
|
{
|
|
fontAsset.name = "Runtime Flow Korean Font";
|
|
return fontAsset;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|