Unity技能工厂——怎样实现丝滑的角色连击动画

Posted Unity游戏开发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity技能工厂——怎样实现丝滑的角色连击动画相关的知识,希望对你有一定的参考价值。

RPG类型的动作游戏因为其敏捷的工作动作,技能连招之间丝滑的衔接,视觉冲击感爆棚的技能释放特效,所谓“拳拳刀肉,刀刀进身”的攻击效果,吸引了一大批忠实角色类扮演游戏的忠实粉丝。

那么,你知道连招是怎么实现的吗?

今天我们就来向大家展示一个简单的角色连击功能,并且手把手来教大家怎样去实现这个功能。

涉及到代码或者一些问题的答疑,各位小伙伴可以扫描下方二维码添加助教咨询。

首先我要在这里说一下,在动作游戏中,一个角色的连续攻击是常见的功能。

而一个连贯性丝滑的技能连击动作可以带给玩家更好的操作手感,以及可以有更多的可发挥性。

实现一个连贯性强的角色连击功能需要掌握2个基础知识,分别是Animator 的熟练运用和播放动画的代码编写。

开端

接下来,前期的准备工作需要做些什么呢?

1.     准备一个avatar为humanoid模型

(人形动画不会做不要紧,我们为你准备好的资源里都是会携带人形动画并且提前设置好的,不会的同学赶紧扫描下方二维码添加你的专属助教一起来实操吧!)

2.     准备人物攻击的动画Animation,也就是角色动画设置

(1)给角色添加组件-> Animator

    点击角色—点击Inspector面板下方的Add Component(添加组件)—输入Animator—点Animator完成添加,这时候角色就成功添加Animator组件了。

(2)创建一个动画控制器-> Animator Controller

新建一个文件夹,专门放动画控制器(如果已经有就必新建啦~);在该文件夹中鼠标右键—Creat—Animator Controller。

(3)设置攻击动画的Loop Time  为false

找到你需要的攻击动画点击Edit—Loop Time取消勾选就是False了

(4)给动画控制器(Animator Controller)添加一个循环的待机动画

点击你所创建的动画控制器Animator Controller,打开Animator面板右键Creat State—Emty—点击创建出来的图形化面板,在右上角命名为Idle(待机)并且给其Motion添加Idle(待机)动画;

三个攻击动作也是这么操作,分别创建atk_1、atk_2、atk_3的图形化面板,选定atk_1、atk_2、atk_3的攻击动作并且给其Motion添加atk_1、atk_2、atk_3的动画。

(5)添加动画变量用于控制攻击动画的切换

①点击Parameters创建Trigger类型的条件atk

atk: 用于判断是否要进行攻击

②点击Parameters创建Float类型的条件animTime

animTime:用于判断动画播放百分之多少之后进行攻击才能连击

(6)设置动画之间的播放条件

①右键Ildle—Make Transition—连atk_1

给Idle状态切换到Atk状态之间的线添加atk条件,并且将Has Exit Time 取消勾选

②右键atk_1—Make Transition—连atk_2

给atk_1状态切换到atk_2状态之间的线添加atk条件和animTime条件,animTime条件设置为Greater为0.5,并且将Has Exit Time 取消勾选

③右键atk_2—Make Transition—连atk_3

给atk_2状态切换到atk_3状态之间的线添加atk条件和animTime条件,animTime条件设置为Greater为0.5,并且将Has Exit Time 取消勾选

④所有的返回的条件设置相同:

分别右键atk_1、atk_2、atk_3—Make Transition—连Idle,将Has Exit Time勾选(默认是勾选的)

(7)最后将角色的动画控制器(Animator Controller)和人形角色动画(humanoid模型)拖拽给角色的Animator组件。

这样角色的动画设置就完成了。接下来,就要通过代码来控制角色进行连击动画的播放了。

3.通过代码控制角色连击:

在这里先说一下我的代码逻辑(为了方便小白们的理解,我说的详细一点哈)

(1)首先是声明一个Animator 组件的变量并且获取角色动画控制器;

(2)写一个方法UpdateAnimStatus();用于更新动画控制器中的动画变量,在UpdateAnimStatus方法中。

