From 67e2d3a8ff9041a6ad28e4e45d7ef39d96d21ebe Mon Sep 17 00:00:00 2001 From: Joywayer Date: Thu, 21 May 2026 11:44:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=95=E5=90=91=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/_Game/Scripts/Core/IDropThrough.cs | 12 ++++ .../Editor/Scene/SceneObjectPlacerTool.cs | 29 ++++++++- Assets/_Game/Scripts/Player/PlayerMovement.cs | 61 +++++++++++++++---- .../_Game/Scripts/Player/States/IdleState.cs | 7 +++ .../_Game/Scripts/Player/States/RunState.cs | 7 +++ Assets/_Game/Scripts/World/PhantomPlate.cs | 3 +- ProjectSettings/Physics2DSettings.asset | 2 +- 7 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 Assets/_Game/Scripts/Core/IDropThrough.cs diff --git a/Assets/_Game/Scripts/Core/IDropThrough.cs b/Assets/_Game/Scripts/Core/IDropThrough.cs new file mode 100644 index 0000000..6792402 --- /dev/null +++ b/Assets/_Game/Scripts/Core/IDropThrough.cs @@ -0,0 +1,12 @@ +namespace BaseGames.Core +{ + /// + /// 单向平台接口:挂载在平台上,允许角色从上方穿落。 + /// 定义在 BaseGames.Core 以避免 Player ↔ World 程序集循环引用。 + /// + public interface IDropThrough + { + /// 触发穿落:临时禁用碰撞器,使角色向下穿过平台。 + void TriggerDropThrough(); + } +} diff --git a/Assets/_Game/Scripts/Editor/Scene/SceneObjectPlacerTool.cs b/Assets/_Game/Scripts/Editor/Scene/SceneObjectPlacerTool.cs index 9533174..cbedfe4 100644 --- a/Assets/_Game/Scripts/Editor/Scene/SceneObjectPlacerTool.cs +++ b/Assets/_Game/Scripts/Editor/Scene/SceneObjectPlacerTool.cs @@ -95,7 +95,9 @@ namespace BaseGames.Editor Transform groundCheckT = GetOrCreateChild(root.transform, "GroundCheck"); groundCheckT.localPosition = new Vector3(0f, -0.75f, 0f); AssignReference(playerMovement, "_groundCheck", groundCheckT, report); - AssignLayerMask(playerMovement, "_groundLayer", "Platform", report); + AssignLayerMask(playerMovement, "_groundLayer", + new[] { "Platform", "OneWayPlatform", "MovingOneWayPlatform", "MidHeightOneWayPlatform" }, + report); // ── SkillHitBox_Slot 子节点(技能 HitBox 实例化挂点)──────────────── Transform skillSocketT = GetOrCreateChild(root.transform, "SkillHitBox_Slot"); @@ -1001,6 +1003,31 @@ namespace BaseGames.Editor so.ApplyModifiedPropertiesWithoutUndo(); } + /// 将多个 Layer 名称合并为一个 LayerMask 并写入 SerializedProperty。 + private static void AssignLayerMask(Object target, string propName, string[] layerNames, List report) + { + int mask = 0; + foreach (var name in layerNames) + { + int layer = LayerMask.NameToLayer(name); + if (layer == -1) + report.Add($"Layer '{name}' 不存在,已跳过({target.GetType().Name}.{propName})。"); + else + mask |= 1 << layer; + } + if (mask == 0) return; + + var so = new SerializedObject(target); + var sp = so.FindProperty(propName); + if (sp == null) + { + report.Add($"{target.GetType().Name}.{propName} 字段不存在,跳过 LayerMask 赋值。"); + return; + } + sp.intValue = mask; + so.ApplyModifiedPropertiesWithoutUndo(); + } + private static void AssignInt(Object target, string propName, int value) { var so = new SerializedObject(target); diff --git a/Assets/_Game/Scripts/Player/PlayerMovement.cs b/Assets/_Game/Scripts/Player/PlayerMovement.cs index 1d4a5ff..95251d3 100644 --- a/Assets/_Game/Scripts/Player/PlayerMovement.cs +++ b/Assets/_Game/Scripts/Player/PlayerMovement.cs @@ -1,3 +1,4 @@ +using BaseGames.Core; using UnityEngine; namespace BaseGames.Player @@ -34,7 +35,8 @@ namespace BaseGames.Player private int _facingDirection = 1; private bool _cancelWindowOpen; private SurfaceType _currentSurface = SurfaceType.Ground; - private readonly Collider2D[] _groundBuffer = new Collider2D[4]; + private readonly Collider2D[] _groundBuffer = new Collider2D[4]; + private int _groundHitCount; #if UNITY_EDITOR // ── 运行时调试(Inspector 中可见)─────────────────────────────── @@ -43,6 +45,7 @@ namespace BaseGames.Player [SerializeField] private float _dbg_VelocityX; [SerializeField] private float _dbg_VelocityY; [SerializeField] private bool _dbg_IsGrounded; + [SerializeField] private bool _dbg_OnOneWayPlatform; [SerializeField] private bool _dbg_HasCoyoteTime; [SerializeField] private bool _dbg_IsWallLeft; [SerializeField] private bool _dbg_IsWallRight; @@ -91,10 +94,11 @@ namespace BaseGames.Player _coyoteTimer = Mathf.Max(0f, _coyoteTimer - Time.fixedDeltaTime); #if UNITY_EDITOR - _dbg_VelocityX = _rb.velocity.x; - _dbg_VelocityY = _rb.velocity.y; - _dbg_IsGrounded = _isGrounded; - _dbg_HasCoyoteTime = _coyoteTimer > 0f; + _dbg_VelocityX = _rb.velocity.x; + _dbg_VelocityY = _rb.velocity.y; + _dbg_IsGrounded = _isGrounded; + _dbg_OnOneWayPlatform = _onOneWayPlatform; + _dbg_HasCoyoteTime = _coyoteTimer > 0f; _dbg_IsWallLeft = _isWallLeft; _dbg_IsWallRight = _isWallRight; _dbg_CancelWindowOpen = _cancelWindowOpen; @@ -243,19 +247,54 @@ namespace BaseGames.Player _rb.velocity = new Vector2(_rb.velocity.x, 0f); } - /// 单向平台穿透(输入下行 + 跳跃键时触发)。 - public void DropThroughPlatform() { } + /// + /// 单向平台穿落(↓ + 跳跃键触发)。 + /// 找到脚下的 并临时禁用其碰撞器; + /// 同帧清除 IsGrounded / OnOneWayPlatform,状态机无需等到下一物理帧即可转换到 FallState。 + /// + public void DropThroughPlatform() + { + if (!_onOneWayPlatform) return; + + for (int i = 0; i < _groundHitCount; i++) + { + if (_groundBuffer[i] != null && + _groundBuffer[i].TryGetComponent(out var platform)) + { + platform.TriggerDropThrough(); + // 立即清除着地状态,让状态机本帧即可切换到 FallState + _isGrounded = false; + _onOneWayPlatform = false; + _coyoteTimer = 0f; + break; + } + } + } // ── Physics 检测 ────────────────────────────────────────────────────── private void CheckGrounded() { if (_groundCheck == null) return; - bool wasGrounded = _isGrounded; - _isGrounded = Physics2D.OverlapBoxNonAlloc(_groundCheck.position, _groundCheckSize, 0f, _groundBuffer, _groundLayer) > 0; + _groundHitCount = Physics2D.OverlapBoxNonAlloc( + _groundCheck.position, _groundCheckSize, 0f, _groundBuffer, _groundLayer); + _isGrounded = _groundHitCount > 0; - if (_isGrounded && !wasGrounded) - _coyoteTimer = _config.CoyoteTime; + // 检测是否站在单向平台(含 IDropThrough 组件的碰撞体) + _onOneWayPlatform = false; + for (int i = 0; i < _groundHitCount; i++) + { + if (_groundBuffer[i] != null && + _groundBuffer[i].TryGetComponent(out _)) + { + _onOneWayPlatform = true; + break; + } + } + + _currentSurface = (_isGrounded && _onOneWayPlatform) + ? SurfaceType.OneWayPlatform + : SurfaceType.Ground; } private void CheckWalls() diff --git a/Assets/_Game/Scripts/Player/States/IdleState.cs b/Assets/_Game/Scripts/Player/States/IdleState.cs index 1cdba21..368e889 100644 --- a/Assets/_Game/Scripts/Player/States/IdleState.cs +++ b/Assets/_Game/Scripts/Player/States/IdleState.cs @@ -26,6 +26,13 @@ namespace BaseGames.Player.States _owner.TransitionTo(_owner.GetState()); return; } + // 单向平台穿落:↓ + 跳跃键,优先于普通跳跃,避免误消耗跳跃缓冲 + if (Move.OnOneWayPlatform && Input.MoveInput.y < -0.5f && Buffer.ConsumeJump()) + { + Move.DropThroughPlatform(); + _owner.TransitionTo(_owner.GetState()); + return; + } if (Buffer.ConsumeJump()) { _owner.TransitionTo(_owner.GetState()); diff --git a/Assets/_Game/Scripts/Player/States/RunState.cs b/Assets/_Game/Scripts/Player/States/RunState.cs index 459549c..97e5dac 100644 --- a/Assets/_Game/Scripts/Player/States/RunState.cs +++ b/Assets/_Game/Scripts/Player/States/RunState.cs @@ -25,6 +25,13 @@ namespace BaseGames.Player.States _owner.TransitionTo(_owner.GetState()); return; } + // 单向平台穿落:↓ + 跳跃键,优先于普通跳跃 + if (Move.OnOneWayPlatform && Input.MoveInput.y < -0.5f && Buffer.ConsumeJump()) + { + Move.DropThroughPlatform(); + _owner.TransitionTo(_owner.GetState()); + return; + } if (Buffer.ConsumeJump()) { _owner.TransitionTo(_owner.GetState()); diff --git a/Assets/_Game/Scripts/World/PhantomPlate.cs b/Assets/_Game/Scripts/World/PhantomPlate.cs index 90c3152..3bdbc3f 100644 --- a/Assets/_Game/Scripts/World/PhantomPlate.cs +++ b/Assets/_Game/Scripts/World/PhantomPlate.cs @@ -1,3 +1,4 @@ +using BaseGames.Core; using UnityEngine; namespace BaseGames.World @@ -10,7 +11,7 @@ namespace BaseGames.World /// [RequireComponent(typeof(Collider2D))] [RequireComponent(typeof(PlatformEffector2D))] - public class PhantomPlate : MonoBehaviour + public class PhantomPlate : MonoBehaviour, IDropThrough { [Header("下蹲跌落")] [Tooltip("按住下方向 + 跳跃时临时禁用碰撞器,允许玩家向下穿过平台")] diff --git a/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset index 0985f84..e8de74c 100644 --- a/ProjectSettings/Physics2DSettings.asset +++ b/ProjectSettings/Physics2DSettings.asset @@ -45,4 +45,4 @@ Physics2DSettings: m_ReuseCollisionCallbacks: 1 m_AutoSyncTransforms: 0 m_GizmoOptions: 10 - m_LayerCollisionMatrix: ffffffffffffffffffffffffbffffffffffffffffffffffff7ebfffffff0ffff7ffdffff7fdeffff3fffffff7fbfffffbfffffffffbdffffffd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_LayerCollisionMatrix: ffffffffffffffffffffffffbffffffffffffffffffffffff7ebfffffff0ffff7fffffff7fdfffff3fffffff7fbfffffbffffef7ffbdffffff57ffdfffbffeffff6fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffffffffffefffffffffffffffbffffdffffffffffffffff