How can I break a wall when player Dash against it?

I´m trying to make player break some walls when Dash. Mesh Destroy code is okay, took it from a tutorial and implemented it with some changes from user “an01f01” comment below link code (code link bellow), Dash works fine too, can detect colliders.

I Want the player cross wall only when dashes into it. Know how to make this two scripts work together? I´m relatively new on coding, only understand some logic to cut and put code together. Project is 3D.

public float dashSpeed;
public float dashTime;
public float dashCoolDown;

 < dashCoolDown -= Time.deltaTime;

 if (Input.GetKeyDown(KeyCode.LeftShift))
 {
     if (dashCoolDown <= 0)
     {
         StartCoroutine(Dash());
     }


 }
    IEnumerator Dash()
    {
        float startTime = Time.time;

        while (Time.time < startTime + dashTime)
        {

            Player.Move(movePlayer * dashSpeed * Time.deltaTime);
            dashCoolDown = 3;



            yield return null;
        }
   
        
    }

Breakable Walls code link: MeshDestroy => Put it on a game object with a mesh filter and renderer. Make sure to have read/write enabled on fbx import · GitHub

This is my Character Controller code (walls doesn´t work yet):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour {

    //Movement
    public float horizontalMove;
    public float verticalMove;
    public CharacterController Player;
    private Vector3 playerInput;
    public float playerSpeed;
    private Vector3 movePlayer;

    //Gravity
    public float gravity = 9.8f;
    public float fallVelocity;
    public float jumpForce;
    //Double Jump
    public float doubleJumpForce;
    private int jumpCount = 0;

 
    
    
    // Camera
    public Camera mainCamera;
    private Vector3 camForward;
    private Vector3 camRight;

    //Slope
    public bool isOnSlope = false;
    private Vector3 hitNormal;
    public float slideVelocity;
    public float slopeForceDown;

    //Dash
    public float dashSpeed;
    public float dashTime;
    public float dashCoolDown;
    private bool isDashing = false;

    //Animation Variables

    public Animator PlayerAnimatorController;

    // Start is called before the first frame update
    void Start()
    {
        Player = GetComponent<CharacterController>();
        PlayerAnimatorController = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        //Movement
        horizontalMove = Input.GetAxis("Horizontal");
        verticalMove = Input.GetAxis("Vertical");

        playerInput = new Vector3(horizontalMove, 0, verticalMove);
        playerInput = Vector3.ClampMagnitude(playerInput, 1);

        PlayerAnimatorController.SetFloat("WalkVelocity", playerInput.magnitude * playerSpeed);

        camDirection();

        // I think it makes character turn his body towards movement direction
        movePlayer = playerInput.x * camRight + playerInput.z * camForward;

        movePlayer = movePlayer * playerSpeed;

        Player.transform.LookAt(Player.transform.position + movePlayer);

        SetGravity();

        PlayerSkills();

      

    }

    void camDirection()
    {
        camForward = mainCamera.transform.forward;
        camRight = mainCamera.transform.right;

        camForward.y = 0;
        camRight.y = 0;

        camForward = camForward.normalized;
        camRight = camRight.normalized;
    }

    // void for Player Skills

    public void PlayerSkills()
    {
        //Jump and Double Jump

        if (Player.isGrounded && Input.GetButtonDown("Jump"))
        {
            fallVelocity = jumpForce;
            movePlayer.y = fallVelocity;

            PlayerAnimatorController.SetTrigger("PlayerJump");

            
        }
        else if (jumpCount < 1 && Player && Input.GetButtonDown("Jump"))
        {
            fallVelocity = doubleJumpForce;
            movePlayer.y = fallVelocity;

            PlayerAnimatorController.SetTrigger("PlayerJump");
            jumpCount ++;
 

         }
        if (Player.isGrounded)
        {
            jumpCount = 0;
        }


        //Dash if
        dashCoolDown -= Time.deltaTime;

        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            if (dashCoolDown <= 0)
            {
                StartCoroutine(Dash());
            }


        }


        Player.Move(movePlayer * Time.deltaTime);
    }

    //Gravity
    void SetGravity()
    {

        if (Player.isGrounded)
        {
            fallVelocity = -gravity * Time.deltaTime;
            movePlayer.y = fallVelocity;
        }
        else
        {
            fallVelocity -= gravity * Time.deltaTime;
            movePlayer.y = fallVelocity;
            PlayerAnimatorController.SetFloat("PlayerVerticalVelocity", Player.velocity.y);
        }
        PlayerAnimatorController.SetBool("isGrounded", Player.isGrounded);

        SlideDown();
    }
    // Slope

    public void SlideDown()
    {
        isOnSlope = Vector3.Angle(Vector3.up, hitNormal) >= Player.slopeLimit;

        if (isOnSlope)
        {
            movePlayer.x += ((1f - hitNormal.y) * hitNormal.x) * slideVelocity;
            movePlayer.z +=((1f - hitNormal.y) * hitNormal.z) * slideVelocity;
            
            movePlayer.y += slopeForceDown;
        }
    }

    private void OnControllerColliderHit(ControllerColliderHit hit)
    {
        hitNormal = hit.normal;

    
    }
    

    //Dash IEnumerator
    IEnumerator Dash()
    {
        float startTime = Time.time;

        while (Time.time < startTime + dashTime)
        {

            Player.Move(movePlayer * dashSpeed * Time.deltaTime);
            dashCoolDown = 3;


            yield return null;
        }
   
        
    }

    //VRARDAJ modifications

    void OnTriggerEnter(Collider other)
    {

        if (other.tag.Equals("Player") && other.GetComponent<Dash>().isDashing)
            _meshDestroy.DestroyMesh();
    }

    //End of VRARDAJ modifications

    private void OnAnimatorMove()
    {

    }
}

