mirror of
https://github.com/kuhyx/praca_magisterska.git
synced 2026-07-04 14:43:07 +02:00
386 lines
11 KiB
C#
386 lines
11 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace TicTacToe3D
|
|
{
|
|
public class TicTacToeGameManager : MonoBehaviour
|
|
{
|
|
public enum Player
|
|
{
|
|
None = 0,
|
|
X,
|
|
O
|
|
}
|
|
|
|
[Header("Board Layout")]
|
|
[SerializeField] private int gridSize = 3;
|
|
[SerializeField] private float tileSize = 1.0f;
|
|
[SerializeField] private float tileHeight = 0.15f;
|
|
[SerializeField] private float tileSpacing = 1.1f;
|
|
[SerializeField] private Transform boardRoot;
|
|
|
|
[Header("Visuals")]
|
|
[SerializeField] private Color boardBaseColor = new Color(0.18f, 0.22f, 0.26f, 1f);
|
|
[SerializeField] private Color boardHoverColor = new Color(0.28f, 0.75f, 0.69f, 1f);
|
|
[SerializeField] private Color boardWinColor = new Color(0.94f, 0.77f, 0.38f, 1f);
|
|
[SerializeField] private Color xMarkerColor = new Color(0.89f, 0.22f, 0.22f, 1f);
|
|
[SerializeField] private Color oMarkerColor = new Color(0.22f, 0.35f, 0.89f, 1f);
|
|
|
|
[Header("Gameplay")]
|
|
[SerializeField] private float markerHeight = 0.55f;
|
|
[SerializeField] private bool allowDiagonalWin = true;
|
|
|
|
private readonly List<Tile> tiles = new List<Tile>();
|
|
private Player[,] boardState;
|
|
private Player currentPlayer = Player.X;
|
|
private bool isGameOver;
|
|
private readonly List<Tile> winningTiles = new List<Tile>();
|
|
|
|
public Color BaseColor => boardBaseColor;
|
|
public Color HoverColor => boardHoverColor;
|
|
public Color WinColor => boardWinColor;
|
|
public Color XColor => xMarkerColor;
|
|
public Color OColor => oMarkerColor;
|
|
public float MarkerHeight => markerHeight;
|
|
|
|
private void Awake()
|
|
{
|
|
if (gridSize < 3)
|
|
{
|
|
gridSize = 3;
|
|
}
|
|
|
|
if (boardRoot == null)
|
|
{
|
|
boardRoot = transform;
|
|
}
|
|
|
|
boardState = new Player[gridSize, gridSize];
|
|
BuildBoard();
|
|
UpdateStatusMessage();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (Input.GetKeyDown(KeyCode.R))
|
|
{
|
|
ResetGame();
|
|
}
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
var style = new GUIStyle(GUI.skin.box)
|
|
{
|
|
fontSize = 24,
|
|
alignment = TextAnchor.MiddleCenter,
|
|
wordWrap = true
|
|
};
|
|
|
|
GUILayout.BeginArea(new Rect(20f, 20f, 280f, 120f));
|
|
GUILayout.Box(GetStatusMessage(), style);
|
|
GUILayout.EndArea();
|
|
}
|
|
|
|
public bool CanInteract(Vector2Int coordinates)
|
|
{
|
|
return !isGameOver && boardState[coordinates.x, coordinates.y] == Player.None;
|
|
}
|
|
|
|
public void TryPlay(Vector2Int coordinates, Tile tile)
|
|
{
|
|
if (!CanInteract(coordinates))
|
|
{
|
|
return;
|
|
}
|
|
|
|
boardState[coordinates.x, coordinates.y] = currentPlayer;
|
|
tile.AttachMarker(CreateMarker(currentPlayer, tile.transform));
|
|
|
|
if (DidPlayerWin(currentPlayer))
|
|
{
|
|
isGameOver = true;
|
|
HighlightWinningTiles();
|
|
UpdateStatusMessage();
|
|
return;
|
|
}
|
|
|
|
if (IsBoardFull())
|
|
{
|
|
isGameOver = true;
|
|
UpdateStatusMessage();
|
|
return;
|
|
}
|
|
|
|
currentPlayer = currentPlayer == Player.X ? Player.O : Player.X;
|
|
UpdateStatusMessage();
|
|
}
|
|
|
|
public void NotifyHoverEnter(Tile tile)
|
|
{
|
|
if (!isGameOver && CanInteract(tile.Coordinates))
|
|
{
|
|
tile.SetColor(boardHoverColor);
|
|
}
|
|
}
|
|
|
|
public void NotifyHoverExit(Tile tile)
|
|
{
|
|
if (isGameOver && winningTiles.Contains(tile))
|
|
{
|
|
tile.SetColor(boardWinColor);
|
|
}
|
|
else
|
|
{
|
|
tile.SetColor(boardBaseColor);
|
|
}
|
|
}
|
|
|
|
private void BuildBoard()
|
|
{
|
|
foreach (var tile in tiles)
|
|
{
|
|
if (tile != null)
|
|
{
|
|
Destroy(tile.gameObject);
|
|
}
|
|
}
|
|
|
|
tiles.Clear();
|
|
boardState = new Player[gridSize, gridSize];
|
|
|
|
for (int x = 0; x < gridSize; x++)
|
|
{
|
|
for (int y = 0; y < gridSize; y++)
|
|
{
|
|
var tileObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
tileObject.name = $"Tile_{x}_{y}";
|
|
tileObject.transform.SetParent(boardRoot, false);
|
|
tileObject.transform.localScale = new Vector3(tileSize, tileHeight, tileSize);
|
|
tileObject.transform.localPosition = new Vector3(
|
|
(x - (gridSize - 1) * 0.5f) * tileSpacing,
|
|
tileHeight * 0.5f,
|
|
(y - (gridSize - 1) * 0.5f) * tileSpacing
|
|
);
|
|
|
|
var tile = tileObject.AddComponent<Tile>();
|
|
tile.Initialize(this, new Vector2Int(x, y), boardBaseColor);
|
|
tiles.Add(tile);
|
|
}
|
|
}
|
|
}
|
|
|
|
private GameObject CreateMarker(Player player, Transform parent)
|
|
{
|
|
var markerRoot = new GameObject(player.ToString());
|
|
markerRoot.transform.SetParent(parent, false);
|
|
markerRoot.transform.localPosition = Vector3.up * markerHeight;
|
|
|
|
if (player == Player.X)
|
|
{
|
|
BuildCross(markerRoot.transform, xMarkerColor);
|
|
}
|
|
else
|
|
{
|
|
BuildSphere(markerRoot.transform, oMarkerColor);
|
|
}
|
|
|
|
return markerRoot;
|
|
}
|
|
|
|
private void BuildCross(Transform parent, Color color)
|
|
{
|
|
CreateBar(parent, Quaternion.Euler(0f, 0f, 45f), color);
|
|
CreateBar(parent, Quaternion.Euler(0f, 0f, -45f), color);
|
|
}
|
|
|
|
private void BuildSphere(Transform parent, Color color)
|
|
{
|
|
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
|
sphere.transform.SetParent(parent, false);
|
|
sphere.transform.localScale = new Vector3(0.6f, 0.25f, 0.6f);
|
|
|
|
var renderer = sphere.GetComponent<Renderer>();
|
|
renderer.sharedMaterial = new Material(renderer.sharedMaterial)
|
|
{
|
|
color = color
|
|
};
|
|
|
|
var collider = sphere.GetComponent<Collider>();
|
|
if (collider != null)
|
|
{
|
|
Destroy(collider);
|
|
}
|
|
}
|
|
|
|
private void CreateBar(Transform parent, Quaternion rotation, Color color)
|
|
{
|
|
var bar = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
bar.transform.SetParent(parent, false);
|
|
bar.transform.localRotation = rotation;
|
|
bar.transform.localScale = new Vector3(0.15f, 0.15f, 0.9f);
|
|
|
|
var renderer = bar.GetComponent<Renderer>();
|
|
renderer.sharedMaterial = new Material(renderer.sharedMaterial)
|
|
{
|
|
color = color
|
|
};
|
|
|
|
var collider = bar.GetComponent<Collider>();
|
|
if (collider != null)
|
|
{
|
|
Destroy(collider);
|
|
}
|
|
}
|
|
|
|
private bool DidPlayerWin(Player player)
|
|
{
|
|
winningTiles.Clear();
|
|
|
|
// Rows and columns
|
|
for (int i = 0; i < gridSize; i++)
|
|
{
|
|
if (IsFullLine(player, true, i))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (IsFullLine(player, false, i))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (allowDiagonalWin)
|
|
{
|
|
if (IsFullDiagonal(player, true))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (IsFullDiagonal(player, false))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
winningTiles.Clear();
|
|
return false;
|
|
}
|
|
|
|
private bool IsFullLine(Player player, bool horizontal, int index)
|
|
{
|
|
var temp = new List<Tile>();
|
|
|
|
for (int i = 0; i < gridSize; i++)
|
|
{
|
|
int x = horizontal ? i : index;
|
|
int y = horizontal ? index : i;
|
|
|
|
if (boardState[x, y] != player)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
temp.Add(GetTile(x, y));
|
|
}
|
|
|
|
winningTiles.Clear();
|
|
winningTiles.AddRange(temp);
|
|
return true;
|
|
}
|
|
|
|
private bool IsFullDiagonal(Player player, bool mainDiagonal)
|
|
{
|
|
var temp = new List<Tile>();
|
|
|
|
for (int i = 0; i < gridSize; i++)
|
|
{
|
|
int x = mainDiagonal ? i : i;
|
|
int y = mainDiagonal ? i : (gridSize - 1) - i;
|
|
|
|
if (boardState[x, y] != player)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
temp.Add(GetTile(x, y));
|
|
}
|
|
|
|
winningTiles.Clear();
|
|
winningTiles.AddRange(temp);
|
|
return true;
|
|
}
|
|
|
|
private Tile GetTile(int x, int y)
|
|
{
|
|
return tiles[y + x * gridSize];
|
|
}
|
|
|
|
private bool IsBoardFull()
|
|
{
|
|
for (int x = 0; x < gridSize; x++)
|
|
{
|
|
for (int y = 0; y < gridSize; y++)
|
|
{
|
|
if (boardState[x, y] == Player.None)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void HighlightWinningTiles()
|
|
{
|
|
foreach (var tile in winningTiles)
|
|
{
|
|
tile.SetColor(boardWinColor);
|
|
}
|
|
}
|
|
|
|
private void ResetGame()
|
|
{
|
|
for (int x = 0; x < gridSize; x++)
|
|
{
|
|
for (int y = 0; y < gridSize; y++)
|
|
{
|
|
boardState[x, y] = Player.None;
|
|
}
|
|
}
|
|
|
|
foreach (var tile in tiles)
|
|
{
|
|
tile.ResetTile(boardBaseColor);
|
|
}
|
|
|
|
winningTiles.Clear();
|
|
isGameOver = false;
|
|
currentPlayer = Player.X;
|
|
UpdateStatusMessage();
|
|
}
|
|
|
|
private string GetStatusMessage()
|
|
{
|
|
if (!isGameOver)
|
|
{
|
|
return $"Current Player: {currentPlayer}\nClick a tile to place a marker.\nPress R to reset.";
|
|
}
|
|
|
|
if (winningTiles.Count > 0)
|
|
{
|
|
return $"Player {currentPlayer} wins!\nPress R to play again.";
|
|
}
|
|
|
|
return "It's a draw!\nPress R to play again.";
|
|
}
|
|
|
|
private void UpdateStatusMessage()
|
|
{
|
|
// Intentionally left blank; OnGUI queries status message when needed.
|
|
}
|
|
}
|
|
}
|