首页 > 解决方案 > Unity2D:无法平衡跳墙力量

问题描述

我的目的是让我的角色墙跳跃/爬升/滑动,我让滑动部分工作正常,但如果他在滑动墙壁时跳跃,他应该“反弹”回墙上,问题是我无法平衡军队。在我看到的所有教程中,这只是检测角色是否在滑动墙壁,如果他在滑动并且他跳跃,那么你在墙壁的对面添加一个力。

这对我不起作用,因为如果我增加足够的力量让他跳起来,他跑得太快了,玩家几乎看不到他跳了,他只是看到角色现在在墙上更高了。如果我增加较小的力量,不足以进行相当大的跳跃,玩家必须击打空间一千次才能让他在墙上爬上几厘米。

感谢您的帮助,我已经尝试了很多事情,甚至尝试冻结控件,将重力比例设置为 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;
    }

}

标签: c#unity3d

解决方案


正如您之前尝试过的那样,您需要降低跳跃时的水平跳跃加速度/速度。

当你跳墙时,你会压向墙。正如您的代码当前一样,当您在空中时,您的水平速度设置为您按下的方向。这使得从墙壁跳跃的任何水平运动都很难看到,除非它大到足以在一帧中将您推得很远。

这(以及我们在评论中讨论的变化)是您之前尝试低墙跳幅度的尝试不起作用的原因。

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

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;
    }

}

推荐阅读