实现Unity2D游戏中跳跃功能和相关问题解决

一、跳跃功能基本实现

二、跳跃bug解决

三、跳跃优化

一、跳跃功能基本实现

在unity中我们要实现跳跃功能基本依赖代码实现。在这里我们先构建场景,笔者在这里使用tilemap构建地面,创建正方形代表物体。如图所示

我们要为其添加代码,我在这创建名为Control.cs的C#脚本,用来控制正方形的行为,将其绑定到正方形组件上,同时为正方形添加rigidbody2d和boxcollider组件,将rigidbody2d中z轴锁定。并且也为tilemap添加组合碰撞体和tilemapcollider,将tilemapcollider中Use by Composite打钩,使用tilemap组合碰撞体,调整碰撞区域,锁定x,y,z轴。同时为了方便,引入Cinemachine跟随正方形,以便实时观察。

Control.cs的代码如下

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

public class Control : MonoBehaviour
{
    //声明刚体组件
    Rigidbody2D rigidbody;
    //力大小
    float force = 300.0f;
    void Start()
    {
        //获取刚体组件
        rigidbody = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        
        //如果按下W,实现跳跃功能
        if (Input.GetKeyDown(KeyCode.W))
        {
            Jump();
        }

    }

    void Jump()
    {
        //对物体施加方向向上的力,也可以对rigidbody.velocity进行操作,但是不推荐
        rigidbody.AddForce(Vector2.up * force);
    }
}

这样基本的跳跃功能我们就实现了。

二、跳跃bug解决

在运行中,我们很快就能发现一个问题,只要我们多次按W键,它就能一直跳跃,这当然不符合实际情况。我们需要代码监测它是否在地面上。我们可以重载OnCollisionEnter2D和OnCollisionExit2D来监测它是否在地面上。

首先,我们要把tilemap的tag设置为Ground,没有的自行添加。

 将代码修改为:

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

public class Control : MonoBehaviour
{
    //声明刚体组件
    Rigidbody2D rigidbody;
    //力大小
    float force = 300.0f;
    //设置是否可以跳跃的bool值,初始值为true
    bool jumpable = true;
    void Start()
    {
        //获取刚体组件
        rigidbody = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        
        //如果按下W,实现跳跃功能
        if (Input.GetKeyDown(KeyCode.W))
        {
            Jump();
        }

    }

    void Jump()
    {
        if (jumpable)
        {
            //对物体施加方向向上的力,也可以对rigidbody.velocity进行操作,但是不推荐
            rigidbody.AddForce(Vector2.up * force);
        }
    }

    //与地面碰撞,jumpable为true
    private void OnCollisionEnter2D(Collision2D collision)
    {
        //如果碰撞体的tag为Ground,设置jumpable
        if(collision.gameObject.tag == "Ground")
        {
            jumpable = true;
        }
    }

    //离开地面,jumpable为false
    private void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Ground")
        {
            jumpable = false;
        }
    }
}

这样就可以解决这个问题了。

在很多游戏中,我们要设置多段跳功能。所以我们要设置一个int存放当前跳跃次数。

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

public class Control : MonoBehaviour
{
    //声明刚体组件
    Rigidbody2D rigidbody;
    //力大小
    float force = 300.0f;
    //设置是否可以跳跃的bool值,初始值为true
    bool jumpable = true;
    //设置n段跳
    int n;
    void Start()
    {
        //获取刚体组件
        rigidbody = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        
        //如果按下W,实现跳跃功能
        if (Input.GetKeyDown(KeyCode.W))
        {
            Jump();
        }

    }

    void Jump()
    {
        //二段跳 要放在update里面 因为没fixedDeltaTime
        if (jumpable)
        {
            n = 1;//踩在地面上的时候,可以跳n+1次,想要设置三段跳只要改为2即可
        }
        if (Input.GetKeyDown(KeyCode.W) && n >= 0)
        {
            rigidbody.AddForce(Vector2.up * force);
            n--;
        }
    }

    //与地面碰撞,jumpable为true
    private void OnCollisionEnter2D(Collision2D collision)
    {
        //如果碰撞体的tag为Ground,设置jumpable
        if(collision.gameObject.tag == "Ground")
        {
            jumpable = true;
        }
    }

    //离开地面,jumpable为false
    private void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Ground")
        {
            jumpable = false;
        }
    }
}

墙体是2d游戏中很常见的情景,我们当前的代码只能监测物体的碰撞,如果他碰撞到墙体,jumpable=true,那么它还能进行跳跃,这也不符合生活实际,解决这个问题有些困难,由于各种原因,以下的方法不一定能成功解决,请大家一一测试再应用。

第一种方法:可以通过射线监测,从中心点发射一条竖直向下,固定长度的射线,如果射线碰撞到地面设置jumpbable = true。

使用前检查sprite的中心点,射线从此处发射的。

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

