Unity2D基础之人物动画、移动、跳跃

Unity2D基础之人物动画、移动、跳跃

一、人物动画

从Window->Assets Store可以打开资源商店页面,可以选购一个免费的2D资源。本文就以这个骑士资源为角色.
Hero Knight

购买完成之后就导入Unity项目了。
通常购买的资源会有一个Demo项目运行,可以看看大概效果。动画、脚本等都有现成写好的。本文以学习为目的,所以会从头走一遍。

1.制作动画

找到Sprites文件夹,里面的HeroKnight文件,点击箭头可以看到这是已经帮我们做好了切割的。
我们先将第一张拖入Hierarchy,取名为Player
在这里插入图片描述

我们浏览一下下面的图片,可以发现:
0-6是站立,7-17是奔跑,18-36是攻击…
接着我们先选中0-6的图片,拖入到Player中
在这里插入图片描述

这时候就会出现一个保存的弹窗
在这里插入图片描述

我们选择好保存的路径和名称即可。

然后分别将其他的动作也以相同的方式创建动画,
在这里插入图片描述

可以看到左上角里动画已经都创建好了。

2.搭建场景

动画的切换会在角色控制的时候进行,这里我们先简单搭一个场景。

在Hierarchy里右键->2D Object-> Tilemap->Rectangular,就可以创建一个Tilemap
在这里插入图片描述

创建好后点击,左边的视图里就会出现很多小格子。
接着我们找到Environment文件夹下面的图片,简单的在格子里搭一个地板。将文件拖入即可。
[]

这时候点击运行,会发现人物会直接穿过地板掉下去。这是因为还没有碰撞检测。

3.设置碰撞检测

