Files
FutureMen/Assets/Scripts/Player/PlayerHands.cs
2025-07-10 14:34:41 +08:00

236 lines
7.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using DragonLi.Core;
using Knife.Effects.SimpleController;
using UnityEngine;
/// <summary>
/// 管理玩家手部武器:
/// - 拾取后加入可用列表并切换;
/// - 弹尽后自动移除、切回初始武器;
/// - 滚轮/摇杆循环切换。
/// 已去除相机 FOV 同步(与瞄准放大无关),并优化了字典查找与数组长度缓存,减少循环与属性访问开销。
/// </summary>
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<int> idToIndex; // weaponId -> WeaponsObjects 下标
// “当前已拾取并可用”列表里存的是真实数组下标
private List<int> availableWeaponIndices = new List<int>();
private int currentAvailableListIndex = -1;
void Start()
{
int weaponCount = WeaponsObjects.Length;
weapons = new IWeapon[weaponCount];
// 构建 weaponId→数组下标 的字典
idToIndex = new List<int>(weaponCount);
for (int i = 0; i < weaponCount; i++)
{
weapons[i] = WeaponsObjects[i].GetComponent<IWeapon>();
// 只要实现 IWeapon就取它的 weaponId向下转型
var w = WeaponsObjects[i].GetComponent<Weapon>();
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
// VRPICO摇杆的切换由 Player 脚本里调用
}
/// <summary>
/// UI 圆环选中时,传入 selectorIndex映射到 WeaponsObjects 下标后切换
/// </summary>
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);
}
/// <summary>
/// 切到下一个可用武器(循环)
/// </summary>
public void SwitchToNextAvailable()
{
int count = availableWeaponIndices.Count;
if (count <= 1) return;
int next = (currentAvailableListIndex + 1) % count;
DeployByAvailableListIndex(next);
}
/// <summary>
/// 切到上一个可用武器(循环)
/// </summary>
public void SwitchToPreviousAvailable()
{
int count = availableWeaponIndices.Count;
if (count <= 1) return;
int prev = (currentAvailableListIndex - 1 + count) % count;
DeployByAvailableListIndex(prev);
}
/// <summary>
/// 内部:切换到 availableWeaponIndices[listIdx] 对应的武器
/// </summary>
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<Weapon>();
EventDispatcher.TriggerEvent("ChangeWeaponIcon",(PlayerWeaponType)weaponArrayIndex,curPlayerWeapon.bulletsLeft);
}
/// <summary>
/// 玩家捡到武器道具时调用:
/// 1. 将 weaponArrayIndex 加入可用列表(若尚未存在);
/// 2. 给它加 ammoCount 子弹;
/// 3. 切到它。
/// </summary>
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<Weapon>();
if (w != null)
w.PickUpWeapon(w.weaponId, ammoCount);
// 3. 切换到这一把
int listPos = availableWeaponIndices.IndexOf(weaponArrayIndex);
DeployByAvailableListIndex(listPos);
}
/// <summary>
/// 监听 Weapon.OnAmmoDepleted(weaponId) 事件,移除对应武器并自动切回 0 号
/// </summary>
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--;
}
}
/// <summary>
/// 武器开火时的后坐力回调,将 recoilDelta 传给 Player
/// </summary>
private void OnFire(Vector2 recoilDelta)
{
if (player != null)
player.ApplyRecoil(recoilDelta);
}
}