【unity实战】使用Unity实现动作游戏的攻击 连击 轻重攻击和打击感

news2024/7/7 15:31:19

最终效果

在这里插入图片描述

文章目录

  • 最终效果
  • 前言
  • 素材下载:
  • 玩家移动跳跃控制
  • 攻击动画配置
    • 轻攻击
    • 重攻击
  • 攻击时禁止移动和攻击移动补偿
  • 敌人击退和播放受击动画
  • 受击特效
  • 攻击停顿和屏幕震动
  • 局部顿帧(补充)
  • 参考
  • 源码
  • 完结

前言

注意本文为自己的学习记录笔记,主要是对游戏攻击 连击 轻重攻击和打击感进行探究,其中打击感实现一般依靠播放受击动画、击退、攻击特效、时停和屏幕震动反馈等来实现,如果你有其他的好方法也欢迎补充。

素材下载:

人物
https://legnops.itch.io/red-hood-character
在这里插入图片描述

敌人
https://jesse-m.itch.io/skeleton-pack
在这里插入图片描述

环境
https://szadiart.itch.io/pixel-fantasy-caves
在这里插入图片描述

攻击特效
https://v-ktor.itch.io/pixelated-attackhit-animations
在这里插入图片描述

玩家移动跳跃控制

public class PlayerController : MonoBehaviour
{
    [Header("移动和跳跃参数")] 
    public float moveSpeed; // 移动速度
    public float jumpForce; // 跳跃力量
    new private Rigidbody2D rigidbody; // 刚体组件
    private Animator animator; // 动画控制器
    private float input; // 输入
    private bool isGround; // 是否在地面上
    [SerializeField] private LayerMask layer; // 地面碰撞层

    [SerializeField] private Vector3 check; // 地面检测向量

    void Start()
    {
        rigidbody = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        input = Input.GetAxisRaw("Horizontal");
        isGround = Physics2D.OverlapCircle(transform.position + new Vector3(check.x, check.y, 0), check.z, layer);

        animator.SetFloat("Horizontal", rigidbody.velocity.x);
        animator.SetFloat("Vertical", rigidbody.velocity.y);
        animator.SetBool("isGround", isGround);

        Move();
        Attack();
    }

    void Move()
    {
        // 根据输入来移动角色
        rigidbody.velocity = new Vector2(input * moveSpeed, rigidbody.velocity.y);
       
        // 处理跳跃
        if (Input.GetButtonDown("Jump") && isGround)
        {
            rigidbody.velocity = new Vector2(0, jumpForce);
            animator.SetTrigger("Jump"); // 触发跳跃动画
        }

        // 根据水平速度方向更新角色朝向
        if (rigidbody.velocity.x < 0)
            transform.localScale = new Vector3(-1, 1, 1); // 向左
        else if (rigidbody.velocity.x > 0)
            transform.localScale = new Vector3(1, 1, 1); // 向右
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(transform.position + new Vector3(check.x, check.y, 0), check.z);
    }

}

攻击动画配置

攻击动画分为轻攻击和重攻击

轻攻击

在这里插入图片描述
在这里插入图片描述

重攻击

在这里插入图片描述

[Header("攻击")]
public float interval = 2f; // 攻击间隔时间
private float timer; // 计时器
private bool isAttack; // 是否正在攻击
private string attackType; // 攻击类型
private int comboStep; // 连击步骤

void Update()
{
    //...
    Attack();
}


void Attack()
{
    // 轻攻击输入检测
    if (Input.GetKeyDown(KeyCode.Return) && !isAttack)
    {
        isAttack = true;
        attackType = "Light";
        comboStep++; // 连击步骤加一
        if (comboStep > 3)
            comboStep = 1; // 连击步骤循环
        timer = interval; // 设置计时器
        animator.SetTrigger("LightAttack"); // 触发轻攻击动画
        animator.SetInteger("ComboStep", comboStep); // 设置连击步骤参数
    }
    // 重攻击输入检测
    if (Input.GetKeyDown(KeyCode.RightShift) && !isAttack)
    {
        isAttack = true;
        attackType = "Heavy";
        comboStep++; // 连击步骤加一
        if (comboStep > 3)
            comboStep = 1; // 连击步骤循环
        timer = interval; // 设置计时器
        animator.SetTrigger("HeavyAttack"); // 触发重攻击动画
        animator.SetInteger("ComboStep", comboStep); // 设置连击步骤参数
    }

    // 处理连击计时器
    if (timer != 0)
    {
        timer -= Time.deltaTime;
        if (timer <= 0)
        {
            timer = 0;
            comboStep = 0; // 重置连击步骤
        }
    }
}

