ArkanoidTG/Assets/Scripts/GameManager.cs

355 lines
11 KiB
C#
Raw Permalink Normal View History

2024-11-25 23:14:57 +03:00
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
[Header("Game Settings")]
public int lives = 3;
public int score = 0;
[Header("Prefabs")]
public Ball ballPrefab;
public DestructibleBrick brickPrefab;
public Paddle paddlePrefab;
public MultiBallBrick multiBallBrickPrefab;
public GameObject splitBallPrefab; // Префаб дочернего мяча
[Header("Field Configuration")]
public int rows = 5;
public int columns = 8;
public float brickSpacing = 0.2f;
public Vector2 brickOffset = new Vector2(0, 4);
[Header("Special Bricks")]
[Range(0, 10)]
public int multiBallBricksCount = 3; // Количество специальных блоков на поле
[Header("Scale Settings")]
[Range(0.1f, 3.0f)]
public float brickScale = 1.0f;
[Range(0.1f, 3.0f)]
public float ballScale = 1.0f;
[Range(0.1f, 3.0f)]
public float paddleScale = 1.0f;
[Header("Wall Settings")]
public float wallThickness = 0.5f;
public float wallHeight = 10f;
public Color wallColor = Color.gray;
// Properties for UI access
public int DestroyedBricks => destroyedBricks;
public int TotalBricks => totalBricks;
public int UsedLives => usedLives;
public int InitialLives => initialLives;
private Ball currentBall;
private Paddle paddle;
private GameObject leftWall;
private GameObject rightWall;
private GameObject topWall;
private int totalBricks;
private int destroyedBricks;
private int initialLives;
private int usedLives;
private int activeBallsCount = 0;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
void Start()
{
InitializeGame();
}
void InitializeGame()
{
score = 0;
destroyedBricks = 0;
usedLives = 0;
initialLives = lives;
totalBricks = rows * columns;
CreateWalls();
// Создаем платформу с заданным масштабом
paddle = Instantiate(paddlePrefab, new Vector3(0, -4, 0), Quaternion.identity);
paddle.transform.localScale = new Vector3(paddleScale, paddleScale, 1f);
SpawnNewBall();
CreateBricks();
}
void CreateBricks()
{
Vector2 brickSize = brickPrefab.GetComponent<SpriteRenderer>().bounds.size;
// Применяем масштаб к размеру блока
brickSize *= brickScale;
// Создаем список всех возможных позиций
Vector2[,] positions = new Vector2[rows, columns];
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < columns; col++)
{
positions[row, col] = new Vector2(
(-columns/2f + col) * (brickSize.x + brickSpacing) + brickSize.x/2f,
brickOffset.y - row * (brickSize.y + brickSpacing)
);
}
}
// Размещаем специальные блоки случайным образом
int specialBricksPlaced = 0;
while (specialBricksPlaced < multiBallBricksCount)
{
int randomRow = Random.Range(0, rows);
int randomCol = Random.Range(0, columns);
if (positions[randomRow, randomCol] == Vector2.zero)
continue;
// Создаем специальный блок
Brick specialBrick = Instantiate(multiBallBrickPrefab, positions[randomRow, randomCol], Quaternion.identity);
specialBrick.transform.localScale = new Vector3(brickScale, brickScale, 1f);
// Помечаем позицию как использованную
positions[randomRow, randomCol] = Vector2.zero;
specialBricksPlaced++;
}
// Заполняем оставшиеся позиции обычными блоками
for (int row = 0; row < rows; row++)
{
// Количество жизней зависит от строки (сверху вниз)
// Максимум 15 жизней для самого верхнего ряда
int rowHitPoints = Mathf.Min(15, rows - row);
// Очки за блок зависят от количества жизней
int rowPoints = rowHitPoints * 100;
for (int col = 0; col < columns; col++)
{
// Пропускаем использованные позиции
if (positions[row, col] == Vector2.zero)
continue;
DestructibleBrick brick = Instantiate(brickPrefab, positions[row, col], Quaternion.identity);
brick.transform.localScale = new Vector3(brickScale, brickScale, 1f);
brick.hitPoints = rowHitPoints;
brick.points = rowPoints;
}
}
}
public SplitBall CreateSplitBall(Vector3 position, Vector2 velocity, float scale)
{
GameObject splitBallObj = Instantiate(splitBallPrefab.gameObject, position, Quaternion.identity);
SplitBall splitBall = splitBallObj.GetComponent<SplitBall>();
if (splitBall != null)
{
splitBall.Initialize(velocity, scale);
activeBallsCount++; // Увеличиваем счетчик при создании нового мяча
Debug.Log($"Split ball created. Active balls: {activeBallsCount}");
}
return splitBall;
}
public void SpawnNewBall()
{
// Создаем новый мяч только если нет других активных мячей
if (activeBallsCount <= 0)
{
// Создаем мяч над платформой с учетом масштабов
float paddleHeight = paddle.GetComponent<SpriteRenderer>().bounds.size.y;
float ballHeight = ballPrefab.GetComponent<SpriteRenderer>().bounds.size.y * ballScale;
Vector3 spawnPosition = paddle.transform.position + Vector3.up * (paddleHeight/2 + ballHeight/2 + 0.1f);
GameObject ballObj = Instantiate(ballPrefab.gameObject, spawnPosition, Quaternion.identity);
currentBall = ballObj.GetComponent<Ball>();
if (currentBall != null)
{
currentBall.transform.localScale = Vector3.one * ballScale;
activeBallsCount++; // Увеличиваем счетчик при создании основного мяча
Debug.Log($"Main ball created. Active balls: {activeBallsCount}");
}
}
else
{
Debug.Log($"Not spawning main ball - there are still {activeBallsCount} active balls");
}
}
// Перегрузка для обратной совместимости
public void LoseBall()
{
LoseBall(null);
}
public void LoseBall(Ball ball)
{
if (ball != null)
{
if (ball == currentBall)
{
currentBall = null;
}
Destroy(ball.gameObject);
activeBallsCount--;
Debug.Log($"Ball lost. Remaining active balls: {activeBallsCount}");
}
else
{
// Если мяч не указан, считаем что это основной мяч
if (currentBall != null)
{
Destroy(currentBall.gameObject);
currentBall = null;
activeBallsCount--;
Debug.Log($"Main ball lost. Remaining active balls: {activeBallsCount}");
}
}
// Проверяем, остались ли еще мячи
if (activeBallsCount <= 0)
{
lives--;
usedLives++;
UpdateUI();
Debug.Log($"No balls remaining. Lives left: {lives}");
if (lives <= 0)
{
GameOver();
}
else
{
SpawnNewBall();
}
}
}
public void BrickDestroyed()
{
destroyedBricks++;
CheckWinCondition();
}
public void AddScore(int points)
{
score += points;
}
public void CheckWinCondition()
{
if (destroyedBricks >= totalBricks) // Check if this was the last brick
{
WinGame();
}
}
void GameOver()
{
Debug.Log("Game Over! Final Score: " + score);
// Implement game over UI and restart functionality
Invoke("RestartGame", 2f);
}
void WinGame()
{
Debug.Log("You Win! Score: " + score);
// Implement win UI and next level or restart functionality
Invoke("RestartGame", 2f);
}
void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
void CreateWalls()
{
// Calculate wall positions based on camera viewport
float screenHalfWidth = Camera.main.orthographicSize * Camera.main.aspect;
float screenHalfHeight = Camera.main.orthographicSize;
float wallX = screenHalfWidth + (wallThickness / 2f);
float wallY = screenHalfHeight + (wallThickness / 2f);
// Create left wall
leftWall = CreateWall(-wallX, 0, wallThickness, wallHeight, "LeftWall");
// Create right wall
rightWall = CreateWall(wallX, 0, wallThickness, wallHeight, "RightWall");
// Create top wall
topWall = CreateWall(0, wallY, screenHalfWidth * 2, wallThickness, "TopWall");
}
GameObject CreateWall(float xPosition, float yPosition, float width, float height, string name)
{
GameObject wall = new GameObject(name);
wall.transform.position = new Vector3(xPosition, yPosition, 0);
// Add BoxCollider2D
BoxCollider2D collider = wall.AddComponent<BoxCollider2D>();
collider.size = new Vector2(width, height);
// Add SpriteRenderer to make the wall visible
SpriteRenderer renderer = wall.AddComponent<SpriteRenderer>();
renderer.sprite = CreateWallSprite();
renderer.color = wallColor;
// Scale the wall to match the collider
wall.transform.localScale = new Vector3(width, height, 1);
// Add physics material for proper bouncing
collider.sharedMaterial = new PhysicsMaterial2D
{
bounciness = 1f,
friction = 0f
};
return wall;
}
Sprite CreateWallSprite()
{
// Create a 1x1 white texture that we can tint
Texture2D texture = new Texture2D(1, 1);
texture.SetPixel(0, 0, Color.white);
texture.Apply();
return Sprite.Create(texture, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f));
}
// Handle Telegram back button
public void HandleBackButton()
{
// Pause the game
Time.timeScale = 0;
// Here you could show a pause menu or exit confirmation
// For now, we'll just resume the game
Time.timeScale = 1;
}
private void UpdateUI()
{
// Implement UI update logic here
}
}