①这里的”animTime”表示更新的对象;

②用了Repeat, animTime的值只会在0-1之间确保animTime不会出现负值或者大于1的值;

③anim.GetCurrentAnimatorStateInfo(0).normalizedTime,获取当前第0层正在播放的动画的播放时间并且使其值转化为0-1之间。

代码讲解:

如果有看不清或者不理解的小伙伴可以扫描下方二微码添加你的专属助教老师答疑哦,同时还有更多U3D学习资源、面试攻略、求职模版等重磅福利等你来领取哦。

添加助教老师

领取海量U3D资源/面试攻略/简历模版

更有机会领取最新训练营免费入营资格

《ARGP狼人战斗系统》

等你来解锁

Unity架构师之路

Unity教程 | 墙外的Unity | 免费资源 

扫描下方二维码领取

2.Unity2D 横版 帧动画sprite animation+动画状态机animator+丝滑连击动作

ax总目录

1.帧动画sprite animation的创建

 2.动画状态机animator

教学链接https://blog.csdn.net/linxinfa/article/details/94392971?spm=1001.2014.3001.5506

为角色增加一个animator组件,并创建加一个动画控制器,拖入。

打开动画控制器,将动画拖入到动画控制器里面(只有一个控制器会自动添加)。然后右键动画。创建连接,根据逻辑关系将动画连接起来。然后在左上方点击“参数”。点击连接的线。在右边condition下增加一些动画转变的参数条件。通过脚本来控制参数,动画转变条件。

3. 连击动作(四连)

教学链接https://blog.csdn.net/weixin_30877755/article/details/95343666?spm=1001.2014.3001.5506

图层及其参数 。将每个链接的退出时间改成0,且固定时间改成0

 参数条件举例

 为什么设置了attack之后还要设置normalizedTime参数?因为如果设置一个参数的话,你点击攻击按钮会直接跳转到下一个动作。并不能完整的将攻击动作放完,那么就多添加一个。他们参数用来判断行为达到哪个阶段,并通过脚本来控制他们参数在进入因为和退出行为时都设置normalizedTime成零。这样就可以使得你点击攻击按钮动作放完了之后才到下一个动作。

 

 角色脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class playerControl : MonoBehaviour

    public bool Iscanjump = false;//是否能跳跃,默认不能
    private Rigidbody2D rig;//2D刚体

    float ScalX;//获取初始Scale.x,用于转向 
    public float dropConst;//下坠常数
    public float speed;//地面移动速度
    public float jumpspeedUp;//上升速度
    public float jumpspeedVertiacal;//空中左右移动速度

    private Animator ani;//动画控制器
    private AnimatorStateInfo state;//动画状态
                         // Start is called before the first frame update
    void Start()
    
        rig = GetComponent<Rigidbody2D>();//获取刚体
        ani = GetComponent<Animator>();//获取动画控制器
        ScalX = transform.localScale.x;
    

    // Update is called once per frame
    void Update()
    
        move();//移动函数
        attack();//攻击函数-四连击
    
    private void move()
        //水平,垂直俩个轴系
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        float dir = Input.GetAxisRaw("Horizontal");//方向-1 0 1
        //跳跃
        if (v > 0 && Iscanjump == true)
        
            rig.velocity = new Vector2(0, jumpspeedUp);//设置刚体速度,给予向量
        
        //长按高跳
        if (rig.velocity.y > 0 && Input.GetKey(KeyCode.W) && Iscanjump == false)
        
            rig.velocity += Vector2.up * 0.2f;//长按高跳额外得到向上速度
        
        //优化手感
        float a = dropConst * 5 - Mathf.Abs(rig.velocity.y);//通过下坠常数,空中速度快为0时,下坠常数a越大,即越快速 度过这个状态
        rig.velocity -= Vector2.up * a * Time.deltaTime;
        //方向改变
        if (dir != 0)
        
            transform.localScale = new Vector3(dir* ScalX, transform.localScale.y, transform.localScale.z);//通过改变scale改变方向
        
        //左右移动
        Vector3 vt = new Vector3(h, 0, 0).normalized;//vt为俩个轴系合成的方向向量,normalized单位化
            //移动动画
        if (dir != 0)
        
            ani.SetBool("Ismove", true);
        
        else  ani.SetBool("Ismove", false); 
   
        //空中左右移动,为地面jumpspeedVertiacal倍
        if (h != 0 && Iscanjump == false)
        
            gameObject.transform.Translate(vt * speed * jumpspeedVertiacal * Time.deltaTime);//通过这个函数来使用vt使得左右移动
        
        //地面左右移动
        else  gameObject.transform.Translate(vt * speed * Time.deltaTime); 
    
    private void attack() 
        

        state = ani.GetCurrentAnimatorStateInfo(0);
        //判断播放完
        if ((state.IsName("attack1") || state.IsName("attack2") || state.IsName("attack3") || state.IsName("attack4")) && state.normalizedTime >= 1.0f)
        
            
            ani.SetInteger("attack", 0);

        

        if (Input.GetKey(KeyCode.J))
        
    
            if (state.IsName("idle")||  state.IsName("move") && ani.GetInteger("attack")==0 )
            
                ani.SetInteger("attack", 1);
             else if (state.IsName("attack1")|| state.IsName("move") && ani.GetInteger("attack") == 1 )
            
                ani.SetInteger("attack", 2);
            
            else if (state.IsName("attack2")|| state.IsName("move") && ani.GetInteger("attack") == 2)
            
                ani.SetInteger("attack", 3);
            
            else if (state.IsName("attack3")|| state.IsName("move") && ani.GetInteger("attack") == 3)
            
                ani.SetInteger("attack", 4);
            
        
        if (state.normalizedTime >=1.0f) 
        
            ani.SetFloat("normalizedTime", state.normalizedTime);
        

        if (state.normalizedTime>=1&& ani.GetInteger("attack")==0)
        
            ani.SetLayerWeight(ani.GetLayerIndex("attack"), 0);
        
        

