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)这一安全机制。同源策略要求两个页面具有相同的协议、域名和端口号,才能相互访问资源和数据。这一机制旨在防止恶意网站执行跨站脚本攻击,从而保护…...
YimMenu:GTA5游戏增强工具从入门到精通完全指南
YimMenu:GTA5游戏增强工具从入门到精通完全指南 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenu …...
2026有赞春季发布会:有效果的AI驱动增长,智能体和数字员工交出成绩单
5月21日,有赞2026年春季发布会在杭州举办,主题是“有效果的AI”。过去一年,有赞智能体和数字员工已经迈入交付结果的新阶段。数据显示,2025年有赞AI智能体活跃使用商家18220个,整体调用量超3600万次,引导成…...
Orbit间隔重复算法深度解析:从理论到实践
Orbit间隔重复算法深度解析:从理论到实践 【免费下载链接】orbit Experimental spaced repetition platform for exploring ideas in memory augmentation and programmable attention 项目地址: https://gitcode.com/gh_mirrors/orbit1/orbit Orbit是一个实…...
产品经理把PRD写成“天书”,我用AI半小时重写了一遍,他当场愣住
前言 产品经理和开发之间的矛盾,根源往往不在需求本身,而在于需求表达方式。一个合格的需求文档应该包含:功能描述、业务规则、边界条件、异常处理、验收标准。但现实中,很多PRD长这样:“用户点击支付后,系…...
别再重复造轮子!用PADS自带转换器+立创EDA,5分钟搞定原理图符号同步
高效复用立创EDA资源:PADS原理图符号同步实战指南 在硬件设计领域,重复绘制原理图符号堪称工程师的"时间黑洞"。当你在立创EDA上发现完美的元器件模型时,为何还要在PADS中从零开始?本文将揭示一套被多数人忽视的PADS原生…...
别只盯着apt-get install:深入理解Linux头文件路径与编译器搜索机制的坑
别只盯着apt-get install:深入理解Linux头文件路径与编译器搜索机制的坑 当你在Linux环境下进行C/C开发时,是否曾遇到过这样的场景:明明已经安装了所有看似必要的依赖包,却依然被fatal error: drm.h: No such file or directory这…...
Memcached未授权访问漏洞实战防御指南
1. 这个漏洞不是“能连上就完事”的玩具,而是真实压垮服务的导火索Memcached未授权访问漏洞(CVE-2013-7239)——光看编号,很多人第一反应是“老古董漏洞,早该淘汰了”。但我在2023年参与三起生产环境应急响应时&#x…...
终极直播输入可视化指南:如何用开源工具展示键盘鼠标操作
终极直播输入可视化指南:如何用开源工具展示键盘鼠标操作 【免费下载链接】input-overlay Show keyboard, gamepad and mouse input on stream 项目地址: https://gitcode.com/gh_mirrors/in/input-overlay 在游戏直播、教学演示或技术分享中,观众…...
Real-ESRGAN终极指南:5分钟掌握AI图像超分辨率技术,让模糊照片秒变高清
Real-ESRGAN终极指南:5分钟掌握AI图像超分辨率技术,让模糊照片秒变高清 【免费下载链接】Real-ESRGAN Real-ESRGAN aims at developing Practical Algorithms for General Image/Video Restoration. 项目地址: https://gitcode.com/gh_mirrors/re/Real…...
探索AI编程工具的民主化:从技术壁垒到开源共享的技术演进之路
探索AI编程工具的民主化:从技术壁垒到开源共享的技术演进之路 【免费下载链接】cursor-vip cursor IDE enjoy VIP 项目地址: https://gitcode.com/gh_mirrors/cu/cursor-vip "技术不应成为特权,而应是推动文明进步的共同财富。" —— 开…...
