Add travel map flow and update docs
This commit is contained in:
+648
-49
@@ -2,10 +2,30 @@ using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using TMPro;
|
||||
using UnityEngine.UI;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
// 게임 오버 상태를 표현하고, 게임 점수와 UI를 관리하는 게임 매니저
|
||||
// 씬에는 단 하나의 게임 매니저만 존재할 수 있다.
|
||||
public class GameManager : MonoBehaviour {
|
||||
public enum TutorialGuideType {
|
||||
None,
|
||||
Jump,
|
||||
DoubleJump,
|
||||
Slide,
|
||||
Damage,
|
||||
Items
|
||||
}
|
||||
|
||||
public enum TutorialItemType {
|
||||
HealthSushi,
|
||||
InvincibleRamen,
|
||||
Shield,
|
||||
SpeedShoes,
|
||||
MileageCard
|
||||
}
|
||||
|
||||
public static GameManager instance; // 싱글톤을 할당할 전역 변수
|
||||
|
||||
public bool isGameover = false; // 게임 오버 상태
|
||||
@@ -16,14 +36,34 @@ public class GameManager : MonoBehaviour {
|
||||
private int currentLife; // 현재 남은 목숨
|
||||
private int stageMileage = 0; // 현재 플레이 중 획득한 마일리지
|
||||
private int totalMileage = 0; // 누적 보유 마일리지
|
||||
private TutorialGuideType tutorialGuideType = TutorialGuideType.None; // 현재 튜토리얼 안내 이미지 종류
|
||||
private string tutorialTitle = ""; // 튜토리얼 안내 제목
|
||||
private string tutorialMessage = ""; // 튜토리얼 안내 문구
|
||||
private Image[] lifeIcons; // 왼쪽 HUD의 목숨 도장 표시
|
||||
private TextMeshProUGUI tutorialText; // 화면 하단 튜토리얼 안내
|
||||
private GameObject tutorialGuideObject; // 화면 하단 이미지 튜토리얼 패널
|
||||
private Image tutorialMainImage; // 점프/슬라이드 대표 이미지
|
||||
private TextMeshProUGUI tutorialMultiplierText; // 2단 점프 x2 표시
|
||||
private TextMeshProUGUI tutorialTitleText; // 튜토리얼 제목
|
||||
private TextMeshProUGUI tutorialBodyText; // 튜토리얼 설명
|
||||
private Image[] tutorialItemImages; // 아이템 순서 설명 이미지
|
||||
private TMP_FontAsset tutorialFontAsset; // 한글 튜토리얼 문구용 런타임 폰트
|
||||
private float tutorialGuideSequenceStartTime; // 튜토리얼 안내 시작 시각
|
||||
private int currentTutorialGuideSequenceIndex = -1; // 현재 표시 중인 안내 단계
|
||||
private bool tutorialGuideSequenceComplete = false; // 기본 튜토리얼 안내 완료 여부
|
||||
private MapDefinition currentMapDefinition; // 현재 플레이 중인 맵 데이터
|
||||
private float stageElapsedTime = 0f; // 현재 맵 진행 시간
|
||||
private float stageDistance = 0f; // 현재 맵 진행 거리
|
||||
private bool stageCompleted = false; // 결과 화면이 이미 처리되었는지 여부
|
||||
|
||||
public int maxLife = 3; // 시작 목숨
|
||||
public int initialMileage = 500; // 첫 시작시 지급할 초기 마일리지
|
||||
public bool grantInitialMileage = true; // 초기 마일리지 지급 여부
|
||||
public bool saveMileageImmediately = true; // 스테이지 정산 전까지는 획득 즉시 누적한다.
|
||||
public bool playTutorialGuideSequence = false; // 시작 시 입력/아이템 안내를 시간 기준으로 보여준다.
|
||||
public float itemInvincibleDuration = 2.2f; // 라멘 아이템 무적 지속 시간
|
||||
public float itemSpeedBoostAmount = 0.35f; // 운동화 아이템 즉시 속도 보정량
|
||||
public float mileageBonusDuration = 7f; // 마일리지 카드 보너스 지속 시간
|
||||
public int mileageBonusMultiplier = 2; // 마일리지 카드 획득 배율
|
||||
|
||||
private const string TotalMileageKey = "UniRun.TotalMileage";
|
||||
private const string InitialMileageGrantedKey = "UniRun.InitialMileageGranted";
|
||||
@@ -31,6 +71,10 @@ public class GameManager : MonoBehaviour {
|
||||
private const string PassportPanelResourcePath = "UI_PassportPanel";
|
||||
private const string LifeStampResourcePath = "UI_LifeStamp";
|
||||
private const string PassportItemSlotResourcePath = "UI_PassportItemSlot";
|
||||
private const float TutorialJumpGuideDuration = 3.5f;
|
||||
private const float TutorialDoubleJumpGuideDuration = 3.5f;
|
||||
private const float TutorialSlideGuideDuration = 3.5f;
|
||||
private const float TutorialItemGuideDuration = 5.2f;
|
||||
|
||||
// 게임의 스피드를 올리는 변수
|
||||
public float gameSpeed = 1f;
|
||||
@@ -38,6 +82,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 mileageBonusEndTime = 0f; // 마일리지 카드 보너스 종료 시각
|
||||
|
||||
// 게임 시작과 동시에 싱글톤을 구성
|
||||
void Awake() {
|
||||
@@ -60,7 +107,9 @@ public class GameManager : MonoBehaviour {
|
||||
|
||||
private void Start() {
|
||||
currentLife = maxLife;
|
||||
totalMileage = PlayerPrefs.GetInt(TotalMileageKey, 0);
|
||||
totalMileage = GameFlowManager.Instance != null
|
||||
? GameFlowManager.Instance.SaveData.totalMileage
|
||||
: PlayerPrefs.GetInt(TotalMileageKey, 0);
|
||||
|
||||
if(grantInitialMileage && PlayerPrefs.GetInt(InitialMileageGrantedKey, 0) == 0)
|
||||
{
|
||||
@@ -71,26 +120,90 @@ public class GameManager : MonoBehaviour {
|
||||
}
|
||||
|
||||
BuildRuntimeHUD();
|
||||
StartTutorialGuideSequence();
|
||||
BeginMap(GameFlowManager.Instance != null
|
||||
? GameFlowManager.Instance.CurrentMap
|
||||
: MapDatabase.GetMap(MapId.TutorialIncheon));
|
||||
SyncTotalMileageToFlow();
|
||||
UpdateScoreUI();
|
||||
}
|
||||
|
||||
void Update() {
|
||||
// 게임 오버 상태에서 게임을 재시작할 수 있게 하는 처리
|
||||
if(isGameover && Input.GetMouseButtonDown(0))
|
||||
if(isGameover && GameFlowManager.Instance == null && Input.GetMouseButtonDown(0))
|
||||
{
|
||||
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
|
||||
}
|
||||
|
||||
if(!GameFlowManager.IsGameplayActive)
|
||||
{
|
||||
UpdateScoreUI();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isGameover)
|
||||
{
|
||||
UpdateTutorialGuideSequence();
|
||||
|
||||
stageElapsedTime += Time.deltaTime;
|
||||
|
||||
// 게임 진행시 게임 속도는 시간에 따라 증가
|
||||
gameSpeed += speedIncreaseRate * Time.deltaTime;
|
||||
// 게임 최대 속도는 max를 넘어서지 못하게
|
||||
gameSpeed = Mathf.Min(gameSpeed, maxGameSpeed);
|
||||
|
||||
stageDistance += 10f * gameSpeed * Time.deltaTime;
|
||||
|
||||
if(currentMapDefinition != null && currentMapDefinition.targetDistance > 0f && stageDistance >= currentMapDefinition.targetDistance)
|
||||
{
|
||||
CompleteStage(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentMapDefinition != null && currentMapDefinition.timeLimit > 0f && stageElapsedTime >= currentMapDefinition.timeLimit)
|
||||
{
|
||||
CompleteStage(false);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateScoreUI();
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginMap(MapDefinition mapDefinition) {
|
||||
currentMapDefinition = mapDefinition != null
|
||||
? mapDefinition
|
||||
: MapDatabase.GetMap(MapId.TutorialIncheon);
|
||||
|
||||
score = 0;
|
||||
stageMileage = 0;
|
||||
currentLife = maxLife;
|
||||
gameSpeed = minGameSpeed;
|
||||
shieldCharges = 0;
|
||||
itemInvincibleEndTime = 0f;
|
||||
mileageBonusEndTime = 0f;
|
||||
stageElapsedTime = 0f;
|
||||
stageDistance = 0f;
|
||||
stageCompleted = false;
|
||||
isGameover = false;
|
||||
tutorialGuideSequenceComplete = false;
|
||||
currentTutorialGuideSequenceIndex = -1;
|
||||
SetTutorialGuide(TutorialGuideType.None, "", "");
|
||||
|
||||
if(gameoverUI != null)
|
||||
{
|
||||
gameoverUI.SetActive(false);
|
||||
}
|
||||
|
||||
PlatformSpawner platformSpawner = FindFirstObjectByType<PlatformSpawner>();
|
||||
if(platformSpawner != null)
|
||||
{
|
||||
platformSpawner.ApplyMap(currentMapDefinition);
|
||||
}
|
||||
|
||||
UpdateScoreUI();
|
||||
}
|
||||
|
||||
// 점수를 증가시키는 메서드
|
||||
public void AddScore(int newScore) {
|
||||
|
||||
@@ -103,10 +216,95 @@ public class GameManager : MonoBehaviour {
|
||||
}
|
||||
|
||||
public void SetTutorialMessage(string message) {
|
||||
SetTutorialGuide(TutorialGuideType.None, "", message);
|
||||
}
|
||||
|
||||
public void SetTutorialGuide(TutorialGuideType guideType, string title, string message) {
|
||||
tutorialGuideType = guideType;
|
||||
tutorialTitle = title;
|
||||
tutorialMessage = message;
|
||||
UpdateScoreUI();
|
||||
}
|
||||
|
||||
private void StartTutorialGuideSequence() {
|
||||
if(!playTutorialGuideSequence)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tutorialGuideSequenceStartTime = Time.time;
|
||||
tutorialGuideSequenceComplete = false;
|
||||
currentTutorialGuideSequenceIndex = -1;
|
||||
UpdateTutorialGuideSequence();
|
||||
}
|
||||
|
||||
private void UpdateTutorialGuideSequence() {
|
||||
if(!playTutorialGuideSequence || tutorialGuideSequenceComplete)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float elapsedTime = Time.time - tutorialGuideSequenceStartTime;
|
||||
int guideIndex = GetTutorialGuideSequenceIndex(elapsedTime);
|
||||
|
||||
if(guideIndex == currentTutorialGuideSequenceIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentTutorialGuideSequenceIndex = guideIndex;
|
||||
ApplyTutorialGuideSequenceStep(guideIndex);
|
||||
}
|
||||
|
||||
private int GetTutorialGuideSequenceIndex(float elapsedTime) {
|
||||
if(elapsedTime < TutorialJumpGuideDuration)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
elapsedTime -= TutorialJumpGuideDuration;
|
||||
if(elapsedTime < TutorialDoubleJumpGuideDuration)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
elapsedTime -= TutorialDoubleJumpGuideDuration;
|
||||
if(elapsedTime < TutorialSlideGuideDuration)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
elapsedTime -= TutorialSlideGuideDuration;
|
||||
if(elapsedTime < TutorialItemGuideDuration)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
private void ApplyTutorialGuideSequenceStep(int guideIndex) {
|
||||
switch(guideIndex)
|
||||
{
|
||||
case 0:
|
||||
SetTutorialGuide(TutorialGuideType.Jump, "점프", "왼쪽 클릭으로 가볍게 뛰어오릅니다.");
|
||||
break;
|
||||
case 1:
|
||||
SetTutorialGuide(TutorialGuideType.DoubleJump, "2단 점프", "공중에서 왼쪽 클릭을 한 번 더 누릅니다.");
|
||||
break;
|
||||
case 2:
|
||||
SetTutorialGuide(TutorialGuideType.Slide, "슬라이드", "오른쪽 클릭을 누르고 있으면 낮게 지나갑니다.");
|
||||
break;
|
||||
case 3:
|
||||
SetTutorialGuide(TutorialGuideType.Items, "아이템", "초밥 -> 라멘 -> 방어막 -> 운동화 -> 마일리지 카드 순서입니다.");
|
||||
break;
|
||||
default:
|
||||
tutorialGuideSequenceComplete = true;
|
||||
SetTutorialGuide(TutorialGuideType.None, "", "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 마일리지 토큰을 먹었을 때 호출되는 메서드
|
||||
public void AddMileage(int amount) {
|
||||
if(isGameover)
|
||||
@@ -114,14 +312,19 @@ public class GameManager : MonoBehaviour {
|
||||
return;
|
||||
}
|
||||
|
||||
stageMileage += amount;
|
||||
score += Mathf.Max(1, amount / 10);
|
||||
int finalAmount = IsMileageBonusActive()
|
||||
? amount * Mathf.Max(1, mileageBonusMultiplier)
|
||||
: amount;
|
||||
|
||||
stageMileage += finalAmount;
|
||||
score += Mathf.Max(1, finalAmount / 10);
|
||||
|
||||
if(saveMileageImmediately)
|
||||
{
|
||||
totalMileage += amount;
|
||||
totalMileage += finalAmount;
|
||||
PlayerPrefs.SetInt(TotalMileageKey, totalMileage);
|
||||
PlayerPrefs.Save();
|
||||
SyncTotalMileageToFlow();
|
||||
}
|
||||
|
||||
UpdateScoreUI();
|
||||
@@ -129,7 +332,7 @@ public class GameManager : MonoBehaviour {
|
||||
|
||||
// 플레이어가 장애물에 부딪혔을 때 목숨과 속도를 줄이는 메서드
|
||||
public bool TakeDamage(int damage = 1) {
|
||||
if(isGameover)
|
||||
if(isGameover || !GameFlowManager.IsGameplayActive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -145,18 +348,126 @@ public class GameManager : MonoBehaviour {
|
||||
return currentLife <= 0;
|
||||
}
|
||||
|
||||
public void ApplyTutorialItem(TutorialItemType itemType) {
|
||||
if(isGameover || !GameFlowManager.IsGameplayActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch(itemType)
|
||||
{
|
||||
case TutorialItemType.HealthSushi:
|
||||
currentLife = Mathf.Min(maxLife, currentLife + 1);
|
||||
AddScore(3);
|
||||
SetTutorialGuide(TutorialGuideType.Items, "초밥", "방금 잃은 목숨을 1 회복합니다.");
|
||||
break;
|
||||
case TutorialItemType.InvincibleRamen:
|
||||
itemInvincibleEndTime = Time.time + itemInvincibleDuration;
|
||||
SetTutorialGuide(TutorialGuideType.Items, "라멘", "짧은 시간 동안 다음 충돌 피해를 받지 않습니다.");
|
||||
break;
|
||||
case TutorialItemType.Shield:
|
||||
shieldCharges = Mathf.Max(shieldCharges, 1);
|
||||
SetTutorialGuide(TutorialGuideType.Items, "방어막", "다음 충돌 1회를 방어막이 대신 막습니다.");
|
||||
break;
|
||||
case TutorialItemType.SpeedShoes:
|
||||
gameSpeed = Mathf.Min(maxGameSpeed, gameSpeed + itemSpeedBoostAmount);
|
||||
SetTutorialGuide(TutorialGuideType.Items, "운동화", "속도를 즉시 회복해 늦어진 일정을 따라잡습니다.");
|
||||
break;
|
||||
case TutorialItemType.MileageCard:
|
||||
mileageBonusEndTime = Time.time + mileageBonusDuration;
|
||||
SetTutorialGuide(TutorialGuideType.Items, "마일리지 카드", "잠시 동안 먹는 마일리지가 두 배로 들어옵니다.");
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateScoreUI();
|
||||
}
|
||||
|
||||
public bool TryBlockDamageWithItem() {
|
||||
if(isGameover || !GameFlowManager.IsGameplayActive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(IsItemInvincibleActive())
|
||||
{
|
||||
SetTutorialGuide(TutorialGuideType.Items, "라멘", "무적 중이라 이번 충돌은 피해 없이 지나갑니다.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(shieldCharges > 0)
|
||||
{
|
||||
shieldCharges--;
|
||||
SetTutorialGuide(TutorialGuideType.Items, "방어막", "방어막이 충돌 피해를 한 번 막았습니다.");
|
||||
UpdateScoreUI();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsItemInvincibleActive() {
|
||||
return Time.time < itemInvincibleEndTime;
|
||||
}
|
||||
|
||||
private bool IsMileageBonusActive() {
|
||||
return Time.time < mileageBonusEndTime;
|
||||
}
|
||||
|
||||
// 플레이어 캐릭터가 사망시 게임 오버를 실행하는 메서드
|
||||
public void OnPlayerDead() {
|
||||
isGameover = true;
|
||||
gameoverUI.SetActive(true);
|
||||
|
||||
if(gameoverUI != null)
|
||||
{
|
||||
gameoverUI.SetActive(GameFlowManager.Instance == null);
|
||||
}
|
||||
|
||||
CompleteStage(false);
|
||||
}
|
||||
|
||||
private void CompleteStage(bool cleared) {
|
||||
if(stageCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
stageCompleted = true;
|
||||
isGameover = true;
|
||||
|
||||
if(GameFlowManager.Instance != null)
|
||||
{
|
||||
GameFlowManager.Instance.CompleteMap(cleared, score, stageMileage, stageElapsedTime, stageDistance);
|
||||
}
|
||||
else if(gameoverUI != null)
|
||||
{
|
||||
gameoverUI.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncTotalMileageToFlow() {
|
||||
if(GameFlowManager.Instance != null)
|
||||
{
|
||||
GameFlowManager.Instance.SetTotalMileage(totalMileage);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateScoreUI() {
|
||||
if(scoreText != null)
|
||||
{
|
||||
scoreText.text = "SCORE " + score
|
||||
string mapName = currentMapDefinition != null ? currentMapDefinition.displayName : "RUN";
|
||||
string distanceText = currentMapDefinition != null
|
||||
? Mathf.FloorToInt(stageDistance) + "/" + Mathf.FloorToInt(currentMapDefinition.targetDistance) + "m"
|
||||
: Mathf.FloorToInt(stageDistance) + "m";
|
||||
string timeText = currentMapDefinition != null && currentMapDefinition.timeLimit > 0f
|
||||
? Mathf.Max(0f, currentMapDefinition.timeLimit - stageElapsedTime).ToString("0") + "s"
|
||||
: stageElapsedTime.ToString("0") + "s";
|
||||
|
||||
scoreText.text = mapName
|
||||
+ "\nSCORE " + score
|
||||
+ "\nMILE " + stageMileage
|
||||
+ "\nTOTAL " + totalMileage
|
||||
+ "\nDIST " + distanceText
|
||||
+ "\nTIME " + timeText
|
||||
+ "\nSPD " + gameSpeed.ToString("0.0");
|
||||
}
|
||||
|
||||
@@ -171,16 +482,73 @@ public class GameManager : MonoBehaviour {
|
||||
|
||||
bool hasLife = i < currentLife;
|
||||
lifeIcons[i].color = hasLife
|
||||
? Color.white
|
||||
? GetActiveLifeIconColor(lifeIcons[i])
|
||||
: new Color(0.34f, 0.34f, 0.34f, 0.72f);
|
||||
}
|
||||
}
|
||||
|
||||
if(tutorialText != null)
|
||||
RefreshTutorialGuide();
|
||||
}
|
||||
|
||||
private void RefreshTutorialGuide() {
|
||||
if(tutorialGuideObject == null)
|
||||
{
|
||||
tutorialText.text = tutorialMessage;
|
||||
tutorialText.gameObject.SetActive(!string.IsNullOrEmpty(tutorialMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasGuide = tutorialGuideType != TutorialGuideType.None
|
||||
|| !string.IsNullOrEmpty(tutorialTitle)
|
||||
|| !string.IsNullOrEmpty(tutorialMessage);
|
||||
tutorialGuideObject.SetActive(hasGuide);
|
||||
|
||||
if(!hasGuide)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(tutorialTitleText != null)
|
||||
{
|
||||
tutorialTitleText.text = tutorialTitle;
|
||||
}
|
||||
|
||||
if(tutorialBodyText != null)
|
||||
{
|
||||
tutorialBodyText.text = tutorialMessage;
|
||||
}
|
||||
|
||||
bool showItemRow = tutorialGuideType == TutorialGuideType.Items;
|
||||
Sprite mainSprite = GetTutorialMainSprite(tutorialGuideType);
|
||||
|
||||
if(tutorialMainImage != null)
|
||||
{
|
||||
tutorialMainImage.gameObject.SetActive(!showItemRow && mainSprite != null);
|
||||
tutorialMainImage.sprite = mainSprite;
|
||||
}
|
||||
|
||||
if(tutorialMultiplierText != null)
|
||||
{
|
||||
tutorialMultiplierText.gameObject.SetActive(tutorialGuideType == TutorialGuideType.DoubleJump);
|
||||
}
|
||||
|
||||
if(tutorialItemImages != null)
|
||||
{
|
||||
for(int i = 0; i < tutorialItemImages.Length; i++)
|
||||
{
|
||||
if(tutorialItemImages[i] != null)
|
||||
{
|
||||
tutorialItemImages[i].gameObject.SetActive(showItemRow && tutorialItemImages[i].sprite != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Color GetActiveLifeIconColor(Image lifeIcon) {
|
||||
if(lifeIcon != null && lifeIcon.sprite == null)
|
||||
{
|
||||
return new Color(0.94f, 0.18f, 0.12f, 0.9f);
|
||||
}
|
||||
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
private void BuildRuntimeHUD() {
|
||||
@@ -193,7 +561,7 @@ public class GameManager : MonoBehaviour {
|
||||
|
||||
ConfigureRightInfoText();
|
||||
CreateStatusHUD(canvasTransform);
|
||||
CreateTutorialText(canvasTransform);
|
||||
CreateTutorialGuide(canvasTransform);
|
||||
}
|
||||
|
||||
private void ConfigureRightInfoText() {
|
||||
@@ -202,11 +570,11 @@ public class GameManager : MonoBehaviour {
|
||||
rectTransform.anchorMax = new Vector2(1f, 1f);
|
||||
rectTransform.pivot = new Vector2(1f, 1f);
|
||||
rectTransform.anchoredPosition = new Vector2(-12f, -9f);
|
||||
rectTransform.sizeDelta = new Vector2(132f, 62f);
|
||||
rectTransform.sizeDelta = new Vector2(176f, 96f);
|
||||
|
||||
scoreText.alignment = TextAlignmentOptions.TopRight;
|
||||
scoreText.fontSize = 9.5f;
|
||||
scoreText.lineSpacing = -10f;
|
||||
scoreText.fontSize = 8.5f;
|
||||
scoreText.lineSpacing = -13f;
|
||||
scoreText.color = new Color(0.94f, 0.97f, 1f, 0.92f);
|
||||
scoreText.raycastTarget = false;
|
||||
scoreText.textWrappingMode = TextWrappingModes.NoWrap;
|
||||
@@ -220,11 +588,11 @@ public class GameManager : MonoBehaviour {
|
||||
hudRect.anchorMin = new Vector2(0f, 1f);
|
||||
hudRect.anchorMax = new Vector2(0f, 1f);
|
||||
hudRect.pivot = new Vector2(0f, 1f);
|
||||
hudRect.anchoredPosition = new Vector2(10f, -8f);
|
||||
hudRect.sizeDelta = new Vector2(176f, 54f);
|
||||
hudRect.anchoredPosition = new Vector2(8f, -7f);
|
||||
hudRect.sizeDelta = new Vector2(108f, 34f);
|
||||
|
||||
Image hudBackground = hudObject.GetComponent<Image>();
|
||||
hudBackground.sprite = Resources.Load<Sprite>(PassportPanelResourcePath);
|
||||
hudBackground.sprite = LoadHudSprite(PassportPanelResourcePath);
|
||||
hudBackground.color = hudBackground.sprite != null
|
||||
? Color.white
|
||||
: new Color(0.04f, 0.08f, 0.11f, 0.68f);
|
||||
@@ -243,11 +611,11 @@ 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(9f, -10f);
|
||||
frameRect.sizeDelta = new Vector2(29f, 29f);
|
||||
frameRect.anchoredPosition = new Vector2(6f, -6f);
|
||||
frameRect.sizeDelta = new Vector2(20f, 20f);
|
||||
|
||||
Image frameImage = faceFrame.GetComponent<Image>();
|
||||
frameImage.color = new Color(0.84f, 0.87f, 0.80f, 0.58f);
|
||||
frameImage.color = new Color(0.12f, 0.16f, 0.17f, 0.36f);
|
||||
frameImage.raycastTarget = false;
|
||||
|
||||
GameObject faceObject = new GameObject("Character Face", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||||
@@ -260,13 +628,16 @@ public class GameManager : MonoBehaviour {
|
||||
faceRect.offsetMax = new Vector2(-2f, -2f);
|
||||
|
||||
Image faceImage = faceObject.GetComponent<Image>();
|
||||
faceImage.sprite = Resources.Load<Sprite>(CharacterFaceResourcePath);
|
||||
faceImage.sprite = LoadHudSprite(CharacterFaceResourcePath);
|
||||
faceImage.preserveAspect = true;
|
||||
faceImage.raycastTarget = false;
|
||||
faceImage.color = faceImage.sprite != null
|
||||
? Color.white
|
||||
: new Color(1f, 1f, 1f, 0f);
|
||||
}
|
||||
|
||||
private void CreateLifeIcons(Transform parent) {
|
||||
Sprite lifeSprite = Resources.Load<Sprite>(LifeStampResourcePath);
|
||||
Sprite lifeSprite = LoadHudSprite(LifeStampResourcePath);
|
||||
int iconCount = Mathf.Max(maxLife, 1);
|
||||
lifeIcons = new Image[iconCount];
|
||||
|
||||
@@ -279,12 +650,14 @@ 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(50f + (i * 21f), -10f);
|
||||
lifeRect.sizeDelta = new Vector2(18f, 18f);
|
||||
lifeRect.anchoredPosition = new Vector2(33f + (i * 13f), -7f);
|
||||
lifeRect.sizeDelta = new Vector2(10f, 10f);
|
||||
|
||||
Image lifeImage = lifeObject.GetComponent<Image>();
|
||||
lifeImage.sprite = lifeSprite;
|
||||
lifeImage.color = Color.white;
|
||||
lifeImage.color = lifeSprite != null
|
||||
? Color.white
|
||||
: new Color(0.94f, 0.18f, 0.12f, 0.9f);
|
||||
lifeImage.preserveAspect = true;
|
||||
lifeImage.raycastTarget = false;
|
||||
lifeIcons[i] = lifeImage;
|
||||
@@ -292,7 +665,7 @@ public class GameManager : MonoBehaviour {
|
||||
}
|
||||
|
||||
private void CreateItemSlots(Transform parent) {
|
||||
Sprite slotSprite = Resources.Load<Sprite>(PassportItemSlotResourcePath);
|
||||
Sprite slotSprite = LoadHudSprite(PassportItemSlotResourcePath);
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
@@ -303,8 +676,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(50f + (i * 21f), -31f);
|
||||
slotRect.sizeDelta = new Vector2(18f, 18f);
|
||||
slotRect.anchoredPosition = new Vector2(33f + (i * 13f), -20f);
|
||||
slotRect.sizeDelta = new Vector2(10f, 10f);
|
||||
|
||||
Image slotImage = slotObject.GetComponent<Image>();
|
||||
slotImage.sprite = slotSprite;
|
||||
@@ -316,25 +689,251 @@ public class GameManager : MonoBehaviour {
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTutorialText(Transform canvasTransform) {
|
||||
GameObject tutorialObject = new GameObject("Tutorial Text", typeof(RectTransform), typeof(CanvasRenderer));
|
||||
tutorialObject.transform.SetParent(canvasTransform, false);
|
||||
private Sprite LoadHudSprite(string resourcePath) {
|
||||
Sprite sprite = Resources.Load<Sprite>(resourcePath);
|
||||
if(sprite != null)
|
||||
{
|
||||
return sprite;
|
||||
}
|
||||
|
||||
RectTransform tutorialRect = tutorialObject.GetComponent<RectTransform>();
|
||||
tutorialRect.anchorMin = new Vector2(0.5f, 0f);
|
||||
tutorialRect.anchorMax = new Vector2(0.5f, 0f);
|
||||
tutorialRect.pivot = new Vector2(0.5f, 0f);
|
||||
tutorialRect.anchoredPosition = new Vector2(0f, 18f);
|
||||
tutorialRect.sizeDelta = new Vector2(360f, 24f);
|
||||
Texture2D texture = Resources.Load<Texture2D>(resourcePath);
|
||||
#if UNITY_EDITOR
|
||||
if(texture == null)
|
||||
{
|
||||
sprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Resources/" + resourcePath + ".png");
|
||||
if(sprite != null)
|
||||
{
|
||||
return sprite;
|
||||
}
|
||||
|
||||
tutorialText = tutorialObject.AddComponent<TextMeshProUGUI>();
|
||||
tutorialText.font = scoreText.font;
|
||||
tutorialText.alignment = TextAlignmentOptions.Center;
|
||||
tutorialText.fontSize = 9.5f;
|
||||
tutorialText.fontStyle = FontStyles.Bold;
|
||||
tutorialText.color = new Color(0.96f, 0.98f, 1f, 0.95f);
|
||||
tutorialText.raycastTarget = false;
|
||||
tutorialText.textWrappingMode = TextWrappingModes.NoWrap;
|
||||
tutorialText.gameObject.SetActive(false);
|
||||
texture = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Resources/" + resourcePath + ".png");
|
||||
}
|
||||
#endif
|
||||
if(texture == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Sprite.Create(
|
||||
texture,
|
||||
new Rect(0f, 0f, texture.width, texture.height),
|
||||
new Vector2(0.5f, 0.5f),
|
||||
100f);
|
||||
}
|
||||
|
||||
private void CreateTutorialGuide(Transform canvasTransform) {
|
||||
tutorialGuideObject = new GameObject("Tutorial Guide", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||||
tutorialGuideObject.transform.SetParent(canvasTransform, false);
|
||||
|
||||
RectTransform guideRect = tutorialGuideObject.GetComponent<RectTransform>();
|
||||
guideRect.anchorMin = new Vector2(0.5f, 0f);
|
||||
guideRect.anchorMax = new Vector2(0.5f, 0f);
|
||||
guideRect.pivot = new Vector2(0.5f, 0f);
|
||||
guideRect.anchoredPosition = new Vector2(18f, 12f);
|
||||
guideRect.sizeDelta = new Vector2(372f, 72f);
|
||||
|
||||
Image guideBackground = tutorialGuideObject.GetComponent<Image>();
|
||||
guideBackground.color = new Color(0.035f, 0.055f, 0.07f, 0.76f);
|
||||
guideBackground.raycastTarget = false;
|
||||
|
||||
tutorialFontAsset = CreateKoreanTutorialFontAsset();
|
||||
|
||||
CreateTutorialMainImage(tutorialGuideObject.transform);
|
||||
CreateTutorialItemRow(tutorialGuideObject.transform);
|
||||
tutorialTitleText = CreateTutorialText(tutorialGuideObject.transform, "Tutorial Title", new Vector2(70f, -9f), new Vector2(286f, 18f), 12f, FontStyles.Bold);
|
||||
tutorialBodyText = CreateTutorialText(tutorialGuideObject.transform, "Tutorial Body", new Vector2(70f, -28f), new Vector2(286f, 34f), 9f, FontStyles.Normal);
|
||||
tutorialGuideObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void CreateTutorialMainImage(Transform parent) {
|
||||
GameObject imageObject = new GameObject("Tutorial Main Image", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||||
imageObject.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform imageRect = imageObject.GetComponent<RectTransform>();
|
||||
imageRect.anchorMin = new Vector2(0f, 0.5f);
|
||||
imageRect.anchorMax = new Vector2(0f, 0.5f);
|
||||
imageRect.pivot = new Vector2(0f, 0.5f);
|
||||
imageRect.anchoredPosition = new Vector2(14f, 0f);
|
||||
imageRect.sizeDelta = new Vector2(48f, 48f);
|
||||
|
||||
tutorialMainImage = imageObject.GetComponent<Image>();
|
||||
tutorialMainImage.preserveAspect = true;
|
||||
tutorialMainImage.raycastTarget = false;
|
||||
|
||||
GameObject multiplierObject = new GameObject("Double Jump Marker", typeof(RectTransform), typeof(CanvasRenderer));
|
||||
multiplierObject.transform.SetParent(imageObject.transform, false);
|
||||
|
||||
RectTransform multiplierRect = multiplierObject.GetComponent<RectTransform>();
|
||||
multiplierRect.anchorMin = new Vector2(1f, 0f);
|
||||
multiplierRect.anchorMax = new Vector2(1f, 0f);
|
||||
multiplierRect.pivot = new Vector2(1f, 0f);
|
||||
multiplierRect.anchoredPosition = new Vector2(-2f, 1f);
|
||||
multiplierRect.sizeDelta = new Vector2(22f, 15f);
|
||||
|
||||
tutorialMultiplierText = multiplierObject.AddComponent<TextMeshProUGUI>();
|
||||
tutorialMultiplierText.font = scoreText.font;
|
||||
tutorialMultiplierText.text = "x2";
|
||||
tutorialMultiplierText.alignment = TextAlignmentOptions.Center;
|
||||
tutorialMultiplierText.fontSize = 9f;
|
||||
tutorialMultiplierText.fontStyle = FontStyles.Bold;
|
||||
tutorialMultiplierText.color = new Color(1f, 0.91f, 0.33f, 1f);
|
||||
tutorialMultiplierText.raycastTarget = false;
|
||||
tutorialMultiplierText.textWrappingMode = TextWrappingModes.NoWrap;
|
||||
tutorialMultiplierText.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
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"
|
||||
};
|
||||
|
||||
tutorialItemImages = new Image[itemAssetPaths.Length];
|
||||
|
||||
for(int i = 0; i < itemAssetPaths.Length; i++)
|
||||
{
|
||||
GameObject itemObject = new GameObject("Tutorial Item " + (i + 1), typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||||
itemObject.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform itemRect = itemObject.GetComponent<RectTransform>();
|
||||
itemRect.anchorMin = new Vector2(0f, 0.5f);
|
||||
itemRect.anchorMax = new Vector2(0f, 0.5f);
|
||||
itemRect.pivot = new Vector2(0f, 0.5f);
|
||||
itemRect.anchoredPosition = new Vector2(11f + (i * 27f), 0f);
|
||||
itemRect.sizeDelta = new Vector2(24f, 24f);
|
||||
|
||||
Image itemImage = itemObject.GetComponent<Image>();
|
||||
itemImage.sprite = LoadTutorialSprite(itemAssetPaths[i]);
|
||||
itemImage.preserveAspect = true;
|
||||
itemImage.raycastTarget = false;
|
||||
itemImage.gameObject.SetActive(false);
|
||||
tutorialItemImages[i] = itemImage;
|
||||
}
|
||||
}
|
||||
|
||||
private TextMeshProUGUI CreateTutorialText(Transform parent, string objectName, Vector2 anchoredPosition, Vector2 sizeDelta, float fontSize, FontStyles fontStyle) {
|
||||
GameObject textObject = new GameObject(objectName, typeof(RectTransform), typeof(CanvasRenderer));
|
||||
textObject.transform.SetParent(parent, false);
|
||||
|
||||
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 = anchoredPosition;
|
||||
textRect.sizeDelta = sizeDelta;
|
||||
|
||||
TextMeshProUGUI text = textObject.AddComponent<TextMeshProUGUI>();
|
||||
text.font = tutorialFontAsset != null ? tutorialFontAsset : scoreText.font;
|
||||
text.alignment = TextAlignmentOptions.Left;
|
||||
text.fontSize = fontSize;
|
||||
text.fontStyle = fontStyle;
|
||||
text.color = new Color(0.96f, 0.98f, 1f, 0.96f);
|
||||
text.raycastTarget = false;
|
||||
text.textWrappingMode = TextWrappingModes.Normal;
|
||||
return text;
|
||||
}
|
||||
|
||||
private TMP_FontAsset CreateKoreanTutorialFontAsset() {
|
||||
string[] familyNames = {
|
||||
"Malgun Gothic",
|
||||
"맑은 고딕",
|
||||
"Arial Unicode MS"
|
||||
};
|
||||
|
||||
string[] styleNames = {
|
||||
"Regular",
|
||||
"Normal"
|
||||
};
|
||||
|
||||
for(int familyIndex = 0; familyIndex < familyNames.Length; familyIndex++)
|
||||
{
|
||||
for(int styleIndex = 0; styleIndex < styleNames.Length; styleIndex++)
|
||||
{
|
||||
TMP_FontAsset fontAsset = TMP_FontAsset.CreateFontAsset(familyNames[familyIndex], styleNames[styleIndex], 80);
|
||||
if(fontAsset != null)
|
||||
{
|
||||
fontAsset.name = "Runtime Korean Tutorial Font";
|
||||
return fontAsset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Sprite GetTutorialMainSprite(TutorialGuideType guideType) {
|
||||
if(guideType == TutorialGuideType.Jump || guideType == TutorialGuideType.DoubleJump)
|
||||
{
|
||||
return LoadTutorialSprite("Assets/Sprites/JJ_Jump.png", "JJ_Jump_2");
|
||||
}
|
||||
|
||||
if(guideType == TutorialGuideType.Slide)
|
||||
{
|
||||
return LoadTutorialSprite("Assets/Sprites/JJ_Slide.png", "JJ_Slide_0");
|
||||
}
|
||||
|
||||
if(guideType == TutorialGuideType.Damage)
|
||||
{
|
||||
return LoadTutorialSprite("Assets/Resources/UI_LifeStamp.png");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Sprite LoadTutorialSprite(string assetPath, string spriteName = "") {
|
||||
#if UNITY_EDITOR
|
||||
Object[] assets = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath);
|
||||
Sprite firstSprite = null;
|
||||
|
||||
if(assets != null && assets.Length > 0)
|
||||
{
|
||||
for(int i = 0; i < assets.Length; i++)
|
||||
{
|
||||
Sprite sprite = assets[i] as Sprite;
|
||||
if(sprite == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(firstSprite == null)
|
||||
{
|
||||
firstSprite = sprite;
|
||||
}
|
||||
|
||||
if(sprite.name == spriteName)
|
||||
{
|
||||
return sprite;
|
||||
}
|
||||
}
|
||||
|
||||
if(firstSprite != null)
|
||||
{
|
||||
return firstSprite;
|
||||
}
|
||||
}
|
||||
|
||||
Sprite directSprite = AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);
|
||||
if(directSprite != null)
|
||||
{
|
||||
return directSprite;
|
||||
}
|
||||
|
||||
Texture2D 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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user