草庐IT

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

我的猫叫冰彬 2023-04-20 原文

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打勾

  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;
        }
    }
}

有关Unity2D基础之人物动画、移动、跳跃的更多相关文章

  1. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  2. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  3. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  4. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  5. unity---接入Admob - 2

    目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里​编辑 3.解析依赖到项目中

  6. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  7. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  8. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

  9. 软件测试基础 - 2

    Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功

  10. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

随机推荐