2021-9-4 爆肝一整天,关于2D横板游戏中攀爬楼梯的考虑

在这里插入图片描述

引言

以前觉得2D横板游戏制作起来很简单,殊不知一个小小的动作就需要考虑这么多要素。通过对这个小小的2D横版游戏的设计,我对那些开发出好玩炫酷兼顾打击手感,不停琢磨提高游戏的性能及优化的开发者们的敬意油然而生啊!!!

关于攀爬楼梯考虑

在这里插入图片描述

总体考虑

  • 玩家在梯子上将不能左右移动,不能跳跃
  • 增加梯子上的停留动画,保证在梯子上停留时不会出现原地攀爬的情况

玩家站在1处

  • 按下s键,仍应播放下蹲动作,而不是攀爬下降动作,无竖直方向受力
  • 按下w键,播放攀爬上升动作,受力向上

玩家站在2处

  • 按下w键,不播放攀爬上升动画,无竖直方向受力
  • 按下s键, 播放攀爬下降动画,受力向下

玩家通过跳跃来到3处

  • 按下w键,播放攀爬上升动画,受力向上
  • 按下s键,播放攀爬下降动画,受力向下

1处升至3处 和 2处降至3处

  • 暂时没想到有什么可考虑的

3处升至2处

  • 停止播放攀爬上升动画,播放站立动画
  • 不影响跳跃(检测盒子检测到的不再是地面)
  • 下蹲无效
  • 到达桥的顶点后,桥上是可以站立的

3处降至1处

  • 停止播放攀爬下降动画,播放站立动画
  • 不影响跳跃(检测盒子检测到再次变成地面)
  • 不影响下蹲

其他考虑

  • 到达梯子什么地方可以攀爬,如果太宽,可能会出现没有借助梯子而是悬空攀爬的情况
  • 被攻击就会掉落

解决方案及代码

情况极其复杂,经过今天一整天的调试,我才成功得完成我对于攀爬的各种需求。

动画条件

在这里插入图片描述

  • 很明显,我将跳跃与下落分开,然后又将向上攀爬,向下攀爬,停止攀爬分开。
  • 本来连线还是较为简单的,因为将转换状态时间设置为0,为通过不断调试,发现多条连线尽管不需要转换时间,但如果没有直接连通的线,仍会出现许多不合常理的bug。例如到梯子顶部仍无法结束攀爬动画的情况。因此,经过不断调试,我增加了很多连线。

解决代码(非完全代码)

自己动手敲一敲哦,就会发现这其中还是很消耗脑细胞的。

首先是一些设置

    [Header("地面检测盒子的高度")]
    public float BoxHeight = 0.2f;

    [Header("地面检测层")]
    public LayerMask groundMask;

    [Header("桥检测层")]
    public LayerMask bridgeMask;

    [Header("攀爬速度")]
    public float ClimbSpeed = 10.0f;

    private bool isOnLadder = false; // 是否在梯子上

    private bool isClimb = false; // 是否正在攀爬

    [HideInInspector]
    public bool bridgeBottom = false; // 是否在梯子底

    [HideInInspector]
    public bool bridgeTop = false; // 是否在梯子顶

    private Animator _animator;

    private Rigidbody2D _rigidbody2D;

    private Collider2D _collider2D;

    private Collider2D BridgeCollider;

    private Vector2 playersize; // 玩家大小

    private Vector2 boxsize; // 检测盒子尺寸

    // 所有状态参数的id定义
    #region
    private int id_run;
    private int id_jump;
    private int id_fall;
    private int id_climbup;
    private int id_climbdown;
    private int id_onladder;
    private int id_bridgebottom;
    private int id_bridgetop;
    private int id_crouch;
    private int id_hurt;
    #endregion

初始化

    void Awake()
    {
        _animator = GetComponent<Animator>();
        _rigidbody2D = GetComponent<Rigidbody2D>();
        _collider2D = GetComponent<Collider2D>();
        playersize = GetComponent<SpriteRenderer>().bounds.size;
        boxsize = new Vector2(playersize.x * 0.3f, BoxHeight);  // 设置检测盒的大小

        healthscontroller = GameObject.FindGameObjectWithTag("healths").GetComponent<HealthsController>();

        BridgeCollider = GameObject.FindGameObjectWithTag("bridgetop").GetComponent<Collider2D>();

        // 所有状态参数转为其id,提高性能
        #region
        id_run = Animator.StringToHash("run");
        id_jump = Animator.StringToHash("jump");
        id_fall = Animator.StringToHash("fall");
        id_climbup = Animator.StringToHash("climbup");
        id_climbdown = Animator.StringToHash("climbdown");
        id_onladder = Animator.StringToHash("onladder");
        id_bridgebottom = Animator.StringToHash("bridgebottom");
        id_bridgetop = Animator.StringToHash("bridgetop");
        id_crouch = Animator.StringToHash("crouch");
        id_hurt = Animator.StringToHash("hurt");
        #endregion
    }

