Files
Backyard-CTF/Backyard CTF/Assets/Scripts/Game.cs
John Lamb e4ac24f989 feat(grid): Replace real-time CTF with turn-based grid system
Replace continuous free-form movement with discrete 20x50 grid-based
gameplay featuring asymmetric movement mechanics:

- Blue/Red teams with 3 units each
- Zone-based movement: orthogonal (4 dir) in offense, diagonal (8 dir) in defense
- Alternating turns with click-to-select, click-to-move input
- Fog of war: 3-cell Chebyshev vision radius per unit
- Defense speed nerf: skip every 4th move in own zone
- AI opponent that chases flag carriers and advances toward enemy flag
- Collision resolution: defender wins in their zone, lower ID wins in neutral

Implements all 3 phases from the plan:
- Phase 1: Playable grid with hot-seat two-player
- Phase 2: Fog of war + defense speed nerf
- Phase 3: AI opponent

Deleted obsolete files: Flag.cs, Unit.cs, RouteDrawer.cs, SimpleAI.cs, Visibility.cs

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-07 17:05:44 -06:00

148 lines
5.2 KiB
C#

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<Game>();
}
// ===========================================
// CONFIGURATION
// ===========================================
// Camera settings for grid view
public const float CameraZoom = 28f; // Fits 20x50 grid
public const float CameraMinZoom = 15f;
public const float CameraMaxZoom = 35f;
// References
public static Game Instance { get; private set; }
GridBoard gridBoard;
TextMeshProUGUI scoreText;
TextMeshProUGUI gameOverText;
TextMeshProUGUI turnText;
void Awake()
{
Instance = this;
}
void Start()
{
SetupCamera();
CreateUI();
CreateGridBoard();
}
void SetupCamera()
{
var cam = Camera.main;
if (cam != null)
{
cam.orthographic = true;
cam.orthographicSize = CameraZoom;
cam.transform.position = new Vector3(0, 0, -10);
cam.backgroundColor = new Color(0.1f, 0.1f, 0.15f);
// Add camera controller for zoom/pan
var controller = cam.gameObject.AddComponent<CameraController>();
controller.minZoom = CameraMinZoom;
controller.maxZoom = CameraMaxZoom;
}
}
void CreateUI()
{
// Create Canvas
var canvasGO = new GameObject("Canvas");
var canvas = canvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvasGO.AddComponent<UnityEngine.UI.CanvasScaler>();
canvasGO.AddComponent<UnityEngine.UI.GraphicRaycaster>();
// Score text (top center)
var scoreGO = new GameObject("ScoreText");
scoreGO.transform.SetParent(canvasGO.transform, false);
scoreText = scoreGO.AddComponent<TextMeshProUGUI>();
scoreText.text = "0 - 0";
scoreText.fontSize = 48;
scoreText.alignment = TextAlignmentOptions.Center;
scoreText.color = Color.white;
var scoreRect = scoreGO.GetComponent<RectTransform>();
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);
// Turn indicator (below score)
var turnGO = new GameObject("TurnText");
turnGO.transform.SetParent(canvasGO.transform, false);
turnText = turnGO.AddComponent<TextMeshProUGUI>();
turnText.text = "BLUE'S TURN";
turnText.fontSize = 32;
turnText.alignment = TextAlignmentOptions.Center;
turnText.color = Color.blue;
var turnRect = turnGO.GetComponent<RectTransform>();
turnRect.anchorMin = new Vector2(0.5f, 1f);
turnRect.anchorMax = new Vector2(0.5f, 1f);
turnRect.pivot = new Vector2(0.5f, 1f);
turnRect.anchoredPosition = new Vector2(0, -80);
turnRect.sizeDelta = new Vector2(300, 50);
// Instructions (bottom)
var instructionsGO = new GameObject("InstructionsText");
instructionsGO.transform.SetParent(canvasGO.transform, false);
var instructionsText = instructionsGO.AddComponent<TextMeshProUGUI>();
instructionsText.text = "Click unit to select, then click destination to move";
instructionsText.fontSize = 20;
instructionsText.alignment = TextAlignmentOptions.Center;
instructionsText.color = new Color(0.7f, 0.7f, 0.7f);
var instrRect = instructionsGO.GetComponent<RectTransform>();
instrRect.anchorMin = new Vector2(0.5f, 0f);
instrRect.anchorMax = new Vector2(0.5f, 0f);
instrRect.pivot = new Vector2(0.5f, 0f);
instrRect.anchoredPosition = new Vector2(0, 20);
instrRect.sizeDelta = new Vector2(500, 40);
// Game over text (hidden initially)
var gameOverGO = new GameObject("GameOverText");
gameOverGO.transform.SetParent(canvasGO.transform, false);
gameOverText = gameOverGO.AddComponent<TextMeshProUGUI>();
gameOverText.text = "";
gameOverText.fontSize = 72;
gameOverText.alignment = TextAlignmentOptions.Center;
gameOverText.color = Color.white;
gameOverText.gameObject.SetActive(false);
var gameOverRect = gameOverGO.GetComponent<RectTransform>();
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 CreateGridBoard()
{
var boardGO = new GameObject("GridBoard");
gridBoard = boardGO.AddComponent<GridBoard>();
// Pass UI references
gridBoard.scoreText = scoreText;
gridBoard.gameOverText = gameOverText;
gridBoard.turnText = turnText;
}
}