Unity2D:无法平衡墙跳力

时间:2018-10-09 19:33:42

标签: unity3d

我的目的是使角色墙跳/爬/滑,我的滑动部分可以正常工作,但是如果他在墙滑动时跳,他应该“反弹”回墙,问题是我可以平衡力量。在我看到的所有教程中,仅需检测角色是否在墙面滑动,如果他在并且他跳了,就向墙施加相反的力。

这对我不起作用,因为如果我施加足够大的力使他跳起来,他走得太快了,玩家几乎看不到他跳了,他只是看到角色现在在墙上更高。如果我施加较小的力,这还不足以做出很大的跳跃,那么玩家将不得不击打一千次才能使他在墙壁上上升几厘米。

感谢任何帮助,我已经尝试了很多事情,甚至尝试冻结控件,将重力比例设置为0,并使用MoveTowards将角色fo设置到正确的点,这就是我的绝望。

我也是Unity的新手,所以我可能缺少一些非常简单的东西。

以下是显示角色行为的gif: https://imgur.com/a/TgUHzP6

这是我角色的剧本的相关部分:

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

public class TheBot : MonoBehaviour {

    public float speed;
    public int jumpForce;
    public Transform groundCheck;
    public Transform meleeCheck;
    public Transform bulletSpawner;
    public LayerMask layerGround;
    public float meleeCoolDown;
    public float meleeDamage;

    private Rigidbody2D body;
    private Animator anim;
    private Dash dashController;
    private Shooter shotController;
    private float unloadWaitingTime = 3;
    private float idleGunTime = 0;

    private bool facingRight = true;
    private bool onGround = true;
    private bool jumping = false;
    private bool attacking = false;
    private bool dead = false;
    private bool isGunLoaded = false;
    private bool isGunLoading = false;
    private bool isGunUnloading = false;
    private bool takingDamage = false;
    private bool dashing = false;
    private bool isWallSliding = false;

    private float wallJumpTime = 0f;
    private Vector3[] wallJumpControlPoint;

    // Use this for initialization
    void Start () {
        body = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        dashController = GetComponent<Dash>();
        shotController = GetComponent<Shooter>();
    }

    // Update is called once per frame
    void Update () {
        PlayAnimations();
        CheckIfGrounded();
        checkIfWallSliding();
        dashing = dashController.IsDashing();

        if (Input.GetButtonDown("Jump") && (onGround || isWallSliding)  && !isGunLoading && !jumping && !takingDamage){
            jumping = true;
            wallJumpControlPoint = new Vector3[3];
            wallJumpControlPoint[0] = body.position;
            wallJumpControlPoint[1] = new Vector3(body.position.x +4, body.position.y + 2);
            wallJumpControlPoint[2] = new Vector3(body.position.x, body.position.y + 4);
        }
        if (Input.GetButtonDown("Melee") && !attacking && !isGunLoading){
            Attack();
        }
        if(Input.GetButtonDown("Ranged") && !attacking  && !isGunLoading && onGround){
            Shoot();
        }

        if(Input.GetButtonDown("Dash") && !attacking && !isGunLoading && onGround){
            dashController.DashTo(facingRight? Dash.RIGHT : Dash.LEFT);
        }


        if(isGunLoaded){
            idleGunTime += Time.deltaTime;
            if (idleGunTime >= unloadWaitingTime){
                UnloadGun();
            }
        }

    }

