Unity-平台跳跃任务控制器

前言:

Unity制作2D游戏中,跳跃机制是个问题,今天我们就来学习一下一个很好手感的平台跳跃人物控制器。

跟着的教程是b站的此教程

 

 

 

 


成品展示

 

关于陷阱机关这些图片请在Unity商店中随便找个2D冒险题材的素材,关于人物请下载这张图片:

 

好了,接下来我们来动手制作

 


优秀的移动

新建一个2D场景,然后可以去找一些2D素材,我这里在Unity Assert里找了Pixel Adventure,准备用它来搭建基本地面。

建立两个Tilemap,一个是背景,一个是素材地面,我的最后效果是这样的:

当然,这只是个背景,不弄也行,毕竟这篇文章主要是学人物的控制。

然后把我们的主角弄进来,加个碰撞盒和刚体,给我们的Tilemap也加上tilemap的碰撞检测。

把人物放好之后我们开始写脚本:

    [Range (0,5)]
    public float walkSpeed=3.5f;
    private Rigidbody2D _rigidbody2D;
    private float _velocityX;
    public float AccelerateTime=0.09f;
    private void Awake()
    {
        _rigidbody2D = GetComponent<Rigidbody2D>();
    }
    void FixedUpdate()
    {
        //velocity:刚体的线速度
        //三目运算符来返回一个速度
        float speed =
            (Input.GetAxis("Horizontal") == 0 ?
                0 : (Input.GetAxis("Horizontal") > 0 ?
                      walkSpeed : -walkSpeed));
        if (speed < 0)
        {
            _rigidbody2D.transform.eulerAngles = 
                            new Vector3(0, 180, 0);
        }
        if (speed > 0) {
            _rigidbody2D.transform.eulerAngles = 
                            new Vector3(0, 0, 0);
        }
        //平滑的移动
        _rigidbody2D.velocity =
            new Vector2(
                Mathf.SmoothDamp(
                    _rigidbody2D.velocity.x,
                    speed * Time.fixedDeltaTime*60,
                    ref _velocityX,
                    AccelerateTime),
                _rigidbody2D.velocity.y);
    }

可能会出现的问题及解决方法:

  1. 人物不能立即停下:在Project Setting的Input中找到Horizontal的设置,然后将Gravity调高
  2. 人物行走中会突然卡住:使用椭圆的碰撞盒或者box碰撞盒中调整一点点Edge Radius(有一个圆角)

我们加上动画后看一下效果,加上动画我就不演示了,相信你可以的:

有一说一这个控制手感是真的好!

 


优秀的跳跃

接下来我们来实现跳跃功能