我们点击Player,在Inspector视图里选择Add Compoent,然后依次添加下面三个组件:

  1. RigidBody 2D 然后将Freeze Rotation z打勾
    [(img-BuLe9IV3-1676116054121)(en-resource://database/567:1)]

  2. Capsule Collider 2D 点击Edit Collider 将胶囊体缩小到和人物一样
    在这里插入图片描述

  3. BoxCollider 2D 点击Edit Collider 将检测范围缩小到和角色的脚一样,勾选is Trigger
    在这里插入图片描述

接下来点击Tilemap,添加Tilemap Collider 2D组件。
在这里插入图片描述

现在再运行游戏,角色就会站在地板上了。

二、人物控制与动画切换

角色的移动通过脚本来实现。在这部分我们也会涉及到动画的切换。
同样点击Player -> Add Component -> new script 即可创建脚本。
脚本里有Start()和Update()方法,可以理解为Start()是在初始化的时候执行,Update()是在每一帧刷新的时候执行。

1.移动

1.1 控制移动

要控制角色移动,首先我们要获取角色的RigidBody.添加一个成员变量
同时为了移动速度可控,我们将速度也抽成一个变量。

public float runSpeed ;
private Rigidbody2D myRigidBody2D;

然后在Start()里进行初始化

void Start(){    
    myRigidBody2D = GetComponent<Rigidbody2D>();    
}

控制移动的方法应该在Update()里,为了代码的可读性和可维护性,我们定义一个函数Run()
然后在这个方法里获取按键事件,然后给角色赋值一个速度.具体代码如下:

private void Run(){    
    var moveDir = Input.GetAxis("Horizontal");    
    var playerVel = new Vector2(moveDir * runSpeed, myRigidBody2D.velocity.y);    
    myRigidBody2D.velocity = playerVel;
    }

最后记得将Run()函数添加到Update()里

void Update() {
     Run();
}

接着回到Unity,点击Player,我们可以在右边看到刚才定义的Run Speed变量,可以在这里进行赋值。
在这里插入图片描述

此时运行游戏,按下左右方向键就可以移动了。但此时有两个问题:

  • 角色一直朝着一个方向
  • 角色的动画一直是站立

接下来就解决这两个问题

1.2 改变角色面朝方向

要改变角色的朝向,首先我们要知道角色是往哪边移动的。
这一点可以通过角色x轴速度的正负来判断,如果为正则是往右,为负则是往左。
这样我们就可以编写一个Flip()函数了

private void Flip(){   
    if (myRigidBody2D.velocity.x > 0.1f)    
    {        
        transform.localRotation = Quaternion.Euler(0, 0, 0);    
    }    
    if (myRigidBody2D.velocity.x < -0.1f)   
    {        
        transform.localRotation = Quaternion.Euler(0, 180, 0);    
    }
}

可以将Flip()放到Run()里调用。

1.3 切换动画

到重点了,如何切换动画。
我们回到Unity,来到动画这里。切换动画是由参数来控制的,所以我们先创建一个控制移动的参数。
在这里插入图片描述

接着右键点击stand动画,选择Make Transition拉出一个箭头到run动画,接着选中这跟箭头,在Inspector页面取消过渡时间,设置条件isRuning为True的
在这里插入图片描述

这样就表示isRuning这个变量为True的时候,就会从stand切换到run。
同样我们也要从run拉一条线回到stand,条件为isRuning为False的时候。
在这里插入图片描述

这样我们完成了动画切换的设置。

而给isRuning变量赋值,就是脚本里做的事了。我们需要判断角色当前是否有x轴方向的速度,如果有就将isRuning设置成True, 没有就设置为False。修改一下Run()函数

private void Run(){    
    var moveDir = Input.GetAxis("Horizontal");    
        var playerVel = new Vector2(moveDir * runSpeed, myRigidBody2D.velocity.y);    
        myRigidBody2D.velocity = playerVel;    
        var playerHasXSpeed = Math.Abs(myRigidBody2D.velocity.x) > Mathf.Epsilon;    
        if (playerHasXSpeed)    {        
            myAnimator.SetBool("isRunning", true);       
            Flip();    
        }   
        else    
        {       
        myAnimator.SetBool("isRunning", false);    
        }
 }

至此,我们的角色就拥有了移动的动画效果。

2.跳跃和降落

完成了移动之后,跳跃和降落就是一样的原理了。大家可以先按移动的逻辑尝试着自己实现跳跃和降落,能实现才说明掌握了。文末我会放出代码。

2.1 解决无限跳跃问题

按照上面的思路实现跳跃,可能会发现角色能够无限跳跃,这通常是不合理的。

要解决这个问题思路就是判断角色当前是否在地面上,如果在就可以跳跃,如果不在就禁止跳跃。

那最终问题就落在了如何判断角色是否在地面上了。还记得我们最开始加的Box Collider 2D组件吗,这时候就派上用场了。

首先做一个准备工作,选中Ground,在右边Layer的地方点击Add Layer,找一个User Layer的地方写上Ground
在这里插入图片描述在这里插入图片描述

然后到脚本里,先在Start方法里获取BoxCollider2D组件,然后通过IsTouchingLayers方法来判断是否触碰了Layer。我们可以添加一个全局变量来保存这个值,并在Update()里去更新调用。

private void CheckIsOnGround(){    
    mIsOnGround = playerFeet.IsTouchingLayers(LayerMask.GetMask("Ground"));
    }

在Jump里呢就可以通过读取mIsOnGround这个值,来控制是否可以跳跃。

我们还能在这基础上加上二段跳。思路很简单,这里就不赘述了,最后放上本文涉及的代码。

public class PlayerControler : MonoBehaviour
{

    public float runSpeed ;
    public float jumpSpeed;
    public bool canDoubleJump;
    private Rigidbody2D myRigidBody2D;
    private Animator myAnimator;
    private BoxCollider2D playerFeet;
    private bool mIsOnGround = true;
    private int jumpTimes = 0;
    

    // Start is called before the first frame update
    void Start()
    {
        myRigidBody2D = GetComponent<Rigidbody2D>();
        myAnimator = GetComponent<Animator>();
        playerFeet = GetComponent<BoxCollider2D>();
    }

    // Update is called once per frame
    void Update()
    {
        CheckIsOnGround();
        Run();
        Jump();
        Fall();
    }


    private void CheckIsOnGround()
    {
        mIsOnGround = playerFeet.IsTouchingLayers(LayerMask.GetMask("Ground"));
    }
    
    private void Run()
    {
        var moveDir = Input.GetAxis("Horizontal");
        var playerVel = new Vector2(moveDir * runSpeed, myRigidBody2D.velocity.y);
        myRigidBody2D.velocity = playerVel;
        var playerHasXSpeed = Math.Abs(myRigidBody2D.velocity.x) > Mathf.Epsilon;
        if (playerHasXSpeed)
        {
            myAnimator.SetBool("isRunning", true);
            Flip();
        }
        else
        {
            myAnimator.SetBool("isRunning", false);
        }
    }

    private void Flip()
    {
        if (myRigidBody2D.velocity.x > 0.1f)
        {
            transform.localRotation = Quaternion.Euler(0, 0, 0);
        }
        if (myRigidBody2D.velocity.x < -0.1f)
        {
            transform.localRotation = Quaternion.Euler(0, 180, 0);
        }
    }

    private void Jump()
    {
        if (Input.GetButtonDown("Jump"))
        {
            if (mIsOnGround || (canDoubleJump && jumpTimes < 1))
            {
                var playerVel = new Vector2(myRigidBody2D.velocity.x, jumpSpeed);
                myRigidBody2D.velocity = playerVel;
                myAnimator.SetBool("isJumping", true);
                jumpTimes++;
            }
        }
    }

    private void Fall()
    {
        if (myRigidBody2D.velocity.y < -0.1f)
        {
            myAnimator.SetBool("isJumping", false);
            myAnimator.SetBool("isFalling", true);
        }

        if (mIsOnGround)
        {
            myAnimator.SetBool("isFalling", false);
            jumpTimes = 0;
        }
    }
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>