Unity2D角色跳跃
文章目录
- Unity2D角色跳跃
- 思路
- 事前准备
- 通过速度判断状态
- 通过位置判断状态
思路来自B站麦扣的第一期教程:https://www.bilibili.com/video/BV1z4411o7W4
思路
跳跃的动作可以拆分为两个动作,即上升动作和下落动作。
玩家可以在站立过程中起跳,也可以在移动过程中起跳。所以在动画控制器中,待机和移动都可以起跳,转变为上升状态,而下落状态则只能通过上升状态过渡。当玩家角色下落至地面时,应该由下落状态转变为待机,当然也可以转变为移动。
事前准备
public Rigidbody2D rb = GetComponent<RigidBody2D>(); // 获取物体的刚体
public CircleCollider2D cirColl = GetComponent<CircleCollider2D>(); // 获取圆形碰撞体
public Animator anim = GetComponent<Animator>(); // 获取动画控制器
public LayerMask floor; // 获取图层,这个建议自己到Inspector中选
public float jumpForce; // 起跳时的速度
请在你的角色的Game Object添加这些Component。
你需要有四个Anim文件,我的命名为StandAnim,RunAnim,UpAnim,DownAnim。分别对应待机动画,移动动画,上升动画和下落动画。
通过速度判断状态
先配置一下Animator:
创建一个float类型的Parameter命名为velocity
- StandAnim和RunAnim到UpAnim的Transition:velocity 大于 0
- UpAnim到DownAnim的Transition : velocity 小于0
- DownAnim到StandAnim的Transition : velocity 大于 -0.1; velocity 小于0.1
要实现跳跃,先要对角色的位置进行判定,如果在地面上则能够进行跳跃,如果不在地面上则不能进行跳跃。二段跳的设置在此不作讨论。在Unity中,我们可以通过RigidBody来对碰撞进行检测,通过IsTouchingLayers来检测当前刚体是否与图层发生碰撞,这个方法返回一个布尔值,发生碰撞返回true,不发生碰撞返回false。该方法的蚕食是一个LayerMask,指的是Inspector窗口中右上角的Layers,与Sorting Layers不同,Layers常用于碰撞检测,而Sorting Layers则控制物体在相机中的渲染层次。在创建LayerMask以后,通过GetMask方法来赋值,或者在Inspector中选择。
// Checks whether this collider is touching any colliders on the specified layerMask or not
bool Collider2D.IsTouchingLayers(int LayerMask)
如果角色在地面上,当玩家按下跳跃按钮时,给角色一个向上的速度。要给角色添加一个速度或者说力有很多种方法,在这里我采用的是直接为RigidBody的Velocity赋值,在2D游戏中,rb.velocity是一个Vector2的对象,但在3D游戏中,是一个Vector3的对象。在跳跃时,保持x的速度不变,让y轴随时间进行变化。
rb.velocity = new Vector2(rb.velocity.x, jumpForce*Time.deltaTime);
velocity指的是刚体的速度,为正值时速度方向延坐标轴正方向,就是说如果velocity.y的值为正值,即y轴速度大于0,那么角色就会向上移动。角色在起跳时速度向上,为正值,下落时速度向下,就是负值。在Animator中我们在设置Animation之间的Transition时,可以通过这个方法来判断。当y轴的速度大于0时,动画从待机转换为上升;当y轴的速度小于0时,动画从上升转换为下降。
这样就实现了从待机到上升再到下降的动画转换。但是在落地时角色会保持下降的动画而不会转换回待机。当角色在地上待机时y轴速度为0,我们可以设置当y轴速度为0时,动画从下落转换为待机。但是Animator的Condition并没有提供相等的判断,所以我选择的是大于-0.1,小于0.1,当y轴速度在这个区间内时,就切换动画。虽然角色在到达最高点时也会有一瞬间速度为0,但是Animator中上升是无法转换为待机的,所以不会出现在空中播放待机动画的场景。
以下是实现跳跃的逻辑:
void Jump()
{
anim.SetFloat("velocity", rb.velocity.y);// 监测角色的速度
if (cirColl.IsTouchingLayers(floor)) // 判断角色是否在地面上,就是判断与tilemap的碰撞
{
if (Input.GetButtonDown("Jump") ) // 监听跳跃按钮
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce*Time.deltaTime);
}
}
}
以上方法就实现了角色跳跃和动画的切换。但是有一个严重的缺陷,我们是通过y轴的速度来判断运动状态的,如果角色在斜坡上运动,y轴的速度也会变化。就会出现虽然是在上坡,但是动画却是上升的动画。要想避免这个问题,建议不使用速度作为判断标准。
通过位置判断状态
在Animator中创建三个布尔型参数,命名为up,down和stand。
- 在StandAnim和RunAnim到UpAnim的Transition中添加条件:当up是True时转换。
- 在UpAnim到DownAnim的Transition中添加条件:当down是True时转换。
- 在DownAnim到StandAnim的Transition中添加条件:当stand是True时转换。
下面是在代码中实现的逻辑:
- 对角色位置进行判断,在地面则可以起跳,不在地面则不可以起跳。
- 当角色起跳时,将up的值设置为True,将stand的值设为false。
- 当y轴速度小于0时,将down的值设置为true,并把up值改为false。
- 当角色落到地面时,将stand的值设置为True,将down的值设为false。
代码样例:
void Jump()
{
if (cirColl.IsTouchingLayers(floor))
{
anim.SetBool("stand",true);
anim.SetBool("down",false);
if (Input.GetButtonDown("Jump") )
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce*Time.deltaTime);
anim.SetBool("up",true);
anim.SetBool("stand",false);
}
}
else
{
if (rb.velocity.y<0)
{
anim.SetBool("down",true);
anim.SetBool("up", false);
}
}
}
因为从待机到上升,上升到下落,下落到待机,这三个过程都是单向的,所以可以通过三个bool类型的参数进行判断。这样就避免了由于y轴速度变化导致的动画转换出错。