public class Control : MonoBehaviour
{
    //声明刚体组件
    Rigidbody2D rigidbody;
    //力大小
    float force = 300.0f;
    //设置是否可以跳跃的bool值,初始值为true
    bool jumpable = true;
    //设置n段跳
    int n;
    //设置射线长度
    float length = 1.0f;
    void Start()
    {
        //获取刚体组件
        rigidbody = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        //创建射线
        RaycastHit2D info = Physics2D.Raycast(rigidbody.position, -Vector2.up, length);
        if (info.collider != null)
        {//如果发生了碰撞
            GameObject obj = info.collider.gameObject;
            if (obj.CompareTag("Ground"))//用tag判断碰到了什么对象
                jumpable = true;

            //如果按下W,实现跳跃功能
            if (Input.GetKeyDown(KeyCode.W))
            {
                Jump();
            }

        }

        void Jump()
        {
            //二段跳 要放在update里面 因为没fixedDeltaTime
            if (jumpable)
            {
                n = 1;//踩在地面上的时候,可以跳n+1次,想要设置三段跳只要改为2即可
            }
            if (Input.GetKeyDown(KeyCode.W) && n >= 0)
            {
                rigidbody.AddForce(Vector2.up * force);
                n--;
            }
        }

    }
}

但是这会使得射线长度难以确定,长度过长,还没有跳到地面上就就可以跳起了;长度过短,射线监测不到,就跳不起来。这个长度需要反复调试,并且改变参数可能还需要重新调试,所以我不太推荐这种方法。

第二种方法:在物体脚下设置触发器。再添加一个boxcollider,勾选Is Trigger,代表这个碰撞区域是触发器。如图所示

 需要注意的是,触发器的宽度要比原本的碰撞体小,下方可以突出一点,以便代码更好的监测。

在代码中我们通过重载OnTriggerEnter2D和OnTriggerExit2D来监测是否在地面

代码如下

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

public class Control : MonoBehaviour
{
    //声明刚体组件
    Rigidbody2D rigidbody;
    //力大小
    float force = 300.0f;
    //设置是否可以跳跃的bool值,初始值为true
    bool jumpable = true;
    //设置n段跳
    int n;
    void Start()
    {
        //获取刚体组件
        rigidbody = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        //如果按下W,实现跳跃功能
        if (Input.GetKeyDown(KeyCode.W))
        {
            Jump();
        }
    }

    void Jump()
    {
        //二段跳 要放在update里面 因为没fixedDeltaTime
        if (jumpable)
        {
            n = 1;//踩在地面上的时候,可以跳n+1次,想要设置三段跳只要改为2即可
        }
        if (Input.GetKeyDown(KeyCode.W) && n >= 0)
        {
            rigidbody.AddForce(Vector2.up * force);
            n--;
        }
    }

    //重载函数来监测
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "Ground")
        {
            jumpable = true;
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "Ground")
        {
            jumpable = false;
        }
    }
}

这种方法实现可能性较大。

在测试中,跳到墙面上,在添加了移动代码之后跳到墙面上,有可能出现按方向键,物体没有掉下来的情况。我们需要设置物理材料。

在Project视图中,添加物理材料。

 打开物理材料,将摩擦力设置为0

 将物理材料拖拽到rigidbody中。

 就可以有效解决这个bug了。

三、跳跃优化

跳跃的功能完成了,但是运行时感觉距离现实情况仍有些距离。

事实上,如果你让物体从高处落下,你会发现在下落过程中,它的速度并没有增加,更像是匀速下降。所以我们要为其添加重力。

注:此处修改代码来自B站“更好的跳跃”

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

public class Control : MonoBehaviour
{
    //声明刚体组件
    Rigidbody2D rigidbody;
    //力大小
    float force = 300.0f;
    //设置是否可以跳跃的bool值,初始值为true
    bool jumpable = true;
    //设置n段跳
    int n;
    void Start()
    {
        //获取刚体组件
        rigidbody = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        //如果按下W,实现跳跃功能
        if (Input.GetKeyDown(KeyCode.W))
        {
            Jump();
        }
        //上升和下落过程中,手动改变速度
        if(rigidbody.velocity.y < 0)
        {
            rigidbody.velocity -= Vector2.up * Physics2D.gravity.y * Time.deltaTime;
        }
        else if(rigidbody.velocity.y > 0)
        {
            rigidbody.velocity += Vector2.up * Physics2D.gravity.y * Time.deltaTime;
        }
    }

    void Jump()
    {
        //二段跳 要放在update里面 因为没fixedDeltaTime
        if (jumpable)
        {
            n = 1;//踩在地面上的时候,可以跳n+1次,想要设置三段跳只要改为2即可
        }
        if (Input.GetKeyDown(KeyCode.W) && n >= 0)
        {
            rigidbody.AddForce(Vector2.up * force);
            n--;
        }
    }

    //重载函数来监测
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "Ground")
        {
            jumpable = true;
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "Ground")
        {
            jumpable = false;
        }
    }
}

运行后,会发现更加贴近生活生活实际了。

最后,感谢你看完我的文章。本人是大一学生,文章难免有不足之处,恳请专业人士批评指正。

猜你喜欢

转载自blog.csdn.net/m0_66128565/article/details/124509381