Files
zeling_v2/Assets/_Game/Scripts/World/DoorTransition.cs

143 lines
5.8 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 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);
}
}
}