然后,重点来了,各类情况分析

    // Update is called once per frame
    void Update()
    {
        // 判断是否到达梯子顶
        #region
        if (bridgeTop)
        {
            isClimb = false;
            _animator.SetBool(id_bridgetop, true);
        }
        else
        {
            _animator.SetBool(id_bridgetop, false);
        }
        #endregion

        // 判断是否到达梯子底
        #region
        if (bridgeBottom)
        {
            isClimb = false;
            _animator.SetBool(id_bridgebottom, true);
        }
        else
        {
            _animator.SetBool(id_bridgebottom, false);
        }
        #endregion
    }

    private void FixedUpdate()
    {
        // 判断在地面上则下落停止
        if (IsOnGround())
        {
            StopFall();
            _animator.SetBool(id_climbdown, false);
            _animator.SetBool(id_bridgebottom, true);
        }
		
		// 判断在梯子顶部
        if (IsOnBridge())
        {
            StopFall();
            bridgeTop = true;
            BridgeCollider.isTrigger = false; // 空气墙是否有碰撞
            _animator.SetBool(id_climbup, false);
            _animator.SetBool(id_crouch, false);
        }
        else
        {
            bridgeTop = false;
        }

最重要的几点分析(前提时在梯子范围内)

  1. 在梯子底部按下w键,执行向上攀爬
  2. 在梯子顶部按下s键,执行向下攀爬
  3. 不在梯子顶部按下w键,执行向上攀爬
  4. 不在梯子底部按下s键,执行向下攀爬
  5. 按下w或者s键后抬起,执行停止攀爬
        // 攀爬楼梯相关
        if (isOnLadder)
        {
            _animator.SetBool(id_onladder, true);
            1.if (Input.GetButton("Climb") && bridgeBottom) 
            {
                ClimbUp();
            }
            2.else if(Input.GetButton("Crouch") && bridgeTop)
            {
                BridgeCollider.isTrigger = true;
                ClimbDown();
            }
            3.else if (Input.GetButton("Climb") && !bridgeTop)
            {
                ClimbUp();
            }
            4.else if (Input.GetButton("Crouch") && !bridgeBottom)
            {
                ClimbDown();
            }
            5.if (Input.GetButtonUp("Climb") || Input.GetButtonUp("Crouch"))
            {
                StopClimb();
            }
        }
        else
        {
            _animator.SetBool(id_onladder, false);
            BridgeCollider.isTrigger = true;
        }
    }

两个检测函数

   // 利用检测盒子检测人物是否在地面上
   private bool IsOnGround()
   {
       Vector2 boxcenter = (Vector2)transform.position + (Vector2.down * playersize.y * 0.5f);
       return Physics2D.OverlapBox(boxcenter, boxsize, 0, groundMask);
   }

// 利用检测盒子检测人物是否在梯子顶部
   private bool IsOnBridge()
   {
       Vector2 boxcenter = (Vector2)transform.position + (Vector2.down * playersize.y * 0.5f);
       return Physics2D.OverlapBox(boxcenter, boxsize, 0, bridgeMask);
   }

考虑梯子上的受力情况

在梯子上停留的话应该是不动的,因此,要将重力倍数设置为0

    /// <summary>
    /// 改变上升下落重力大小的函数,可以让人物没有飘飘的感觉,提升玩家体验
    /// </summary>
    private void BetterJump()
    {
        if (_rigidbody2D.velocity.y > 0 && !isClimb)
        {
            _rigidbody2D.gravityScale = JumpUpGravity;
        }
        else if (_rigidbody2D.velocity.y < 0 && !isClimb)
        {
            _rigidbody2D.gravityScale = FallGravity;
            _animator.SetBool(id_jump, false);
            _animator.SetBool(id_fall, true);
        }
        else if (isOnLadder && !bridgeTop && !bridgeBottom &&isClimb)
        {
            _rigidbody2D.gravityScale = 0f;  // 重力设为0
        }
        else
        {
            _rigidbody2D.gravityScale = 1.0f;
        }
    }

各类函数实现,没什么好说的

    // 控制攀爬上升受力及动画
    private void ClimbUp()
    {
        isClimb = true;
        _animator.SetBool(id_climbup, isClimb);
        _animator.SetBool(id_climbdown, false);
        _rigidbody2D.velocity = new Vector2(0, ClimbSpeed);
    }

    // 控制攀爬下降受力及动画
    private void ClimbDown()
    {
        isClimb = true;
        _animator.SetBool(id_climbup, false);
        _animator.SetBool(id_climbdown, isClimb);
        _rigidbody2D.velocity = new Vector2(0, -ClimbSpeed);
    }

    // 停止攀爬
    private void StopClimb()
    {
        _animator.SetBool(id_climbup, false);
        _animator.SetBool(id_climbdown, false);
        _rigidbody2D.velocity = new Vector2(0, 0); // 防止莫名缓慢下落
    }

最坑爹的地方来了,如何判断在梯子范围内和位于梯子底部

坑爹并不是说它有多难想到,而是本来我觉得主角控制器上的代码已经够多了,所以我就这段代码挂载到和主角发生触发事件的物体上,结果爬了半天爬不上去,一直很鬼畜的停在最底端,播放攀爬动画,但就是上不去。我调试了很久都没效果。后来才发现在梯子底部时,一按向上攀爬,系统自动判定,主角离开了碰撞物体,这点我也很不明白是为什么!!!

    // 检测是否在梯子上和梯子底部
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "bridge")
        {
            isOnLadder = true;
        }
        
        if(collision.tag == "bridgebottom")
        {
            bridgeBottom = true;
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.tag == "bridge")
        {
            isOnLadder = false;
        }
        if (collision.tag == "bridgebottom")
        {
            bridgeBottom = false;
        }
    }
}

希望有知道为什么的小伙伴不吝赐教,留言交流!

结束

经过一天的调试,总算大功告成

下面放几张效果图

梯子底部不影响下蹲

在这里插入图片描述

底部向上攀爬

在这里插入图片描述

停留在梯子上

在这里插入图片描述

战立在梯子顶端

在这里插入图片描述

顶部向下攀爬

在这里插入图片描述

大家有什么更好的想法可以和我交流交流哦!
在这里插入图片描述

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

giegie界清流

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值