Untitled
using System.Collections; using UnityEngine; using UnityEngine.Tilemaps; public class PlayerController : MonoBehaviour { [Header("Movement Settings")] public float speed; public float jump; public float maxJumpTime; public float jumpMultiplier; public LayerMask groundLayer; public Transform groundCheck; [Header("Attack Settings")] public GameObject meleeCheck; public GameObject projectilePrefab; public Transform shootPoint; public float projectileSpeed = 10f; [Header("Miscellaneous")] public GameObject fallDetector; public AudioClip jumpClip; public AudioClip atkClip; private CapsuleCollider2D boxCollider2d; private Rigidbody2D rigidbody2D; private Animator anim; private AudioSource audioPlayer; private SpriteRenderer spriteRenderer; private bool facingRight = true; public bool isJumping; public float currentJumpTime; public bool grounded = true; private bool isAttacking = false; public bool canShoot; [SerializeField] private GameObject OneWayHitCheck; private IEnvironmentModifier currentEnvironment; [SerializeField] private float wallCheckDistance = 0.5f; // Distance for wall detection [SerializeField] private LayerMask wallLayer; // Layer for walls private bool isTouchingWall; private Vector2 slopeNormalPerp; private float slopeDownAngle; private float slopeSideAngle; private float lastSlopeAngle; public bool canMagicMeat; [SerializeField] private PhysicsMaterial2D noFriction; [SerializeField] private PhysicsMaterial2D fullFriction; //slopes [SerializeField] private float slopeCheckDistance; [SerializeField] private float maxSlopeAngle; [SerializeField] private bool isOnSlope; [SerializeField] private bool canWalkOnSlope; //platform public bool isOnPlatform; public Rigidbody2D platformRb; //double jump powerup public bool canDoubleJump; public bool ShouldDoubleJump = true; private Vector2 newVelocity; private Vector2 newForce; public bool faceRight = true; public bool isOnIce = false; void Awake() { boxCollider2d = GetComponent<CapsuleCollider2D>(); rigidbody2D = GetComponent<Rigidbody2D>(); anim = GetComponent<Animator>(); audioPlayer = GetComponent<AudioSource>(); meleeCheck.SetActive(false); //headCollider = transform.Find("headCheck").GetComponent<BoxCollider2D>(); spriteRenderer = GetComponent<SpriteRenderer>(); if(spriteRenderer.size == new Vector2(-4, 4)) { faceRight = false; } else { faceRight = true; } } private void Update() { grounded = IsTouchingGround(); isTouchingWall = IsTouchingWall(); HandleAttack(); HandleJump(); } void FixedUpdate() { Debug.Log("Grounded: " + grounded); if (grounded) { SlopeCheck(); } HandleMovement(); if (currentEnvironment != null) { currentEnvironment.ApplyModifier(this); } } private void HandleMovement() { float inputHorizontal = Input.GetAxisRaw("Horizontal"); if (currentEnvironment is not WaterModifier) { if (isOnIce && grounded) //Ice physics { ApplyIceEffects(inputHorizontal); } if (grounded && isOnSlope && canWalkOnSlope && !isJumping) { HandleSlopeMovement(inputHorizontal); } else if (grounded && !isOnSlope && !isJumping && !isTouchingWall) { HandleFlatMovement(inputHorizontal); } else if (!grounded) { rigidbody2D.velocity = new Vector2(inputHorizontal * speed, rigidbody2D.velocity.y); } if (!grounded && !isTouchingWall) { rigidbody2D.velocity = new Vector2(inputHorizontal * speed, rigidbody2D.velocity.y); } } // Sprite flip logic if (inputHorizontal > 0 && !facingRight) { Flip(); } else if (inputHorizontal < 0 && facingRight) { Flip(); } anim.SetBool("Run", inputHorizontal != 0); } private void ApplyIceEffects(float inputHorizontal) { if (!grounded) return; // Skip ice effects if airborne if (isOnSlope) { // Calculate the slope's tangent direction Vector2 slopeDirection = slopeNormalPerp.normalized; Debug.Log("slope direction " + slopeDirection); if (inputHorizontal != 0) { // Move along the slope based on input direction float moveDirection = Mathf.Sign(inputHorizontal); // +1 for right, -1 for left Vector2 targetVelocity = slopeDirection * (moveDirection * speed); rigidbody2D.velocity = Vector2.Lerp(rigidbody2D.velocity, targetVelocity, 0.1f); rigidbody2D.gravityScale = 1; } else { // Project velocity onto the slope's tangent for consistent sliding float projectedVelocity = Vector2.Dot(rigidbody2D.velocity, slopeDirection); Debug.Log("PROJECTED vELOCITY :" + projectedVelocity); Vector2 slideVelocity = slopeDirection * projectedVelocity; // Apply deceleration to stop sliding over time if (Mathf.Abs(projectedVelocity) < 0.1f) { rigidbody2D.velocity = Vector2.zero; // Stop sliding completely rigidbody2D.gravityScale = 0; } else { Debug.Log("Sliding!" + slideVelocity * 0.95f); rigidbody2D.velocity = slideVelocity * 0.95f; // Gradual deceleration rigidbody2D.gravityScale = 1; } } } else { // Flat ground: consistent ice physics if (inputHorizontal == 0) { // Gradual deceleration when idle if (Mathf.Abs(rigidbody2D.velocity.x) < 0.1f) { rigidbody2D.velocity = new Vector2(0, rigidbody2D.velocity.y); } else { Debug.Log("Sliding!" + new Vector2( rigidbody2D.velocity.x * 0.98f, rigidbody2D.velocity.y )); rigidbody2D.velocity = new Vector2( rigidbody2D.velocity.x * 0.98f, rigidbody2D.velocity.y ); } } else { // Smooth movement based on input Vector2 targetVelocity = new Vector2(inputHorizontal * speed, rigidbody2D.velocity.y); Debug.Log("targetedVelocity: " + targetVelocity); rigidbody2D.velocity = Vector2.Lerp(rigidbody2D.velocity, targetVelocity, 0.1f); } } } private void HandleFlatMovement(float inputHorizontal) { if (isOnIce) { //ApplyIceEffects(inputHorizontal); } else { rigidbody2D.sharedMaterial = noFriction; Vector2 newVelocity = new Vector2( inputHorizontal * speed, rigidbody2D.velocity.y > 0 ? 0 : rigidbody2D.velocity.y // Prevent upward launch ); // Prevent upward launch rigidbody2D.velocity = newVelocity; rigidbody2D.gravityScale = 1; } if (inputHorizontal > 0 && !facingRight) { Flip(); } else if (inputHorizontal < 0 && facingRight) { Flip(); } anim.SetBool("Run", inputHorizontal != 0); } private void HandleSlopeMovement(float inputHorizontal) { if (inputHorizontal == 0) { // When stationary if (isOnIce) { ApplyIceEffects(inputHorizontal); } if (isOnSlope) { // Apply friction to prevent sliding on slopes rigidbody2D.sharedMaterial = fullFriction; rigidbody2D.velocity = Vector2.zero; rigidbody2D.gravityScale = 0; // Disable gravity when stationary on a slope } else { // Flat ground behavior rigidbody2D.sharedMaterial = fullFriction; rigidbody2D.velocity = Vector2.zero; rigidbody2D.gravityScale = 1; // Enable normal gravity } } else { // When moving rigidbody2D.sharedMaterial = noFriction; if (isOnSlope && slopeDownAngle > 0) { // Align velocity with slope newVelocity.Set( speed * slopeNormalPerp.x * -inputHorizontal, Mathf.Clamp(speed * slopeNormalPerp.y * -inputHorizontal, -5f,0) ); rigidbody2D.velocity = newVelocity; rigidbody2D.gravityScale = 1; // Ensure normal gravity during movement } else { // Flat ground or non-slope movement newVelocity.Set(speed * inputHorizontal, 0f); rigidbody2D.velocity = newVelocity; rigidbody2D.gravityScale = 1; } } } private void HandleJump() { //grounded = IsTouchingGround(); if (currentEnvironment is not WaterModifier) { if (Input.GetButtonDown("Jump")) { if (grounded) { isOnSlope = false; isOnIce = false; Debug.Log("Begun Jump"); ShouldDoubleJump = true; // Jump only when grounded isJumping = true; currentJumpTime = 0f; audioPlayer.PlayOneShot(jumpClip); } else if (isTouchingWall && !isJumping) { isJumping = true; currentJumpTime = 0f; rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, jump); audioPlayer.PlayOneShot(jumpClip); } else if (ShouldDoubleJump && canDoubleJump && !isJumping && rigidbody2D.velocity.y > 0) { ShouldDoubleJump = false; isJumping = true; currentJumpTime = 0f; rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, jump); audioPlayer.PlayOneShot(jumpClip); } if (Input.GetButton("Jump") && isJumping) { if (currentJumpTime < maxJumpTime) { Debug.Log("Current Jump Time" + currentJumpTime); rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, jump + (currentJumpTime * jumpMultiplier)); currentJumpTime += Time.deltaTime; } else { Debug.Log("Max Jump Time"); isJumping = false; } } if (Input.GetButtonUp("Jump")) { isJumping = false; Debug.Log("Not Jumping"); } // Ceiling detection to cancel jump if (IsTouchingCeiling()) { Debug.Log("Touched Ceiling"); rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, 0); // Cancel vertical velocity currentJumpTime = maxJumpTime; isJumping = false; // Stop the jump } } // Hold jump to increase height if (Input.GetButton("Jump") && isJumping) { if (currentJumpTime < maxJumpTime) { rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, jump + (currentJumpTime * jumpMultiplier)); currentJumpTime += Time.deltaTime; } else { isJumping = false; } } // Release jump button if (Input.GetButtonUp("Jump")) { isJumping = false; } if (!grounded) { rigidbody2D.sharedMaterial = noFriction; rigidbody2D.gravityScale = 1; } } anim.SetBool("grounded", grounded); } private bool IsTouchingGround() { float yOffset = boxCollider2d.bounds.size.y / 2f; float xOffset = boxCollider2d.bounds.size.x / 2f; Vector2 bottomLeft = new Vector2(transform.position.x - xOffset, transform.position.y - yOffset); Vector2 bottomRight = new Vector2(transform.position.x + xOffset, transform.position.y - yOffset); float rayLength = 0.1f; // Adjust the ray length as needed bool notTouchingGround = (!isJumping && (currentJumpTime >= maxJumpTime) && (rigidbody2D.velocity.y > 0)); Vector2 bottomCenter = new Vector2(transform.position.x, transform.position.y - yOffset); // Perform a box cast straight down from the bottom center of the player RaycastHit2D[] hits = Physics2D.BoxCastAll(bottomCenter, new Vector2(xOffset * 2, rayLength), 0f, Vector2.down, rayLength, groundLayer); // Visualize the box cast in the Scene view Debug.DrawRay(bottomCenter, Vector2.down * rayLength, Color.red); foreach (var hit in hits) { // Return true if the box cast hits the ground if (hit.collider != null) { //Debug.Log(hit.collider.gameObject.tag); if (hit.collider.gameObject.CompareTag("Ice")) { Debug.Log("Is on Ice"); isOnIce = true; } else { isOnIce = false; } // Add a condition to allow jumping through one-way platforms if (hit.collider.gameObject.layer == 3) { // Handle one-way platforms in a Tilemap if (hit.collider is CompositeCollider2D compositeCollider) { Tilemap tilemap = hit.collider.GetComponent<Tilemap>(); if (tilemap != null) { // Check tiles under both bottom corners Vector3Int leftTilePosition = tilemap.WorldToCell(bottomLeft); Vector3Int rightTilePosition = tilemap.WorldToCell(bottomRight); TileBase leftTile = tilemap.GetTile(leftTilePosition); TileBase rightTile = tilemap.GetTile(rightTilePosition); // Ensure both corners are above tiles if (leftTile != null && rightTile != null) { Vector3 leftTileWorldPosition = tilemap.GetCellCenterWorld(leftTilePosition); Vector3 rightTileWorldPosition = tilemap.GetCellCenterWorld(rightTilePosition); // Check for PlatformEffector2D components in the Tilemap PlatformEffector2D leftEffector = tilemap.GetComponent<PlatformEffector2D>(); PlatformEffector2D rightEffector = tilemap.GetComponent<PlatformEffector2D>(); Debug.Log("Not touching ground: " + notTouchingGround); // Skip tiles with upward-facing Platform Effectors during upward movement if (notTouchingGround) { if ((leftEffector != null && Mathf.Approximately(leftEffector.rotationalOffset, 0f)) || (rightEffector != null && Mathf.Approximately(rightEffector.rotationalOffset, 0f))) { Debug.Log("Plateforme"); return false; } if ((transform.position.y - boxCollider2d.bounds.size.y) > leftTileWorldPosition.y && (transform.position.y - boxCollider2d.bounds.size.y) > rightTileWorldPosition.y) { Debug.Log("Sur le sol"); return true; } } else { Debug.Log("Pas de sol"); return false; } } } } // Handle other one-way platforms or solid ground else if (hit.collider.TryGetComponent(out PlatformEffector2D effector)) { if (currentJumpTime >= maxJumpTime && (transform.position.y - boxCollider2d.bounds.size.y) > hit.collider.bounds.max.y - 0.01f && bottomLeft.y > hit.collider.bounds.max.y - 0.01f && bottomRight.y > hit.collider.bounds.max.y - 0.01f) { Debug.Log("Plateforme solide"); return true; } } else { // Handle solid ground if (bottomLeft.y > hit.collider.bounds.max.y - 0.01f && bottomRight.y > hit.collider.bounds.max.y - 0.01f) { Debug.Log("Sol solide"); return true; } } } anim.SetBool("grounded", hit.collider != null); // Return true if the box cast hits the ground return hit.collider != null; } } return false; } private void HandleAttack() { if (Input.GetButtonDown("Fire2") && !isAttacking) { anim.SetTrigger("Attack2"); StartCoroutine(DoAttack()); if (canShoot) { ShootProjectile(); } } } IEnumerator DoAttack() { isAttacking = true; meleeCheck.SetActive(true); audioPlayer.PlayOneShot(atkClip); yield return new WaitForSeconds(0.3f); isAttacking = false; meleeCheck.SetActive(false); } private void Flip() { facingRight = !facingRight; Vector3 currentScale = transform.localScale; currentScale.x *= -1; transform.localScale = currentScale; } private void OnTriggerEnter2D(Collider2D collision) { if (collision.TryGetComponent<IEnvironmentModifier>(out var modifier)) { currentEnvironment = modifier; } } private void OnTriggerExit2D(Collider2D collision) { if (collision.TryGetComponent<IEnvironmentModifier>(out var modifier) && modifier == currentEnvironment) { currentEnvironment = null; } } private bool IsTouchingCeiling() { float yOffset = boxCollider2d.bounds.extents.y; float xOffset = boxCollider2d.bounds.extents.x; Vector2 topLeft = new Vector2(transform.position.x - xOffset, transform.position.y + yOffset); Vector2 topRight = new Vector2(transform.position.x + xOffset, transform.position.y + yOffset); RaycastHit2D[] hits = Physics2D.BoxCastAll(topLeft, new Vector2(boxCollider2d.bounds.size.x, 0.1f), 0f, Vector2.up, 0.1f, groundLayer); foreach (var hit in hits) { if (hit.collider != null) { // Skip upward-facing Platform Effectors during upward movement if (hit.collider.TryGetComponent(out PlatformEffector2D effector)) { if (Mathf.Approximately(effector.rotationalOffset, 0f) && rigidbody2D.velocity.y > 0) { Debug.Log("Plateforme"); return false ; } } else if (hit.collider is CompositeCollider2D compositeCollider) { Tilemap tilemap = hit.collider.GetComponent<Tilemap>(); if (tilemap != null) { Vector3Int tilePosition = tilemap.WorldToCell(transform.position + new Vector3(0, yOffset + 0.05f, 0)); TileBase tile = tilemap.GetTile(tilePosition); if (tile != null) { PlatformEffector2D tileEffector = tilemap.GetComponent<PlatformEffector2D>(); // Skip tiles with upward-facing Platform Effectors during upward movement if (tileEffector != null && Mathf.Approximately(tileEffector.rotationalOffset, 0f) && rigidbody2D.velocity.y > 0) { Debug.Log("Plateforme"); continue; } Vector3 tileWorldPosition = tilemap.GetCellCenterWorld(tilePosition); if (transform.position.y < tileWorldPosition.y) { Debug.Log("Plafond!"); return true; } } } } else { // Handle solid objects if (topLeft.y < hit.collider.bounds.min.y && topRight.y < hit.collider.bounds.min.y) { Debug.Log("Plafond Solide!"); return true; } } } } return false; } private bool IsTouchingWall() { // Cast ray to the left and right of the player Vector2 position = transform.position; RaycastHit2D wallHitLeft = Physics2D.Raycast(position, Vector2.left, wallCheckDistance, wallLayer); RaycastHit2D wallHitRight = Physics2D.Raycast(position, Vector2.right, wallCheckDistance, wallLayer); Debug.DrawRay(position, Vector2.left * wallCheckDistance, Color.red); // Visualize left ray Debug.DrawRay(position, Vector2.right * wallCheckDistance, Color.red); // Visualize right ray // Check if the left wall is not an upward-facing Platform Effector bool wallOnLeft = wallHitLeft.collider != null && (!wallHitLeft.collider.TryGetComponent(out PlatformEffector2D effectorLeft) || !Mathf.Approximately(effectorLeft.rotationalOffset, 0f)); // Check if the right wall is not an upward-facing Platform Effector bool wallOnRight = wallHitRight.collider != null && (!wallHitRight.collider.TryGetComponent(out PlatformEffector2D effectorRight) || !Mathf.Approximately(effectorRight.rotationalOffset, 0f)); Debug.Log("Murs: " + (wallOnLeft || wallOnRight)); return wallOnLeft || wallOnRight; } void ShootProjectile() { if (projectilePrefab != null && shootPoint != null) { // Instantiate the projectile at the shootPoint position and rotation GameObject newProjectile = Instantiate(projectilePrefab.gameObject, shootPoint.position, shootPoint.rotation); PlayerProjectle playerProjectle = newProjectile.GetComponent<PlayerProjectle>(); Debug.Log("Tir"); // Calculate the direction of the projectile Vector2 shootDirection = facingRight ? Vector2.right : Vector2.left; playerProjectle.facingRight = facingRight ? true : false; // Access the projectile's Rigidbody2D component Rigidbody2D projectileRigidbody = playerProjectle.GetComponent<Rigidbody2D>(); if (projectileRigidbody != null) { // Apply force to the projectile in the shoot direction //playerProjectle.maxDistance += Math.Abs(rigidbody2D.velocity.x/2); projectileRigidbody.velocity = shootDirection * playerProjectle.speed + rigidbody2D.velocity; Debug.Log("Vitesse projectile:" + projectileRigidbody.velocity); } } } //Slope methods private void SlopeCheck() { // Position at the player's feet (adjusted to align with bottom edge) Vector2 checkPos = (Vector2)transform.position - new Vector2(0f, boxCollider2d.bounds.extents.y); // Perform horizontal and vertical slope checks SlopeCheckHorizontal(checkPos); SlopeCheckVertical(checkPos); if (isOnSlope) { Debug.Log("Is on Slope"); rigidbody2D.sharedMaterial = fullFriction; Debug.Log("SlopeNormalPerp:" + slopeNormalPerp.x + ", " + slopeNormalPerp.y); } else { Debug.Log("Not on Slope"); } } private void SlopeCheckHorizontal(Vector2 checkPos) { RaycastHit2D slopeHitFront = Physics2D.Raycast(checkPos, transform.right, slopeCheckDistance*2, groundLayer); RaycastHit2D slopeHitBack = Physics2D.Raycast(checkPos, -transform.right, slopeCheckDistance*2, groundLayer); Debug.DrawRay(checkPos, transform.right * slopeCheckDistance, Color.red); Debug.DrawRay(checkPos, -transform.right * slopeCheckDistance, Color.blue); if (slopeHitFront || slopeHitBack) { slopeSideAngle = slopeHitFront ? Vector2.Angle(slopeHitFront.normal, Vector2.up) : Vector2.Angle(slopeHitBack.normal, Vector2.up); if (slopeSideAngle <= maxSlopeAngle) { Debug.Log("Is On Slope H"); isOnSlope = true; Debug.Log("slope side angle " + slopeSideAngle); return; } } else { Debug.Log("Is not on slope H"); slopeSideAngle = 0f; isOnSlope = false; } } private void SlopeCheckVertical(Vector2 checkPos) { float colliderWidth = boxCollider2d.bounds.size.x; int rayCount = 5; // Number of rays to cast float raySpacing = colliderWidth / (rayCount - 1); float rayStartOffset = -colliderWidth / 2; // Start at the left of the collider slopeDownAngle = 0f; // Reset slope angle isOnSlope = false; // Reset slope state canWalkOnSlope = false; for (int i = 0; i < rayCount; i++) { // Calculate the position for this ray Vector2 rayOrigin = checkPos + new Vector2(rayStartOffset + (i * raySpacing), 0); RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.down, slopeCheckDistance, groundLayer); Debug.DrawRay(rayOrigin, Vector2.down * slopeCheckDistance, Color.yellow); // Debug visualization if (hit) { // Calculate the slope angle and perpendicular direction float currentSlopeAngle = Vector2.Angle(hit.normal, Vector2.up); // Update slope values if this ray detects a valid slope if ((currentSlopeAngle>0)&&(currentSlopeAngle <= maxSlopeAngle)) { slopeNormalPerp = Vector2.Perpendicular(hit.normal).normalized; slopeDownAngle = currentSlopeAngle; isOnSlope = true; canWalkOnSlope = true; Debug.DrawRay(hit.point, slopeNormalPerp, Color.green); // Slope perpendicular Debug.DrawRay(hit.point, hit.normal, Color.red); // Slope normal return; // Stop further checks once a valid slope is found } } } // If no valid slope is detected slopeNormalPerp = Vector2.zero; slopeDownAngle = 0f; isOnSlope = false; canWalkOnSlope = false; } private void OnCollisionEnter2D(Collision2D collision) { if (collision.collider.CompareTag("Ice")) { isOnIce = true; } } private void OnCollisionExit2D(Collision2D collision) { if (collision.collider.CompareTag("Ice")) { isOnIce = false; } } }
Leave a Comment