using System; using System.Collections.Generic; using DragonLi.Core; using Knife.Effects.SimpleController; using UnityEngine; /// /// 管理玩家手部武器: /// - 拾取后加入可用列表并切换; /// - 弹尽后自动移除、切回初始武器; /// - 滚轮/摇杆循环切换。 /// 已去除相机 FOV 同步(与瞄准放大无关),并优化了字典查找与数组长度缓存,减少循环与属性访问开销。 /// public class PlayerHands : MonoBehaviour { [Header("所有武器预置(含初始武器 + 可捡起武器),0号为初始武器")] public GameObject[] WeaponsObjects; public int[] Indices; // 可选:UI 圆环选择器索引 public WeaponSelector weaponSelector; // 可选:UI 武器选择器事件源 [SerializeField] private Player player; // 用于后坐力回调 private IWeapon[] weapons; // 缓存的 IWeapon 组件 private List idToIndex; // weaponId -> WeaponsObjects 下标 // “当前已拾取并可用”列表里存的是真实数组下标 private List availableWeaponIndices = new List(); private int currentAvailableListIndex = -1; void Start() { int weaponCount = WeaponsObjects.Length; weapons = new IWeapon[weaponCount]; // 构建 weaponId→数组下标 的字典 idToIndex = new List(weaponCount); for (int i = 0; i < weaponCount; i++) { weapons[i] = WeaponsObjects[i].GetComponent(); // 只要实现 IWeapon,就取它的 weaponId(向下转型) var w = WeaponsObjects[i].GetComponent(); w.weaponId = i+1; if (w != null && !idToIndex.Contains(w.weaponId)) idToIndex.Add(w.weaponId); // 初始全部隐藏 weapons[i].SetActive(false); } // —————————————————————————————— // 把索引 0 号初始武器加入可用列表并激活 availableWeaponIndices.Clear(); availableWeaponIndices.Add(0); currentAvailableListIndex = 0; weapons[0].SetActive(true); weapons[0].OnFire += OnFire; // —————————————————————————————— // 注册 WeaponSelector 回调(若不需要可留空) if (weaponSelector != null) weaponSelector.OnSelectedEvent += OnWeaponSelected; // 监听弹尽事件 Weapon.OnAmmoDepleted += HandleAmmoDepleted; } void OnDestroy() { if (weaponSelector != null) weaponSelector.OnSelectedEvent -= OnWeaponSelected; Weapon.OnAmmoDepleted -= HandleAmmoDepleted; // 卸载所有 OnFire 监听 foreach (var w in weapons) { if (w != null) w.OnFire -= OnFire; } } void Update() { // Editor 下用滚轮切换 #if UNITY_EDITOR float scroll = Input.GetAxis("Mouse ScrollWheel"); if (scroll > 0f) SwitchToNextAvailable(); else if (scroll < 0f) SwitchToPreviousAvailable(); #endif // VR(PICO)摇杆的切换由 Player 脚本里调用 } /// /// UI 圆环选中时,传入 selectorIndex,映射到 WeaponsObjects 下标后切换 /// public void OnWeaponSelected(int selectorIndex) { if (Indices == null || Indices.Length == 0) return; int foundIdx = -1; for (int i = 0, len = Indices.Length; i < len; i++) { if (Indices[i] == selectorIndex) { foundIdx = i; break; } } if (foundIdx < 0) return; int listPos = availableWeaponIndices.IndexOf(foundIdx); if (listPos >= 0) DeployByAvailableListIndex(listPos); } /// /// 切到下一个可用武器(循环) /// public void SwitchToNextAvailable() { int count = availableWeaponIndices.Count; if (count <= 1) return; int next = (currentAvailableListIndex + 1) % count; DeployByAvailableListIndex(next); } /// /// 切到上一个可用武器(循环) /// public void SwitchToPreviousAvailable() { int count = availableWeaponIndices.Count; if (count <= 1) return; int prev = (currentAvailableListIndex - 1 + count) % count; DeployByAvailableListIndex(prev); } /// /// 内部:切换到 availableWeaponIndices[listIdx] 对应的武器 /// private void DeployByAvailableListIndex(int listIdx) { int availCount = availableWeaponIndices.Count; if (listIdx < 0 || listIdx >= availCount) return; int weaponArrayIndex = availableWeaponIndices[listIdx]; // 如果已经就是这把,直接返回 int oldArrayIndex = availableWeaponIndices[currentAvailableListIndex]; if (weaponArrayIndex == oldArrayIndex) return; // 停掉旧武器 weapons[oldArrayIndex].OnFire -= OnFire; weapons[oldArrayIndex].SetActive(false); // 激活新武器 currentAvailableListIndex = listIdx; weapons[weaponArrayIndex].OnFire += OnFire; weapons[weaponArrayIndex].SetActive(true); Weapon curPlayerWeapon = WeaponsObjects[weaponArrayIndex].GetComponent(); EventDispatcher.TriggerEvent("ChangeWeaponIcon",(PlayerWeaponType)weaponArrayIndex,curPlayerWeapon.bulletsLeft); } /// /// 玩家捡到武器道具时调用: /// 1. 将 weaponArrayIndex 加入可用列表(若尚未存在); /// 2. 给它加 ammoCount 子弹; /// 3. 切到它。 /// public void PickUpAndSwitchTo(int weaponArrayIndex, int ammoCount) { weaponArrayIndex -= 1; int maxIdx = WeaponsObjects.Length - 1; if (weaponArrayIndex < 0 || weaponArrayIndex > maxIdx) return; // 1. 加入可用列表(若没在里头) if (!availableWeaponIndices.Contains(weaponArrayIndex)) availableWeaponIndices.Add(weaponArrayIndex); // 2. 给这一把上弹 var w = WeaponsObjects[weaponArrayIndex].GetComponent(); if (w != null) w.PickUpWeapon(w.weaponId, ammoCount); // 3. 切换到这一把 int listPos = availableWeaponIndices.IndexOf(weaponArrayIndex); DeployByAvailableListIndex(listPos); } /// /// 监听 Weapon.OnAmmoDepleted(weaponId) 事件,移除对应武器并自动切回 0 号 /// private void HandleAmmoDepleted(int weaponId) { int weaponArrayIndex = weaponId-1; if (!idToIndex.Contains(weaponArrayIndex)) return; int listPos = availableWeaponIndices.IndexOf(weaponArrayIndex); if (listPos < 0) return; availableWeaponIndices.RemoveAt(listPos); // 如果移除的正是当前持有的武器,切回 0 号 if (listPos == currentAvailableListIndex) { weapons[weaponArrayIndex].OnFire -= OnFire; weapons[weaponArrayIndex].SetActive(false); // 确保 0 号在列表里 if (!availableWeaponIndices.Contains(0)) availableWeaponIndices.Insert(0, 0); currentAvailableListIndex = availableWeaponIndices.IndexOf(0); weapons[0].SetActive(true); weapons[0].OnFire += OnFire; } else if (listPos < currentAvailableListIndex) { // 若移除的索引小于当前索引要减 1 currentAvailableListIndex--; } } /// /// 武器开火时的后坐力回调,将 recoilDelta 传给 Player /// private void OnFire(Vector2 recoilDelta) { if (player != null) player.ApplyRecoil(recoilDelta); } }