Unity教程(十九)战斗系统 受击反馈

Unity开发2D类银河恶魔城游戏学习笔记

Unity教程(零)Unity和VS的使用相关内容
Unity教程(一)开始学习状态机
Unity教程(二)角色移动的实现
Unity教程(三)角色跳跃的实现
Unity教程(四)碰撞检测
Unity教程(五)角色冲刺的实现
Unity教程(六)角色滑墙的实现
Unity教程(七)角色蹬墙跳的实现
Unity教程(八)角色攻击的基本实现
Unity教程(九)角色攻击的改进

Unity教程(十)Tile Palette搭建平台关卡
Unity教程(十一)相机
Unity教程(十二)视差背景

Unity教程(十三)敌人状态机
Unity教程(十四)敌人空闲和移动的实现
Unity教程(十五)敌人战斗状态的实现
Unity教程(十六)敌人攻击状态的实现
Unity教程(十七)敌人战斗状态的完善

Unity教程(十八)战斗系统 攻击逻辑
Unity教程(十九)战斗系统 受击反馈
Unity教程(二十)战斗系统 角色反击


如果你更习惯用知乎
Unity开发2D类银河恶魔城游戏学习笔记目录


文章目录

  • Unity开发2D类银河恶魔城游戏学习笔记
  • 前言
  • 一、概述
  • 二、闪烁特效的实现
    • (1)创建闪烁特效材质
    • (2)实现闪烁特效脚本
  • 三、击退效果的实现
  • 四、攻击方向问题的修复
  • 总结 完整代码
    • EntityFX.cs
    • Entity.cs
    • PlayerPrimaryAttackState.cs


前言

本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记,如有错误,欢迎指正。

本节实现战斗系统的受击反馈部分。

Udemy课程地址

对应视频:
On Hit Fx
On Hit Impact
Attack’s direction hot fix


一、概述

本节我们实现角色的受击反馈,包括闪烁特效,击退效果两部分。
为实现特效,创建了实体特效组件,用于存放作用于实体的特效。闪烁特效用两种材质交替实现。击退效果设置受击角色速度实现。
除此之外,还修复了学习过程中发现的攻击方向bug。
在这里插入图片描述

二、闪烁特效的实现

在开始之前我们先整理一下脚本文件,把相应脚本放入创建的文件夹中,文件整理大致如下:
在这里插入图片描述
在这里插入图片描述

(1)创建闪烁特效材质

首先我们在Materials文件夹中创建闪烁特效FlashFXMaterial。
在Project面板中
右键->Create->Material->重命名为FlashFXMaterial
在这里插入图片描述

在这里插入图片描述
Shader选择GUI->Text Shader,Text Color选择白色
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
更换Playerd Animator的材质看一下效果
在这里插入图片描述
在这里插入图片描述
将原始材质与这个纯白的来回交替就可以产生闪烁的特效。

(2)实现闪烁特效脚本

首先我们创建一个特效组件EntityFX,用来实现实体的特效。

特效由更换精灵的材质实现,即要调用接口,修改对应实体下Animator中SpriteRenderer组件的Material属性。
首先要在EntityFX中获取相应SpriteRenderer组件,并设置两个变量分别存储原始材质和受击后的材质。还要创建变量flashDuration,定义受击后闪烁的时长。

//EntityFX:实体特效
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EntityFX : MonoBehaviour
{private SpriteRenderer sr;[Header("Flash FX")][SerializeField] private Material hitMat;private Material originalMat;[SerializeField] private float flashDuration;private void Start(){sr = GetComponentInChildren<SpriteRenderer>();originalMat = sr.material;}
}

接着使用协程实现材质每隔一小段时间更替一次材质。

    //闪烁特性private IEnumerator FlashFX(){sr.material = hitMat;yield return new WaitForSeconds(flashDuration);sr.material = originalMat;}

在Entity中创建特效组件EntityFX并赋值。
    #region 组件public EntityFX fx {  get; private set; }public Rigidbody2D rb { get; private set; }public Animator anim { get; private set; }#endregion//获取组件protected virtual void Start(){fx= GetComponent<EntityFX>();rb= GetComponent<Rigidbody2D>();anim= GetComponentInChildren<Animator>();}

