0
votes

I'm working on a third person controller and I'm trying to prevent movement when a raycast in front of the player hits something. Currently when the player collides with a block the blend tree is set to 0 so that it shows the Idle animation. In this case the player appears to be not moving, which it obviously isn't because it's colliding with something, but the input of the user is still there.

This is visible when the player presses the jump button, because the player moves in the direction the user was initially trying to move. In the player controller there is a part that prevents the user from moving after the jump button has been pressed during the length of the jump (Until the player is grounded again). What I'm trying to achieve is that when the user is moving against something and presses the jump button the player will just jump up and won't go in the direction the user was initially trying to go, even if there is no more collider or obstruction.

Something like if the raycast hits something the user won't be able to add movement in that direction, but is still able to turn the player away from the object and move in that direction as long as the raycast doesn't hit a collider.

I'm no c# wizard, so most of the code below comes from a tutorial. I can write a raycast myself, but have no clue how to write the rest of what I'm trying to achieve.

Edit: Current script

public RaycastHit _Hit;
public LayerMask _RaycastCollidableLayers; //Set this in inspector, makes you able to say which layers should be collided with and which not.
public float _CheckDistance = 5f;

void Update()
    {

        PushStates();

        // Input
        Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
        Vector2 inputDir = input.normalized;
        bool running = Input.GetButton("Run");


        if (IsNotBlocked())
        {
            Move(inputDir, running);
        }

        float animationSpeedPercent = ((running) ? currentSpeed / runSpeed : currentSpeed / walkSpeed * .5f);
        anim.SetFloat("speedPercent", animationSpeedPercent, speedSmoothTime, Time.deltaTime);
        anim.SetFloat("speedPercentPush", animationSpeedPercent, speedSmoothTime, Time.deltaTime);

        // Check if walking or running

        if (animationSpeedPercent < 0.51f)
        {
            if (ShortJumpRaycast())
            {
                if (Input.GetButtonDown("Jump") && Time.time > canJump)
                {
                ShortJump();
                }
            }
            else
            {
                if (Input.GetButtonDown("Jump") && Time.time > canJump)
                {
                    JumpWalk();
                }
            }
        }
        else
        {
            //Debug.Log("You are Running");
            if (Input.GetButtonDown("Jump") && Time.time > canJump)
            {
                JumpRun();
            }
        }

        JumpCheck();
    }

void Move(Vector2 inputDir, bool running)
    {
        if (inputDir != Vector2.zero)
        {
            float targetRotation = Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg + cameraT.eulerAngles.y;
            transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
        }

        float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude;
        currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));

        velocityY += Time.deltaTime * gravity;

        Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY;

        controller.Move(velocity * Time.deltaTime);
        currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z).magnitude;

        // Checks if player is not grounded and is falling

        if (GroundCheck())
        {
            velocityY = 0;
            anim.SetBool("onAir", false);
        }
        else
        {
            anim.SetBool("onAir", true);
        }
    }

bool IsNotBlocked()
    {
        Vector3 forward = transform.TransformDirection(Vector3.forward);

        if (Physics.Raycast(transform.position, forward, out _Hit, _CheckDistance + 0.1f, _RaycastCollidableLayers))
            if (_Hit.collider == null)
            {
                Debug.Log("Raycast hit nothing");
                return true;
            }
        GameObject go = _Hit.collider.gameObject;
        if (go == null) //If no object hit, nothing is blocked.
            return true;
        else //An object was hit.
            return false;
    }
2

2 Answers

1
votes

Directly before move you can raycast in the direction of the movement and if it hits something you can cancel movement.

Something like:

Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY;
if(!Physics.Raycast(transform.position, transform.forward, distance)
{
    controller.Move(velocity * Time.deltaTime);
}
else
{
    controller.Move(Vector3.up * velocityY * Time.deltaTime);
}
1
votes

Something like if the raycast hits something the user won't be able to add movement in that direction

Here is an example of how you can perform a raycast to check whether something was hit or not.

//Example raycast code
//Variables
public RayCastHit _Hit;
public LayerMask _RaycastCollidableLayers; //Set this in inspector, makes you able to say which layers should be collided with and which not.
public float _CheckDistance = 5f;

//Method
bool IsNotBlocked(){
Vector3 forward = transform.TransformDirection(Vector3.forward);

if (Physics.Raycast(transform.position, forward, out _Hit, _CheckDistance + 0.1f, _RaycastCollidableLayers))
if (_Hit.collider == null)
{
    Debug.Log("Raycast hit nothing");
    return true;
}
GameObject go = _Hit.collider.gameObject;
if (go == null) //If no object hit, nothing is blocked.
return true;
else //An object was hit.
return false;
}

Basically,

  • The length of the ray is CheckDistance.

  • RayCastCollidableLayers determines which layers an object can be in for it to be collidable with the ray we create.

  • The code sets a direction stored in "forward", a raycast is then performed from the transform.position (position of object this script is attached to) in direction Vector3.Forward.

  • _Hit saves whatever the raycast hits. An object can then be accessed through it and stored as a GameObject. I call this object 'go'.

Feel free to ask questions.