using UnityEngine; using UnityEngine.InputSystem; using System.Collections; public class Player : MonoBehaviour { public GameObject gameBoard; public AudioClip flip; public AudioClip normalShoot; public AudioClip laserCharge; public AudioClip laserDischarge; public AudioClip laserShoot; public AudioClip magnetIdle; public AudioClip magnetShoot; public AudioClip death; public AudioClip killed; public AudioClip defeat; public AudioClip denied; public MusicManager musicManager; public Fader fader; public GameObject horizontalBarrierPrefab; public GameObject verticalBarrierPrefab; public GameObject horizontalLaserPrefab; public GameObject verticalLaserPrefab; public int level = 1; public int lives = 8; public int score = 0; public int bonus = 3100; public float speedBonus = 0.25f; public Animator gunAnimator; public Animator muzzleAnimator; public SpriteRenderer muzzleRenderer; public GameObject debrisPrefab; public int magnetCount = 0; public int laserCount = 0; public GameObject magnetLeft; public GameObject magnetRight; public GameObject magnetTop; public GameObject magnetBottom; public BallSpawner ballSpawner; public int percentCovered = 0; public float multiplier = 1.0f; private bool flipped = false; private bool gameOver = false; private AudioSource audioSource; private bool canFire = true; private float damageTimer = 0.0f; private float damageCooldown = 0.5f; private bool exploding = false; private Bounds playerBounds; private Bounds boardBounds; private bool laserActive = false; private bool magnetActive = false; private float magnetDuration = 5.0f; private float magnetTimer = 0.0f; private bool killedByShark = false; private float bonusTimer = bonusDecrementInterval; private static float bonusDecrementInterval = 0.25f; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { audioSource = GetComponent(); Cursor.visible = false; if (!TryGetWorldBounds(gameObject, out playerBounds)) { Debug.LogWarning("Could not get world bounds for player."); } if (!TryGetWorldBounds(gameBoard, out boardBounds)) { Debug.LogWarning("Could not get world bounds for gameBoard."); return; } // TODO: Generalize this to work with subset of the board bounds. // Initialize the magnets. magnetLeft.GetComponent().enabled = false; magnetRight.GetComponent().enabled = false; magnetTop.GetComponent().enabled = false; magnetBottom.GetComponent().enabled = false; magnetLeft.transform.position = new Vector3( boardBounds.min.x + magnetLeft.GetComponent().bounds.extents.x, transform.position.y, transform.position.z ); magnetRight.transform.position = new Vector3( boardBounds.max.x - magnetRight.GetComponent().bounds.extents.x, transform.position.y, transform.position.z ); magnetTop.transform.position = new Vector3( transform.position.x, boardBounds.max.y - magnetTop.GetComponent().bounds.extents.y, transform.position.z ); magnetBottom.transform.position = new Vector3( transform.position.x, boardBounds.min.y + magnetBottom.GetComponent().bounds.extents.y, transform.position.z ); } // Update is called once per frame void Update() { // Foce reset the rotation. transform.rotation = Quaternion.identity; transform.eulerAngles = new Vector3(0, 0, flipped ? 90 : 0); // Update the damage timer. if (damageTimer > 0.0f) { damageTimer -= Time.deltaTime; } // Player movement is based on the mouse position, clamped to the game board bounds. if (!exploding) { Vector2 mouseScreen = Mouse.current.position.ReadValue(); Camera cam = Camera.main; // Keep movement on the same depth plane as the current player position. float screenZ = cam.WorldToScreenPoint(transform.position).z; Vector3 mouseWorld = cam.ScreenToWorldPoint(new Vector3(mouseScreen.x, mouseScreen.y, screenZ)); Vector3 extents = playerBounds.extents; float clampedX = Mathf.Clamp(mouseWorld.x, boardBounds.min.x + extents.x / 2.0f, boardBounds.max.x - extents.x / 2.0f); float clampedY = Mathf.Clamp(mouseWorld.y, boardBounds.min.y + extents.y / 2.0f, boardBounds.max.y - extents.y / 2.0f); transform.position = new Vector3(clampedX, clampedY, transform.position.z); } // Handle bonus countdown and decrement. if (bonus > 0 && percentCovered < 80) { bonusTimer -= Time.deltaTime; if (bonusTimer <= 0.0f) { bonus -= 10; if (bonus < 0) { bonus = 0; } bonusTimer = bonusDecrementInterval; } } // Handle keyboard input for flipping, game over, and toggling laser/magnet modes. // Disable keyboard input if the player has covered 80% or more of the area. if (Keyboard.current != null && percentCovered < 80) { if (Keyboard.current.spaceKey.wasPressedThisFrame) { flipped = !flipped; audioSource.PlayOneShot(flip); } if (Keyboard.current.escapeKey.wasPressedThisFrame) { gameOver = true; } if (Keyboard.current.wKey.wasPressedThisFrame) { if (laserActive) { DisableLaser(); } else { EnableLaser(); } } if (Keyboard.current.dKey.wasPressedThisFrame) { if (magnetActive) { DisableMagnet(); } else { EnableMagnet(); } } } // Mouse input handling for firing lasers, magnets, or normal barriers. if (Mouse.current.leftButton.wasPressedThisFrame && canFire && !exploding) { if (laserActive) { FireLaser(); } else if (magnetActive) { FireMagnet(); } else { FireNormal(); } } // Player has covered enough area to win the level. if (percentCovered >= 80) { canFire = false; } // Check for game over or revival conditions. if (gameOver) { StartCoroutine(EndGame()); gameOver = false; // Reset gameOver to prevent multiple coroutine starts } if (killedByShark) { killedByShark = false; // Reset the flag to prevent multiple sound plays. StartCoroutine(Revive()); } // Handle magnet timer countdown and deactivation. if (magnetTimer > 0.0f) { magnetTimer -= Time.deltaTime; if (magnetTimer <= 0.0f) { magnetLeft.GetComponent().enabled = false; magnetRight.GetComponent().enabled = false; magnetTop.GetComponent().enabled = false; magnetBottom.GetComponent().enabled = false; ballSpawner.MagnetInactive(); } } } private static bool TryGetWorldBounds(GameObject target, out Bounds bounds) { Collider2D collider2D = target.GetComponent(); if (collider2D != null) { bounds = collider2D.bounds; return true; } Renderer renderer = target.GetComponent(); if (renderer != null) { bounds = renderer.bounds; return true; } Collider collider3D = target.GetComponent(); if (collider3D != null) { bounds = collider3D.bounds; return true; } bounds = default; return false; } private IEnumerator Revive() { yield return new WaitForSeconds(1.0f); if (lives > 0) { audioSource.PlayOneShot(killed); yield return new WaitForSeconds(1.0f); canFire = true; exploding = false; gunAnimator.SetTrigger("revived"); SpriteRenderer[] renderers = GetComponentsInChildren(); foreach (SpriteRenderer renderer in renderers) { renderer.enabled = true; } } else { gameOver = true; } } private IEnumerator EndGame() { musicManager.PauseMusic(); audioSource.PlayOneShot(defeat); yield return new WaitForSeconds(1.3f); fader.ResetFader(); } public void BarrierFinished() { canFire = true; } public IEnumerator SetGameOver() { yield return new WaitForSeconds(3.0f); gameOver = true; } private void EnableLaser() { if (laserCount > 0) { laserActive = true; magnetActive = false; audioSource.PlayOneShot(laserCharge); gunAnimator.SetBool("laserActivated", true); gunAnimator.SetBool("magnetActivated", false); } else { if (!audioSource.isPlaying) { audioSource.PlayOneShot(denied); } } } private void EnableMagnet() { if (magnetCount > 0) { laserActive = false; magnetActive = true; audioSource.PlayOneShot(magnetIdle); gunAnimator.SetBool("laserActivated", false); gunAnimator.SetBool("magnetActivated", true); } else { if (!audioSource.isPlaying) { audioSource.PlayOneShot(denied); } } } private void DisableLaser() { if (laserActive) { laserActive = false; audioSource.PlayOneShot(laserDischarge); gunAnimator.SetBool("laserActivated", false); } } private void DisableMagnet() { if (magnetActive) { magnetActive = false; gunAnimator.SetBool("magnetActivated", false); } } private void FireNormal() { canFire = false; audioSource.PlayOneShot(normalShoot); muzzleAnimator.SetTrigger("firing"); if (flipped) { Instantiate(verticalBarrierPrefab, transform.position, Quaternion.identity); } else { Instantiate(horizontalBarrierPrefab, transform.position, Quaternion.identity); } } private void FireLaser() { canFire = false; audioSource.PlayOneShot(laserShoot); muzzleAnimator.SetTrigger("firing"); laserCount--; if (flipped) { Instantiate(verticalLaserPrefab, transform.position, Quaternion.identity); } else { Instantiate(horizontalLaserPrefab, transform.position, Quaternion.identity); } DisableLaser(); } private void FireMagnet() { audioSource.PlayOneShot(magnetShoot); muzzleAnimator.SetTrigger("firing"); magnetCount--; magnetTimer = magnetDuration; if (flipped) { magnetTop.GetComponent().enabled = true; magnetBottom.GetComponent().enabled = true; } else { magnetLeft.GetComponent().enabled = true; magnetRight.GetComponent().enabled = true; } ballSpawner.MagnetActive(flipped); DisableMagnet(); } private void Explode() { if (exploding) { return; } exploding = true; gunAnimator.SetTrigger("playerKilled"); muzzleRenderer.enabled = false; audioSource.PlayOneShot(death); for (int i = 0; i < 8; i++) { SpawnDebris(); } } private void SpawnDebris() { Vector2 randomOffset = Random.insideUnitCircle * playerBounds.extents.magnitude; Instantiate( debrisPrefab, transform.position + new Vector3(randomOffset.x, randomOffset.y, 0), Quaternion.identity ); } public void OnBarHit() { if (damageTimer > 0.0f) { return; } lives--; damageTimer = damageCooldown; if (lives <= 0) { Explode(); StartCoroutine(SetGameOver()); } canFire = true; // Allow firing again after bar hit. speedBonus -= 0.05f; // Decrease speed bonus on bar hit. if (speedBonus < 0.0f) { speedBonus = 0.0f; // Clamp to minimum value. } } void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Shark")) { if (exploding) { return; } killedByShark = true; lives--; Explode(); } else { Physics2D.IgnoreCollision(collision.collider, GetComponent(), true); } } public void OnWallSpawned(int areaPercentage) { canFire = true; percentCovered += areaPercentage; score += areaPercentage * 10; } public void OnLevelEnd() { // Reset the player state for the next level. canFire = true; exploding = false; laserActive = false; magnetActive = false; bonus = 3100; level++; multiplier = 1.0f; flipped = false; percentCovered = 0; } }