Unity类银河战士恶魔城学习总结(P179 Enemy Archer 弓箭手)
教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/
本章节实现了敌人弓箭手的制作

Enemy_Archer.cs
核心功能
-
状态机管理敌人的行为
- 定义了多个状态对象(如
idleState、moveState、attackState等),通过状态机管理敌人的状态切换。 - 状态包括空闲、移动、攻击、跳跃、受击眩晕、死亡等。
- 定义了多个状态对象(如
-
攻击逻辑
- 使用
AnimationSepcoalAttackTrigger方法生成箭矢。 - 创建箭矢对象时,调用
Arrow_Controller.SetUpArrow为其设置速度和伤害信息。 - 箭矢会根据面向方向(
facingDir)飞向目标。
- 使用
-
地形检测
GroundBenhundCheck方法:检查敌人背后的地面是否存在,通过Physics2D.BoxCast实现。WallBehind方法:检测敌人背后的墙壁是否存在,通过Physics2D.Raycast实现。- 这些方法用于辅助弓箭手的移动或跳跃逻辑,确保其行为合理。
-
受击与死亡
CanBeStunned方法:判断弓箭手是否能被眩晕,并切换到stunnedState。Die方法:当弓箭手死亡时,切换到deadState。
-
可视化调试
OnDrawGizmos方法:在 Unity 编辑器中绘制背后地面检测区域,帮助开发者直观了解地形检测的范围。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//2024.12.11
public class Enemy_Archer : Enemy
{[Header("弓箭手特殊信息")]//Archer specific info[SerializeField] private GameObject arrowPrefab;[SerializeField] private float arrowSpeed;[SerializeField] private int arrowDamage;public Vector2 jumpVelocity;public float jumpCooldown;public float safeDistance;//安全距离[HideInInspector]public float lastTimeJumped;[Header("额外的碰撞检查")]//Additional collision check[SerializeField] private Transform groundBehindCheck;[SerializeField] private Vector2 groundBehindCheckSize;#region Statespublic ArcherIdleState idleState { get; private set; }public ArcherMoveState moveState { get; private set; }public ArcherBattleState battleState { get; private set; }public ArcherAttackState attackState { get; private set; }public ArcherDeadState deadState { get; private set; }public ArcherStunnedState stunnedState { get; private set; }public ArcherJumpState ArcherJumpState { get; private set; }#endregionprotected override void Awake(){base.Awake();idleState = new ArcherIdleState(this, stateMachine, "Idle", this);moveState = new ArcherMoveState(this, stateMachine, "Move", this);battleState = new ArcherBattleState(this, stateMachine, "Idle", this);attackState = new ArcherAttackState(this, stateMachine, "Attack", this);deadState = new ArcherDeadState(this, stateMachine, "Move", this);stunnedState = new ArcherStunnedState(this, stateMachine, "Stun", this);ArcherJumpState = new ArcherJumpState(this, stateMachine, "Jump", this);}protected override void Start(){base.Start();stateMachine.Initialize(idleState);}public override bool CanBeStunned(){if (base.CanBeStunned()){stateMachine.ChangeState(stunnedState);return true;}return false;}public override void Die(){base.Die();stateMachine.ChangeState(deadState);}public override void AnimationSepcoalAttackTrigger(){GameObject newArrow = Instantiate(arrowPrefab, attackCheck.position, Quaternion.identity);newArrow.GetComponent<Arrow_Controller>().SetUpArrow(arrowSpeed * facingDir, stats);}public bool GroundBenhundCheck() => Physics2D.BoxCast(groundBehindCheck.position, groundBehindCheckSize, 0, Vector2.zero, 0, whatIsGround);public bool WallBehind() => Physics2D.Raycast(wallCheck.position, Vector2.right * -facingDir, wallCheckDistance +2, whatIsGround);protected override void OnDrawGizmos(){base.OnDrawGizmos();Gizmos.DrawWireCube(groundBehindCheck.position, groundBehindCheckSize);}
}
ArcherMoveState.cs
using System.Collections;
using UnityEngine;public class ArcherMoveState : ArcherGroundState
{public ArcherMoveState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Archer _enemy) : base(_enemyBase, _stateMachine, _animBoolName, _enemy){}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.IsWallDetected() || !enemy.IsGroundDetected())//撞墙或者没有路反转{enemy.Flip();stateMachine.ChangeState(enemy.idleState);}}
}
Arrow_Controller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//2024.12.11
public class Arrow_Controller : MonoBehaviour
{[SerializeField] private int damage;//箭矢造成的伤害值[SerializeField] private string targetLayerName = "Player";//箭矢的目标层[SerializeField] private float xVelocity;[SerializeField] private Rigidbody2D rb;[SerializeField] private bool canMove;[SerializeField] private bool flipped;private CharacterStats myStats;private void Update(){if(canMove)rb.velocity = new Vector2(xVelocity, rb.velocity.y);}public void SetUpArrow(float _speed,CharacterStats _myStats){xVelocity = _speed;myStats = _myStats;}private void OnTriggerEnter2D(Collider2D collision){if(collision.gameObject.layer == LayerMask.NameToLayer(targetLayerName)){collision.GetComponent<CharacterStats>().TakeDamage(damage);//箭的伤害myStats.DoDamage(collision.GetComponent<CharacterStats>());//弓箭手的额外属性StuckInto(collision);}else if (collision.gameObject.layer == LayerMask.NameToLayer("Ground")){StuckInto(collision);}}private void StuckInto(Collider2D collision)//射中目标{GetComponentInChildren<ParticleSystem>().Stop();//停止粒子系统GetComponent<Collider2D>().enabled = false;canMove = false;rb.isKinematic = true;//刚体为运动rb.constraints = RigidbodyConstraints2D.FreezeAll;transform.parent = collision.transform;//箭矢的父物体为碰撞物体Destroy(gameObject, Random.Range(5,7));}public void FlipArrow(){if (flipped)return;xVelocity = xVelocity * -1;flipped = true;transform.Rotate(0,180,0);targetLayerName = "Enemy";}
}
ArcherBattleState.cs
using System.Collections;
using UnityEngine;//2024.12.11
public class ArcherBattleState : EnemyState
{private Transform player;private Enemy_Archer enemy;private int moveDir;public ArcherBattleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Archer _enemy) : base(_enemyBase, _stateMachine, _animBoolName){this.enemy = _enemy;}public override void Enter(){base.Enter();//player = GameObject.Find("Player").transform;player = PlayerManager.instance.player.transform;if (player.GetComponent<PlayerStats>().isDead)stateMachine.ChangeState(enemy.moveState);}public override void Update(){base.Update();if (enemy.IsPlayerDetected()){stateTimer = enemy.battleTime;if (enemy.IsPlayerDetected().distance < enemy.safeDistance)//小于安全距离{if (CanJump())stateMachine.ChangeState(enemy.ArcherJumpState);//跳走}if (enemy.IsPlayerDetected().distance < enemy.attackDistance){if (CanAttack())stateMachine.ChangeState(enemy.attackState);}}else{if (stateTimer < 0 || Vector2.Distance(player.transform.position, enemy.transform.position) > 10)//超过距离或者时间到了stateMachine.ChangeState(enemy.idleState);}BattleStateFlipController();}private void BattleStateFlipController(){if (player.position.x > enemy.transform.position.x && enemy.facingDir == -1)enemy.Flip();else if (player.position.x < enemy.transform.position.x && enemy.facingDir == 1)enemy.Flip();}public override void Exit(){base.Exit();}private bool CanAttack(){if (Time.time >= enemy.lastTimeAttack + enemy.attackCoolDown){enemy.attackCoolDown = Random.Range(enemy.minAttackCoolDown, enemy.maxAttackCoolDown);enemy.lastTimeAttack = Time.time;return true;}elsereturn false;}private bool CanJump(){if(enemy.GroundBenhundCheck() == false || enemy.WallBehind() ==true)return false;if (Time.time >= enemy.lastTimeJumped + enemy.jumpCooldown){enemy.lastTimeJumped = Time.time;return true;}return false;}
}
ArcherGroundState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//2024.12.11
public class ArcherGroundState : EnemyState
{protected Transform player;protected Enemy_Archer enemy;public ArcherGroundState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Archer _enemy) : base(_enemyBase, _stateMachine, _animBoolName){enemy = _enemy;}public override void Enter(){base.Enter();player = PlayerManager.instance.player.transform;//p63 3:43改}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (enemy.IsPlayerDetected() || Vector2.Distance(enemy.transform.position, player.transform.position) < enemy.agroDistance){stateMachine.ChangeState(enemy.battleState);}}
}
ArcherIdleState.cs
using System.Collections;
using UnityEngine;public class ArcherIdleState : ArcherGroundState
{public ArcherIdleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Archer _enemy) : base(_enemyBase, _stateMachine, _animBoolName, _enemy){}public override void Enter(){base.Enter();stateTimer = enemy.idleTime;}public override void Exit(){base.Exit();AudioManager.instance.PlaySFX(24, enemy.transform);}public override void Update(){base.Update();if (stateTimer < 0){stateMachine.ChangeState(enemy.moveState);}}
}
ArcherStunnedState.cs
using System.Collections;
using UnityEngine;//2024.12.11
public class ArcherStunnedState : EnemyState
{private Enemy_Archer enemy;public ArcherStunnedState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Archer enemy) : base(_enemyBase, _stateMachine, _animBoolName){this.enemy = enemy;}public override void Enter(){base.Enter();enemy.fx.InvokeRepeating("RedColorBlink", 0, .1f); //这行代码使用 InvokeRepeating 方法,每隔 0.1 秒调用一次 RedColorBlink 方法。stateTimer = enemy.stunDuration;rb.velocity = new Vector2(-enemy.facingDir * enemy.stunDirection.x, enemy.stunDirection.y);}public override void Exit(){base.Exit();enemy.fx.Invoke("CancelColorChange", 0);//Invoke 方法用于在指定的延迟时间后调用某个方法。在这里,延迟时间为 0}public override void Update(){base.Update();if (stateTimer < 0)stateMachine.ChangeState(enemy.idleState);}
}
ArcherAttackState.cs
using System.Collections;
using UnityEngine;public class ArcherAttackState : EnemyState
{private Enemy_Archer enemy;public ArcherAttackState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Archer _enemy) : base(_enemyBase, _stateMachine, _animBoolName){this.enemy = _enemy;}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();enemy.lastTimeAttack = Time.time;}public override void Update(){base.Update();enemy.SetZeroVelocity();if (triggerCalled)stateMachine.ChangeState(enemy.battleState);}
}
相关文章:
Unity类银河战士恶魔城学习总结(P179 Enemy Archer 弓箭手)
教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了敌人弓箭手的制作 Enemy_Archer.cs 核心功能 状态机管理敌人的行为 定义了多个状态对象(如 idleState、moveState、attackState 等),通过状态机管理敌人的…...
SpringCloud集成sleuth和zipkin实现微服务链路追踪
文章目录 前言技术积累spring cloud sleuth介绍zipkin介绍Zipkin与Sleuth的协作 SpringCloud多模块搭建Zipkin Server部署docker pull 镜像启动zipkin server SpringCloud 接入 Sleuth 与 Zipkinpom引入依赖 (springboot2.6)appilication.yml配置修改增加测试链路代码 调用微服…...
Python随机抽取Excel数据并在处理后整合为一个文件
本文介绍基于Python语言,针对一个文件夹下大量的Excel表格文件,基于其中每一个文件,随机从其中选取一部分数据,并将全部文件中随机获取的数据合并为一个新的Excel表格文件的方法。 首先,我们来明确一下本文的具体需求。…...
Linux+Docker onlyoffice 启用 HTTPS 端口支持
文章目录 一、需求二、配置2.1 创建容器2.2 进入容器2.3 生成私钥和证书 2.4 测试访问 一、需求 上篇文章介绍了如何搭建一个 onlyoffice 在线预览服务,但是我们实际场景调用该服务的网站是协议是 https 的 ,但是 onlyoffice 服务还没做配置,…...
在 Visual Studio Code 中编译、调试和执行 Makefile 工程 llama2.c
在 Visual Studio Code 中编译、调试和执行 Makefile 工程 llama2.c 1. Installing the extension (在 Visual Studio Code 中安装插件)1.1. Extensions for Visual Studio Code1.2. C/C1.2.1. Pre-requisites 1.3. Makefile Tools 2. Configuring your project (配置项目)2.1.…...
python中math模块常用函数
文章目录 math模块简介各种三角函数反三角函数取整函数欧几里得距离绝对值最大公约数开根号幂阶乘函数 math模块简介 math模块是python标准库的一部分,提供了对于浮点数相关的数学运算,下面是常用的一些function 各种三角函数反三角函数 math.cos、ma…...
优化 Vue 3 开发体验:配置 Vite 使用 WebStorm 作为 Vue DevTools 的默认编辑器
优化 Vue 3 开发体验:配置 Vite 使用 WebStorm 替代 VS Code 作为 Vue DevTools 的默认编辑器 在 Vue 3 项目开发中,合理配置开发工具可以大大提升我们的工作效率。本文将介绍如何配置 Vite,使其在使用 Vue DevTools 时将默认编辑器从 VS Co…...
【C语言练习(9)—有一个正整数,求是几位数然后逆序打印】
C语言练习(9) 文章目录 C语言练习(9)前言题目题目解析结果总结 前言 主要到整数的取余(%)和整数的取商(/),判断语句if…else if …else的使用 题目 给一个不多于3位的正整数,要求:一、求它是几位数&…...
热敏打印机的控制
首次接触热敏打印机,本来没有特别之处,花了大概十天时间完成一款猫学王热敏打印机,给到客户体验后,客户反馈说打字看起来不明显,打印照片有条纹,所以引起了我对于他的关注,几点不足之处需要优化…...
【closerAI ComfyUI】电商赋能,AI模特套图生产,各种姿势自定义,高度保持人物服饰场景一致性,摆拍街拍专用
closerAIGCcloserAI,一个深入探索前沿人工智能与AIGC领域的资讯平台,我们旨在让AIGC渗入我们的工作与生活中,让我们一起探索AIGC的无限可能性!aigc.douyoubuy.cn 【closerAI ComfyUI】电商赋能,AI模特套图生产,各种姿势自定义,高度保持人物服饰场景一致性,摆拍街拍专用…...
ARM学习(36)静态扫描规则学习以及工具使用
笔者来学习了解一下静态扫描以及其规则,并且亲身是实践一下对arm 架构的代码进行扫描。 1、静态扫描认识 静态扫描:对代码源文件按照一定的规则进行扫描,来发现一些潜在的问题或者风险,因为不涉及代码运行,所以其一般只是发现一些规范或则一些质量问题,当然这些可能存在潜…...
使用 Docker Compose 部署 Redis 主从与 Sentinel 高可用集群
文章目录 使用 Docker Compose 部署 Redis 主从与 Sentinel 高可用集群Redis 主从架构简介Redis Sentinel 简介配置文件1. 主节点配置 (redis-master.conf)2. 从节点配置 (redis-slave1.conf 和 redis-slave2.conf)redis-slave1.confredis-slave2.conf3. Sentinel 配置 (sentin…...
警惕!手动调整服务器时间可能引发的系统灾难
警惕!手动调整服务器时间可能引发的系统灾难 1. 鉴权机制1.1 基于时间戳的签名验证1.2 基于会话的认证机制(JWT、TOTP) 2. 雪花算法生成 ID 的影响2.1 时间戳回拨导致 ID 冲突2.2 ID 顺序被打乱 3. 日志记录与审计3.1 日志顺序错误3.2 审计日…...
MySQL追梦旅途之性能优化
1、索引优化 索引可以显著加速查询操作,但过多或不适当的索引也会带来负面影响(如增加写入开销)。因此,选择合适的索引至关重要。 创建索引: 为经常用于WHERE子句、JOIN条件和ORDER BY排序的列创建索引。 CREATE I…...
【机器学习】【无监督学习——聚类】从零开始掌握聚类分析:探索数据背后的隐藏模式与应用实例
从零开始掌握聚类分析:探索数据背后的隐藏模式与应用实例 基本概念聚类分类聚类算法的评价指标(1)内部指标轮廓系数(Silhouette Coefficient)DB指数(Davies-Bouldin Index)Dunn指数 (…...
基于深度Q网络(Deep Q-Network,DQN)的机器人路径规划,可以自定义地图,MATLAB代码
深度Q网络(Deep Q-Network,DQN)是一种结合了深度学习和Q学习的强化学习算法,由DeepMind在2015年提出。 1. 算法介绍 DQN算法通过使用深度神经网络来近似Q值函数,解决了传统Q-learning在处理具有大量状态和动作的复杂…...
Python-从文件中读取数据-Sat-Sun
10.1 文件读取数据可以整个文件读取,也可以逐行读取。 首先在保存有.py文件的文件夹里创建一个pi_digist.txt文件,文件内容是 3.14 9265 3589执行程序 file_reader.py with open(pi_digist.txt) as file_object: #接受文件名参数,在程序所…...
测试工程师的职业规划
测试人员在管理上的发展 基层测试管理者:测试组长 工作内容:安排小组工作,提升小组成员测试能力,负责重要的测试工作。 负责对象:版本,项目 中层测试管理者:测试经理 负责对象࿱…...
使用 Puppeteer 快速上手 Node.js 爬虫
使用 Puppeteer 库通过自动化浏览器来访问百度图片搜索,并在搜索结果中下载图片。代码分为两部分: 自动化浏览器任务:使用 Puppeteer 浏览百度图片搜索并获取图片 URL。图片下载:检查图片 URL 类型(base64 或 URL&…...
浏览器的跨域问题与解决方案
浏览器的跨域问题与解决方案 浏览器的跨域问题源于同源策略(Same-Origin Policy)这一安全机制。同源策略要求两个页面具有相同的协议、域名和端口号,才能相互访问资源和数据。这一机制旨在防止恶意网站执行跨站脚本攻击,从而保护…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...
leetcode_69.x的平方根
题目如下 : 看到题 ,我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历,我们是整数的平方根,所以我们分两…...
Vue 实例的数据对象详解
Vue 实例的数据对象详解 在 Vue 中,数据对象是响应式系统的核心,也是组件状态的载体。理解数据对象的原理和使用方式是成为 Vue 专家的关键一步。我将从多个维度深入剖析 Vue 实例的数据对象。 一、数据对象的定义方式 1. Options API 中的定义 在 Options API 中,使用 …...