代码:

    private Rigidbody2D _rigidbody2D;
    private Animator _animator;

    private float _velocityX;

    private int _speedID = Animator.StringToHash("speed");
    private int _jumpID = Animator.StringToHash("jump");

    private bool _isOnGround = false;


    [Header("移动速度")]
    public float walkSpeed=3.5f;
    public float AccelerateTime = 0.09f;
    [Header("跳跃")]
    public float JumpingSpeed;
    public float FallMultipier;
    public float LowJumpMultiplier;
    private bool _isJumping=false;

    [Header("触地检测盒")]
    public Vector2 PointOffset;   //地面盒形接触框的位置偏差
    public Vector2 Size;     //地面盒形接触框的大小
    public LayerMask GroundLayerMask; //地面盒形接触框的检测层

    private void Awake()
    {
        _rigidbody2D = GetComponent<Rigidbody2D>();
        _animator = GetComponent<Animator>();
    }
    void FixedUpdate()
    {
        //判断是否碰到地面
        _isOnGround = OnGround();

        //velocity:刚体的线速度
        float speed =
            (Input.GetAxis("Horizontal") == 0 ?
                0 : (Input.GetAxis("Horizontal") > 0 ?
                      walkSpeed : -walkSpeed));
        if (speed < 0)
        {
            _rigidbody2D.transform.eulerAngles = new Vector3(0, 180, 0);
            _animator.SetFloat(_speedID, 1);
        }
        else if (speed > 0)
        {
            _rigidbody2D.transform.eulerAngles = new Vector3(0, 0, 0);
            _animator.SetFloat(_speedID, 1);
        }
        else {
            _animator.SetFloat(_speedID, 0);
        }

        //平滑的移动
        _rigidbody2D.velocity =
            new Vector2(
                Mathf.SmoothDamp(
                    _rigidbody2D.velocity.x,
                    speed * Time.fixedDeltaTime*60,
                    ref _velocityX,
                    AccelerateTime),
                _rigidbody2D.velocity.y);


        /* 注意:GetButtonDown等类似方法不可以
         *      在fixedUpdate中使用,因为这FixedUpdate
         *      是按照时间执行的,你必须保证你按键的那一帧
         *      正好fixedUpdate也该执行了,才会检验到你的按键
         *      所以我们使用GetAxis("Jump"),GetAxis与帧率无关
         *      ,只要按键就在0到1变化
          */
        if (Input.GetAxis("Jump") == 1&&!_isJumping) {
            //给一个向上的线速度
            _rigidbody2D.velocity = new Vector2(
                _rigidbody2D.velocity.x, JumpingSpeed);

            _animator.SetTrigger(_jumpID);

            _isJumping = true;
        }
        if (_isOnGround&&Input.GetAxis("Jump")==0) {
            _isJumping = false;
        }
        //线速度是负值(即下坠中)(加速下坠)
        if (_rigidbody2D.velocity.y < 0)
        {
            _rigidbody2D.velocity += Vector2.up *
                                    Physics2D.gravity.y *  //重力的y是一个负值
                                    FallMultipier  *
                                    Time.fixedDeltaTime;
            print(Physics2D.gravity.y);
        }
        //当玩家上升时,玩家不再按跳跃键了(减缓上升)
        else if (_rigidbody2D.velocity.y > 0 && Input.GetAxis("Jump") != 1) {
            _rigidbody2D.velocity += Vector2.up *
                                    Physics2D.gravity.y *  
                                    LowJumpMultiplier *
                                    Time.fixedDeltaTime;
        }
    }
   private  bool OnGround() {
        //物理判定框
        //参数:盒子中心、大小、角度、层筛选器
        Collider2D Coll=Physics2D.OverlapBox((Vector2)transform.position+PointOffset,
                Size,
                0,
                GroundLayerMask);
        if (Coll != null) {
            return true;
        }
        return false;
    }
    //可视效果
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireCube((Vector2)transform.position + PointOffset,
                Size);
    }

在外边记得给变量们赋值,还要设置好Layer。

效果图:


中间休息

我们为下半场来做一些准备:

优化

这里有个可以优化的地方,就是上面你可以使用getAxisRaw来替代getAxis方法。

getAxis是返回一个渐变,0->0.3->0.6->1,而getAxisRaw会直接到1

这样就避免了人物滑行问题,你可以将Project Setting的Input中找到Horizontal的Gravity调回原来的值了。

DOTween

下面我们会用到Unity的插件——DoTween,在Unity Assert里面就可以找到免费的doTween

冲刺准备

修改Project Setting的Input中的一个fire1,修改成dash:

代码缩写

