using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; public class Game : MonoBehaviour { // Auto-bootstrap when game starts - no Editor setup needed [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] static void Bootstrap() { if (Instance != null) return; var gameGO = new GameObject("Game"); gameGO.AddComponent(); } // Constants - tune later public const float UnitSpeed = 5f; public const float VisionRadius = 4f; public const float TagRadius = 0.75f; public const float RespawnDelay = 3f; public const float FlagReturnDelay = 5f; public const int WinScore = 3; // Map dimensions const float MapWidth = 40f; const float MapHeight = 30f; // References set during setup public static Game Instance { get; private set; } public List playerUnits = new(); public List enemyUnits = new(); public Transform playerBase; public Transform enemyBase; public Flag playerFlag; public Flag enemyFlag; int playerScore; int enemyScore; bool gameOver; TextMeshProUGUI scoreText; TextMeshProUGUI gameOverText; void Awake() { Instance = this; } void Start() { SetupCamera(); CreateGround(); CreateObstacles(); CreateBases(); CreateFlags(); SpawnUnits(); CreateUI(); SetupSystems(); } void SetupCamera() { var cam = Camera.main; if (cam != null) { cam.orthographic = true; cam.orthographicSize = MapHeight / 2f + 2f; cam.transform.position = new Vector3(0, 0, -10); } } void CreateGround() { var ground = CreateSprite("Ground", new Color(0.2f, 0.4f, 0.2f), MapWidth, MapHeight); ground.transform.position = Vector3.zero; var sr = ground.GetComponent(); sr.sortingOrder = -10; } void CreateObstacles() { // Houses as obstacles - asymmetric backyard layout Vector2[] housePositions = { new(-12f, 8f), new(-8f, -5f), new(0f, 3f), new(5f, -8f), new(10f, 6f), new(14f, -3f), }; Vector2[] houseSizes = { new(4f, 3f), new(3f, 4f), new(5f, 2.5f), new(3f, 3f), new(4f, 4f), new(3.5f, 3f), }; for (int i = 0; i < housePositions.Length; i++) { var house = CreateSprite($"House_{i}", new Color(0.4f, 0.4f, 0.4f), houseSizes[i].x, houseSizes[i].y); house.transform.position = new Vector3(housePositions[i].x, housePositions[i].y, 0); var collider = house.AddComponent(); // Size is (1,1) in local space - scale handles actual size collider.size = Vector2.one; var sr = house.GetComponent(); sr.sortingOrder = -5; } } void CreateBases() { // Player base - bottom left var playerBaseGO = CreateSprite("PlayerBase", new Color(0.2f, 0.3f, 0.8f, 0.5f), 6f, 6f); playerBaseGO.transform.position = new Vector3(-MapWidth / 2f + 5f, -MapHeight / 2f + 5f, 0); playerBase = playerBaseGO.transform; var sr1 = playerBaseGO.GetComponent(); sr1.sortingOrder = -8; var playerBaseTrigger = playerBaseGO.AddComponent(); playerBaseTrigger.isTrigger = true; playerBaseTrigger.size = Vector2.one; // Local space - scale handles actual size var playerBaseZone = playerBaseGO.AddComponent(); playerBaseZone.team = Unit.Team.Player; // Enemy base - top right var enemyBaseGO = CreateSprite("EnemyBase", new Color(0.8f, 0.2f, 0.2f, 0.5f), 6f, 6f); enemyBaseGO.transform.position = new Vector3(MapWidth / 2f - 5f, MapHeight / 2f - 5f, 0); enemyBase = enemyBaseGO.transform; var sr2 = enemyBaseGO.GetComponent(); sr2.sortingOrder = -8; var enemyBaseTrigger = enemyBaseGO.AddComponent(); enemyBaseTrigger.isTrigger = true; enemyBaseTrigger.size = Vector2.one; // Local space - scale handles actual size var enemyBaseZone = enemyBaseGO.AddComponent(); enemyBaseZone.team = Unit.Team.Enemy; } void CreateFlags() { // Player flag at player base var playerFlagGO = CreateSprite("PlayerFlag", new Color(0.3f, 0.5f, 1f), 1f, 1.5f); playerFlagGO.transform.position = playerBase.position; playerFlag = playerFlagGO.AddComponent(); playerFlag.team = Unit.Team.Player; playerFlag.homePosition = playerBase; var flagCollider1 = playerFlagGO.AddComponent(); flagCollider1.isTrigger = true; flagCollider1.radius = 0.75f; var rb1 = playerFlagGO.AddComponent(); rb1.bodyType = RigidbodyType2D.Kinematic; var sr1 = playerFlagGO.GetComponent(); sr1.sortingOrder = 5; // Enemy flag at enemy base var enemyFlagGO = CreateSprite("EnemyFlag", new Color(1f, 0.3f, 0.3f), 1f, 1.5f); enemyFlagGO.transform.position = enemyBase.position; enemyFlag = enemyFlagGO.AddComponent(); enemyFlag.team = Unit.Team.Enemy; enemyFlag.homePosition = enemyBase; var flagCollider2 = enemyFlagGO.AddComponent(); flagCollider2.isTrigger = true; flagCollider2.radius = 0.75f; var rb2 = enemyFlagGO.AddComponent(); rb2.bodyType = RigidbodyType2D.Kinematic; var sr2 = enemyFlagGO.GetComponent(); sr2.sortingOrder = 5; } void SpawnUnits() { // Spawn 5 player units near player base for (int i = 0; i < 5; i++) { var offset = new Vector3((i - 2) * 1.5f, -2f, 0); var unit = CreateUnit($"PlayerUnit_{i}", Unit.Team.Player, playerBase.position + offset); playerUnits.Add(unit); } // Spawn 5 enemy units near enemy base for (int i = 0; i < 5; i++) { var offset = new Vector3((i - 2) * 1.5f, 2f, 0); var unit = CreateUnit($"EnemyUnit_{i}", Unit.Team.Enemy, enemyBase.position + offset); enemyUnits.Add(unit); } } Unit CreateUnit(string name, Unit.Team team, Vector3 position) { Color color = team == Unit.Team.Player ? new Color(0.3f, 0.5f, 1f) : new Color(1f, 0.3f, 0.3f); var unitGO = CreateSprite(name, color, 1f, 1f); unitGO.transform.position = position; var unit = unitGO.AddComponent(); unit.team = team; var collider = unitGO.AddComponent(); collider.isTrigger = true; collider.radius = 0.5f; var rb = unitGO.AddComponent(); rb.bodyType = RigidbodyType2D.Kinematic; var sr = unitGO.GetComponent(); sr.sortingOrder = 10; return unit; } void CreateUI() { // Create Canvas var canvasGO = new GameObject("Canvas"); var canvas = canvasGO.AddComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvasGO.AddComponent(); canvasGO.AddComponent(); // Score text var scoreGO = new GameObject("ScoreText"); scoreGO.transform.SetParent(canvasGO.transform, false); scoreText = scoreGO.AddComponent(); scoreText.text = "0 - 0"; scoreText.fontSize = 48; scoreText.alignment = TextAlignmentOptions.Center; scoreText.color = Color.white; var scoreRect = scoreGO.GetComponent(); scoreRect.anchorMin = new Vector2(0.5f, 1f); scoreRect.anchorMax = new Vector2(0.5f, 1f); scoreRect.pivot = new Vector2(0.5f, 1f); scoreRect.anchoredPosition = new Vector2(0, -20); scoreRect.sizeDelta = new Vector2(200, 60); // Game over text (hidden initially) var gameOverGO = new GameObject("GameOverText"); gameOverGO.transform.SetParent(canvasGO.transform, false); gameOverText = gameOverGO.AddComponent(); gameOverText.text = ""; gameOverText.fontSize = 72; gameOverText.alignment = TextAlignmentOptions.Center; gameOverText.color = Color.white; gameOverText.gameObject.SetActive(false); var gameOverRect = gameOverGO.GetComponent(); gameOverRect.anchorMin = new Vector2(0.5f, 0.5f); gameOverRect.anchorMax = new Vector2(0.5f, 0.5f); gameOverRect.pivot = new Vector2(0.5f, 0.5f); gameOverRect.anchoredPosition = Vector2.zero; gameOverRect.sizeDelta = new Vector2(400, 100); } void SetupSystems() { // Add RouteDrawer gameObject.AddComponent(); // Add Visibility system var visibility = gameObject.AddComponent(); visibility.playerUnits = playerUnits.ToArray(); visibility.enemyUnits = enemyUnits.ToArray(); visibility.enemyFlag = enemyFlag; // Add SimpleAI var ai = gameObject.AddComponent(); ai.aiUnits = enemyUnits.ToArray(); ai.playerFlag = playerFlag; ai.aiBase = enemyBase; } public void Score(Unit.Team team) { if (gameOver) return; if (team == Unit.Team.Player) playerScore++; else enemyScore++; UpdateScoreUI(); Debug.Log($"Score: {playerScore} - {enemyScore}"); if (playerScore >= WinScore) EndGame(true); else if (enemyScore >= WinScore) EndGame(false); else ResetRound(); } void UpdateScoreUI() { if (scoreText != null) scoreText.text = $"{playerScore} - {enemyScore}"; } void ResetRound() { // Return flags to home playerFlag.ReturnHome(); enemyFlag.ReturnHome(); // Respawn all units at bases for (int i = 0; i < playerUnits.Count; i++) { var offset = new Vector3((i - 2) * 1.5f, -2f, 0); playerUnits[i].ForceRespawn(playerBase.position + offset); } for (int i = 0; i < enemyUnits.Count; i++) { var offset = new Vector3((i - 2) * 1.5f, 2f, 0); enemyUnits[i].ForceRespawn(enemyBase.position + offset); } } void EndGame(bool playerWon) { gameOver = true; if (gameOverText != null) { gameOverText.text = playerWon ? "YOU WIN!" : "YOU LOSE!"; gameOverText.color = playerWon ? Color.green : Color.red; gameOverText.gameObject.SetActive(true); } Debug.Log(playerWon ? "Player wins!" : "Enemy wins!"); } public Transform GetBase(Unit.Team team) { return team == Unit.Team.Player ? playerBase : enemyBase; } public Flag GetFlag(Unit.Team team) { return team == Unit.Team.Player ? playerFlag : enemyFlag; } // Helper to create a colored sprite GameObject CreateSprite(string name, Color color, float width, float height) { var go = new GameObject(name); var sr = go.AddComponent(); sr.sprite = CreateRectSprite(); sr.color = color; go.transform.localScale = new Vector3(width, height, 1); return go; } // Create a simple white 1x1 sprite static Sprite cachedSprite; static Sprite CreateRectSprite() { if (cachedSprite != null) return cachedSprite; var tex = new Texture2D(1, 1); tex.SetPixel(0, 0, Color.white); tex.Apply(); cachedSprite = Sprite.Create(tex, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f), 1); return cachedSprite; } } // Simple component to identify base zones public class BaseZone : MonoBehaviour { public Unit.Team team; }