I have some other scripts for pushRigidBody and for move with mobile platforms that are invoked in my Player script.

With VRARDAJ modifications console now shows me this:

Thanks to him I understand what to do but don´t know how. haha

My source tutorials for Player Controller are this youtube list (spanish alert), for dash is this one and double jump was coded by me taking both videos as reference. Dash code isn´t the same because the version I needed is on comments.

@dstears After cleaning some errors on console and learning in the process, I can play again but Dash doesn´t work when I push Left Shift and console doesn´t show me any error.

It looks my Walls Script worked once when pushed my test wall and then knocked down it somehow. Didn´t look broke at all but could see a triangle on floor after I pushed the whole object. Couldn´t do it again since.

Here are my script changes:

        //Dash if
        dashCoolDown -= Time.deltaTime;

        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            if (dashCoolDown <= 0)
            {
                GetComponent("Dash");
            }


        }

Made a public class for Dash:

public class Dash
{
    //Dash
    public float dashSpeed;
    public float dashTime;
    public float dashCoolDown;
    public bool isDashing = false;
    private Vector3 movePlayer;

    public CharacterController Player;

    IEnumerator Dashing()
    {
        float startTime = Time.time;

        while (Time.time < startTime + dashTime)
        {

            Player.Move(movePlayer * dashSpeed * Time.deltaTime);
            dashCoolDown = 3;


            yield return null;
        }


    }
}

Put OnEnterTrigger in the Wall Script.

Restarted Unity and now Shows me this:

Let’s take a step back here and describe the individual steps that we need to reach our goal:

  1. Player can dash
  2. Wall can be destroyed
  3. Wall can detect collision with the player
  4. PlayerController needs to track if the player is currently dashing.
  5. PlayerController needs to expose the dashing state publicly so other scripts can get that information.
  6. Wall determines if the player was dashing when the collision occurred.

For #1, if your original Dash code was working, just go with that.
Test #1 to make sure it works on its own.

For #2, it sounds like you got this working for the github project that you linked.
Test #2 to makes sure it works on its own.