利用C#内部的预处理器指令#region来整合代码块,从而让思路清晰:

    void FixedUpdate()
    {
        //判断是否碰到地面
        _isOnGround = OnGround();

        #region 左右移动
        //velocity:刚体的线速度
        float speed =
            (Input.GetAxisRaw("Horizontal") == 0 ?
                0 : (Input.GetAxisRaw("Horizontal") > 0 ?
                      walkSpeed : -walkSpeed));
        if (speed < 0)
        {
            _rigidbody2D.transform.eulerAngles = new Vector3(0, 180, 0);
            _animator.SetFloat(_speedID, 1);
        }
        else if (speed > 0)
        {
            _rigidbody2D.transform.eulerAngles = new Vector3(0, 0, 0);
            _animator.SetFloat(_speedID, 1);
        }
        else {
            _animator.SetFloat(_speedID, 0);
        }

        //平滑的移动
        _rigidbody2D.velocity =
            new Vector2(
                Mathf.SmoothDamp(
                    _rigidbody2D.velocity.x,
                    speed * Time.fixedDeltaTime*60,
                    ref _velocityX,
                    AccelerateTime),
                _rigidbody2D.velocity.y);
        #endregion

        #region 跳跃控制
        /* 注意:GetButtonDown等类似方法不可以
         *      在fixedUpdate中使用,因为这FixedUpdate
         *      是按照时间执行的,你必须保证你按键的那一帧
         *      正好fixedUpdate也该执行了,才会检验到你的按键
         *      所以我们使用GetAxis("Jump"),GetAxis与帧率无关
         *      ,只要按键就在0到1变化
          */
        if (Input.GetAxis("Jump") == 1&&!_isJumping) {
            //给一个向上的线速度
            _rigidbody2D.velocity = new Vector2(
                _rigidbody2D.velocity.x, JumpingSpeed);

            _animator.SetTrigger(_jumpID);

            _isJumping = true;
        }
        if (_isOnGround&&Input.GetAxis("Jump")==0) {
            _isJumping = false;
        }
        #endregion

        #region 重力调整器
        //线速度是负值(即下坠中)(加速下坠)
        if (_rigidbody2D.velocity.y < 0)
        {
            _rigidbody2D.velocity += Vector2.up *
                                    Physics2D.gravity.y *  //重力的y是一个负值
                                    FallMultipier  *
                                    Time.fixedDeltaTime;
        }
        //当玩家上升时,玩家不再按跳跃键了(减缓上升)
        else if (_rigidbody2D.velocity.y > 0 && Input.GetAxis("Jump") != 1) {
            _rigidbody2D.velocity += Vector2.up *
                                    Physics2D.gravity.y *  
                                    LowJumpMultiplier *
                                    Time.fixedDeltaTime;
        }
        #endregion
    }

关闭后是这样:

 

 


实现冲刺

实现冲刺的逻辑其实很简单,就是先取消掉物理限制之类的,然后,给主角一个方向一个力,然后再恢复那些限制即可。

 

public class PlayerControl : MonoBehaviour
{
    private Rigidbody2D _rigidbody2D;
    private Animator _animator;

    private float _velocityX;

    private int _speedID = Animator.StringToHash("speed");
    private int _jumpID = Animator.StringToHash("jump");

    private bool _isOnGround = false;


    [Header("移动速度")]
    public float walkSpeed=3.5f;
    public float AccelerateTime = 0.09f;
    bool _canMove=true;
    [Header("跳跃")]
    public float JumpingSpeed;
    public float FallMultipier;
    public float LowJumpMultiplier;
    private bool _isJumping=false;
    bool _canJump = true;
    [Header("触地检测盒")]
    public Vector2 PointOffset;   //地面盒形接触框的位置偏差
    public Vector2 Size;     //地面盒形接触框的大小
    public LayerMask GroundLayerMask; //地面盒形接触框的检测层

    bool _gravityModifier = true;   //允许重力调整
    [Header("冲刺")]
    private bool _wasDashed = false;
    private Vector2 _dir;
    public float DashForce;
    public float DragMaxForce;
    public float DragDuration;
    public float DashWaitTime;

