本节最终效果
前言
本节紧跟着上一篇,主要实现武器功能。
近战武器
新增Bullet,子弹脚本
public class Bullet : MonoBehaviour { public float damage; // 子弹的伤害值 /// <summary> /// 初始化子弹的属性 /// </summary> /// <param name="damage">伤害值</param> public void Init(float damage) { this.damage = damage; // 设置子弹的伤害值 } }
新增近战武器预制体,添加触发器,挂载脚本,配置参数
修改Enemy敌人脚本,添加触发器检测
void OnTriggerEnter2D(Collider2D collision) { if (!collision.CompareTag("Bullet")) return; health -= collision.GetComponent<Bullet>().damage; if (health > 0) { } else { Dead(); } } void Dead() { gameObject.SetActive(false); }
效果,敌人触碰死亡
控制近战武器生成
配置近战武器进对象池
新增Weapon。控制武器/子弹的生成
public class Weapon : MonoBehaviour { public int id; // 武器/子弹的ID public int prefabId; // 武器/子弹预制体的ID public float damage; // 武器/子弹的伤害值 public int count; // 发射武器/子弹的数量 public float speed; // 武器旋转的速度 void Start() { Init(); // 初始化武器属性 } void Update() { switch (id) { case 0: transform.Rotate(Vector3.back, speed * Time.deltaTime); // 绕z轴旋转武器 break; default: break; } } /// <summary> /// 初始化武器/子弹属性 /// </summary> public void Init() { switch (id) { case 0: speed = 150; // 设置武器旋转的速度 Batch(); break; default: break; } } /// <summary> /// 批量配置武器属性 /// </summary> void Batch() { for (int index = 0; index < count; index++) { Transform bullet = GameManager.instance.pool.Get(prefabId).transform; // 从对象池中获取武器预制体的实例 bullet.parent = transform; // 设置武器的父级为武器 Vector3 rotvec = Vector3.forward * 360 * index / count; // 计算武器的旋转角度,使其均匀分布在圆周上 bullet.Rotate(rotvec); // 设置武器的旋转角度 bullet.Translate(bullet.up * 1.5f, Space.World); // 将武器沿着自身的y轴方向向前移动一定距离 bullet.GetComponent<Bullet>().Init(damage); // 初始化武器的属性 } } }
挂载配置参数
单武器效果
多武器效果
升级增加武器伤害和数量
修改Weapon
void Update() { //。。。 //测试 if (Input.GetKeyDown(KeyCode.Space)) { LevelUp(20, 5); } } public void LevelUp(float damage, int count) { this.damage = damage * Character.Damage; this.count += count; if (id == 0) Batch(); } /// <summary> /// 批量配置武器属性 /// </summary> void Batch() { for (int index = 0; index < count; index++) { Transform bullet; if (index < transform.childCount) { bullet = transform.GetChild(index); } else { bullet = GameManager.instance.pool.Get(prefabId).transform;// 从对象池中获取武器预制体的实例 bullet.parent = transform;// 设置武器的父级为武器 } //。。。 } }
效果
查找离主角最近的敌人
新增Scanner
public class Scanner : MonoBehaviour { public float scanRange; // 扫描范围 public LayerMask targetLayer; // 目标层 public RaycastHit2D[] targets; // 扫描到的目标 public Transform nearestTarget; // 最近的目标 void FixedUpdate() { targets = Physics2D.CircleCastAll(transform.position, scanRange, Vector2.zero, 0, targetLayer); // 在扫描范围内发射圆形光线,获取扫描到的目标 nearestTarget = GetNearest(); } // 获取最近的目标 Transform GetNearest() { Transform result = null; // 初始化最近的目标为null float diff = scanRange; // 初始化最小距离为100,用于后面比较 foreach (RaycastHit2D target in targets) { Vector3 myPos = transform.position; // 获取自身位置 Vector3 targetPos = target.transform.position; // 获取目标位置 float curDiff = Vector3.Distance(myPos, targetPos); // 计算自身与目标的距离 if (curDiff < diff) // 如果当前目标距离比之前的最小距离还小,说明这个目标更近 { diff = curDiff; // 更新最小距离 result = target.transform; // 更新最近的目标 } } return result; // 返回最近的目标 } //场景窗口可视化射线 private void OnDrawGizmos() { Gizmos.DrawWireSphere(transform.position, scanRange); Gizmos.color = Color.red; } }
挂载脚本,配置参数
效果,获取了离自己最近的敌人
子弹预制体
对象池挂载子弹
生成子弹
修改Weapon
float timer; Player player; void Start() { player = GameManager.instance.player; Init(); // 初始化武器属性 } void Update() { switch (id) { //。。。 default: timer += Time.deltaTime; if (timer > speed) { timer = 0f; Fire(); } break; } } /// <summary> /// 初始化武器/子弹属性 /// </summary> public void Init() { switch (id) { //。。。 default: speed = 0.3f; break; } } //发射子弹 void Fire() { if (!player.scanner.nearestTarget) return; Transform bullet = GameManager.instance.pool.Get(prefabId).transform; bullet.position = transform.position; }
修改Player,获取最近的敌人数据
[NonReorderable] public Scanner scanner; scanner = GetComponent<Scanner>();
配置子弹生成容器及数据
效果,每0.3秒生成一个子弹
发射子弹
修改Bullet
Rigidbody2D rigid; private int per; // 子弹的穿透次数 void Awake() { rigid = GetComponent<Rigidbody2D>(); // 获取子弹的刚体组件 } public void Init(float damage, int per, Vector3 dir) { this.damage = damage; // 初始化子弹的伤害值 this.per = per; // 初始化子弹的穿透次数 if (per > -1) { rigid.velocity = dir * 15f; // 如果子弹具有穿透次数,则设置子弹的初始速度和方向 } } void OnTriggerEnter2D(Collider2D collision) { if (!collision.CompareTag("Enemy") || per == -1) return; // 如果碰撞物体不是敌人或子弹没有穿透次数,直接返回 per--; // 穿透次数减一 if (per == -1) { rigid.velocity = Vector2.zero; // 如果穿透次数减到-1,即没有穿透能力了,将子弹的速度设为零 gameObject.SetActive(false); // 将子弹设为非激活状态,使其隐藏或回收 } }
修改Weapon
void Batch() { //。。。 bullet.GetComponent<Bullet>().Init(damage, -1, Vector3.zero); // 初始化武器的属性 } } // 发射子弹 void Fire() { if (!player.scanner.nearestTarget) return; // 如果玩家的扫描器没有最近的目标,直接返回 Vector3 targetPos = player.scanner.nearestTarget.position; // 获取最近目标的位置 Vector3 dir = targetPos - transform.position; // 计算从当前位置指向目标位置的方向向量 dir = dir.normalized; // 将方向向量归一化,使其成为单位向量 Transform bullet = GameManager.instance.pool.Get(prefabId).transform; // 从对象池中获取一个子弹对象 bullet.position = transform.position; // 设置子弹的初始位置为当前位置 bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir); // 将子弹的朝向调整为指向目标位置的方向 bullet.GetComponent<Bullet>().Init(damage, count, dir); // 初始化子弹的伤害值、穿透次数和初始速度方向 }
效果
修改Weapon,升级测试
//测试 if (Input.GetKeyDown(KeyCode.Space)) { LevelUp(10, 1); }
效果,升级后子弹具有了一定穿透性
参考
【视频】https://www.youtube.com/watch?v=MmW166cHj54&list=PLO-mt5Iu5TeZF8xMHqtT_DhAPKmjF6i3x
源码
源码在最后一节