bottom脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class IsJump : MonoBehaviour

    //为了优化跳跃碰撞盒,在父物体下面新建空对象bottoom,并添加碰撞盒,用来检测底部(以前碰撞盒头碰到顶部地面还能跳不符合常理,所以需要作此操作)
 
    // Start is called before the first frame update
    void Start()
    

    

    // Update is called once per frame
    void Update()
    
  
    
    //检测否在地面碰撞盒子检测,通过给地面碰撞盒子transform的tag标签为ground
    private void OnCollisionStay2D(Collision2D collision)
    
        if (collision.transform.tag == "ground")
        
 
            gameObject.GetComponentInParent<playerControl>().Iscanjump = true;//获取父物体脚本变量并赋值
            
        

    
    private void OnCollisionExit2D(Collision2D collision)
    
        if (collision.transform.tag == "ground")
        

            gameObject.GetComponentInParent<playerControl>().Iscanjump = false;//获取父物体脚本变量并赋值

        
    

状态机里的动画行为脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SetNormalizedTime : StateMachineBehaviour

    private string normalizedTime = "normalizedTime";

    // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    
        animator.SetFloat(normalizedTime, 0);
    

    // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
    override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    
        animator.SetFloat(normalizedTime, stateInfo.normalizedTime);
    

    // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    
        animator.SetFloat(normalizedTime, 0);
    

 

下一篇

3.Unity2D 横板 比较丝滑的相机移动_ζั͡ ั͡雾 ั͡狼 ั͡✾的博客-CSDN博客Unity2D 横板 比较丝滑的相机移动直接将相机作为物体的子物体,相机会比较生硬,转向时候感觉非常的膈应人,下面是我的相机丝滑优化https://blog.csdn.net/qq_54263076/article/details/125640244

以上是关于Unity技能工厂——怎样实现丝滑的角色连击动画的主要内容,如果未能解决你的问题,请参考以下文章

如此丝滑的按钮交互效果

unity2d在animator组件里一个anim动画运行的过程中怎样直接切换到另一个?

unity如何让技能释放者变亮 其余人变暗

原生javascript手写一个丝滑的轮播图

大牛耗时一年最佳总结,让你的app体验更丝滑的11种方法建议收藏

关于Unity中新版动画系统的使用