143 lines
5.8 KiB
C#
143 lines
5.8 KiB
C#
using System.Collections;
|
||
using Animancer;
|
||
using BaseGames.Core.Events;
|
||
using UnityEngine;
|
||
|
||
namespace BaseGames.World
|
||
{
|
||
/// <summary>
|
||
/// 门过渡组件。挂载在有物理门对象的 GameObject 上。
|
||
/// <para>
|
||
/// 触发流程:
|
||
/// <list type="number">
|
||
/// <item>玩家进入触发器或按交互键 → 播放 <see cref="_openClip"/> 开门动画</item>
|
||
/// <item>动画完成后发出 <see cref="SceneLoadRequest"/>,类型由 <see cref="_transitionType"/> 决定</item>
|
||
/// <item>目标场景出生点侧的 <see cref="DoorTransition"/> 被 <see cref="PlayerSpawnPoint"/> 引用,
|
||
/// 由外部调用 <see cref="PlayEnterAnimation"/> 播放玩家进入门的过渡动画</item>
|
||
/// </list>
|
||
/// </para>
|
||
/// <para>
|
||
/// 若不需要门动画,直接使用 <see cref="RoomTransition"/> 即可。
|
||
/// </para>
|
||
/// </summary>
|
||
[RequireComponent(typeof(Collider2D))]
|
||
public class DoorTransition : MonoBehaviour, IInteractable
|
||
{
|
||
[Header("目标")]
|
||
[SerializeField] private string _targetSceneAddress;
|
||
[SerializeField] private string _targetTransitionId;
|
||
|
||
[Header("过渡类型")]
|
||
[Tooltip("Room:极短淡出,无加载画面。\nScene:完整淡出 + 加载画面。")]
|
||
[SerializeField] private TransitionType _transitionType = TransitionType.Room;
|
||
|
||
[Header("触发方式")]
|
||
[Tooltip("true = 玩家进入触发器自动触发;false = 需要玩家按交互键。")]
|
||
[SerializeField] private bool _autoTrigger = false;
|
||
|
||
[Header("门动画")]
|
||
[SerializeField] private AnimancerComponent _animancer;
|
||
|
||
[Tooltip("玩家从外侧进入时播放的开门动画(出口侧)。留空则跳过动画直接过渡。")]
|
||
[SerializeField] private AnimationClip _openClip;
|
||
|
||
[Tooltip("玩家从内侧走出时播放的动画(入口侧,由 PlayerSpawnPoint 在玩家出生后触发)。留空则跳过。")]
|
||
[SerializeField] private AnimationClip _enterClip;
|
||
|
||
[Header("钥匙物品校验")]
|
||
[SerializeField] private bool _requiresKeyItem;
|
||
[SerializeField] private string _requiredItemId;
|
||
|
||
[Header("事件频道")]
|
||
[SerializeField] private SceneLoadRequestEventChannelSO _onSceneLoadRequest;
|
||
|
||
[Header("世界状态")]
|
||
[SerializeField] private WorldStateRegistry _worldState;
|
||
|
||
private bool _triggered;
|
||
|
||
// ── IInteractable ─────────────────────────────────────────────────────
|
||
|
||
public bool CanInteract => !_autoTrigger;
|
||
public string InteractPrompt => "进入";
|
||
|
||
public void Interact(Transform player) => TryTrigger();
|
||
|
||
public void OnPlayerEnterRange(Transform player) { }
|
||
public void OnPlayerExitRange() { }
|
||
|
||
// ── 触发 ──────────────────────────────────────────────────────────────
|
||
|
||
private void OnTriggerEnter2D(Collider2D other)
|
||
{
|
||
if (!_autoTrigger) return;
|
||
if (!other.CompareTag("Player")) return;
|
||
TryTrigger();
|
||
}
|
||
|
||
private void TryTrigger()
|
||
{
|
||
if (_triggered) return;
|
||
if (_requiresKeyItem && !HasItem(_requiredItemId)) return;
|
||
_triggered = true;
|
||
StartCoroutine(OpenAndTransition());
|
||
}
|
||
|
||
private void OnDisable() => _triggered = false;
|
||
|
||
// ── 动画流程 ──────────────────────────────────────────────────────────
|
||
|
||
private IEnumerator OpenAndTransition()
|
||
{
|
||
if (_animancer != null && _openClip != null)
|
||
{
|
||
var state = _animancer.Play(_openClip);
|
||
yield return state; // 等待动画完成
|
||
}
|
||
|
||
_onSceneLoadRequest?.Raise(new SceneLoadRequest
|
||
{
|
||
SceneName = _targetSceneAddress,
|
||
EntryTransitionId = _targetTransitionId,
|
||
TransitionType = _transitionType,
|
||
ShowLoadingScreen = _transitionType == TransitionType.Scene,
|
||
IsRespawn = false,
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 玩家从门内侧走出时的动画(由出生点侧的 <see cref="PlayerSpawnPoint"/> 在玩家出生后调用)。
|
||
/// 若未配置 <see cref="_enterClip"/> 则静默跳过。
|
||
/// </summary>
|
||
public void PlayEnterAnimation()
|
||
{
|
||
if (_animancer != null && _enterClip != null)
|
||
_animancer.Play(_enterClip);
|
||
}
|
||
|
||
// ── 辅助 ──────────────────────────────────────────────────────────────
|
||
|
||
private bool HasItem(string itemId)
|
||
{
|
||
if (string.IsNullOrEmpty(itemId)) return true;
|
||
if (_worldState == null)
|
||
{
|
||
Debug.LogWarning($"[DoorTransition] WorldStateRegistry 未配置,钥匙 {itemId} 检查跳过");
|
||
return false;
|
||
}
|
||
return _worldState.IsCollected(itemId);
|
||
}
|
||
|
||
private void OnDrawGizmos()
|
||
{
|
||
var col = GetComponent<Collider2D>();
|
||
if (col == null) return;
|
||
// 紫色区分门过渡
|
||
Gizmos.color = new Color(0.7f, 0.3f, 1f, 0.7f);
|
||
Gizmos.DrawWireCube(transform.position, col.bounds.size);
|
||
Gizmos.color = new Color(0.7f, 0.3f, 1f, 0.3f);
|
||
Gizmos.DrawCube(transform.position, col.bounds.size);
|
||
}
|
||
}
|
||
}
|