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;再根据请求…

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

Scala的正则表达式二

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

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;运转平…

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

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

【OpenCV】图像阈值

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

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

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

【开源】一款基于SpringBoot 的全开源充电桩平台

一、下载项目文件 下载源码项目文件口令&#xff1a;动作璆璜量子屏多好/~d1b8356ox2~:/复制口令后&#xff0c;进入夸克网盘app即可保存&#xff08;如果复制到夸克app没有跳转资源&#xff0c;可以复制粘贴口令到夸克app的搜索框也可以打开&#xff08;不用点搜索按钮&#…

Ubuntu上使用system()函数运行不需要输入密码

使用system()运行一些终端命令的时候&#xff0c;需要sudo权限&#xff0c;也就是必须输入密码&#xff0c;那么在程序自启动的时候就无法成功启动。如果设置Ubuntu下所有操作都不需要密码&#xff0c;安全性太低&#xff0c;所以我们可以将需要用到的终端指令给予无需输入密码…

国产GPU中,VLLM0.5.0发布Qwen2.5-14B-Instruct-GPTQ-Int8模型,请求返回结果乱码

概述 国产GPU: DCU Z100 推理框架&#xff1a; vllm0.5.0 docker容器化部署 运行如下代码&#xff1a; python -m vllm.entrypoints.openai.api_server --model /app/models/Qwen2.5-14B-Instruct-GPTQ-Int8 --served-model-name qwen-gptq --trust-remote-code --enforce…

WireShark速成

1.WireShark安装 官网&#xff1a; Wireshark Go Deep Kali Linux系统自带WireShark工具。 2.WireShark介绍 WireShark是一个网络包分析工具&#xff0c;该工具主要用于捕获网络数据包&#xff0c;并自动解析数据包&#xff0c;为用户显示数据包的详情信息&#xff0c;供…

算法-字符串-72.编辑距离

一、题目 二、思路解析 1.思路&#xff1a; 最少操作数——动态数组 res[i][j]:长度为i的字符串转化为长度为j字符串的最少操作 2.常用方法&#xff1a; 无 3.核心逻辑&#xff1a; 1.情况一&#xff1a;当word1为空&#xff0c;word2不为空时 for(int i0;i<size2;i){res[0…

大模型在辅导场景的深度应用,猿辅导素养课推出启发性“AI作文通”

猿辅导集团旗下的飞象星球面向学校发布“飞象AI作文”&#xff0c;让教育大模型成为老师的AI批改助手、学生的写作助手。芥末堆注意到&#xff0c;猿辅导集团旗下的猿辅导素养课也推出了名为“AI作文通”的AI作文功能&#xff0c;已于7月正式大规模上线&#xff0c;在AI教育领域…

Node.js系统模块

【图书介绍】《Node.jsMongoDBVue.js全栈开发实战》-CSDN博客 《Node.jsMongoDBVue.js全栈开发实战&#xff08;Web前端技术丛书&#xff09;》(邹琼俊)【摘要 书评 试读】- 京东图书 (jd.com) 2.2.1 什么是系统模块 由于Node.js运行环境提供的API都是以模块化的方式进行开…

路由封装,连接导航router-link

路由的封装抽离&#xff1a; 所有路由配置堆在main.js中不合适&#xff0c;需将路由模块抽离出来&#xff0c;以便维护 将与路由相关信息提取到src文件夹下的router文件夹下的index.js文件中 在main.js中就只需要导入当前路由&#xff0c;并且注入到当前实例中&#xff0c;其他…

工业4.0下的IT网络与OT网络

https://zhuanlan.zhihu.com/p/498984722 随着“中国制造2025”的深入推进&#xff0c;制药行业以手工为主的传统生产方式正在被以“工业4.0 ”为核心的自动化生产方式逐步替代。 为了实现生产自动化&#xff0c;很多制药企业都引入了由PLC&#xff08;可编程逻辑控制器 &am…