    void FixedUpdate(){
        if(!takingDamage){

            float move = Input.GetAxis("Horizontal");

            //while charachter is wall sliding, slowly fall
            if (isWallSliding){
                body.velocity = new Vector2(body.velocity.x, -0.7f);
            }

            if(!dashing){
                if(onGround){
                    //if not dashing on on ground, walk with normal speed
                    body.velocity = new Vector2(move * speed, body.velocity.y);
                } else {
                    //if character is not on ground, reduce the speed so he doesn't jump too far away
                    body.velocity = new Vector2(move * (speed * 0.7f), body.velocity.y);
                }
            }

            if((move < 0 && facingRight) || (move > 0 && !facingRight) ){
                //control direction character is facing
                Flip();
            }

            if (jumping){

                if(isWallSliding){
                    body.velocity = new Vector2(30, 20);
                } else {
                    body.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
                }

                if(Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.LeftArrow)){
                    //if is moving while jumping, reduce jump height
                    body.velocity = new Vector2(body.velocity.x, body.velocity.y*0.8f);
                }
                onGround = false;
                jumping = false;
            }       
        }
    }

    void CheckIfGrounded(){
        onGround = false;
        Collider2D[] collisionResults = new Collider2D[2];
        int objectsBeneath = Physics2D.OverlapBoxNonAlloc(groundCheck.position, new Vector2(0.9f, 0.3f), 0.0f, collisionResults, layerGround);
        for (int i=0; i <objectsBeneath; i++ ){
            if (!GameObject.ReferenceEquals(gameObject, collisionResults[i].gameObject)){
                onGround = true;
            }
        }
    }

    void checkIfWallSliding(){
        if (!onGround){
            RaycastHit2D[] ray = new RaycastHit2D[1];
            int totalRayHits = Physics2D.LinecastNonAlloc(bulletSpawner.position, body.position, ray, 1 << LayerMask.NameToLayer("SolidGround"));
            bool wallFound = totalRayHits > 0 && ray[0].collider.gameObject.tag == "SolidGround";

            isWallSliding = wallFound && ( (facingRight && Input.GetKey(KeyCode.RightArrow)) ||  (!facingRight && Input.GetKey(KeyCode.LeftArrow))) ;
        } else {
            isWallSliding = false;
            if (body.velocity.y > 10){
                body.velocity = new Vector2(body.velocity.x, 5);
            }
        }
    }


    public void Die(){
        dead = true;
    }

}

1 个答案:

答案 0 :(得分:1)

如前所述,您将需要降低跳跃时的水平跳跃加速度/速度。

当您跳墙时,您将向墙壁施压。就像您的代码当前一样,当您处于空中时,您的水平速度将设置为按您所按的方向。这样一来,墙壁上的任何水平运动都很难看清,除非它大得足以在一帧中将您推得很远。

这(以及我们在评论中讨论的更改)是为什么您以前尝试的低跳跃幅度无效的原因。

要解决此问题,您必须更改空气控制的工作方式。解决该问题的一种方法是使它在水平速度上添加一个固定的修改器,而不是直接将其设置为目标速度。

if(!dashing){
    if(onGround){
        //if not dashing on on ground, walk with normal speed
        body.velocity = new Vector2(move * speed, body.velocity.y);
    } else {
        //if character is not on ground, reduce the speed so he doesn't jump too far away
        float airControlAccelerationLimit = 0.5f;  // Higher = more responsive air control
        float airSpeedModifier = 0.7f; // the 0.7f in your code, affects max air speed
        float targetHorizVelocity = move 
                * speed 
                * airSpeedModifier;  // How fast we are trying to move horizontally
        float targetHorizChange = targetHorizVelocity 
                - body.velocity.x; // How much we want to change the horizontal velocity
        float horizChange = Mathf.Clamp(
                targetHorizChange ,
                -airControlAccelerationLimit , 
                airControlAccelerationLimit ); // How much we are limiting ourselves 
                                               // to changing the horizontal velocity
        body.velocity = new Vector2(body.velocity.x + horizChange, body.velocity.y);
    }
}

在您的代码中,这是在确保我们仅更新一次速度或每次AddForce使用FixedUpdate的同时。而且,我们还更改了滑墙减速代码,使其仅在玩家下楼速度快于滑坡速度时激活。

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

public class TheBot : MonoBehaviour {

    public float speed;
    public int jumpForce;
    public Transform groundCheck;
    public Transform meleeCheck;
    public Transform bulletSpawner;
    public LayerMask layerGround;
    public float meleeCoolDown;
    public float meleeDamage;

    private Rigidbody2D body;
    private Animator anim;
    private Dash dashController;
    private Shooter shotController;
    private float unloadWaitingTime = 3;
    private float idleGunTime = 0;

    private bool facingRight = true;
    private bool onGround = true;
    private bool jumping = false;
    private bool attacking = false;
    private bool dead = false;
    private bool isGunLoaded = false;
    private bool isGunLoading = false;
    private bool isGunUnloading = false;
    private bool takingDamage = false;
    private bool dashing = false;
    private bool isWallSliding = false;

    private float wallJumpTime = 0f;
    private Vector3[] wallJumpControlPoint;

    // Use this for initialization
    void Start () {
        body = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        dashController = GetComponent<Dash>();
        shotController = GetComponent<Shooter>();
    }