//攻击结束
public void AttackOver()
{
    isAttack = false;
}

配置每个攻击动画,在执行的位置执行攻击结束事件,通常攻击结束事件都不会放在动画的最后一帧,因为连击一般都存在预输入,也就是在上一个动画还未结束时就进行输入,这样能很好的提升combo的连贯性
在这里插入图片描述
效果
在这里插入图片描述

攻击时禁止移动和攻击移动补偿

攻击时我们不希望玩家还能移动,但是简单粗暴的禁止移动又会影响我们的攻击操作手感,在死亡细胞等游戏中,攻击时会朝前方以一个较小的速度移动,这样可以一定程度的补偿攻击时无法移动的缺陷
在这里插入图片描述

[Header("攻击补偿速度")]
public float lightSpeed; // 轻攻击速度
public float heavySpeed; // 重攻击速度

void Move()
{
    // 如果玩家没有在攻击状态下,根据输入来移动角色
    if (!isAttack)
        rigidbody.velocity = new Vector2(input * moveSpeed, rigidbody.velocity.y);
    else
    {
        // 如果正在攻击,则根据攻击类型设置速度
        if (attackType == "Light")
            rigidbody.velocity = new Vector2(transform.localScale.x * lightSpeed, rigidbody.velocity.y);
        else if (attackType == "Heavy")
            rigidbody.velocity = new Vector2(transform.localScale.x * heavySpeed, rigidbody.velocity.y);
    }

    // ...
}

配置
在这里插入图片描述
效果
在这里插入图片描述

敌人击退和播放受击动画

配置敌人受击动画
在这里插入图片描述
配置玩家每个动画的攻击范围
在这里插入图片描述
新增Enemy 敌人脚本,实现击退和受击动画

public class Enemy : MonoBehaviour
{
    public float speed;                 // 敌人的移动速度
    private Vector2 direction;          // 受击时的移动方向
    private bool isHit;                 // 是否正在受击状态
    private AnimatorStateInfo info;     // 动画状态信息

    private Animator animator;          // 敌人的主动画控制器
    new private Rigidbody2D rigidbody;  // 敌人的刚体组件

    void Start()
    {
        // 获取组件的引用
        animator = transform.GetComponent<Animator>();
        rigidbody = transform.GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        info = animator.GetCurrentAnimatorStateInfo(0); // 获取当前动画状态信息
        if (isHit)
        {
            rigidbody.velocity = direction * speed; // 根据受击方向设置速度
            if (info.normalizedTime >= .6f)
                isHit = false; // 当动画播放超过60%时结束受击状态
        }
    }

    // 外部调用,使敌人进入受击状态
    public void GetHit(Vector2 direction)
    {
        transform.localScale = new Vector3(-direction.x, 1, 1); // 根据受击方向调整朝向
        isHit = true; // 进入受击状态
        this.direction = direction; // 设置受击方向
        animator.SetTrigger("Hit"); // 播放主动画的受击动画状态
    }
}

修改PlayerController,调用击退敌人

private void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("Enemy"))
    {
        // 根据角色朝向确定敌人受击方向
        if (transform.localScale.x > 0)
            other.GetComponent<Enemy>().GetHit(Vector2.right); // 右侧受击
        else if (transform.localScale.x < 0)
            other.GetComponent<Enemy>().GetHit(Vector2.left); // 左侧受击
    }
}

效果
在这里插入图片描述

受击特效

在这里插入图片描述
特效动画配置
在这里插入图片描述
修改Enemy

private Animator hitAnimator;       // 敌人的受击特效动画控制器

hitAnimator = transform.GetChild(0).GetComponent<Animator>(); // 敌人的受击特效动画控制器在子对象中