在Entity的Damage函数中调用闪烁特效。

    public virtual void Damage(){fx.StartCoroutine("FlashFX");Debug.Log(gameObject.name + " was damaged");}

将EntityFX拖到Player上作为组件,拖入闪烁特效作为受击材质,并给闪烁持续时间赋一个合适的值。
在这里插入图片描述
骷髅小怪Enemy_Skeleton同理,最终效果如下:
在这里插入图片描述

三、击退效果的实现

击退效果我们依然用协程实现。在实体受到攻击时,设置实体后退速度,等待一小段时间后恢复原来速度设置。

在Entity中创建击退效果相关的变量,分别为击退方向,是否被击退,击退持续的时间。

    [Header("Knockback Info")][SerializeField] protected Vector2 knockbackDirection;[SerializeField] protected float knockbackDuration;protected bool isKnocked;

创建一个协程HitKnockback设置击退速度。一个实体受到攻击后,后退方向与它面向的方向相反。这里存在一个问题,在玩家从背后攻击骷髅时,一般骷髅会在检测后迅速转身,但个别时候会发生它没有转身就受到攻击的情况,这时骷髅会向它朝向的方向跌,这个问题本节暂不解决。

isKnocked用来记录实体是否处于被击退的状态,在击退效果持续期间我们需要让其他涉及设置速度的操作都不生效,在击退结束后再恢复原来状态里的速度设置。

    protected virtual IEnumerator HitKnockback(){isKnocked = true;rb.velocity = new Vector2(knockbackDirection.x * -facingDir, knockbackDirection.y);yield return new WaitForSeconds(knockbackDuration);isKnocked= false;}

在速度设置和速度置零函数中添加一个判定,当处于击退状态时直接return,不执行后面设置速度的部分,直到击退结束。

    #region 速度设置//速度置零public void ZeroVelocity(){if (isKnocked)return;rb.velocity = new Vector2(0, 0);}//设置速度public void SetVelocity(float _xVelocity, float _yVelocity){if (isKnocked)return;rb.velocity = new Vector2(_xVelocity, _yVelocity);FlipController(_xVelocity);}#endregion

在Damage函数中调用击退效果。

    public virtual void Damage(){fx.StartCoroutine("FlashFX");StartCoroutine("HitKnockback");Debug.Log(gameObject.name + " was damaged");}

在这里插入图片描述
骷髅受到攻击后直接飘出去了。加大重力就可以解决这一问题。但需要注意加大重力后,骷髅的移速也会变迟缓,可以根据需要设定。

在这里插入图片描述

在这里插入图片描述

四、攻击方向问题的修复

在学习过程中,发现了一个问题,在个别先向左后向右跑的时候,角色面向右进行攻击时,攻击方向却是向左。
在这里插入图片描述
问题出在下图位置。在基本攻击状态PlayerPrimaryAttackState中,我们原本设置的是攻击时如果有输入,攻击方向为输入方向,但在进入攻击状态时xInput并不是最新的。
在这里插入图片描述
xInput的定义在状态基类PlayerState中,它的赋值在Update函数中进行,PlayerPrimaryAttackState继承自状态基类,所以每次进入攻击状态获取的xInput都是上次攻击Update中获取的。
在这里插入图片描述
教程里的改法是在进入状态时,xInput赋0。我选择了在进入时,重新获取一次xInput。

    //进入public override void Enter(){base.Enter();xInput = Input.GetAxisRaw("Horizontal");//攻击方向问题教程改法//xInput = 0;if (comboCounter > 2 || Time.time > lastTimeAttacked + comboWindow)comboCounter = 0;player.anim.SetInteger("comboCounter",comboCounter);float attackDir = player.facingDir;if (xInput != 0)attackDir = xInput;player.SetVelocity(player.attackMovement[comboCounter].x * attackDir, player.attackMovement[comboCounter].y);stateTimer = 0.1f;}

