Unity教程(十五)敌人战斗状态的实现
Unity开发2D类银河恶魔城游戏学习笔记
Unity教程(零)Unity和VS的使用相关内容
Unity教程(一)开始学习状态机
Unity教程(二)角色移动的实现
Unity教程(三)角色跳跃的实现
Unity教程(四)碰撞检测
Unity教程(五)角色冲刺的实现
Unity教程(六)角色滑墙的实现
Unity教程(七)角色蹬墙跳的实现
Unity教程(八)角色攻击的基本实现
Unity教程(九)角色攻击的改进
Unity教程(十)Tile Palette搭建平台关卡
Unity教程(十一)相机
Unity教程(十二)视差背景
Unity教程(十三)敌人状态机
Unity教程(十四)敌人空闲和移动的实现
Unity教程(十五)敌人战斗状态的实现
如果你更习惯用知乎
Unity开发2D类银河恶魔城游戏学习笔记目录
文章目录
- Unity开发2D类银河恶魔城游戏学习笔记
- 前言
- 一、概述
- 二、实现Player的检测
- 三、创建接地状态
- 三、实现战斗状态
- (1)创建战斗状态
- (2)状态切换
- 总结 完整代码
- Enemy.cs
- SkeletonGroundedState.cs
- SkeletonIdleState.cs
- SkeletonMoveState.cs
- SkeletonBattleState.cs
- Enemy_Skeleton.cs
前言
本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记,如有错误,欢迎指正。
本节实现敌人的战斗状态。
对应b站视频:
【Unity教程】从0编程制作类银河恶魔城游戏P50
一、概述
本节中我们实现骷髅的战斗状态。
骷髅小怪在检测到玩家时,会向着玩家移动。在移动到一定距离时停止移动进入攻击状态。
由于我们想让骷髅在空闲和移动时均能转入战斗状态,我们创建超级状态接地状态,直接写接地状态到战斗状态的切换即可。
状态切换条件如下:
二、实现Player的检测
玩家的检测在Enemy基类中实现。
碰撞检测的详细讲解见Unity教程(四)碰撞检测
这里我们不再仅仅使用bool类型作为函数的返回值,而是使用RaycastHit2D返回更详细的信息。
RaycastHit2DUnity官方手册
Raycast包含的变量如下表:
变量 | 介绍 |
---|---|
centroid | 用于执行投射的图元的质心 |
collider | 射线命中的碰撞体 |
distance | 从射线原点到撞击点的距离 |
fraction | 射线上发生命中的距离的分数 |
normal | 射线命中的表面的法线矢量 |
point | 世界空间中射线命中碰撞体表面的点 |
rigidbody | 附加到命中的对象的 Rigidbody2D |
transform | 命中的对象的变换 |
我们要新建一个过滤器WhatIsPlayer进行检测。
这里我们以骷髅的位置为起点,向它面向的方向发射射线检测,检测的最大距离设置为50.
为了方便调试我们还要绘制出攻击检测的线条,在Enemy里对实体中的OnDrawGizmos()函数进行重写。
攻击检测的线条绘制:起点为Enemy_Skeleton的位置,向右attackDistance找到终点。
在Enemy中添加如下代码:
[SerializeField] protected LayerMask WhatIsPlayer;[Header("Attack Info")]public float attackDistance;public virtual RaycastHit2D IsPlayerDetected()=>Physics2D.Raycast(transform.position, Vector2.right * facingDir, 50 ,WhatIsPlayer);protected override void OnDrawGizmos(){base.OnDrawGizmos();Gizmos.color = Color.yellow;Gizmos.DrawLine(transform.position, new Vector3(transform.position.x + attackDistance * facingDir, transform.position.y));}
将Enemy_Skeleton的大小调大一点
创建一个新的WallCheck,并拖到Enemy_Skeleton中
将Wallcheck往下拖一些,以免与攻击检测重合。
在Enemy中重写OnDrawGizmos(),换一个颜色绘制出攻击检测。
给attackDistancce赋一个恰当的值。
将Enemy_Skeleton中WhatIsPlayer改为Player
新建Player和Enemy层。
Player改为Player层,Enemy_Skeleton改为Enemy层,修改时选择将它们所有子物体也改为对应层次。
三、创建接地状态
创建接地状态SkeletonGroundedState,它继承自EnemyState。
在子菜单中创建构造函数,生成重写。
添加Enemy_Skeleton enermy,传递骷髅小怪独有的变量和函数,并修改构造函数。
//SkeletonGroundedState:骷髅接地状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonGroundedState : EnemyState
{protected Enemy_Skeleton enemy;public SkeletonGroundedState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy,string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){enemy=_enemy;}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();}
}
将SkeletonIdleState和SkeletonMoveState的父类改为SkeletonGroundedState,这时两个子类构造函数参数与父类相同,可以删除原有构造函数和Enemy_Skeleton enemy,直接在子菜单重新生成一个构造函数了。
三、实现战斗状态
(1)创建战斗状态
创建战斗状态SkeletonBattleState,它继承自EnemyState。
在子菜单中创建构造函数,生成重写。添加Enemy_Skeleton enermy,并修改构造函数。
private Enemy_Skeleton enemy;public SkeletonBattleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){enemy=_enemy;}
在EnemySkeleton类中创建battleState。这里条件变量仍然选择“Move”,因为战斗状态仍然播放移动动画。
#region 状态public SkeletonIdleState idleState { get; private set; }public SkeletonMoveState moveState { get; private set; }public SkeletonBattleState battleState { get; private set; }#endregionprotected override void Awake(){base.Awake();idleState = new SkeletonIdleState(stateMachine,this,this,"Idle");moveState = new SkeletonMoveState(stateMachine, this,this, "Move");battleState = new SkeletonBattleState(stateMachine, this, this, "Move");}
(2)状态切换
当检测到玩家时,骷髅由接地状态转换为战斗状态。
在SkeletonGroundedState的Update()函数中添加
public override void Update(){base.Update();if(enemy.IsPlayerDetected())stateMachine.ChangeState(enemy.battleState);}
骷髅进入战斗状态时,要随玩家位置的变化改变移动方向。因此我们引入player的位置这个变量,通过它与骷髅位置的对比确定战斗状态骷髅移动的方向。
player.position.x > enemy.transform.position.x,玩家在骷髅右边,移动方向向右
player.position.x < enemy.transform.position.x,玩家在骷髅左边,移动方向向左
//SkeletonBattleState:骷髅战斗状态
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonBattleState : EnemyState
{private Transform player;private Enemy_Skeleton enemy;private int moveDir;public SkeletonBattleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){enemy=_enemy;}public override void Enter(){base.Enter();player = GameObject.Find("Player").transform;}public override void Exit(){base.Exit();}public override void Update(){base.Update();if(player.position.x > enemy.transform.position.x)moveDir = 1;else if(player.position.x < enemy.transform.position.x)moveDir = -1;enemy.SetVelocity(enemy.moveSpeed * moveDir, enemy.rb.velocity.y);}
}
效果如下:
而当玩家进入骷髅的攻击距离时,骷髅停止移动切换为攻击状态。根据上述内容所讲,骷髅与玩家的距离可由RaycastHit2D里的distance获得。攻击状态我们先控制输出代替,具体攻击状态内容我们下一节再进行实现。
Update中添加如下代码:
public override void Update(){base.Update();if (enemy.IsPlayerDetected()){if (enemy.IsPlayerDetected().distance < enemy.attackDistance){Debug.Log("attack");enemy.ZeroVelocity();return;}}if(player.position.x > enemy.transform.position.x)moveDir = 1;else if(player.position.x < enemy.transform.position.x)moveDir = -1;enemy.SetVelocity(enemy.moveSpeed * moveDir, enemy.rb.velocity.y);}
总结 完整代码
Enemy.cs
添加碰撞检测和攻击距离变量,实现攻击检测的函数和攻击检测的绘制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy : Entity
{[SerializeField] protected LayerMask WhatIsPlayer;[Header("Move Info")]public float moveSpeed = 1.5f;public float idleTime = 2.0f;[Header("Attack Info")]public float attackDistance;public EnemyStateMachine stateMachine;protected override void Awake(){base.Awake();stateMachine = new EnemyStateMachine();}protected override void Update(){base.Update();stateMachine.currentState.Update();}public virtual RaycastHit2D IsPlayerDetected()=>Physics2D.Raycast(transform.position, Vector2.right * facingDir, 50 ,WhatIsPlayer);protected override void OnDrawGizmos(){base.OnDrawGizmos();Gizmos.color = Color.yellow;Gizmos.DrawLine(transform.position, new Vector3(transform.position.x + attackDistance * facingDir, transform.position.y));}
}
SkeletonGroundedState.cs
创建接地状态,由移动和空闲状态继承。实现切换到战斗状态。
//SkeletonGroundedState:骷髅接地状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonGroundedState : EnemyState
{protected Enemy_Skeleton enemy;public SkeletonGroundedState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy,string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){enemy=_enemy;}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (enemy.IsPlayerDetected())stateMachine.ChangeState(enemy.battleState);}
}
SkeletonIdleState.cs
修改构造函数
//SkeletonIdleState:骷髅空闲状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonIdleState : SkeletonGroundedState
{public SkeletonIdleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton enemy, string _animBoolName) : base(_stateMachine, _enemyBase, enemy, _animBoolName){}public override void Enter(){base.Enter();stateTimer = enemy.idleTime;}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (stateTimer < 0)stateMachine.ChangeState(enemy.moveState);}
}
SkeletonMoveState.cs
//SkeletonMoveState:骷髅移动状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonMoveState : SkeletonGroundedState
{public SkeletonMoveState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton enemy, string _animBoolName) : base(_stateMachine, _enemyBase, enemy, _animBoolName){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();enemy.SetVelocity(enemy.moveSpeed*enemy.facingDir,enemy.rb.velocity.y);if(!enemy.isGroundDetected() || enemy.isWallDetected()){enemy.Flip();stateMachine.ChangeState(enemy.idleState);}}
}
SkeletonBattleState.cs
创建战斗状态,实现切换到攻击状态,和随玩家移动改变方向。
//SkeletonBattleState:骷髅战斗状态
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonBattleState : EnemyState
{private Transform player;private Enemy_Skeleton enemy;private int moveDir;public SkeletonBattleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){enemy=_enemy;}public override void Enter(){base.Enter();player = GameObject.Find("Player").transform;}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (enemy.IsPlayerDetected()){if (enemy.IsPlayerDetected().distance < enemy.attackDistance){Debug.Log("attack");enemy.ZeroVelocity();return;}}if(player.position.x > enemy.transform.position.x)moveDir = 1;else if(player.position.x < enemy.transform.position.x)moveDir = -1;enemy.SetVelocity(enemy.moveSpeed * moveDir, enemy.rb.velocity.y);}
}
Enemy_Skeleton.cs
创建战斗状态
//Enemy_Skeleton:骷髅敌人
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy_Skeleton : Enemy
{#region 状态public SkeletonIdleState idleState { get; private set; }public SkeletonMoveState moveState { get; private set; }public SkeletonBattleState battleState { get; private set; }#endregionprotected override void Awake(){base.Awake();idleState = new SkeletonIdleState(stateMachine,this,this,"Idle");moveState = new SkeletonMoveState(stateMachine, this,this, "Move");battleState = new SkeletonBattleState(stateMachine, this, this, "Move");}protected override void Start(){base.Start();stateMachine.Initialize(idleState);}protected override void Update(){base.Update();}}
相关文章:

Unity教程(十五)敌人战斗状态的实现
Unity开发2D类银河恶魔城游戏学习笔记 Unity教程(零)Unity和VS的使用相关内容 Unity教程(一)开始学习状态机 Unity教程(二)角色移动的实现 Unity教程(三)角色跳跃的实现 Unity教程&…...

利用深度学习实现验证码识别-3-ResNet18
在当今数字化时代,验证码作为一种重要的安全验证手段,广泛应用于各种网络场景。然而,传统的验证码识别方法往往效率低下,准确率不高。今天,我们将介绍一种基于 ResNet18 的验证码识别方法,它能够高效、准确…...

UDP通信实现
目录 前言 一、基础知识 1、跨主机传输 1、字节序 2、主机字节序和网络字节序 3、IP转换 2、套接字 3、什么是UDP通信 二、如何实现UDP通信 1、socket():创建套接字 2、bind():绑定套接字 3、sendto():发送指定套接字文件数据 4、recvfrom():接收指定地址信息的数据 三…...

windows下使用vscode编写运行以及调试C/C++
vscode支持类似于vs的断点调试c/c,也可以直接编译&运行c/c 先是编译运行 c/c的方法 微软官方起初设定的科学做法(这也是现在的科学做法)是通过在vscode集成控制台写命令行的方式来实现编译运行程序的,但也可以通过code runner插件…...
python容器4--集合
(1) 什么是集合 集合:Python中使用关键字set表示 集合中存储多个、没有顺序的、不能重复的、可以是不同类型的多个数据! (2) 集合的声明 python中通过set()或者花括号声明空集合、非空集合 # 声明空集…...

MySQL record 01 part
更改密码: alter user rootlocalhost identified with mysql_native_password by ‘123456’; 注意: 在命令行方式下,每条MySQL的命令都是以分号结尾的,如果不加分号,MySQL会继续等待用户输入命令,直到MyS…...
2024年高教社杯全国大学生数学建模竞赛A题思路(2024数学建模国赛A题思路)
A题 “板凳龙” 闹元宵 “板凳龙”,又称“盘龙”,是浙闽地区的传统地方民俗文化活动。人们将少则几十条,多则上百条的板凳首尾相连,形成蜿蜒曲折的板凳龙。盘龙时,龙头在前领头,龙身和龙尾相随盘旋,整体呈圆盘状。一般来说,在舞龙队能够自如地盘入和盘出的前提下,盘龙…...
Go语言基础语法 20240904更新
代码开源地址 https://github.com/zhangdapeng520/zdpgo_basic 快速入门 示例代码: package mainimport "fmt"func main() {fmt.Println("Hello World") }第一行代码 package 用来声明包名。main包时整个程序的入口包,在一个Go语…...

软件测试 | 性能测试
性能测试的概念 为了 发现系统性能问题 或 获取系统性能相关指标 而进行的测试。 常见性能测试指标 并发数 即并发用户数。 从业务层面看,并发用户数指的是 实际使用系统的用户总数。从后端服务器层面看,指的是 web服务器在一段时间内处理浏览器请求而建…...
Arduino IDE
Arduino IDE(集成开发环境)的安装过程是一个相对直观且易于操作的流程,主要步骤包括下载、安装和配置。以下将详细阐述Arduino IDE的安装过程,同时提供一些背景信息和注意事项,确保安装过程顺利进行。 一、Arduino ID…...

统计学习方法与实战——统计学习方法之感知机
感知机 感知机三要素分析模型策略损失函数选择 算法原始形式对偶形式 相关问题 例子iris数据集分类实战数据集查看 显示结果sklearn 实战感知机 习题解答习题2.1解题步骤反证法 习题2.2习题2.3凸壳线性可分线性可分证明凸壳不相交证明充分性:凸壳不相交\Rightarrow⇒…...

语言学习有捷径?没错!这4个方法让你轻松搞定英语翻译
现在全世界都在用英语,这门语言真的超级重要。不管你是学习、上班还是出去玩,会点英语翻译肯定能帮上大忙。但是,对很多人来说,翻译英语还是挺难的。别急,今天我就来给你介绍几个超好用的英语翻译工具,让你…...

聊一聊大型网站稳定性建设思路
目录 架构阶段的稳定性建设项目 编码阶段的稳定性建设 测试阶段的稳定性建设 发布阶段的稳定性建设 运行阶段的稳定性建设项目 故障发生时的稳定性建设 网站稳定性的建设是一项综合的系统工程,就像人的健康一样,如果平时不注意健康饮食、不注意锻炼…...

Nginx常用配置
Windows版本Nginx开机自启动 可直接下载已经配置好的文件,点击即可下载:Windows版本Nginx1.26.0 下载WinSW v2.12.0 首先从https://github.com/winsw/winsw/releases下载WinSW v2.12.0 下载Nginx 下载地址https://nginx.org/en/download.html 修…...

前端开发中遇到的小问题以及解决方案记录2
1、H5中适配屏幕的工具-postcss-px-to-viewport postcss-px-to-viewport。因为设计稿一般给的都是375px宽度的,所以假如一个字体是16px,那么在开发中不能直接写死为16px,因为各个厂商的手机屏幕大小是不同的,所以要根据屏幕大小去…...

Qt-常用控件(3)-输入类
1. QLineEdit QLineEdit 用来表示单行输入框.可以输入一段文本,但是不能换行 核心属性 属性说明text输入框中的文本inputMask输入内容格式约束maxLength最大长度frame是否添加边框echoMode显示方式. QLineEdit::Normal :这是默认值,文本框会显示输入的文本。QLineE…...
使用Docker启动Redis容器并映射端口
在现代软件开发中,Redis 是一种非常流行的开源内存数据结构存储,通常用作数据库、缓存或消息传递系统。Docker 是一个开源的应用容器引擎,它允许开发者打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux …...

用fastapi搭建cpca地址提取服务接口
以前的客户地址比较乱,现在想提取出省份城市, 开始了解分词技术,后发现python有这样的库 cpca提取地址挺不错,可以从垃圾地址中提取省市区以及区号。 文章会用fastapi搭建服务端 通过post调用cpca,提取来了后&#…...
libvncclient编写多线程qt的VNC客户端
概述 使用qt和libvncclient编写vnc的客户端程序,多线程读写,拒绝卡顿。qt环境:5.15.3libvncclient:0.9.14下载地址:https://github.com/LibVNC/libvncserver/releases 编译libvncclient 打开CMakeList文件ÿ…...
视频处理基础之gradio框架实现
这些函数是用于处理视频文件的Python代码片段,它们依赖于ffmpeg和ffprobe工具,这些工具是FFmpeg项目的一部分,用于处理视频和音频数据。下面是每个函数的用途和用法的总结: 1. ffmpeg_installed() 函数: - 用途&am…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...
Python常用模块:time、os、shutil与flask初探
一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...