using TMPro; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif public enum FinishGateStyle { None, AirportDeparture, JapanTorii, ChinaPaifang, AmericaLiberty } public class FinishGate : MonoBehaviour { private const string ForegroundSortingLayer = "Foreground"; private const float DespawnX = -18f; private const float RuntimeSpritePixelsPerUnit = 256f; private static Sprite blockSprite; private bool cleared = false; public void Setup(FinishGateStyle style) { cleared = false; ScrollingObject scrollingObject = GetComponent(); if(scrollingObject == null) { scrollingObject = gameObject.AddComponent(); } scrollingObject.speed = 10f; BoxCollider2D trigger = GetComponent(); if(trigger == null) { trigger = gameObject.AddComponent(); } ConfigureTrigger(trigger); BuildGate(style); } private void Update() { if(transform.position.x < DespawnX) { Destroy(gameObject); } } private void OnTriggerEnter2D(Collider2D other) { if(cleared || !other.CompareTag("Player") || !GameFlowManager.IsGameplayActive) { return; } cleared = true; if(GameManager.instance != null) { GameManager.instance.CompleteCurrentStageFromFinishGate(); } } private void BuildGate(FinishGateStyle style) { ClearChildren(); if(TryBuildSpriteGate(style)) { return; } switch(style) { case FinishGateStyle.JapanTorii: BuildJapanTorii(); break; case FinishGateStyle.ChinaPaifang: BuildChinaPaifang(); break; case FinishGateStyle.AmericaLiberty: BuildAmericaLibertyFallback(); break; case FinishGateStyle.AirportDeparture: default: BuildAirportDepartureGate(); break; } } private void ConfigureTrigger(BoxCollider2D trigger) { trigger.isTrigger = true; trigger.offset = new Vector2(0f, 1.75f); trigger.size = new Vector2(2.2f, 4.2f); } private bool TryBuildSpriteGate(FinishGateStyle style) { string resourcePath = GetSpriteResourcePath(style); if(string.IsNullOrEmpty(resourcePath)) { return false; } Sprite sprite = LoadGateSprite(resourcePath); if(sprite == null) { return false; } GameObject spriteObject = new GameObject("Finish Gate Sprite"); spriteObject.transform.SetParent(transform, false); SpriteRenderer spriteRenderer = spriteObject.AddComponent(); spriteRenderer.sprite = sprite; spriteRenderer.sortingLayerName = ForegroundSortingLayer; spriteRenderer.sortingOrder = 6; float targetHeight = GetSpriteGateHeight(style); float spriteHeight = Mathf.Max(0.01f, sprite.bounds.size.y); float scale = targetHeight / spriteHeight; spriteObject.transform.localScale = new Vector3(scale, scale, 1f); spriteObject.transform.localPosition = new Vector3(0f, targetHeight * 0.5f - GetSpriteGateBottomInset(style), 0f); return true; } private Sprite LoadGateSprite(string resourcePath) { Sprite sprite = Resources.Load(resourcePath); if(sprite != null) { return sprite; } Sprite[] sprites = Resources.LoadAll(resourcePath); if(sprites != null && sprites.Length > 0 && sprites[0] != null) { return sprites[0]; } Texture2D texture = Resources.Load(resourcePath); if(texture != null) { return CreateRuntimeSprite(texture); } #if UNITY_EDITOR string assetPath = "Assets/Resources/" + resourcePath + ".png"; sprite = AssetDatabase.LoadAssetAtPath(assetPath); if(sprite != null) { return sprite; } texture = AssetDatabase.LoadAssetAtPath(assetPath); if(texture != null) { return CreateRuntimeSprite(texture); } #endif return null; } private Sprite CreateRuntimeSprite(Texture2D texture) { return Sprite.Create( texture, new Rect(0f, 0f, texture.width, texture.height), new Vector2(0.5f, 0.5f), RuntimeSpritePixelsPerUnit); } private string GetSpriteResourcePath(FinishGateStyle style) { switch(style) { case FinishGateStyle.AirportDeparture: return "FinishGate_Tutorial_Incheon"; case FinishGateStyle.JapanTorii: return "FinishGate_Japan_Torii"; case FinishGateStyle.ChinaPaifang: return "FinishGate_China_Paifang"; case FinishGateStyle.AmericaLiberty: return "FinishGate_America_Liberty"; default: return ""; } } private float GetSpriteGateHeight(FinishGateStyle style) { switch(style) { case FinishGateStyle.ChinaPaifang: return 4.65f; case FinishGateStyle.AmericaLiberty: return 4.45f; default: return 4.35f; } } private float GetSpriteGateBottomInset(FinishGateStyle style) { switch(style) { case FinishGateStyle.JapanTorii: return 0.36f; default: return 0f; } } private void BuildAirportDepartureGate() { Color navy = new Color(0.05f, 0.2f, 0.34f, 1f); Color blue = new Color(0.08f, 0.42f, 0.67f, 1f); Color cyan = new Color(0.18f, 0.83f, 0.95f, 1f); Color yellow = new Color(1f, 0.78f, 0.22f, 1f); Color glass = new Color(0.6f, 0.9f, 1f, 0.55f); CreateBlock("Airport Left Pillar", new Vector2(-1.15f, 1.45f), new Vector2(0.22f, 3.1f), navy, 3); CreateBlock("Airport Right Pillar", new Vector2(1.15f, 1.45f), new Vector2(0.22f, 3.1f), navy, 3); CreateBlock("Airport Top Sign", new Vector2(0f, 3.05f), new Vector2(2.75f, 0.64f), blue, 4); CreateBlock("Airport Sign Highlight", new Vector2(0f, 3.35f), new Vector2(2.75f, 0.08f), cyan, 5); CreateBlock("Airport Gate Floor", new Vector2(0f, -0.04f), new Vector2(2.9f, 0.12f), yellow, 4); CreateBlock("Airport Glass Left", new Vector2(-0.62f, 1.35f), new Vector2(0.16f, 2.2f), glass, 2); CreateBlock("Airport Glass Right", new Vector2(0.62f, 1.35f), new Vector2(0.16f, 2.2f), glass, 2); CreateBlock("Airport Clear Light", new Vector2(0f, 2.55f), new Vector2(0.26f, 0.14f), yellow, 5); CreateWorldText("DEPARTURE", new Vector2(0f, 3.07f), 0.34f, Color.white, 6); } private void BuildJapanTorii() { Color red = new Color(0.86f, 0.08f, 0.05f, 1f); Color darkRed = new Color(0.48f, 0.02f, 0.02f, 1f); Color black = new Color(0.08f, 0.04f, 0.03f, 1f); Color gold = new Color(1f, 0.76f, 0.28f, 1f); CreateBlock("Torii Left Pillar", new Vector2(-1.18f, 1.35f), new Vector2(0.28f, 2.9f), red, 4); CreateBlock("Torii Right Pillar", new Vector2(1.18f, 1.35f), new Vector2(0.28f, 2.9f), red, 4); CreateBlock("Torii Left Base", new Vector2(-1.18f, -0.14f), new Vector2(0.48f, 0.22f), black, 3); CreateBlock("Torii Right Base", new Vector2(1.18f, -0.14f), new Vector2(0.48f, 0.22f), black, 3); CreateBlock("Torii Lower Beam", new Vector2(0f, 2.55f), new Vector2(2.75f, 0.24f), darkRed, 5); CreateBlock("Torii Main Beam", new Vector2(0f, 3.05f), new Vector2(3.35f, 0.32f), red, 6); CreateBlock("Torii Top Cap", new Vector2(0f, 3.31f), new Vector2(3.75f, 0.16f), black, 7); CreateBlock("Torii Center Plaque", new Vector2(0f, 2.82f), new Vector2(0.36f, 0.5f), gold, 7); } private void BuildChinaPaifang() { Color red = new Color(0.74f, 0.05f, 0.04f, 1f); Color gold = new Color(1f, 0.72f, 0.18f, 1f); Color roof = new Color(0.12f, 0.3f, 0.22f, 1f); Color dark = new Color(0.24f, 0.04f, 0.03f, 1f); CreateBlock("Paifang Left Pillar", new Vector2(-1.25f, 1.28f), new Vector2(0.28f, 2.8f), red, 4); CreateBlock("Paifang Right Pillar", new Vector2(1.25f, 1.28f), new Vector2(0.28f, 2.8f), red, 4); CreateBlock("Paifang Center Pillar Left", new Vector2(-0.42f, 1.48f), new Vector2(0.18f, 2.2f), dark, 3); CreateBlock("Paifang Center Pillar Right", new Vector2(0.42f, 1.48f), new Vector2(0.18f, 2.2f), dark, 3); CreateBlock("Paifang Sign", new Vector2(0f, 2.65f), new Vector2(2.45f, 0.46f), gold, 5); CreateBlock("Paifang Roof", new Vector2(0f, 3.18f), new Vector2(3.25f, 0.26f), roof, 6); CreateBlock("Paifang Roof Cap", new Vector2(0f, 3.42f), new Vector2(2.35f, 0.2f), roof, 7); CreateBlock("Paifang Lantern Left", new Vector2(-0.78f, 2.18f), new Vector2(0.26f, 0.34f), red, 7); CreateBlock("Paifang Lantern Right", new Vector2(0.78f, 2.18f), new Vector2(0.26f, 0.34f), red, 7); } private void BuildAmericaLibertyFallback() { Color metal = new Color(0.48f, 0.56f, 0.58f, 1f); Color green = new Color(0.03f, 0.38f, 0.22f, 1f); Color yellow = new Color(1f, 0.8f, 0.18f, 1f); Color white = Color.white; CreateBlock("Road Sign Left Pole", new Vector2(-1.3f, 1.35f), new Vector2(0.18f, 3.0f), metal, 3); CreateBlock("Road Sign Right Pole", new Vector2(1.3f, 1.35f), new Vector2(0.18f, 3.0f), metal, 3); CreateBlock("Road Sign Panel", new Vector2(0f, 2.65f), new Vector2(3.05f, 0.9f), green, 5); CreateBlock("Road Sign Stripe", new Vector2(0f, 2.25f), new Vector2(2.75f, 0.08f), white, 6); CreateBlock("Road Sign Arrow", new Vector2(0f, 2.72f), new Vector2(0.5f, 0.16f), yellow, 7); CreateWorldText("ARRIVAL", new Vector2(0f, 2.88f), 0.36f, white, 8); } private SpriteRenderer CreateBlock(string objectName, Vector2 localPosition, Vector2 size, Color color, int sortingOrder) { GameObject block = new GameObject(objectName); block.transform.SetParent(transform, false); block.transform.localPosition = new Vector3(localPosition.x, localPosition.y, 0f); block.transform.localScale = new Vector3(size.x, size.y, 1f); SpriteRenderer renderer = block.AddComponent(); renderer.sprite = GetBlockSprite(); renderer.color = color; renderer.sortingLayerName = ForegroundSortingLayer; renderer.sortingOrder = sortingOrder; return renderer; } private TextMeshPro CreateWorldText(string text, Vector2 localPosition, float fontSize, Color color, int sortingOrder) { GameObject textObject = new GameObject("Gate Text " + text); textObject.transform.SetParent(transform, false); textObject.transform.localPosition = new Vector3(localPosition.x, localPosition.y, -0.05f); TextMeshPro textMesh = textObject.AddComponent(); textMesh.text = text; textMesh.fontSize = fontSize; textMesh.alignment = TextAlignmentOptions.Center; textMesh.color = color; textMesh.textWrappingMode = TextWrappingModes.NoWrap; textMesh.rectTransform.sizeDelta = new Vector2(3f, 0.55f); textMesh.renderer.sortingLayerName = ForegroundSortingLayer; textMesh.renderer.sortingOrder = sortingOrder; return textMesh; } private static Sprite GetBlockSprite() { if(blockSprite != null) { return blockSprite; } Texture2D texture = new Texture2D(1, 1); texture.SetPixel(0, 0, Color.white); texture.Apply(); texture.wrapMode = TextureWrapMode.Clamp; texture.filterMode = FilterMode.Point; blockSprite = Sprite.Create(texture, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f); blockSprite.name = "Runtime Finish Gate Block"; return blockSprite; } private void ClearChildren() { for(int i = transform.childCount - 1; i >= 0; i--) { Destroy(transform.GetChild(i).gameObject); } } }