// 外部调用,使敌人进入受击状态
 public void GetHit(Vector2 direction)
 {
     //...
     hitAnimator.SetTrigger("Hit"); // 播放受击特效动画
 }

效果
在这里插入图片描述

攻击停顿和屏幕震动

新增AttackSense

public class AttackSense : MonoBehaviour
{
    private static AttackSense instance;
    public static AttackSense Instance
    {
        get
        {
            if (instance == null)
                instance = FindObjectOfType<AttackSense>(); // 查找当前场景中的 AttackSense 实例
            return instance;
        }
    }

    private bool isShake; // 是否正在进行摄像机震动

    // 暂停游戏一段时间
    public void HitPause(int duration)
    {
        StartCoroutine(Pause(duration));
    }

    // 使用协程实现暂停功能
    IEnumerator Pause(int duration)
    {
        float pauseTime = duration / 60f; // 将帧数转换为实际暂停时间
        Time.timeScale = 0.2f; // 将游戏时间缩放设为0.2
        yield return new WaitForSecondsRealtime(pauseTime); // 等待指定的暂停时间
        Time.timeScale = 1; // 恢复游戏时间正常
    }

    // 触发摄像机震动效果
    public void CameraShake(float duration, float strength)
    {
        if (!isShake) // 如果当前没有进行震动
            StartCoroutine(Shake(duration, strength));
    }

    // 使用协程实现摄像机震动效果
    IEnumerator Shake(float duration, float strength)
    {
        isShake = true; // 标记正在进行震动
        Transform camera = Camera.main.transform; // 获取主摄像机的 Transform 组件
        Vector3 startPosition = camera.position; // 记录摄像机震动前的初始位置

        while (duration > 0)
        {
            // 将摄像机位置随机偏移一定范围来模拟震动效果
            camera.position = Random.insideUnitSphere * strength + startPosition;
            duration -= Time.deltaTime; // 每帧减去时间
            yield return null; // 等待下一帧
        }
        
        camera.position = startPosition; // 震动结束后将摄像机位置恢复到初始位置
        isShake = false; // 结束震动状态
    }
}

修改PlayerController调用

[Header("打击感")]
public float shakeTime; // 摇晃时间
public int lightPause; // 轻攻击暂停时间
public float lightStrength; // 轻攻击相机震动强度
public int heavyPause; // 重攻击暂停时间
public float heavyStrength; // 重攻击相机震动强度

private void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("Enemy"))
    {
        // 根据攻击类型处理打击感
        if (attackType == "Light")
        {
            AttackSense.Instance.HitPause(lightPause); // 轻攻击暂停
            AttackSense.Instance.CameraShake(shakeTime, lightStrength); // 相机震动
        }
        else if (attackType == "Heavy")
        {
            AttackSense.Instance.HitPause(heavyPause); // 重攻击暂停
            AttackSense.Instance.CameraShake(shakeTime, heavyStrength); // 相机震动
        }

        // 根据角色朝向确定敌人击退方向
        if (transform.localScale.x > 0)
            other.GetComponent<Enemy>().GetHit(Vector2.right); // 右侧受击
        else if (transform.localScale.x < 0)
            other.GetComponent<Enemy>().GetHit(Vector2.left); // 左侧受击
    }
}

配置参数
在这里插入图片描述
最终效果
在这里插入图片描述

局部顿帧(补充)

前面的顿帧这个思路其实不太好,可以看看有的游戏,他们不是所有物体都停顿的,一般是攻击者和受击者停顿,其他的不受影响。animator有个scale播放速度scale值应该可以实现这种效果,连续攻击可能用局部顿帧好一点,下面大概分享一下思路

public class AttackController : MonoBehaviour
{
    public Animator animator;
    public float slowMotionTimeScale = 0.5f; // 慢动作时的时间缩放值

    public void PerformAttack()
    {
        StartCoroutine(AttackCoroutine());
    }

    IEnumerator AttackCoroutine()
    {
        // 播放攻击动画前先设置慢动作
        animator.speed = slowMotionTimeScale;

        // 等待攻击动画播放完成
        yield return new WaitForSeconds(animator.GetCurrentAnimatorStateInfo(0).length / slowMotionTimeScale);

        // 恢复正常时间缩放
        animator.speed = 1f;
    }
}

