ArkanoidTG/Assets/Scripts/GameManager.cs

355 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}