Files
Travel_Run/Assets/Scripts/Platform.cs
T
2026-07-09 02:50:48 +09:00

403 lines
13 KiB
C#

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
// 발판으로서 필요한 동작을 담은 스크립트
public class Platform : MonoBehaviour {
public enum PlatformPattern {
Random,
Empty,
LowLeft,
LowMid,
LowRight,
Slide,
ForceHit
}
public enum MileagePattern {
Random,
None,
SafeLine,
JumpArc,
SlideLine
}
public enum ItemPattern {
None,
HealthSushi,
InvincibleRamen,
EnergyDrink
}
public enum PlatformSkin {
Airport,
Japan,
China,
USA
}
public Sprite mileageSprite; // 이 발판에서 사용할 마일리지 토큰 스프라이트
public int mileagePatternChance = 50; // 마일리지 패턴 등장 확률
public int smallMileageValue = 10; // 일반 토큰 가치
public int bonusMileageValue = 30; // 어려운 루트 보너스 토큰 가치
private bool stepped = false; // 플레이어 캐릭터가 밟았었는가
private MileagePickup[] mileagePickups; // 런타임에 생성하는 마일리지 토큰 풀
private ItemPickup[] itemPickups; // 런타임에 생성하는 튜토리얼 아이템 풀
private MileagePattern reservedMileagePattern = MileagePattern.Random; // 다음 활성화 때 사용할 마일리지 패턴
private ItemPattern reservedItemPattern = ItemPattern.None; // 다음 활성화 때 사용할 아이템 배치
private PlatformSkin reservedSkin = PlatformSkin.Airport; // 현재 맵에서 사용할 발판/장애물 스킨
private const int MileagePoolSize = 5;
private const int ItemPoolSize = 1;
private const float MileageTokenScale = 0.55f;
private const float MileageColliderRadius = 0.4f;
private const float ItemTokenScale = 1.36f;
private const float ItemColliderRadius = 0.46f;
private const float JumpMileageMinWidthScale = 1.8f;
private const float JumpMileageMaxWidthScale = 2.5f;
public void ConfigureForTutorial(MileagePattern mileagePattern, ItemPattern itemPattern = ItemPattern.None, PlatformSkin platformSkin = PlatformSkin.Airport) {
reservedMileagePattern = mileagePattern;
reservedItemPattern = itemPattern;
reservedSkin = platformSkin;
}
public void ConfigureForRandom(PlatformSkin platformSkin = PlatformSkin.Airport) {
reservedMileagePattern = MileagePattern.Random;
reservedItemPattern = ItemPattern.None;
reservedSkin = platformSkin;
}
// 컴포넌트가 활성화될때 마다 매번 실행되는 메서드
private void OnEnable() {
// 발판을 리셋하는 처리
stepped = false;
ApplyVisualSkin();
EnsureMileagePickups();
EnsureItemPickups();
HideMileagePickups();
HideItemPickups();
PlaceReservedMileagePattern();
PlaceReservedItemPattern();
}
void OnCollisionEnter2D(Collision2D collision) {
// 플레이어 캐릭터가 자신을 밟았을때 점수를 추가하는 처리
if(collision.collider.tag == "Player" && !stepped && GameFlowManager.IsGameplayActive)
{
stepped = true;
GameManager.instance.AddScore(1);
}
}
private void EnsureMileagePickups() {
if(mileagePickups != null && mileagePickups.Length == MileagePoolSize)
{
return;
}
mileagePickups = new MileagePickup[MileagePoolSize];
for(int i = 0; i < MileagePoolSize; i++)
{
GameObject pickupObject = new GameObject("Mileage Pickup " + (i + 1));
pickupObject.transform.SetParent(transform);
pickupObject.transform.localScale = GetParentScaleCompensatedVector(MileageTokenScale);
SpriteRenderer spriteRenderer = pickupObject.AddComponent<SpriteRenderer>();
spriteRenderer.sprite = mileageSprite;
spriteRenderer.sortingLayerName = "Foreground";
spriteRenderer.sortingOrder = 1;
CircleCollider2D pickupCollider = pickupObject.AddComponent<CircleCollider2D>();
pickupCollider.isTrigger = true;
pickupCollider.radius = MileageColliderRadius;
mileagePickups[i] = pickupObject.AddComponent<MileagePickup>();
mileagePickups[i].Setup(mileageSprite, smallMileageValue);
pickupObject.SetActive(false);
}
}
private void EnsureItemPickups() {
if(itemPickups != null && itemPickups.Length == ItemPoolSize)
{
return;
}
itemPickups = new ItemPickup[ItemPoolSize];
for(int i = 0; i < ItemPoolSize; i++)
{
GameObject pickupObject = new GameObject("Tutorial Item Pickup " + (i + 1));
pickupObject.transform.SetParent(transform);
pickupObject.transform.localScale = GetParentScaleCompensatedVector(ItemTokenScale);
SpriteRenderer spriteRenderer = pickupObject.AddComponent<SpriteRenderer>();
spriteRenderer.sortingLayerName = "Foreground";
spriteRenderer.sortingOrder = 2;
CircleCollider2D pickupCollider = pickupObject.AddComponent<CircleCollider2D>();
pickupCollider.isTrigger = true;
pickupCollider.radius = ItemColliderRadius;
itemPickups[i] = pickupObject.AddComponent<ItemPickup>();
pickupObject.SetActive(false);
}
}
private void HideMileagePickups() {
if(mileagePickups == null)
{
return;
}
for(int i = 0; i < mileagePickups.Length; i++)
{
if(mileagePickups[i] != null)
{
mileagePickups[i].gameObject.SetActive(false);
}
}
}
private void HideItemPickups() {
if(itemPickups == null)
{
return;
}
for(int i = 0; i < itemPickups.Length; i++)
{
if(itemPickups[i] != null)
{
itemPickups[i].gameObject.SetActive(false);
}
}
}
private void TryPlaceMileagePattern() {
if(mileageSprite == null || mileagePickups == null || Random.Range(0, 100) >= mileagePatternChance)
{
return;
}
PlaceSafeMileageLine();
}
private void PlaceReservedMileagePattern() {
if(mileageSprite == null || mileagePickups == null || reservedMileagePattern == MileagePattern.None)
{
return;
}
if(reservedMileagePattern == MileagePattern.SafeLine)
{
PlaceSafeMileageLine();
return;
}
if(reservedMileagePattern == MileagePattern.JumpArc)
{
PlaceJumpMileageArc(0f);
return;
}
if(reservedMileagePattern == MileagePattern.SlideLine)
{
PlaceSlideMileageLine();
return;
}
TryPlaceMileagePattern();
}
private void PlaceReservedItemPattern() {
if(reservedItemPattern == ItemPattern.None || itemPickups == null || itemPickups.Length == 0)
{
return;
}
GameManager.TutorialItemType itemType = GameplayItemCatalog.ToTutorialItemType(reservedItemPattern);
ItemPickup pickup = itemPickups[0];
pickup.Setup(LoadItemSprite(itemType), itemType);
pickup.transform.localPosition = GetParentScaleCompensatedPosition(GetItemPosition(reservedItemPattern));
pickup.ResetPickup();
}
private Vector2 GetItemPosition(ItemPattern itemPattern) {
const float frontX = -1.45f;
switch(itemPattern)
{
case ItemPattern.HealthSushi:
return new Vector2(frontX, 1.55f);
case ItemPattern.InvincibleRamen:
return new Vector2(frontX, 1.55f);
case ItemPattern.EnergyDrink:
return new Vector2(frontX, 1.55f);
default:
return new Vector2(0f, 1.55f);
}
}
private void ApplyVisualSkin() {
#if UNITY_EDITOR
Sprite platformSprite = LoadSpriteAtPath(GetPlatformSkinPath(reservedSkin));
Sprite mileageSkinSprite = LoadSpriteAtPath(GetMileageSkinPath(reservedSkin));
ApplySprite(GetComponent<SpriteRenderer>(), platformSprite);
if(mileageSkinSprite != null)
{
mileageSprite = mileageSkinSprite;
}
#endif
}
#if UNITY_EDITOR
private void ApplySprite(SpriteRenderer spriteRenderer, Sprite sprite) {
if(spriteRenderer != null && sprite != null)
{
spriteRenderer.sprite = sprite;
}
}
private Sprite LoadSpriteAtPath(string assetPath) {
if(string.IsNullOrEmpty(assetPath))
{
return null;
}
return AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);
}
private string GetPlatformSkinPath(PlatformSkin platformSkin) {
switch(platformSkin)
{
case PlatformSkin.Japan:
return "Assets/Sprites/ObstacleSkins/Skin_Platform_Japan.png";
case PlatformSkin.China:
return "Assets/Sprites/ObstacleSkins/Skin_Platform_China.png";
case PlatformSkin.USA:
return "Assets/Sprites/ObstacleSkins/Skin_Platform_USA.png";
case PlatformSkin.Airport:
default:
return "Assets/Sprites/ObstacleSkins/Skin_Platform_AirportFloorTile.png";
}
}
private string GetMileageSkinPath(PlatformSkin platformSkin) {
switch(platformSkin)
{
case PlatformSkin.Japan:
return "Assets/Sprites/Mileage/Mileage_Japan.png";
case PlatformSkin.China:
return "Assets/Sprites/Mileage/Mileage_China.png";
case PlatformSkin.USA:
return "Assets/Sprites/Mileage/Mileage_USA.png";
case PlatformSkin.Airport:
default:
return "Assets/Sprites/Mileage/Mileage_Korea.png";
}
}
#endif
private Sprite LoadItemSprite(GameManager.TutorialItemType itemType) {
#if UNITY_EDITOR
string assetPath = GameplayItemCatalog.GetSpriteAssetPath(itemType, reservedSkin);
return AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);
#else
return null;
#endif
}
private void PlaceSafeMileageLine() {
Vector2[] positions = {
new Vector2(-2f, 1.35f),
new Vector2(-1f, 1.35f),
new Vector2(0f, 1.35f),
new Vector2(1f, 1.35f),
new Vector2(2f, 1.35f)
};
PlaceMileageTokens(positions, smallMileageValue);
}
private void PlaceJumpMileageArc(float obstacleX) {
float widthScale = GetJumpMileageArcWidthScale();
Vector2[] positions = {
new Vector2(obstacleX - 2.25f * widthScale, 1.88f),
new Vector2(obstacleX - 1.15f * widthScale, 2.88f),
new Vector2(obstacleX, 3.58f),
new Vector2(obstacleX + 1.15f * widthScale, 2.88f),
new Vector2(obstacleX + 2.25f * widthScale, 1.88f)
};
PlaceMileageTokens(positions, smallMileageValue);
SetPickupValue(2, bonusMileageValue);
}
private float GetJumpMileageArcWidthScale() {
if(GameManager.instance == null)
{
return JumpMileageMinWidthScale;
}
float minSpeed = Mathf.Max(0.1f, GameManager.instance.minGameSpeed);
float maxSpeed = Mathf.Max(minSpeed + 0.1f, GameManager.instance.maxGameSpeed);
float speedProgress = Mathf.InverseLerp(minSpeed, maxSpeed, GameManager.instance.gameSpeed);
return Mathf.Lerp(JumpMileageMinWidthScale, JumpMileageMaxWidthScale, speedProgress);
}
private void PlaceSlideMileageLine() {
Vector2[] positions = {
new Vector2(-1.5f, 1.05f),
new Vector2(-0.5f, 1.05f),
new Vector2(0.5f, 1.05f),
new Vector2(1.5f, 1.05f)
};
PlaceMileageTokens(positions, smallMileageValue);
SetPickupValue(2, bonusMileageValue);
}
private void PlaceMileageTokens(Vector2[] positions, int value) {
int count = Mathf.Min(positions.Length, mileagePickups.Length);
for(int i = 0; i < count; i++)
{
mileagePickups[i].Setup(mileageSprite, value);
mileagePickups[i].transform.localPosition = GetParentScaleCompensatedPosition(positions[i]);
mileagePickups[i].ResetPickup();
}
}
private Vector3 GetParentScaleCompensatedVector(float scale) {
float parentScaleX = Mathf.Approximately(transform.localScale.x, 0f) ? 1f : transform.localScale.x;
float parentScaleY = Mathf.Approximately(transform.localScale.y, 0f) ? 1f : transform.localScale.y;
return new Vector3(scale / parentScaleX, scale / parentScaleY, 1f);
}
private Vector3 GetParentScaleCompensatedPosition(Vector2 position) {
float parentScaleX = Mathf.Approximately(transform.localScale.x, 0f) ? 1f : transform.localScale.x;
float parentScaleY = Mathf.Approximately(transform.localScale.y, 0f) ? 1f : transform.localScale.y;
return new Vector3(position.x / parentScaleX, position.y / parentScaleY, 0f);
}
private void SetPickupValue(int index, int value) {
if(index < 0 || index >= mileagePickups.Length || !mileagePickups[index].gameObject.activeSelf)
{
return;
}
mileagePickups[index].Setup(mileageSprite, value);
}
}