ArkanoidTG/Assets/Scripts/Ball.cs

284 lines
9.9 KiB
C#

using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CircleCollider2D))]
public class Ball : MonoBehaviour
{
public float speed = 8f;
public float maxSpeed = 15f;
public float speedIncrease = 0.1f;
public float maxBounceAngle = 75f; // Maximum angle the ball can bounce
public float randomBounceIntensity = 0.2f; // Интенсивность случайного отклонения
public float paddleBounceIntensity = 0.3f; // Дополнительная интенсивность для отскока от ракетки
public float minVerticalVelocity = 2f; // Минимальная вертикальная скорость
public Color ballColor = Color.white; // Цвет мяча
[HideInInspector]
public bool canSpawnBalls = true; // Флаг, определяющий может ли мяч создавать новые мячи
protected Rigidbody2D rb;
protected bool isPlaying;
private Vector2 startPosition;
private Transform paddleTransform;
private Vector2 offset;
private Paddle paddle;
protected Vector2 lastFrameVelocity;
protected virtual void Start()
{
rb = GetComponent<Rigidbody2D>();
if (rb == null)
{
Debug.LogError("Rigidbody2D component is missing!");
return;
}
// Устанавливаем цвет мяча
SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
if (spriteRenderer != null)
{
spriteRenderer.color = ballColor;
}
// Set up physics properties for proper reflection
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
rb.velocity = Vector2.zero;
rb.angularVelocity = 0f;
rb.gravityScale = 0f;
rb.sharedMaterial = new PhysicsMaterial2D
{
bounciness = 1f,
friction = 0f
};
var collider = GetComponent<CircleCollider2D>();
if (collider == null)
{
Debug.LogError("CircleCollider2D component is missing!");
return;
}
Debug.Log("Ball components initialized successfully");
startPosition = transform.position;
// Find the paddle
GameObject paddleObj = GameObject.FindGameObjectWithTag("Paddle");
if (paddleObj != null)
{
paddleTransform = paddleObj.transform;
paddle = paddleObj.GetComponent<Paddle>();
// Calculate initial offset from paddle
offset = transform.position - paddleTransform.position;
}
else
{
Debug.LogError("Paddle not found! Make sure it has the 'Paddle' tag.");
}
PrepareNewBall();
}
protected virtual void Update()
{
if (!isPlaying)
{
// Stick to the paddle
if (paddleTransform != null)
{
transform.position = paddleTransform.position + (Vector3)offset;
}
// Launch on space or left mouse click
if (Input.GetButtonDown("Fire1") || Input.GetKeyDown(KeyCode.Space))
{
LaunchBall();
}
}
else
{
// Maintain constant speed without changing direction
if (rb.velocity.magnitude != speed)
{
rb.velocity = rb.velocity.normalized * speed;
}
}
// Сохраняем скорость предыдущего кадра
lastFrameVelocity = rb.velocity;
}
void LaunchBall()
{
isPlaying = true;
// Get base launch velocity (upward direction)
Vector2 launchVelocity = Vector2.up * speed;
// Add paddle's velocity influence if available
if (paddle != null)
{
Vector2 paddleVelocity = paddle.GetLaunchVelocity();
launchVelocity += paddleVelocity;
// Ensure the ball always goes upward regardless of paddle movement
if (launchVelocity.y < speed * 0.5f)
{
launchVelocity.y = speed * 0.5f;
}
// Normalize and apply speed to maintain consistent velocity
launchVelocity = launchVelocity.normalized * speed;
}
rb.velocity = launchVelocity;
}
public void PrepareNewBall()
{
isPlaying = false;
rb.velocity = Vector2.zero;
if (paddleTransform != null)
{
// Reset position relative to paddle
transform.position = paddleTransform.position + (Vector3)offset;
}
else
{
transform.position = startPosition;
}
}
protected virtual void OnCollisionEnter2D(Collision2D collision)
{
if (!isPlaying) return;
// Get the incoming direction
Vector2 inDirection = lastFrameVelocity.normalized;
if (collision == null)
{
Debug.LogError("Collision is null!");
return;
}
// Special handling for paddle collisions
if (collision.gameObject.CompareTag("Paddle"))
{
HandlePaddleCollision(collision);
return;
}
// For walls and bricks, let physics handle the reflection naturally
// Just maintain the constant speed without changing the direction
Vector2 reflected = Vector2.Reflect(lastFrameVelocity.normalized, collision.contacts[0].normal);
// Добавляем случайное отклонение
float intensity = randomBounceIntensity;
// Применяем случайное отклонение
reflected = AddRandomness(reflected, intensity);
// Обеспечиваем минимальную вертикальную скорость
if (Mathf.Abs(reflected.y) < minVerticalVelocity)
{
reflected.y = reflected.y < 0 ? -minVerticalVelocity : minVerticalVelocity;
// Нормализуем вектор и сохраняем скорость
reflected = reflected.normalized;
}
// Применяем скорость, сохраняя текущую величину
float currentSpeed = lastFrameVelocity.magnitude;
rb.velocity = reflected * Mathf.Min(currentSpeed, maxSpeed);
}
void HandlePaddleCollision(Collision2D collision)
{
rb = GetComponent<Rigidbody2D>();
Paddle paddle = collision.gameObject.GetComponent<Paddle>();
if (paddle == null) return;
// Get the paddle's velocity
Vector2 paddleVel = paddle.GetBounceVelocity();
// Calculate hit position relative to paddle center (-1 to 1)
float hitPosition = (transform.position.x - collision.transform.position.x) /
(collision.collider.bounds.size.x / 2f);
// Calculate base bounce angle
float bounceAngle = hitPosition * maxBounceAngle;
float angleRad = bounceAngle * Mathf.Deg2Rad;
// Calculate base reflection direction
Vector2 baseDirection = new Vector2(Mathf.Sin(angleRad), Mathf.Cos(angleRad));
// Calculate incoming velocity relative to paddle
Vector2 relativeVelocity = rb.velocity - paddleVel;
// Calculate reflection velocity
Vector2 reflection = Vector2.Reflect(relativeVelocity.normalized, collision.contacts[0].normal);
// Combine base direction with reflection and paddle velocity
Vector2 finalDirection = (baseDirection + reflection.normalized).normalized;
// Add paddle velocity influence
Vector2 newVelocity = finalDirection * speed + paddleVel * 0.5f;
// Ensure the ball always goes upward
if (newVelocity.y < speed * 0.2f)
{
newVelocity.y = speed * 0.2f;
newVelocity = newVelocity.normalized * speed;
}
// Добавляем случайное отклонение
float intensity = randomBounceIntensity + paddleBounceIntensity;
// Применяем случайное отклонение
newVelocity = AddRandomness(newVelocity, intensity);
// Обеспечиваем минимальную вертикальную скорость
if (Mathf.Abs(newVelocity.y) < minVerticalVelocity)
{
newVelocity.y = newVelocity.y < 0 ? -minVerticalVelocity : minVerticalVelocity;
// Нормализуем вектор и сохраняем скорость
newVelocity = newVelocity.normalized;
}
// Применяем скорость, сохраняя текущую величину
float currentSpeed = lastFrameVelocity.magnitude;
rb.velocity = newVelocity * Mathf.Min(currentSpeed, maxSpeed);
// Легкая вибрация при отскоке от ракетки
if (HapticManager.Instance != null)
{
HapticManager.Instance.LightTap();
}
}
Vector2 AddRandomness(Vector2 direction, float intensity)
{
// Генерируем случайный угол в радианах
float randomAngle = Random.Range(-intensity * Mathf.PI / 3f, intensity * Mathf.PI / 3f);
// Поворачиваем вектор на случайный угол
float cos = Mathf.Cos(randomAngle);
float sin = Mathf.Sin(randomAngle);
return new Vector2(
direction.x * cos - direction.y * sin,
direction.x * sin + direction.y * cos
).normalized;
}
void OnTriggerEnter2D(Collider2D other)
{
Debug.Log("Triggered");
// If ball hits the bottom of the screen
if (other.CompareTag("DeathZone"))
{
Debug.Log("Ball hit DeathZone");
GameManager.Instance.LoseBall(this);
}
}
}