总结 完整代码

EntityFX.cs

实体特效组件。实现闪烁特效。

//EntityFX:实体特效
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EntityFX : MonoBehaviour
{private SpriteRenderer sr;[Header("Flash FX")][SerializeField] private Material hitMat;private Material originalMat;[SerializeField] private float flashDuration;private void Start(){sr = GetComponentInChildren<SpriteRenderer>();originalMat = sr.material;}//闪烁特效private IEnumerator FlashFX(){sr.material = hitMat;yield return new WaitForSeconds(flashDuration);sr.material = originalMat;}
}

Entity.cs

获取实体特效组件,调用闪烁特效。实现并调用击退效果,修改速度设置。

//Entity:实体类
using System.Collections;
using System.Collections.Generic;
using Unity.IO.LowLevel.Unsafe;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;public class Entity : MonoBehaviour
{[Header("Knockback Info")][SerializeField] protected Vector2 knockbackDirection;[SerializeField] protected float knockbackDuration;protected bool isKnocked;[Header("Flip Info")]protected bool facingRight = true;public int facingDir { get; private set; } = 1;[Header("Collision Info")]public Transform attackCheck;public float attackCheckRadius;[SerializeField] protected Transform groundCheck;[SerializeField] protected float groundCheckDistance;[SerializeField] protected Transform wallCheck;[SerializeField] protected float wallCheckDistance;[SerializeField] protected LayerMask whatIsGround;#region 组件public EntityFX fx {  get; private set; }public Rigidbody2D rb { get; private set; }public Animator anim { get; private set; }#endregionprotected virtual void Awake(){}//获取组件protected virtual void Start(){fx= GetComponent<EntityFX>();rb= GetComponent<Rigidbody2D>();anim= GetComponentInChildren<Animator>();}// 更新protected virtual void Update(){}public virtual void Damage(){fx.StartCoroutine("FlashFX");StartCoroutine("HitKnockback");Debug.Log(gameObject.name + " was damaged");}protected virtual IEnumerator HitKnockback(){isKnocked = true;rb.velocity = new Vector2(knockbackDirection.x * -facingDir, knockbackDirection.y);yield return new WaitForSeconds(knockbackDuration);isKnocked= false;}#region 速度设置//速度置零public void ZeroVelocity(){if (isKnocked)return;rb.velocity = new Vector2(0, 0);}//设置速度public void SetVelocity(float _xVelocity, float _yVelocity){if (isKnocked)return;rb.velocity = new Vector2(_xVelocity, _yVelocity);FlipController(_xVelocity);}#endregion#region 翻转//翻转实现public virtual void Flip(){facingDir = -1 * facingDir;facingRight = !facingRight;transform.Rotate(0, 180, 0);}//翻转控制public virtual void FlipController(float _x){if (_x > 0 && !facingRight)Flip();else if (_x < 0 && facingRight)Flip();}#endregion#region 碰撞//碰撞检测public virtual bool isGroundDetected() => Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);public virtual bool isWallDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);//绘制碰撞检测protected virtual void OnDrawGizmos(){Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));Gizmos.DrawWireSphere(attackCheck.position, attackCheckRadius);}#endregion
}

PlayerPrimaryAttackState.cs

修复攻击方向问题。

//PlayerPrimaryAttackState:基本攻击状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerPrimaryAttackState : PlayerState
{private int comboCounter;private float lastTimeAttacked;private float comboWindow = 2;public PlayerPrimaryAttackState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName) : base(_stateMachine, _player, _animBoolName){}//进入public override void Enter(){base.Enter();xInput = Input.GetAxisRaw("Horizontal");//攻击方向问题教程改法//xInput = 0;if (comboCounter > 2 || Time.time > lastTimeAttacked + comboWindow)comboCounter = 0;player.anim.SetInteger("comboCounter",comboCounter);float attackDir = player.facingDir;if (xInput != 0)attackDir = xInput;player.SetVelocity(player.attackMovement[comboCounter].x * attackDir, player.attackMovement[comboCounter].y);stateTimer = 0.1f;}//退出public override void Exit(){base.Exit();player.StartCoroutine("BusyFor", 0.15f);comboCounter++;lastTimeAttacked = Time.time;}// 更新public override void Update(){base.Update();if (stateTimer < 0)player.ZeroVelocity();if(triggerCalled)stateMachine.ChangeState(player.idleState);}
}

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

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

