调整linkdoor

This commit is contained in:
2026-05-19 16:04:40 +08:00
parent 0559d2cd36
commit aac24d825f
6 changed files with 410 additions and 72 deletions

View File

@@ -1,5 +1,6 @@
using System.Collections;
using Animancer;
using BaseGames.Feedback;
using BaseGames.Player;
using UnityEngine;
namespace BaseGames.World
@@ -9,9 +10,9 @@ namespace BaseGames.World
/// <para>
/// 触发流程:
/// <list type="number">
/// <item>玩家进入触发器或按交互键 → 播放 <see cref="_openClip"/> 开门动画(本门</item>
/// <item>传送玩家到 <see cref="_linkedDoor"/> 的出生位置,并设定朝向</item>
/// <item>目标门播放 <see cref="_enterClip"/> 玩家走出动画</item>
/// <item>玩家进入触发器或按交互键 → 播放 <see cref="_transitionOut"/> 转场反馈(淡出</item>
/// <item>等待反馈完成后传送玩家到 <see cref="_linkedDoor"/> 的 <see cref="_spawnPoint"/> 位置,并设定朝向</item>
/// <item>播放 <see cref="_linkedDoor"/> 的 <see cref="_transitionIn"/> 转场反馈(淡入),目标门进入冷却</item>
/// </list>
/// </para>
/// <para>不涉及场景加载;适用于同房间内不同小区域之间的传送门、电梯、秘道等。</para>
@@ -24,8 +25,8 @@ namespace BaseGames.World
[SerializeField] private LinkedDoorTransition _linkedDoor;
[Header("出生配置")]
[Tooltip("传送后玩家在此门的出生位置偏移(相对于本 GameObject 中心,默认 0。")]
[SerializeField] private Vector2 _spawnOffset = new Vector2(0f, 0f);
[Tooltip("传送到此门后玩家的出生位置。拖动场景中的子节点 SpawnPoint 来调整。\n为空时回退到 GameObject 中心。")]
[SerializeField] private Transform _spawnPoint;
[Tooltip("传送到此门后玩家的朝向(+1 = 朝右,-1 = 朝左)。")]
[SerializeField] private int _facingDirectionOnArrive = 1;
@@ -34,14 +35,12 @@ namespace BaseGames.World
[Tooltip("true = 玩家进入触发器自动触发false = 需玩家按交互键。")]
[SerializeField] private bool _autoTrigger = false;
[Header("门动画")]
[SerializeField] private AnimancerComponent _animancer;
[Header("转场反馈")]
[Tooltip("传送前播放(淡出)。留空则跳过,直接传送。")]
[SerializeField] private SceneFeedback _transitionOut;
[Tooltip("玩家从外侧进入时的开门动画。留空则跳过。")]
[SerializeField] private AnimationClip _openClip;
[Tooltip("玩家从内侧走出时的动画(目标门调用)。留空则跳过。")]
[SerializeField] private AnimationClip _enterClip;
[Tooltip("传送后播放(淡入)。留空则跳过。由目标门在玩家到达后自动播放。")]
[SerializeField] private SceneFeedback _transitionIn;
[Header("钥匙物品校验")]
[SerializeField] private bool _requiresKeyItem;
@@ -50,7 +49,6 @@ namespace BaseGames.World
[Header("世界状态")]
[SerializeField] private WorldStateRegistry _worldState;
// 传送后短暂冷却,防止玩家在目标门处被立即再次传回
[Header("冷却")]
[Tooltip("传送完成后本门的冷却时间(秒)。防止玩家被反复来回传送。")]
[SerializeField] private float _cooldown = 1.5f;
@@ -98,54 +96,37 @@ namespace BaseGames.World
private IEnumerator TransitionCoroutine(Transform player)
{
// 1. 播放本门开门动画
if (_animancer != null && _openClip != null)
{
var state = _animancer.Play(_openClip);
yield return state;
}
// 1. 淡出
if (_transitionOut != null)
yield return _transitionOut.PlayAndWait();
// 2. 传送玩家到目标门
Vector3 destination = _linkedDoor.GetSpawnWorldPosition();
player.position = destination;
// 2. 传送
player.position = _linkedDoor.GetSpawnWorldPosition();
// 朝向(通过 localScale X 翻转,或通知 PlayerController
ApplyFacing(player, _linkedDoor._facingDirectionOnArrive);
// 通过 PlayerMovement 设定朝向(同步清零速度,防止旧速度覆盖朝向
var movement = player.GetComponentInChildren<PlayerMovement>()
?? player.GetComponent<PlayerMovement>();
if (movement != null)
movement.SetFacingImmediate(_linkedDoor._facingDirectionOnArrive);
// 3. 目标门播放玩家走出动画,并进入冷却
_linkedDoor.PlayEnterAnimation();
// 4. 淡入(目标门播放+ 冷却
_linkedDoor.PlayTransitionIn();
_linkedDoor.StartCooldown(_cooldown);
_triggered = false;
}
/// <summary>玩家从门内侧走出时的动画。由传送发起方在传送完成后调用。</summary>
public void PlayEnterAnimation()
{
if (_animancer != null && _enterClip != null)
_animancer.Play(_enterClip);
}
/// <summary>播放此门的淡入反馈。由传送发起方在玩家到达后调用。</summary>
public void PlayTransitionIn() => _transitionIn?.Play();
/// <summary>启动传送冷却,在此期间本门不会再次触发传送。</summary>
public void StartCooldown(float duration)
{
_cooldownUntil = Time.time + duration;
}
public void StartCooldown(float duration) => _cooldownUntil = Time.time + duration;
// ── 辅助 ──────────────────────────────────────────────────────────────
/// <summary>返回传送目标门的出生世界坐标(含偏移)。</summary>
/// <summary>返回传送目标位置。优先使用 _spawnPoint 子节点,否则回退到门中心。</summary>
public Vector3 GetSpawnWorldPosition()
=> transform.position + new Vector3(_spawnOffset.x, _spawnOffset.y, 0f);
private static void ApplyFacing(Transform player, int facing)
{
if (facing == 0) return;
Vector3 s = player.localScale;
float absX = Mathf.Abs(s.x);
s.x = facing > 0 ? absX : -absX;
player.localScale = s;
}
=> _spawnPoint != null ? _spawnPoint.position : transform.position;
private bool HasItem(string itemId)
{
@@ -158,29 +139,45 @@ namespace BaseGames.World
return _worldState.IsCollected(itemId);
}
// ── Gizmos ────────────────────────────────────────────────────────────
#if UNITY_EDITOR
private void OnDrawGizmos()
{
var col = GetComponent<Collider2D>();
if (col != null)
{
// 青色区分成对门
Gizmos.color = new Color(0.2f, 0.9f, 0.9f, 0.7f);
Gizmos.DrawWireCube(transform.position, col.bounds.size);
Gizmos.color = new Color(0.2f, 0.9f, 0.9f, 0.2f);
Gizmos.DrawCube(transform.position, col.bounds.size);
Gizmos.DrawWireCube(col.bounds.center, col.bounds.size);
Gizmos.color = new Color(0.2f, 0.9f, 0.9f, 0.15f);
Gizmos.DrawCube(col.bounds.center, col.bounds.size);
}
// 出生点
Vector3 spawn = GetSpawnWorldPosition();
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(spawn, 0.2f);
// 配对连线
if (_linkedDoor != null)
{
Gizmos.color = new Color(0.2f, 0.9f, 0.9f, 0.8f);
Gizmos.DrawLine(spawn, _linkedDoor.GetSpawnWorldPosition());
}
Vector3 labelPos = col != null
? col.bounds.center + Vector3.up * (col.bounds.extents.y + 0.3f)
: transform.position + Vector3.up * 1.5f;
string facing = _facingDirectionOnArrive >= 0 ? "→" : "←";
string trigger = _autoTrigger ? "Auto" : "Interact";
string linkName = _linkedDoor != null ? _linkedDoor.name : "未配对";
string keyInfo = _requiresKeyItem ? $" 🔑{_requiredItemId}" : "";
string label = $"{name}\n→ {linkName} | {trigger} | 到达朝向:{facing}{keyInfo}";
UnityEditor.Handles.color = Color.white;
UnityEditor.Handles.Label(labelPos, label);
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8fc461876b6b1e44788334d304144e18
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: