using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class player : MonoBehaviour { [Header ("Movement Settings")] public float speed; public float jumpscale; [Header("Ground & Wall Detection")] public bool isground, iswall; public float rayLength = 0.3f; public LayerMask groundLayer; [Header("Game References")] public GameObject soal; public GameObject gameover; public GameObject finish; public healthmanager healthmanager; [Header("Audio")] public AudioSource jump_audio; public AudioSource soal_audio; public AudioSource walk_audio; public AudioSource fall_audio; public AudioSource finish_audio; public AudioSource gameover_audio; //private references private Rigidbody2D body; private SpriteRenderer sprite; private Animator animator; private Vector3 safePosition; private SoalPapanManager soalManager; private skor_akhir UpdateLevel; private BoxCollider2D wall_detector; private checkpoint[] allCheckpoints; private bool isRespawning = false; private bool canJump = true; private bool justRespawned = false; //water detection private bool inWater = false; private float WaterDangerThreshold = -6.5f; private float respawnYOffset = 0.5f; void Start() { soalManager = soal.GetComponent(); body = GetComponent(); sprite = GetComponent(); animator = GetComponent(); //set initial safe position; safePosition = transform.position; } [Obsolete] void Awake() { allCheckpoints = GameObject.FindObjectsOfType(); } void Update() { if (isRespawning) return; UpdateGroundWallCheck(); if (isground && !canJump && !justRespawned && Mathf.Abs(body.linearVelocity.y) < 0.1f) { canJump = true; Debug.Log("Landed: canJump reset"); } //handle input if (Input.GetKey(KeyCode.RightArrow)) { runright(); } if (Input.GetKey(KeyCode.LeftArrow)) { runleft(); } if (Input.GetKeyUp(KeyCode.RightArrow) || Input.GetKeyUp(KeyCode.LeftArrow)) { stop(); } if (Input.GetKeyDown(KeyCode.UpArrow)) { jump(); } UpdateAnimation(); UpdateSafePosition(); CheckWaterFall(); Debug.DrawRay(transform.position, Vector2.down * rayLength, Color.red); } void UpdateGroundWallCheck() { RaycastHit2D hitGround = Physics2D.Raycast(transform.position, Vector2.down, rayLength, groundLayer); isground = hitGround.collider != null; iswall = Physics2D.Raycast(transform.position, Vector2.right, 0.3f, groundLayer).collider != null || Physics2D.Raycast(transform.position, Vector2.left, 0.3f, groundLayer).collider != null; } void UpdateSafePosition() { //update safe position when player is on solid ground (not water) if (Mathf.Abs(body.linearVelocity.y) < 0.1f && isground) { RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, 1.5f, groundLayer); if (hit.collider != null && !hit.collider.CompareTag("Water")) { safePosition = new Vector3(transform.position.x, hit.point.y + respawnYOffset, transform.position.z); } } } void CheckWaterFall() { //check if player has fallen bellow the water danger threshold if(transform.position.y < WaterDangerThreshold && !inWater) { inWater = true; HandleWaterFall(); } //reset water detection when player is back above threshold if(transform.position.y >= WaterDangerThreshold) { inWater = false; } } void HandleWaterFall() { //stop player movement body.linearVelocity = Vector2.zero; body.gravityScale = 0; //disable camera flow Camera.main.GetComponent().enabled = false; //reduce health healthmanager.TakeDamage(1); Debug.Log("Nyawa tersisa: " + healthmanager.health); // play fall sound if (Time.timeScale == 1) { fall_audio.Play(); } // ✅ Ambil checkpoint terdekat checkpoint nearestCP = FindNearestCheckpoint(); if (nearestCP != null) { safePosition = new Vector3(nearestCP.transform.position.x, nearestCP.transform.position.y + respawnYOffset, transform.position.z); Debug.Log("Respawn ke checkpoint terdekat: " + nearestCP.name); } canJump = false; justRespawned = true; //check if player is still alive if (healthmanager.isAlive()) { StartCoroutine(RespawnRoutine()); } else { StartCoroutine(GameOverRoutine()); } } bool IsSoalSedangAktif() { return soalManager != null && soalManager.IsSoalAktif(); } public void runleft() { sprite.flipX = true; if (!IsSoalSedangAktif() && !gameover.activeSelf && !finish.activeSelf) { body.linearVelocity = new Vector2(-speed, body.linearVelocity.y); } } public void runright() { sprite.flipX = false; if (!IsSoalSedangAktif() && !gameover.activeSelf && !finish.activeSelf) { body.linearVelocity = new Vector2(speed, body.linearVelocity.y); } } public void stop() { if (!IsSoalSedangAktif() && !gameover.activeSelf && !finish.activeSelf) { body.linearVelocity = new Vector2(0, body.linearVelocity.y); } } public void jump() { if (justRespawned || !isground || !canJump) return; if (!IsSoalSedangAktif() && !gameover.activeSelf && !finish.activeSelf) { body.linearVelocity = new Vector2(body.linearVelocity.x, jumpscale); canJump = false; if (Time.timeScale == 1) jump_audio.Play(); } } void UpdateAnimation() { if(body.linearVelocity.y > 0.01f){ animator.SetInteger("state", 2); walk_audio.Stop(); } else if(body.linearVelocity.y < -0.01f){ animator.SetInteger("state", 3); walk_audio.Stop(); } else { if(Math.Abs(body.linearVelocity.x) > speed/2f){ animator.SetInteger("state", 1); if(!walk_audio.isPlaying && isground){ if(Time.timeScale == 1){ walk_audio.Play(); } } } else { animator.SetInteger("state", 0); walk_audio.Stop(); } } } void OnTriggerEnter2D(Collider2D obj) { //if player touches water directly if(obj.CompareTag("Water") && transform.position.y < WaterDangerThreshold && !isRespawning) { HandleWaterFall(); } // Jika menyentuh checkpoint if (obj.CompareTag("Checkpoint")) { safePosition = new Vector3(obj.transform.position.x, obj.transform.position.y + respawnYOffset, transform.position.z); Debug.Log("Checkpoint updated at: " + safePosition); } //handle question activation if (obj.CompareTag("pos")){ if (soalManager != null && !soalManager.IsSoalAktif()) { soalManager.TampilkanSoal(); obj.gameObject.SetActive(false); // disable trigger papan soal_audio.Play(); } /*soal.transform.GetChild(obj.transform.GetSiblingIndex()).gameObject.SetActive(true); obj.gameObject.SetActive(false); soal_audio.Play();*/ } //handle finish line if(obj.CompareTag("Finish")) { if (soal.GetComponent().soalterjawab == 5) { finish.SetActive(true); finish_audio.Play(); } } } IEnumerator RespawnRoutine() { isRespawning = true; //yield return new WaitForSeconds(0.3f); body.gravityScale = 2; body.linearVelocity = Vector2.zero; transform.position = safePosition; Camera.main.GetComponent().enabled = true; Camera.main.GetComponent().player = this.transform; inWater = false; yield return new WaitUntil(() => { UpdateGroundWallCheck(); return isground && Mathf.Abs(body.linearVelocity.y) < 0.05f; }); canJump = true; yield return new WaitForSeconds(0.2f); justRespawned = false; isRespawning = false; Debug.Log($"Respawn complete. isground: {isground}, canJump: {canJump}"); } IEnumerator GameOverRoutine(){ yield return new WaitForSeconds(0.3f); gameover.SetActive(true); gameover_audio.Play(); Debug.Log("Game Over!"); } checkpoint FindNearestCheckpoint() { checkpoint nearest = null; float minDistance = Mathf.Infinity; Vector3 pos = transform.position; foreach (checkpoint cp in allCheckpoints) { float dist = Vector3.Distance(pos, cp.transform.position); if (dist < minDistance) { minDistance = dist; nearest = cp; } } return nearest; } // Visualize the raycasts for debugging void OnDrawGizmos() { // Ground check Gizmos.color = Color.red; Gizmos.DrawRay(transform.position, Vector2.down * rayLength); // Jump check Gizmos.color = Color.blue; Gizmos.DrawRay(transform.position, Vector2.down * 0.6f); // Wall checks Gizmos.color = Color.yellow; Gizmos.DrawRay(transform.position, Vector2.right * 0.3f); Gizmos.DrawRay(transform.position, Vector2.left * 0.3f); } }