    private void Awake()
    {
        _rigidbody2D = GetComponent<Rigidbody2D>();
        _animator = GetComponent<Animator>();
    }
    void FixedUpdate()
    {
        //判断是否碰到地面
        _isOnGround = OnGround();

        #region 左右移动
        if (_canMove)
        {
            //velocity:刚体的线速度
            float speed =
                (Input.GetAxisRaw("Horizontal") == 0 ?
                    0 : (Input.GetAxisRaw("Horizontal") > 0 ?
                          walkSpeed : -walkSpeed));
            if (speed < 0)
            {
                _rigidbody2D.transform.eulerAngles = new Vector3(0, 180, 0);
                _animator.SetFloat(_speedID, 1);
            }
            else if (speed > 0)
            {
                _rigidbody2D.transform.eulerAngles = new Vector3(0, 0, 0);
                _animator.SetFloat(_speedID, 1);
            }
            else
            {
                _animator.SetFloat(_speedID, 0);
            }

            //平滑的移动
            _rigidbody2D.velocity =
                new Vector2(
                    Mathf.SmoothDamp(
                        _rigidbody2D.velocity.x,
                        speed * Time.fixedDeltaTime * 60,
                        ref _velocityX,
                        AccelerateTime),
                    _rigidbody2D.velocity.y);
        }
        #endregion

        #region 跳跃控制
        /* 注意:GetButtonDown等类似方法不可以
         *      在fixedUpdate中使用,因为这FixedUpdate
         *      是按照时间执行的,你必须保证你按键的那一帧
         *      正好fixedUpdate也该执行了,才会检验到你的按键
         *      所以我们使用GetAxis("Jump"),GetAxis与帧率无关
         *      ,只要按键就在0到1变化
          */
        if (_canJump)
        {
            if (Input.GetAxis("Jump") == 1 && !_isJumping)
            {
                //给一个向上的线速度
                _rigidbody2D.velocity = new Vector2(
                    _rigidbody2D.velocity.x, JumpingSpeed);

                _animator.SetTrigger(_jumpID);

                _isJumping = true;
            }
            if (_isOnGround && Input.GetAxis("Jump") == 0)
            {
                _isJumping = false;
            }
        }
        #endregion

        #region 重力调整器
        if (_gravityModifier)
        {
            //线速度是负值(即下坠中)(加速下坠)
            if (_rigidbody2D.velocity.y < 0)
            {
                _rigidbody2D.velocity += Vector2.up *
                                        Physics2D.gravity.y *  //重力的y是一个负值
                                        FallMultipier *
                                        Time.fixedDeltaTime;
            }
            //当玩家上升时,玩家不再按跳跃键了(减缓上升)
            else if (_rigidbody2D.velocity.y > 0 && Input.GetAxis("Jump") != 1)
            {
                _rigidbody2D.velocity += Vector2.up *
                                        Physics2D.gravity.y *
                                        LowJumpMultiplier *
                                        Time.fixedDeltaTime;
            }
        }
        #endregion

        #region 冲刺
        if (Input.GetAxisRaw("Dash") == 1&& !_wasDashed) {
            _wasDashed = true;
            _dir = GetDir();

            //将玩家当前所有的动量清零
            _rigidbody2D.velocity = Vector2.zero;
            //施加一个力,让玩家飞出去
            _rigidbody2D.velocity += _dir * DashForce;

            StartCoroutine(Dash());
        }
        if (_isOnGround&&Input.GetAxisRaw("Dash")==0) {
            _wasDashed = false;
        }

        #endregion
    }
    IEnumerator Dash() {
        //关闭玩家的移动和跳跃的功能
        _canJump = false;
        _canMove = false;
        //关闭重力调整器
        _gravityModifier = false;
        //关闭重力影响
        _rigidbody2D.gravityScale = 0;

        //施加一个空气阻力(Rigidbody.Drag)
        //创建一个虚拟tween来不断调用方法更改值
        DOVirtual.Float(DragMaxForce,0, DragDuration, RigidbodyDrag);

        //等待一段时间
        yield return new WaitForSeconds(DashWaitTime);
        //恢复所有关闭的东西
        _canJump = true;
        _canMove = true;
        _gravityModifier = true;
        _rigidbody2D.gravityScale = 1;

    }
    private Vector2 GetDir() {
        Vector2 temp= new Vector2(Input.GetAxisRaw("Horizontal"),
                            Input.GetAxisRaw("Vertical"));
        if (temp.x == 0 && temp.y == 0) {
            //默认向主角朝向进行冲刺
            if (_rigidbody2D.transform.eulerAngles.y == 0)
            {
                temp.x = 1;
            }
            else {
                temp.x = -1;
            }
        }
        return temp;
    }
    private void RigidbodyDrag(float x) {
        _rigidbody2D.drag = x;
        print("阻力:" + x);
    }


    private  bool OnGround() {
        //物理判定框
        //参数:盒子中心、大小、角度、层筛选器
        Collider2D Coll=Physics2D.OverlapBox((Vector2)transform.position+PointOffset,
                Size,
                0,
                GroundLayerMask);
        if (Coll != null) {
            return true;
        }
        return false;
    }
    //可视效果
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireCube((Vector2)transform.position + PointOffset,
                Size);
    }
}

 

 


 

 

 

 

 

 

 

 

 

 

发表评论