相关文章

从感知到认知:解读人工智能技术的核心突破

引言&#xff1a;感知与认知的人工智能之旅 人工智能的演进历程&#xff0c;就像人类的成长过程&#xff0c;从最初学会“看”“听”“感知”这个世界&#xff0c;到逐渐具备“理解”“推理”和“决策”的能力。这两个阶段——感知与认知&#xff0c;不仅是人工智能的技术核心&…

文生图模型开源之光!ComfyUI - AuraFlow本地部署教程

一、模型介绍 AuraFlow 是唯一一个真正开源的文生图模型&#xff0c;由Fal团队开源&#xff0c;其代码和权重都放在了 FOSS 许可证下。基于 6.8B 参数优化模型架构&#xff0c;采用最大更新参数化技术&#xff0c;还重新标注数据集提升指令遵循质量。在物体空间和色彩上有优势…

SpringMVC ——(1)

1.SpringMVC请求流程 1.1 SpringMVC请求处理流程分析 Spring MVC框架也是⼀个基于请求驱动的Web框架&#xff0c;并且使⽤了前端控制器模式&#xff08;是⽤来提供⼀个集中的请求处理机制&#xff0c;所有的请求都将由⼀个单⼀的处理程序处理来进⾏设计&#xff0c;再根据请求…

#HarmonyOS篇: 学习资料

学习课堂 https://developer.huawei.com/consumer/cn/training/ https://developer.huawei.com/consumer/cn/doc/start/training-introduction-0000001181392655 华为开发者指导手册 https://developer.huawei.com/consumer/cn/doc/start/guidebook-0000001056335559 博客…

Docker 安装 中文版 GitLab

Docker 安装系列 安装GitLab、解决服务器内存不足问题、使用域名/IP地址访问项目 1、拉取 [rootTseng ~]# docker pull twang2218/gitlab-ce-zh:latest latest: Pulling from twang2218/gitlab-ce-zh 8ee29e426c26: Pull complete 6e83b260b73b: Pull complete e26b65fd11…

python基于基于自然语言处理技术的话题文本分类

大家好我是君君学姐&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款《python基于基于自然语言处理技术的话题文本分类》毕业设计项目。项目源码以及部署相关请联系君君学姐&#xff0c;文末附上联系信息 。 &#x1f388;作者&#xff1a;君君学姐&#x1f388; …

iOS如何操作更新推送证书

最近收到一份邮件,应该如何操作呢,证书还是跟以前一样冲钥匙串直接申请吗 Hello, As we announced in October, the Certification Authority (CA) for Apple Push Notification service (APNs) is changing. APNs will update the server certificates in sandbox on January…

ShardingSphere-JDBC

1. 什么是分库分表&#xff1f; 分库分表是一种数据库扩展技术&#xff0c;通过将数据拆分到多个数据库&#xff08;分库&#xff09;或多个表&#xff08;分表&#xff09;中来解决单一数据库或表带来的性能瓶颈。分库分表可以有效提升系统的可扩展性、性能和高并发处理能力&…

Scala的正则表达式二

验证用户名是否合法 规则 1.长度在6-12之间 2.不能数字开头 3.只能包含数字&#xff0c;大小写字母&#xff0c;下划线def main(args: Array[String]): Unit {val name1 "1admin"//不合法&#xff0c;是数字开头val name2 "admin123"//合法val name3 &quo…

谈谈web3

全面解析 Web3&#xff1a;未来互联网的革命性进程 引言&#xff1a;互联网进化的三部曲 互联网的发展经历了三个重要阶段&#xff0c;每一个阶段都深刻地改变了我们的生活方式&#xff1a; Web1&#xff08;1990-2005&#xff09;&#xff1a;静态互联网时代&#xff0c;人…