For #3, we need a script on the wall that will be able to detect collision with the player. Assuming your wall has a collider, then you can use OnCollisionEnter to detect a collision with the player. This assumes either the player or the wall has a Rigidbody.

Full disclosure: I have never worked with the CharacterController movement script, so my experience here may not apply. I don’t know if CharacterController handles collisions differently.

private void OnCollisionEnter(Collision collision)
{
    Debug.Log("Object " + gameObject.name + " collided with " + collision.gameObject.name);
    if(collision.gameObject.CompareTag("Player"))
    {
        Debug.Log("Wall was hit by player");
    }
}

Now test #3 to see if it can detect any collision. Don’t move forward until this is working.

For #4, we can add a member variable to your PlayerController script that will be true while the player is in the dashTime period.

private bool dashing = false;  //NEW
IEnumerator Dash()
{
    float startTime = Time.time;
    dashing = true; //NEW

    while (Time.time < startTime + dashTime)
    {
        Player.Move(movePlayer * dashSpeed * Time.deltaTime);
        dashCoolDown = 3; //Note that you don't need to set this cooldown on every
                            //iteration of the loop. You can set it before or after
                            //the while loop depending on your intention.

        yield return null;
    }
    dashing = false; //NEW
}

For #5, you could make dashing a public variable, but that is risky because some other script could change the value. You could define a “getter” as a read-only interface to the variable, but I found these confusing when I was first learning. I find the simplest approach is to add a simple public method to the PlayerController script.

public bool IsDashing()
{
    return dashing;
}

Which brings us finally to #6. In order for the wall to get information about the thing that collided with the wall, we will have to use GetComponent to get a reference to something on the colliding object. If the colliding object has the tag “Player”, then we can hopefully assume it has the PlayerController component which we can get a reference to. Once we have that reference, we can access the public IsDashing method that we added for #5 above.

private void OnCollisionEnter(Collision collision)
{
    Debug.Log("Object " + gameObject.name + " collided with " + collision.gameObject.name);
    if(collision.gameObject.CompareTag("Player"))
    {
        Debug.Log("Wall was hit by player");

        PlayerController pc = collision.gameObject.GetComponent<PlayerController>();
        //For safety, it is best to check if pc is null before trying to access it.
        //You could also use TryGetComponent instead.
        //But for simplicity, let's assume that anything with the Player tag must
        //have a PlayerController component.
        if(pc.IsDashing())
        {
            //Do whatever you need to do to break the wall
        }
    }
}

As implied above, I recommend doing one step at a time and making sure the changes work as expected before trying to get it all working together.

In coroutines,

yield return null

means “Don’t wait.”

It will cause your Dash() method to iterate as fast as the CPU can process it, which is generally going to be a lot faster than your frame rate. If you add a count variable into your coroutine and log the number of executions of Dash, you’ll probably see that it trounces the number of executions in the Update loop of any MonoBehaviour. That being the case, Time.deltaTime is going to malfunction in Dash(), since it is only appropriate to multiply it into the equation once per frame.

Remove:

yield return null

Replace it with:

yield return new WaitForEndOfFrame();

Now, your coroutine should be behaving more as you’d expect.

Next, you just need a trigger to invoke the DestroyMesh() method if conditions are met. You said you can detect colliders. Is that via OnCollionEnter() or OnTriggerEnter()? In either case, the script containing your trigger function needs to be either on the player or the wall.

If wall checks player, it’ll need to access your Dash script somehow and read whether player is currently dashing. In a simple game, you might represent that state with something as basic as a bool value (isDashing). In a larger game, you’d probably want to flesh that out into a full state machine. If “isDashing” is confirmed, call the DestroyMesh() method.

Example:

void OnTriggerEnter(Collider other)
{
    if (other.tag.Equals("Player") && other.GetComponent<Dash>().isDashing)
        _meshDestroy.DestroyMesh();
}

If player checks wall instead, the roles reverse, but implementation is similar. I prefer wall-checks-player.