在这个示例中,PerformAttack 方法可以被调用来开始攻击动作。在攻击动作开始时,时间缩放被设置为 slowMotionTimeScale,然后通过 Coroutine 等待攻击动画播放完成后,再恢复为正常速度。

通过这种方法,你可以在游戏中实现局部的动画停顿效果,而不是整体减速,从而增强游戏的视觉冲击力和玩家的体验。

参考

https://www.bilibili.com/video/BV1fX4y1G7tv

源码

整理好我会放上来

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1894129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微机原理 选择题

D C MOV、PUSH、POP、XLAT&#xff08;查表&#xff09;、IN、OUT不影响标志位 D B D C D C D B 1. (单选题, 5分)8位无符号数(字节)表示的数值范围是( ), 16位无符号数(字)表示的数值范围是( )。 A. 0~128 0~32768B. 0~255 0~655…

Websocket通信实战项目(js)(图片互传应用)(下)客户端H5+css+js实现

Rqtz : 个人主页 ​ 共享IT之美&#xff0c;共创机器未来 Sharing the Beauty of IT and Creating the Future of Machines Together 目录 起始 客户端GUI Javascripts连接websocket 使用localStorage保存用户输入的IP Websocket连接成功 Websocket接收数据 解析…

51-5 权限维持2 - 影子账号(隐藏用户)

权限维持技术 权限维持技术(Persistence,也称为权限持久化)是一种能够在系统重启、用户更改密码或其他可能导致访问中断的情况下保持对系统访问的技术。例如,它包括创建系统服务、利用计划任务、修改系统启动项或注册表、以及映像劫持等方法。 创建影子账户 影子账户是指隐…

Labview_Workers5.0 学习笔记

1.Local Request 个人理解该类型的请求针对自身的&#xff0c;由EHL或者MHL到该vi的MHL中。 使用快速放置快捷键"Ctrl9"创建方法如下&#xff1a; 创建后的API接口命名均为rql开头&#xff0c;并且在所选main.vi中的MHL创建对应的条件分支。 此时使用该API函数就…

【计算机毕业设计】026基于微信小程序的原创音乐

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

行业模板|DataEase旅游行业大屏模板推荐

DataEase开源数据可视化分析工具于2022年6月发布模板市场&#xff08;https://templates-de.fit2cloud.com&#xff09;&#xff0c;并于2024年1月新增适用于DataEase v2版本的模板分类。模板市场旨在为DataEase用户提供专业、美观、拿来即用的大屏模板&#xff0c;方便用户根据…

SpringBoot 启动流程二

SpringBoot启动流程二 我们首先查看构造方法 SpringApplication 我们发现这个构造方法还是在SpringApplication类里面 这个构造方法还是调用了自身的构造方法 传入了两个参数 第一个参数叫resourceLoader 传入的是一个资源加载器 要从外部读入东西 这个方法通过this关键字…

番外篇 | YOLOv8改进之即插即用全维度动态卷积ODConv + 更换Neck网络为GFPN

前言:Hello大家好,我是小哥谈。本文所做出的改进是在YOLOv8中引入即插即用全维度动态卷积ODConv和更换Neck网络为GFPN,希望大家学习之后能够有所收获~!🌈 目录 🚀1.基础概念 🚀2.网络结构 🚀3.添加步骤 🚀4.改进方法 🍀🍀步骤1:block.py文件修改…

【Android面试八股文】你是怎么保证Android设备的时间与服务器时间同步的?(使用NTP和TrueTime方案)

文章目录 一、网络时间协议(NTP)二、使用网络时间协议(NTP)2.1 使用系统提供的 NTP 服务器2.2 使用TrueTime2.2.1 引入TrueTime库2.2.2 初始化 TrueTime2.2.3 用法2.2.4 使用 TrueTime 获取时间2.2.4 自动更新时间2.2.5 注意事项二. 使用 HTTP 请求获取服务器时间2.1. 发送…

技术探索:利用Python库wxauto实现Windows微信客户端的全面自动化管理

