Files
MRArcadia/Assets/_Arcadia/Scripts/Item/MapFragment.cs
2026-04-01 18:38:00 +08:00

659 lines
20 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.Collections;
using UnityEngine;
using DG.Tweening;
using DragonLi.Core;
/// <summary>
/// 地图碎片/拼图碎片 - 可飞向拼图台,支持玩家交互
/// </summary>
public class MapFragment : MonoBehaviour
{
[Header("碎片标识")]
[SerializeField] private int fragmentId = 0; // 碎片ID (0-8)
[SerializeField] private int correctSlotId = 0; // 正确的拼装区槽位ID
[Header("碎片模型(子物体)")]
[SerializeField] private GameObject[] fragmentModels; // 9个碎片子模型按顺序0-8排列赋值ID显示对应模型
[Header("展示设置")]
[SerializeField] private float displayDuration = 2f; // 展示时长(右手上方)
[SerializeField] private float handDisplayHeight = 0.3f; // 右手上方高度
[SerializeField] private float handDisplayRotateSpeed = 90f; // 展示旋转速度
[SerializeField] private bool flyToHandForDisplay = true; // 是否飞到右手上方展示
[Header("飞行设置")]
[SerializeField] private float flyDuration = 1f; // 飞向目标时长
[SerializeField] private float flyHeight = 1f; // 飞行高度
[Header("目标设置")]
[SerializeField] private Transform mapTableTarget; // 地图台/拼图台目标位置
[SerializeField] private PuzzleTable puzzleTable; // 关联的拼图台
[Header("水洼效果(钟乳石事件)")]
[SerializeField] private bool isInWaterPuddle = false; // 是否在水洼中
[SerializeField] private Transform waterPuddle; // 水洼位置
[SerializeField] private float waterDepth = 0.05f; // 水下深度
[SerializeField] private float floatHeight = 0.02f; // 浮动高度
[SerializeField] private float floatSpeed = 1f; // 浮动速度
[SerializeField] private float rotateSpeed = 30f; // 水下旋转速度
[SerializeField] private bool enableWaterGlow = true; // 水下发光效果
[SerializeField] private Color waterGlowColor = new Color(0.6f, 0.8f, 1f); // 水下发光颜色
[Header("状态")]
[SerializeField] private bool inStorageZone = false; // 是否在预制区
[SerializeField] private bool isGrabbed = false; // 是否被抓取
[Header("特效")]
[SerializeField] private ParticleSystem displayEffect; // 展示特效
[SerializeField] private ParticleSystem waterRippleEffect; // 水涟漪特效
[Header("音效")]
[SerializeField] private string displaySound = "1.36"; // 展示音效
[SerializeField] private string flySound = "1.37"; // 飞行音效
[SerializeField] private string arriveSound = "1.38"; // 到达音效
[SerializeField] private string grabSound = "1.43"; // 抓取音效
[SerializeField] private string waterSplashSound = "1.44"; // 水花音效
private bool isFlying = false;
private bool isAnimating = false;
private Vector3 originalPosition;
private Renderer fragmentRenderer;
private Material fragmentMaterial;
// 全局静态变量:当前被抓取的碎片(限制每次只能抓取一个)
private static MapFragment currentlyGrabbedMapFragment = null;
// 触发器检测:右手是否在碎片范围内
private bool rightHandInTrigger = false;
public int FragmentId => fragmentId;
public int CorrectSlotId => correctSlotId;
public bool IsInStorageZone => inStorageZone;
public bool IsGrabbed => isGrabbed;
public bool IsCollected { get; private set; } = false; // 是否已被收集
private void Awake()
{
fragmentRenderer = GetComponent<Renderer>();
if (fragmentRenderer != null)
{
fragmentMaterial = fragmentRenderer.material;
}
originalPosition = transform.position;
}
private void Start()
{
// 如果在水洼中,启动水洼效果
if (isInWaterPuddle)
{
StartCoroutine(WaterPuddleEffect());
}
else if (!inStorageZone)
{
// 开始展示流程(非预制区碎片)
StartCoroutine(DisplayAndFly());
}
}
private void Update()
{
// 检测右手交互
if (!isFlying && !isGrabbed && !inStorageZone)
{
CheckRightHandInteraction();
}
}
/// <summary>
/// 展示并飞向地图台
/// </summary>
private IEnumerator DisplayAndFly()
{
// 播放展示音效
if (!string.IsNullOrEmpty(displaySound))
{
GameManager.Ins.PlaySound2DRPC(displaySound);
}
// 播放展示特效
if (displayEffect != null)
{
displayEffect.Play();
}
Debug.Log($"地图碎片开始展示 {displayDuration} 秒...");
// 展示等待
yield return new WaitForSeconds(displayDuration);
// 飞向地图台
FlyToMapTable();
}
/// <summary>
/// 飞向地图台
/// </summary>
private void FlyToMapTable()
{
if (isFlying) return;
isFlying = true;
// 播放飞行音效
if (!string.IsNullOrEmpty(flySound))
{
GameManager.Ins.PlaySound2DRPC(flySound);
}
Debug.Log("地图碎片开始飞向地图台...");
// 获取目标位置
Vector3 targetPos = mapTableTarget != null ? mapTableTarget.position : Vector3.zero;
// 计算中间点(抛物线)
Vector3 startPos = transform.position;
Vector3 midPos = (startPos + targetPos) / 2f + Vector3.up * flyHeight;
// 使用DOPath创建抛物线路径
Vector3[] path = new Vector3[] { startPos, midPos, targetPos };
transform.DOPath(path, flyDuration, PathType.CatmullRom)
.SetEase(Ease.OutQuad)
.OnComplete(() => {
OnArrivedAtMapTable();
});
// 同时缩小
transform.DOScale(0.5f, flyDuration);
}
/// <summary>
/// 到达地图台
/// </summary>
private void OnArrivedAtMapTable()
{
// 播放到达音效
if (!string.IsNullOrEmpty(arriveSound))
{
GameManager.Ins.PlaySound2DRPC(arriveSound);
}
Debug.Log("地图碎片已到达地图台!");
// 销毁地图碎片
StartCoroutine(Disappear());
}
/// <summary>
/// 消失
/// </summary>
private IEnumerator Disappear()
{
// 缩小消失
transform.DOScale(Vector3.zero, 0.5f);
yield return new WaitForSeconds(0.5f);
Destroy(gameObject);
}
/// <summary>
/// 设置地图台目标位置
/// </summary>
public void SetMapTableTarget(Transform target)
{
mapTableTarget = target;
}
/// <summary>
/// 设置展示时长
/// </summary>
public void SetDisplayDuration(float duration)
{
displayDuration = duration;
}
/// <summary>
/// 设置碎片ID
/// </summary>
public void SetFragmentId(int id)
{
fragmentId = id;
UpdateVisibleModel();
}
/// <summary>
/// 根据fragmentId显示对应的子模型
/// 隐藏所有模型只显示ID对应的模型
/// </summary>
public void UpdateVisibleModel()
{
// 隐藏所有碎片模型
if (fragmentModels != null)
{
foreach (var model in fragmentModels)
{
if (model != null)
{
model.SetActive(false);
}
}
// 只显示对应ID的模型
if (fragmentId >= 0 && fragmentId < fragmentModels.Length)
{
if (fragmentModels[fragmentId] != null)
{
fragmentModels[fragmentId].SetActive(true);
Debug.Log($"碎片 {fragmentId} 显示模型: {fragmentModels[fragmentId].name}");
// 更新渲染器引用(用于水洼发光效果)
fragmentRenderer = fragmentModels[fragmentId].GetComponent<Renderer>();
if (fragmentRenderer != null)
{
fragmentMaterial = fragmentRenderer.material;
}
}
}
else
{
Debug.LogWarning($"碎片ID {fragmentId} 超出模型数组范围 (0-{fragmentModels.Length - 1})");
}
}
else
{
Debug.LogWarning("碎片模型数组未设置!");
}
}
/// <summary>
/// 初始化碎片设置ID、拼图台、显示模型
/// 如果 flyToHandForDisplay = true会飞到玩家右手上方旋转展示
/// </summary>
public void Initialize(int id, PuzzleTable table)
{
puzzleTable = table;
SetFragmentId(id);
correctSlotId = id; // 默认槽位ID等于碎片ID
// 飞到右手上方展示(猴子投篮、宝箱密码任务获取的碎片)
if (flyToHandForDisplay && !isInWaterPuddle)
{
FlyToPlayerHandForDisplay();
}
}
/// <summary>
/// 飞到玩家右手上方展示
/// </summary>
private void FlyToPlayerHandForDisplay()
{
RightHand rightHand = GameManager.Ins?.playerRightHand;
if (rightHand == null)
{
Debug.LogWarning("未找到玩家右手,碎片直接飞向拼图台!");
// 直接飞向拼图台
if (puzzleTable != null)
{
puzzleTable.FlyFragmentToStorage(this);
}
return;
}
// 播放展示音效
if (!string.IsNullOrEmpty(displaySound))
{
GameManager.Ins.PlaySound2DRPC(displaySound);
}
// 播放展示特效
if (displayEffect != null)
{
displayEffect.Play();
}
Debug.Log($"碎片 {fragmentId} 飞向右手上方展示...");
// 飞到右手上方位置
Vector3 handPos = rightHand.transform.position + Vector3.up * handDisplayHeight;
// 先飞到右手上方(缩放动画)
transform.DOMove(handPos, 0.5f).SetEase(Ease.OutBack);
transform.DOScale(1.2f, 0.5f).SetEase(Ease.OutBack);
// 开始跟随右手并旋转展示
StartCoroutine(DisplayAboveHand(rightHand));
}
/// <summary>
/// 在右手上方跟随并旋转展示
/// </summary>
private IEnumerator DisplayAboveHand(RightHand hand)
{
float elapsed = 0f;
while (elapsed < displayDuration && hand != null)
{
// 跟随右手位置(上方一定高度)
Vector3 targetPos = hand.transform.position + Vector3.up * handDisplayHeight;
transform.position = targetPos;
// 旋转展示
transform.Rotate(Vector3.up, handDisplayRotateSpeed * Time.deltaTime);
elapsed += Time.deltaTime;
yield return null;
}
Debug.Log($"碎片 {fragmentId} 展示完成,飞向拼图台...");
// 播放飞行音效
if (!string.IsNullOrEmpty(flySound))
{
GameManager.Ins.PlaySound2DRPC(flySound);
}
// 展示完成,飞向拼图台预制区
if (puzzleTable != null)
{
puzzleTable.FlyFragmentToStorage(this);
}
}
/// <summary>
/// 设置正确槽位ID
/// </summary>
public void SetCorrectSlotId(int id)
{
correctSlotId = id;
}
/// <summary>
/// 设置拼图台引用
/// </summary>
public void SetPuzzleTable(PuzzleTable table)
{
puzzleTable = table;
}
/// <summary>
/// 设置是否在预制区
/// </summary>
public void SetInStorageZone(bool inStorage)
{
inStorageZone = inStorage;
if (inStorage)
{
// 停止水洼效果
isInWaterPuddle = false;
}
}
/// <summary>
/// 设置水洼位置
/// </summary>
public void SetWaterPuddle(Transform puddle)
{
waterPuddle = puddle;
isInWaterPuddle = true;
}
/// <summary>
/// 水洼效果 - 碎片在水洼中的浮动和旋转
/// </summary>
private IEnumerator WaterPuddleEffect()
{
Debug.Log("拼图碎片开始水洼效果...");
// 应用水下发光效果
if (enableWaterGlow && fragmentMaterial != null)
{
if (fragmentMaterial.HasProperty("_EmissionColor"))
{
fragmentMaterial.EnableKeyword("_EMISSION");
fragmentMaterial.SetColor("_EmissionColor", waterGlowColor * 0.5f);
}
}
// 初始位置(沉入水中)
Vector3 basePosition = originalPosition;
if (waterPuddle != null)
{
basePosition = waterPuddle.position + Vector3.down * waterDepth;
}
transform.position = basePosition;
// 水下浮动和旋转循环
while (isInWaterPuddle && !isFlying && !isGrabbed)
{
// 浮动动画
float floatOffset = Mathf.Sin(Time.time * floatSpeed) * floatHeight;
Vector3 floatPosition = basePosition + Vector3.up * floatOffset;
if (!isAnimating)
{
transform.position = floatPosition;
}
// 缓慢旋转
transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime);
// 发光脉冲
if (enableWaterGlow && fragmentMaterial != null)
{
float glowPulse = Mathf.Sin(Time.time * 2f) * 0.3f + 0.5f;
if (fragmentMaterial.HasProperty("_EmissionColor"))
{
fragmentMaterial.SetColor("_EmissionColor", waterGlowColor * glowPulse);
}
}
yield return null;
}
// 结束水洼效果时恢复
if (fragmentMaterial != null && fragmentMaterial.HasProperty("_EmissionColor"))
{
fragmentMaterial.SetColor("_EmissionColor", Color.black);
}
}
/// <summary>
/// 检测右手交互
/// 条件:触发器检测右手进入 + (扳机或鼠标左键) + 当前没有其他碎片被抓取
/// </summary>
private void CheckRightHandInteraction()
{
// 必须右手在触发器范围内
if (!rightHandInTrigger) return;
RightHand rightHand = FindObjectOfType<RightHand>();
if (rightHand == null) return;
// 检测扳机或鼠标左键
bool triggerPressed = rightHand.isTrigger;
#if UNITY_EDITOR
// 编辑器模式:鼠标左键模拟扳机
triggerPressed = triggerPressed || Input.GetMouseButton(0);
#endif
// 每次只能抓取一个碎片
if (triggerPressed && currentlyGrabbedMapFragment == null && !isGrabbed)
{
OnGrabbed(rightHand);
currentlyGrabbedMapFragment = this;
}
// 检测松手(扳机和鼠标左键都释放)
if (isGrabbed)
{
bool triggerReleased = !rightHand.isTrigger;
#if UNITY_EDITOR
triggerReleased = triggerReleased && !Input.GetMouseButton(0);
#endif
if (triggerReleased)
{
OnReleased();
}
}
}
#region
private void OnTriggerEnter(Collider other)
{
// 检测右手碰触
if (!isGrabbed && !isFlying)
{
RightHand rightHand = other.GetComponent<RightHand>();
if (rightHand != null)
{
rightHandInTrigger = true;
Debug.Log($"右手进入MapFragment {fragmentId} 触发器范围");
}
}
}
private void OnTriggerExit(Collider other)
{
// 右手离开触发器范围
if (!isGrabbed && !isFlying)
{
RightHand rightHand = other.GetComponent<RightHand>();
if (rightHand != null)
{
rightHandInTrigger = false;
Debug.Log($"右手离开MapFragment {fragmentId} 触发器范围");
}
}
}
#endregion
/// <summary>
/// 松手时清除全局抓取状态
/// </summary>
private void OnReleased()
{
if (currentlyGrabbedMapFragment == this)
{
currentlyGrabbedMapFragment = null;
}
isGrabbed = false;
Debug.Log($"拼图碎片 {fragmentId} 松手");
}
/// <summary>
/// 被抓取
/// </summary>
private void OnGrabbed(RightHand hand)
{
isGrabbed = true;
isInWaterPuddle = false;
// 播放抓取音效
if (!string.IsNullOrEmpty(grabSound))
{
GameManager.Ins?.PlaySound2DRPC(grabSound);
}
// 播放水花效果(如果从水中抓取)
if (waterRippleEffect != null)
{
waterRippleEffect.transform.position = transform.position;
waterRippleEffect.Play();
}
// 播放水花音效
if (!string.IsNullOrEmpty(waterSplashSound))
{
GameManager.Ins?.PlaySound2DRPC(waterSplashSound);
}
// 从水中升起动画
if (isAnimating)
{
isAnimating = false;
transform.DOKill();
}
// 短暂升起效果
transform.DOMoveY(transform.position.y + 0.1f, 0.2f).SetEase(Ease.OutBack);
Debug.Log($"拼图碎片 {fragmentId} 被抓取");
// 飞向拼图台预制区
if (puzzleTable != null)
{
StartCoroutine(FlyToPuzzleTableAfterDelay(0.5f));
}
}
/// <summary>
/// 延迟飞向拼图台
/// </summary>
private IEnumerator FlyToPuzzleTableAfterDelay(float delay)
{
yield return new WaitForSeconds(delay);
if (puzzleTable != null)
{
puzzleTable.FlyFragmentToStorage(this);
isGrabbed = false;
}
}
/// <summary>
/// 飞向拼图台预制区由PuzzleTable调用
/// </summary>
public void FlyToPuzzleTableStorage()
{
if (puzzleTable == null)
{
Debug.LogWarning("未设置拼图台!");
return;
}
puzzleTable.FlyFragmentToStorage(this);
}
/// <summary>
/// 手动触发飞行(用于外部调用)
/// </summary>
public void TriggerFlyToTarget()
{
if (isFlying) return;
if (puzzleTable != null)
{
FlyToPuzzleTableStorage();
}
else
{
FlyToMapTable();
}
}
private void OnDestroy()
{
// 清理DOTween动画
transform.DOKill();
}
private void OnDrawGizmosSelected()
{
// 绘制水洼范围
if (isInWaterPuddle && waterPuddle != null)
{
Gizmos.color = new Color(0.2f, 0.6f, 0.8f, 0.3f);
Gizmos.DrawSphere(waterPuddle.position, 0.2f);
Gizmos.DrawLine(transform.position, waterPuddle.position);
}
}
}