    // Update is called once per frame
    void Update () {
        PlayAnimations();
        CheckIfGrounded();
        checkIfWallSliding();
        dashing = dashController.IsDashing();

        if (Input.GetButtonDown("Jump") && (onGround || isWallSliding)  && !isGunLoading && !jumping && !takingDamage){
            jumping = true;
            wallJumpControlPoint = new Vector3[3];
            wallJumpControlPoint[0] = body.position;
            wallJumpControlPoint[1] = new Vector3(body.position.x +4, body.position.y + 2);
            wallJumpControlPoint[2] = new Vector3(body.position.x, body.position.y + 4);
        }
        if (Input.GetButtonDown("Melee") && !attacking && !isGunLoading){
            Attack();
        }
        if(Input.GetButtonDown("Ranged") && !attacking  && !isGunLoading && onGround){
            Shoot();
        }

        if(Input.GetButtonDown("Dash") && !attacking && !isGunLoading && onGround){
            dashController.DashTo(facingRight? Dash.RIGHT : Dash.LEFT);
        }


        if(isGunLoaded){
            idleGunTime += Time.deltaTime;
            if (idleGunTime >= unloadWaitingTime){
                UnloadGun();
            }
        }

    }

    void FixedUpdate(){
        if(!takingDamage){

            float move = Input.GetAxis("Horizontal");

            //while charachter is wall sliding, slowly fall
            if (isWallSliding && !jumping && body.velocity.y < -0.7f){ 
                body.velocity = new Vector2(body.velocity.x, -0.7f)
            }

            if(!dashing){
                if(onGround){
                    //if not dashing on on ground, walk with normal speed
                    body.velocity = new Vector2(move * speed, body.velocity.y);
                } else {
                    //if character is not on ground, reduce the speed so he doesn't jump too far away
                    float airControlAccelerationLimit = 0.5f;  // Higher = more responsive air control
                    float airSpeedModifier = 0.7f; // the 0.7f in your code, affects max air speed
                    float targetHorizVelocity = move 
                            * speed 
                            * airSpeedModifier;  // How fast we are trying to move horizontally
                    float targetHorizChange = targetHorizVelocity 
                            - body.velocity.x; // How much we want to change the horizontal velocity
                    float horizChange = Mathf.Clamp(
                            targetHorizChange ,
                            -airControlAccelerationLimit , 
                            airControlAccelerationLimit ); // How much we are limiting ourselves 
                                                           // to changing the horizontal velocity
                    body.velocity = new Vector2(body.velocity.x + horizChange, body.velocity.y);
                }
            }

            if((move < 0 && facingRight) || (move > 0 && !facingRight) ){
                //control direction character is facing
                Flip();
            }

            if (jumping){

                if(isWallSliding){
                    body.velocity = new Vector2(body.velocity.x + 0.25f * jumpForce, jumpForce);
                } else {
                    body.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
                }

                if(Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.LeftArrow)){
                    //if is moving while jumping, reduce jump height
                    body.velocity = new Vector2(body.velocity.x, body.velocity.y*0.8f);
                }
                onGround = false;
                jumping = false;
            }       
        }
    }

    void CheckIfGrounded(){
        onGround = false;
        Collider2D[] collisionResults = new Collider2D[2];
        int objectsBeneath = Physics2D.OverlapBoxNonAlloc(groundCheck.position, new Vector2(0.9f, 0.3f), 0.0f, collisionResults, layerGround);
        for (int i=0; i <objectsBeneath; i++ ){
            if (!GameObject.ReferenceEquals(gameObject, collisionResults[i].gameObject)){
                onGround = true;
            }
        }
    }

    void checkIfWallSliding(){
        if (!onGround){
            RaycastHit2D[] ray = new RaycastHit2D[1];
            int totalRayHits = Physics2D.LinecastNonAlloc(bulletSpawner.position, body.position, ray, 1 << LayerMask.NameToLayer("SolidGround"));
            bool wallFound = totalRayHits > 0 && ray[0].collider.gameObject.tag == "SolidGround";

            isWallSliding = wallFound && ( (facingRight && Input.GetKey(KeyCode.RightArrow)) ||  (!facingRight && Input.GetKey(KeyCode.LeftArrow))) ;
        } else {
            isWallSliding = false;
        }
    }


    public void Die(){
        dead = true;
    }

}