项目地址&#xff1a;github-wxauto 点击即可访问 项目官网&#xff1a;wxauto 点击即可访问 &#x1f602;什么是wxauto? wxauto 是作者在2020年开发的一个基于 UIAutomation 的开源 Python 微信自动化库&#xff0c;最初只是一个简单的脚本&#xff0c;只能获取消息和发送…

同方威视受邀盛装亮相2024长三角快递物流展(杭州)助力行业物畅其流

同方威视技术股份有限公司携安全检测产品和综合解决方案&#xff0c;盛装亮相2024长三角快递物流展&#xff08;杭州&#xff09; 展位号&#xff1a;3C馆A07-1 时间&#xff1a;2024年7月8-10日 地址&#xff1a;杭州国际博览中心&#xff08;浙江省杭州市萧山区奔竞大道35…

【路由交换技术】Cisco Packet Tracer基础入门教程(五)

这一期我们来学习端口聚合&#xff0c;这是针对交换机的技术 前言 不知道大家有没有注意到&#xff0c;我们之前的实验在交换机与交换机之间只用一条线连接&#xff0c;像这样 通过今天的学习&#xff0c;我们要用两条线来连接交换机&#xff0c;就像这样&#xff08;为了能…

球形气膜:现代娱乐场馆的最佳选择—轻空间

随着科技的发展和人们对高品质生活的追求&#xff0c;娱乐场馆的建设迎来了新的变革。球形气膜结构凭借其独特的优势&#xff0c;逐渐成为现代娱乐场馆建设的最佳选择。轻空间将介绍球形气膜的优势&#xff0c;并探讨其在不同应用场景中的广泛应用。 球形气膜的优势 1. 独特的建…

Kotlin和Java的一些不同点

1.Kotlin 的变量是没有默认值的&#xff08;因此要求初始化&#xff09;&#xff0c;Java的成员变量是有默认值的 Java的成员变量&#xff1a; String name; // 默认值是 null int count; // 默认值是 0不过其实 Java 也只是成员变量有默认值&#xff0c;局部变量也是没有默…

以品质为初心,以创新为驱动,光明乳业闪耀第十五届中国奶业大会

2024年7月3日&#xff0c;以“数智赋能引领产业发展增长点&#xff0c;科技创新驱动奶业新质生产力”为主题的中国奶业协会第十五届奶业大会奶业20强&#xff08;D20&#xff09;论坛暨2024中国奶业展览会隆重召开&#xff0c;光明乳业党委书记、董事长黄黎明受邀出席会议&…

Linux 压测工具---ab

安装 yum -y install httpd-tools 本文用于压测k8s集群内pod&#xff0c;k8s集群master可直接测试pod ip 命令&#xff1a; ab -n 10000 -c 100 http://10.42.8.212/ 其中&#xff0c;-n表示请求数&#xff0c;-c表示并发数&#xff0c;ip必须有”/“&#xff0c;表示此目录…

如何清理电脑内存?让电脑运行如飞!

电脑内存&#xff08;RAM&#xff09;的清理对于维持系统的流畅运行至关重要。随着使用时间的增加&#xff0c;系统内存会被各种应用程序和后台进程占用&#xff0c;导致系统响应变慢&#xff0c;甚至出现卡顿现象。通过有效地清理内存&#xff0c;可以提升电脑的性能&#xff…

5.基于SpringBoot的SSMP整合案例-数据层开发

目录 1.新建项目 2.实体类开发&#xff1a; 2.1在pom.xml中增加Lombok坐标&#xff1a; 2.2添加Book实体类 3.数据层开发&#xff1a; 3.1 配置MyBatisPlus与Druid 3.2创建数据层接口 3.3写测试类 3.4点击运行&#xff1a; 4.数据层快速开发&#xff1a; 4.1配置MyB…

【数据结构】02.顺序表

一、顺序表的概念与结构 1.1线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。线性表是⼀种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构&#xff0…

HighConcurrencyCommFramework c++通讯服务器框架 :简介-信号处理

项目是4月份左右做的现在整理到博客上&#xff0c;顺便加深一下印象 介绍 项目描述:该项目是使用 C 实现的高并发服务器脚手架&#xff0c;包含线程池和连接池等技术&#xff0c;支持开发者进行二次开发复用&#xff0c;只需 要添加对应业务逻辑即可完成通信服务器、网络交易…