mysql高级篇 | 尚硅谷 | 第11章_数据库的设计规范

十一、数据库的设计规范 文章目录 十一、数据库的设计规范一、为什么需要数据库设计二、范式1、范式简介2、范式都包括哪些3、键和相关属性的概念4、第一范式(1st NF)5、第二范式(2nd NF)6、第三范式(3rd NF)7.小结 三、反范式化1、概述2、应用举例3、反范式的新问题4、反范式的…

【汽车】-- 发动机类型

汽车发动机根据不同的分类标准可以分为多种类型。以下是常见的发动机类型及其特点&#xff0c;并列举相应的品牌和车型举例&#xff1a; 1. 按燃料类型分类 (1) 汽油发动机 特点&#xff1a;使用汽油作为燃料&#xff0c;通过火花塞点火&#xff0c;转速高&#xff0c;运转平…

自然语言处理的未来愿景

自然语言处理的未来愿景 在这个信息爆炸的时代,计算机如何理解和生成我们日常使用的语言,已经成为一个引人注目的问题。你有没有想过,为什么智能助手能理解你的指令?又或者,为什么社交媒体上的推荐引擎能够精准地推荐你喜爱的内容?这背后,正是自然语言处理(NLP)在发挥…

【蓝桥杯每日一题】砍竹子

砍竹子 2024-12-7 蓝桥杯每日一题 砍竹子 STL 贪心 题目大意 这天, 小明在砍竹子, 他面前有 nn 棵竹子排成一排, 一开始第 ii 棵竹子的 高度为 h i h_i hi​. 他觉得一棵一棵砍太慢了, 决定使用魔法来砍竹子。魔法可以对连续的一 段相同高度的竹子使用, 假设这一段竹子的高度为…

微信小程序开发简易教程

微信小程序文件结构详解 1. 项目配置文件 project.config.json 项目的配置文件包含项目名称、appid、编译选项等配置示例&#xff1a; {"description": "项目配置文件","packOptions": {"ignore": []},"setting": {&quo…

Unix/Linux 命令行重定向操作

2>/dev/null 是一个常见的 Unix/Linux 命令行重定向操作&#xff0c;用于将标准错误&#xff08;stderr&#xff09;输出重定向到 /dev/null&#xff0c;即丢弃错误信息而不显示。理解这个表达式需要了解几个概念&#xff1a;文件描述符、重定向和特殊文件 /dev/null。 ###…

图解LinkedList底层原理

图解LinkedList底层原理 本篇将讲解Java中的一个集合LinkedList的底层实现原理&#xff0c;会查看并分析底层源码&#xff0c;结合图解的方式&#xff0c;理解其添加数据的过程 数据结构 LinkedList 是基于双向链表实现的&#xff0c;节点结构如下&#xff1a; private stati…

react antd tabs router 基础管理后台模版

在构建 React 后台管理系统时&#xff0c;使用标签页的方式展示路由是一种高效且用户友好的设计模式。这种实现方式通常允许用户在多个页面之间快速切换&#xff0c;并保留页面的状态&#xff0c;类似于浏览器的多标签页功能。 需求分析 1.动态标签页&#xff1a;根据用户的导…

【OpenCV】图像阈值

简单阈值法 此方法是直截了当的。如果像素值大于阈值&#xff0c;则会被赋为一个值&#xff08;可能为白色&#xff09;&#xff0c;否则会赋为另一个值&#xff08;可能为黑色&#xff09;。使用的函数是 cv.threshold。第一个参数是源图像&#xff0c;它应该是灰度图像。第二…

初次使用uniapp编译到微信小程序编辑器页面空白,真机预览有内容

uniapp微信小程序页面结构 首页页面代码 微信小程序模拟器 模拟器页面为空白时查了下&#xff0c;有几个说是“Hbuilder编译的时候应该编译出来一个app.js文件 但是却编译出了App.js”&#xff0c;但是我的小程序结构没问题&#xff0c;并且真机预览没有问题 真机调试 根据defi…