102 lines
3.7 KiB
C#
102 lines
3.7 KiB
C#
|
|
using UnityEngine;
|
||
|
|
using Inventory;
|
||
|
|
using DG.Tweening; // 引入 DOTween
|
||
|
|
using Project.Interaction;
|
||
|
|
|
||
|
|
public class HandController : MonoBehaviour
|
||
|
|
{
|
||
|
|
[Header("配置")]
|
||
|
|
[Tooltip("拖入摄像机下的 HandHolder 物体")]
|
||
|
|
public Transform handPosition;
|
||
|
|
|
||
|
|
[Tooltip("切换物品的动画时长")]
|
||
|
|
public float animationDuration = 0.25f;
|
||
|
|
|
||
|
|
[Tooltip("物品收起时的位移偏移量")]
|
||
|
|
public Vector3 hiddenOffset = new Vector3(0, -1.0f, 0);
|
||
|
|
|
||
|
|
private GameObject currentModel;
|
||
|
|
private Tween currentTween; // 记录当前的动画,防止冲突
|
||
|
|
|
||
|
|
// 供外部调用:切换物品
|
||
|
|
public void EquipItem(ItemData item)
|
||
|
|
{
|
||
|
|
// 如果正在播放动画,先杀掉
|
||
|
|
if (currentTween != null && currentTween.IsActive()) currentTween.Kill();
|
||
|
|
|
||
|
|
// 1. 如果手里有东西,先收起来
|
||
|
|
if (currentModel != null)
|
||
|
|
{
|
||
|
|
// 向下移动,并在完成后销毁
|
||
|
|
currentModel.transform.DOLocalMove(hiddenOffset, animationDuration)
|
||
|
|
.SetEase(Ease.InBack) // 回收时用 InBack 更有感觉
|
||
|
|
.OnComplete(() =>
|
||
|
|
{
|
||
|
|
Destroy(currentModel);
|
||
|
|
currentModel = null;
|
||
|
|
// 旧的收完了,再生成新的
|
||
|
|
SpawnNewItem(item);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// 手里本来就是空的,直接生成新的
|
||
|
|
SpawnNewItem(item);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void SpawnNewItem(ItemData item)
|
||
|
|
{
|
||
|
|
if (item == null || item.handModelPrefab == null) return;
|
||
|
|
|
||
|
|
// 生成新模型
|
||
|
|
currentModel = Instantiate(item.handModelPrefab, handPosition);
|
||
|
|
|
||
|
|
// 自动清理物理组件(防止手持物品掉落或把玩家挤飞)
|
||
|
|
// 1. 关闭 Rigidbody 物理影响
|
||
|
|
Rigidbody rb = currentModel.GetComponent<Rigidbody>();
|
||
|
|
ItemPhysicsState physicsState = currentModel.GetComponent<ItemPhysicsState>();
|
||
|
|
if (rb != null)
|
||
|
|
{
|
||
|
|
if (physicsState != null)
|
||
|
|
{
|
||
|
|
Debug.LogWarning($"{nameof(HandController)} 检测到手持模型带有 {nameof(ItemPhysicsState)}(通常依赖 Rigidbody)。已保留 Rigidbody 并关闭其物理影响,避免运行时报错。", currentModel);
|
||
|
|
physicsState.enabled = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
rb.isKinematic = true;
|
||
|
|
rb.useGravity = false;
|
||
|
|
rb.detectCollisions = false;
|
||
|
|
rb.velocity = Vector3.zero;
|
||
|
|
rb.angularVelocity = Vector3.zero;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. 禁用所有 Collider
|
||
|
|
Collider[] cols = currentModel.GetComponentsInChildren<Collider>();
|
||
|
|
foreach (var c in cols) c.enabled = false;
|
||
|
|
|
||
|
|
// 3. (可选) 将层级设置为 "Player" 或 "Weapon" 防止穿模,如果需要的话
|
||
|
|
// SetLayerRecursively(currentModel, LayerMask.NameToLayer("FirstPerson"));
|
||
|
|
|
||
|
|
// 初始位置设在“下面”
|
||
|
|
Vector3 targetPos = Vector3.zero;
|
||
|
|
Quaternion targetRot = Quaternion.identity;
|
||
|
|
Vector3 targetScale = Vector3.one;
|
||
|
|
|
||
|
|
if (item.useCustomHoldSettings)
|
||
|
|
{
|
||
|
|
targetPos = item.holdPositionOffset;
|
||
|
|
targetRot = Quaternion.Euler(item.holdRotationOffset);
|
||
|
|
targetScale = item.holdScale;
|
||
|
|
}
|
||
|
|
|
||
|
|
currentModel.transform.localScale = targetScale;
|
||
|
|
currentModel.transform.localPosition = hiddenOffset; // 依然从隐藏位置开始
|
||
|
|
currentModel.transform.localRotation = targetRot;
|
||
|
|
|
||
|
|
// 2. 播放“掏出来”动画
|
||
|
|
currentTween = currentModel.transform.DOLocalMove(targetPos, animationDuration)
|
||
|
|
.SetEase(Ease.OutBack); // 掏出时用 OutBack 有一种弹出来的动感
|
||
|
|
}
|
||
|
|
}
|