修复内容:
PlayerMovement:新增 _facingLocked 字段 + LockFacing(bool) 方法;UpdateFacing() 锁定时直接返回 WallSlideState:OnStateEnter 调用 LockFacing(true) + FlipFacing(_wallDir);OnStateExit 调用 LockFacing(false) 解锁 WallJumpState:OnStateEnter 保险性再调一次 LockFacing(false);WallJumpAway/Toward 同步写入 _inputVelocityX,确保解锁后 UpdateFacing 朝向正确(背墙跳 = 离墙方向,对墙跳 = 朝墙方向)
This commit is contained in:
@@ -18,7 +18,7 @@ MonoBehaviour:
|
|||||||
weaponType: 1
|
weaponType: 1
|
||||||
groundComboSteps:
|
groundComboSteps:
|
||||||
- clip:
|
- clip:
|
||||||
_FadeDuration: 0.25
|
_FadeDuration: 0
|
||||||
_Speed: 1
|
_Speed: 1
|
||||||
_Events:
|
_Events:
|
||||||
_NormalizedTimes: []
|
_NormalizedTimes: []
|
||||||
@@ -30,13 +30,13 @@ MonoBehaviour:
|
|||||||
hitBoxEnter: 0
|
hitBoxEnter: 0
|
||||||
hitBoxExit: 0.092
|
hitBoxExit: 0.092
|
||||||
comboInputOpen: 0
|
comboInputOpen: 0
|
||||||
comboInputClose: 1
|
comboInputClose: 0.07
|
||||||
cancelWindowOpen: 0
|
cancelWindowOpen: 0
|
||||||
recoveryTime: 0
|
recoveryTime: 0
|
||||||
comboTimeout: 0
|
comboTimeout: 0.2
|
||||||
hitBoxId: ATK_Ground_1
|
hitBoxId: ATK_Ground_1
|
||||||
- clip:
|
- clip:
|
||||||
_FadeDuration: 0.25
|
_FadeDuration: 0
|
||||||
_Speed: 1
|
_Speed: 1
|
||||||
_Events:
|
_Events:
|
||||||
_NormalizedTimes: []
|
_NormalizedTimes: []
|
||||||
|
|||||||
@@ -2842,83 +2842,6 @@ PolygonCollider2D:
|
|||||||
- {x: 3.5, y: 6.5}
|
- {x: 3.5, y: 6.5}
|
||||||
- {x: -15.5, y: 6.5}
|
- {x: -15.5, y: 6.5}
|
||||||
m_UseDelaunayMesh: 0
|
m_UseDelaunayMesh: 0
|
||||||
--- !u!1 &147475844
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 147475846}
|
|
||||||
- component: {fileID: 147475845}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: PassengerSensor
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!61 &147475845
|
|
||||||
BoxCollider2D:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 147475844}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_Density: 1
|
|
||||||
m_Material: {fileID: 0}
|
|
||||||
m_IncludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_ExcludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_LayerOverridePriority: 0
|
|
||||||
m_ForceSendLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_ForceReceiveLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_ContactCaptureLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_CallbackLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_IsTrigger: 1
|
|
||||||
m_UsedByEffector: 0
|
|
||||||
m_UsedByComposite: 0
|
|
||||||
m_Offset: {x: -0.06424427, y: 0.33}
|
|
||||||
m_SpriteTilingProperty:
|
|
||||||
border: {x: 0, y: 0, z: 0, w: 0}
|
|
||||||
pivot: {x: 0, y: 0}
|
|
||||||
oldSize: {x: 0, y: 0}
|
|
||||||
newSize: {x: 0, y: 0}
|
|
||||||
adaptiveTilingThreshold: 0
|
|
||||||
drawMode: 0
|
|
||||||
adaptiveTiling: 0
|
|
||||||
m_AutoTiling: 0
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Size: {x: 1.7309284, y: 0.25}
|
|
||||||
m_EdgeRadius: 0
|
|
||||||
--- !u!4 &147475846
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 147475844}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
|
||||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 1077748573}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1 &148166884
|
--- !u!1 &148166884
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -7952,37 +7875,6 @@ MonoBehaviour:
|
|||||||
_noiseFrequency: 1
|
_noiseFrequency: 1
|
||||||
_dedicatedCamera: {fileID: 1059402937}
|
_dedicatedCamera: {fileID: 1059402937}
|
||||||
_dedicatedPriority: 20
|
_dedicatedPriority: 20
|
||||||
--- !u!1 &405300252
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 405300253}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: WaypointB
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!4 &405300253
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 405300252}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
|
||||||
m_LocalPosition: {x: -13.74, y: 4.27, z: 0}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1 &406872835
|
--- !u!1 &406872835
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -18171,224 +18063,6 @@ Transform:
|
|||||||
- {fileID: 194069122}
|
- {fileID: 194069122}
|
||||||
m_Father: {fileID: 113908853}
|
m_Father: {fileID: 113908853}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &1077748568
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 1077748573}
|
|
||||||
- component: {fileID: 1077748572}
|
|
||||||
- component: {fileID: 1077748571}
|
|
||||||
- component: {fileID: 1077748570}
|
|
||||||
- component: {fileID: 1077748569}
|
|
||||||
- component: {fileID: 1077748575}
|
|
||||||
- component: {fileID: 1077748574}
|
|
||||||
m_Layer: 8
|
|
||||||
m_Name: MovingPlatform
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!114 &1077748569
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1077748568}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: ae997ac2469ff6b4cb58cf825ed67397, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
_moveType: 0
|
|
||||||
_wayPoints:
|
|
||||||
- {fileID: 1112983964}
|
|
||||||
- {fileID: 405300253}
|
|
||||||
_speed: 3
|
|
||||||
_waitAtEndpoint: 0.5
|
|
||||||
_activationChannel: {fileID: 0}
|
|
||||||
_passengerSensor: {fileID: 147475845}
|
|
||||||
_passengerLayer:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 8704
|
|
||||||
--- !u!212 &1077748570
|
|
||||||
SpriteRenderer:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1077748568}
|
|
||||||
m_Enabled: 0
|
|
||||||
m_CastShadows: 0
|
|
||||||
m_ReceiveShadows: 0
|
|
||||||
m_DynamicOccludee: 1
|
|
||||||
m_StaticShadowCaster: 0
|
|
||||||
m_MotionVectors: 1
|
|
||||||
m_LightProbeUsage: 1
|
|
||||||
m_ReflectionProbeUsage: 1
|
|
||||||
m_RayTracingMode: 0
|
|
||||||
m_RayTraceProcedural: 0
|
|
||||||
m_RenderingLayerMask: 1
|
|
||||||
m_RendererPriority: 0
|
|
||||||
m_Materials:
|
|
||||||
- {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}
|
|
||||||
m_StaticBatchInfo:
|
|
||||||
firstSubMesh: 0
|
|
||||||
subMeshCount: 0
|
|
||||||
m_StaticBatchRoot: {fileID: 0}
|
|
||||||
m_ProbeAnchor: {fileID: 0}
|
|
||||||
m_LightProbeVolumeOverride: {fileID: 0}
|
|
||||||
m_ScaleInLightmap: 1
|
|
||||||
m_ReceiveGI: 1
|
|
||||||
m_PreserveUVs: 0
|
|
||||||
m_IgnoreNormalsForChartDetection: 0
|
|
||||||
m_ImportantGI: 0
|
|
||||||
m_StitchLightmapSeams: 1
|
|
||||||
m_SelectedEditorRenderState: 0
|
|
||||||
m_MinimumChartSize: 4
|
|
||||||
m_AutoUVMaxDistance: 0.5
|
|
||||||
m_AutoUVMaxAngle: 89
|
|
||||||
m_LightmapParameters: {fileID: 0}
|
|
||||||
m_SortingLayerID: 0
|
|
||||||
m_SortingLayer: 0
|
|
||||||
m_SortingOrder: 0
|
|
||||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6, type: 3}
|
|
||||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
m_FlipX: 0
|
|
||||||
m_FlipY: 0
|
|
||||||
m_DrawMode: 0
|
|
||||||
m_Size: {x: 1, y: 1}
|
|
||||||
m_AdaptiveModeThreshold: 0.5
|
|
||||||
m_SpriteTileMode: 0
|
|
||||||
m_WasSpriteAssigned: 1
|
|
||||||
m_MaskInteraction: 0
|
|
||||||
m_SpriteSortPoint: 0
|
|
||||||
--- !u!61 &1077748571
|
|
||||||
BoxCollider2D:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1077748568}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_Density: 1
|
|
||||||
m_Material: {fileID: 0}
|
|
||||||
m_IncludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_ExcludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_LayerOverridePriority: 0
|
|
||||||
m_ForceSendLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_ForceReceiveLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_ContactCaptureLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_CallbackLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_IsTrigger: 0
|
|
||||||
m_UsedByEffector: 0
|
|
||||||
m_UsedByComposite: 0
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
m_SpriteTilingProperty:
|
|
||||||
border: {x: 0, y: 0, z: 0, w: 0}
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
oldSize: {x: 1, y: 1}
|
|
||||||
newSize: {x: 1, y: 1}
|
|
||||||
adaptiveTilingThreshold: 0.5
|
|
||||||
drawMode: 0
|
|
||||||
adaptiveTiling: 0
|
|
||||||
m_AutoTiling: 0
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Size: {x: 4, y: 0.4}
|
|
||||||
m_EdgeRadius: 0
|
|
||||||
--- !u!50 &1077748572
|
|
||||||
Rigidbody2D:
|
|
||||||
serializedVersion: 4
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1077748568}
|
|
||||||
m_BodyType: 1
|
|
||||||
m_Simulated: 1
|
|
||||||
m_UseFullKinematicContacts: 0
|
|
||||||
m_UseAutoMass: 0
|
|
||||||
m_Mass: 1
|
|
||||||
m_LinearDrag: 0
|
|
||||||
m_AngularDrag: 0.05
|
|
||||||
m_GravityScale: 1
|
|
||||||
m_Material: {fileID: 0}
|
|
||||||
m_IncludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_ExcludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_Interpolate: 1
|
|
||||||
m_SleepingMode: 1
|
|
||||||
m_CollisionDetection: 0
|
|
||||||
m_Constraints: 4
|
|
||||||
--- !u!4 &1077748573
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1077748568}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
|
||||||
m_LocalPosition: {x: -16.74, y: 4.27, z: 0}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children:
|
|
||||||
- {fileID: 147475846}
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!114 &1077748574
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1077748568}
|
|
||||||
m_Enabled: 0
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: c1483f09f23da6b469f288d63b2f52b5, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
_allowDropThrough: 1
|
|
||||||
_dropDisableDuration: 0.3
|
|
||||||
--- !u!251 &1077748575
|
|
||||||
PlatformEffector2D:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1077748568}
|
|
||||||
m_Enabled: 0
|
|
||||||
m_UseColliderMask: 1
|
|
||||||
m_ColliderMask:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_RotationalOffset: 0
|
|
||||||
m_UseOneWay: 1
|
|
||||||
m_UseOneWayGrouping: 0
|
|
||||||
m_SurfaceArc: 150.47
|
|
||||||
m_UseSideFriction: 0
|
|
||||||
m_UseSideBounce: 0
|
|
||||||
m_SideArc: 1
|
|
||||||
--- !u!1 &1078913098
|
--- !u!1 &1078913098
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -18770,37 +18444,6 @@ Transform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 1658705856}
|
m_Father: {fileID: 1658705856}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &1112983963
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 1112983964}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: WaypointA
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!4 &1112983964
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1112983963}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
|
||||||
m_LocalPosition: {x: -19.74, y: 4.27, z: 0}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1 &1113400486
|
--- !u!1 &1113400486
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -22019,7 +21662,7 @@ Transform:
|
|||||||
m_GameObject: {fileID: 1354690325}
|
m_GameObject: {fileID: 1354690325}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||||
m_LocalPosition: {x: -5.02, y: 12.9783, z: 0}
|
m_LocalPosition: {x: -26.800001, y: 7.27, z: 0}
|
||||||
m_LocalScale: {x: 1, y: 12.316664, z: 1}
|
m_LocalScale: {x: 1, y: 12.316664, z: 1}
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
@@ -30214,7 +29857,7 @@ Transform:
|
|||||||
m_GameObject: {fileID: 1865796628}
|
m_GameObject: {fileID: 1865796628}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||||
m_LocalPosition: {x: -9.4, y: 12.9783, z: 0}
|
m_LocalPosition: {x: -31.18, y: 7.27, z: 0}
|
||||||
m_LocalScale: {x: 1, y: 12.316664, z: 1}
|
m_LocalScale: {x: 1, y: 12.316664, z: 1}
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
@@ -32254,6 +31897,7 @@ MonoBehaviour:
|
|||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
_config: {fileID: 11400000, guid: 8f33c49c9bd20ac47a6867f1f19f24a9, type: 2}
|
_config: {fileID: 11400000, guid: 8f33c49c9bd20ac47a6867f1f19f24a9, type: 2}
|
||||||
_input: {fileID: 11400000, guid: 88fadef8bc554e04483edd7418d20aa2, type: 2}
|
_input: {fileID: 11400000, guid: 88fadef8bc554e04483edd7418d20aa2, type: 2}
|
||||||
|
_stats: {fileID: 0}
|
||||||
_onFormChanged: {fileID: 11400000, guid: 9e1af94c61bb1c04ebc829f3838714f4, type: 2}
|
_onFormChanged: {fileID: 11400000, guid: 9e1af94c61bb1c04ebc829f3838714f4, type: 2}
|
||||||
_onSkillSetChanged: {fileID: 11400000, guid: 249630b7ebe7a094299413cc67c4f3d1, type: 2}
|
_onSkillSetChanged: {fileID: 11400000, guid: 249630b7ebe7a094299413cc67c4f3d1, type: 2}
|
||||||
--- !u!114 &1995033583
|
--- !u!114 &1995033583
|
||||||
@@ -35358,6 +35002,3 @@ SceneRoots:
|
|||||||
- {fileID: 783576435}
|
- {fileID: 783576435}
|
||||||
- {fileID: 1813702095}
|
- {fileID: 1813702095}
|
||||||
- {fileID: 1995033591}
|
- {fileID: 1995033591}
|
||||||
- {fileID: 1077748573}
|
|
||||||
- {fileID: 1112983964}
|
|
||||||
- {fileID: 405300253}
|
|
||||||
|
|||||||
@@ -775,9 +775,13 @@ namespace BaseGames.Editor
|
|||||||
{
|
{
|
||||||
var report = new List<string>();
|
var report = new List<string>();
|
||||||
|
|
||||||
GameObject go = new GameObject("MovingPlatform");
|
// 根节点:平台实体 + 路径点都挂在此节点下,路径点不随平台本体移动
|
||||||
Undo.RegisterCreatedObjectUndo(go, "Place Moving Platform");
|
GameObject root = new GameObject("MovingPlatform_Root");
|
||||||
go.transform.position = GetDropPosition();
|
Undo.RegisterCreatedObjectUndo(root, "Place Moving Platform");
|
||||||
|
root.transform.position = GetDropPosition();
|
||||||
|
|
||||||
|
// 平台实体:作为 root 子节点
|
||||||
|
GameObject go = GetOrCreateChild(root.transform, "MovingPlatform").gameObject;
|
||||||
SetLayer(go, "Platform", report);
|
SetLayer(go, "Platform", report);
|
||||||
|
|
||||||
Rigidbody2D rb = GetOrAddComponent<Rigidbody2D>(go);
|
Rigidbody2D rb = GetOrAddComponent<Rigidbody2D>(go);
|
||||||
@@ -796,22 +800,23 @@ namespace BaseGames.Editor
|
|||||||
sensorCol.size = new Vector2(3.8f, 0.25f);
|
sensorCol.size = new Vector2(3.8f, 0.25f);
|
||||||
sensorCol.offset = new Vector2(0f, 0.33f);
|
sensorCol.offset = new Vector2(0f, 0.33f);
|
||||||
|
|
||||||
// Waypoint markers (LinearAB mode end points)
|
// 路径点:挂在 root 下而非平台下,平台移动时路径点位置不变
|
||||||
Transform wpA = GetOrCreateChild(go.transform, "WaypointA");
|
Transform wpA = GetOrCreateChild(root.transform, "WaypointA");
|
||||||
Transform wpB = GetOrCreateChild(go.transform, "WaypointB");
|
Transform wpB = GetOrCreateChild(root.transform, "WaypointB");
|
||||||
wpA.localPosition = new Vector3(-3f, 0f, 0f);
|
wpA.position = root.transform.position + new Vector3(-3f, 0f, 0f);
|
||||||
wpB.localPosition = new Vector3(3f, 0f, 0f);
|
wpB.position = root.transform.position + new Vector3( 3f, 0f, 0f);
|
||||||
|
|
||||||
MovingPlatform platform = GetOrAddComponent<MovingPlatform>(go);
|
MovingPlatform platform = GetOrAddComponent<MovingPlatform>(go);
|
||||||
AssignReference(platform, "_passengerSensor", sensorCol, report);
|
AssignReference(platform, "_passengerSensor", sensorCol, report);
|
||||||
AssignLayerMask(platform, "_passengerLayer", new[] { "Player", "Enemy" }, report);
|
AssignLayerMask(platform, "_passengerLayer", new[] { "Player", "Enemy" }, report);
|
||||||
AssignObjectArray(platform, "_wayPoints", new Object[] { wpA, wpB }, report);
|
AssignObjectArray(platform, "_wayPoints", new Object[] { wpA, wpB }, report);
|
||||||
|
|
||||||
report.Add("WaypointA / WaypointB 为移动端点,可将其拖出平台并在场景中调整位置。");
|
report.Add("WaypointA / WaypointB 已挂在 MovingPlatform_Root 下(非平台子节点),平台移动时路径点保持原位。");
|
||||||
|
report.Add("在场景中调整 WaypointA / WaypointB 的世界位置即可设置移动端点。");
|
||||||
report.Add("如需触发激活,改 _moveType = TriggeredLinear 并将 VoidEventChannelSO 拖入 _activationChannel。");
|
report.Add("如需触发激活,改 _moveType = TriggeredLinear 并将 VoidEventChannelSO 拖入 _activationChannel。");
|
||||||
|
|
||||||
Selection.activeGameObject = go;
|
Selection.activeGameObject = root;
|
||||||
MarkDirtyAndLog("Moving Platform", go, report);
|
MarkDirtyAndLog("Moving Platform", root, report);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MenuItem("BaseGames/Scene/Place/Tilemap Ground", priority = 160)]
|
[MenuItem("BaseGames/Scene/Place/Tilemap Ground", priority = 160)]
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ namespace BaseGames.Input
|
|||||||
public event Action SpiritSkill2CancelledEvent;
|
public event Action SpiritSkill2CancelledEvent;
|
||||||
public event Action SpellCastEvent;
|
public event Action SpellCastEvent;
|
||||||
public event Action InteractEvent;
|
public event Action InteractEvent;
|
||||||
|
public event Action InteractCancelledEvent;
|
||||||
|
|
||||||
// ── UI Events ─────────────────────────────────────────────────────────
|
// ── UI Events ─────────────────────────────────────────────────────────
|
||||||
public event Action PauseEvent;
|
public event Action PauseEvent;
|
||||||
@@ -178,6 +179,7 @@ namespace BaseGames.Input
|
|||||||
BindCanceled(_gameplay, "SpiritSkill2", () => SpiritSkill2CancelledEvent?.Invoke());
|
BindCanceled(_gameplay, "SpiritSkill2", () => SpiritSkill2CancelledEvent?.Invoke());
|
||||||
BindStarted(_gameplay, "Spell", () => SpellCastEvent?.Invoke());
|
BindStarted(_gameplay, "Spell", () => SpellCastEvent?.Invoke());
|
||||||
BindStarted(_gameplay, "Interact", () => InteractEvent?.Invoke());
|
BindStarted(_gameplay, "Interact", () => InteractEvent?.Invoke());
|
||||||
|
BindCanceled(_gameplay, "Interact", () => InteractCancelledEvent?.Invoke());
|
||||||
|
|
||||||
|
|
||||||
BindStarted(_gameplay, "Pause", HandlePause);
|
BindStarted(_gameplay, "Pause", HandlePause);
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace BaseGames.Player
|
|||||||
private bool _isWallRight;
|
private bool _isWallRight;
|
||||||
private bool _onOneWayPlatform;
|
private bool _onOneWayPlatform;
|
||||||
private int _facingDirection = 1;
|
private int _facingDirection = 1;
|
||||||
|
private bool _facingLocked; // 为 true 时 UpdateFacing() 不覆盖朝向
|
||||||
private bool _cancelWindowOpen;
|
private bool _cancelWindowOpen;
|
||||||
private SurfaceType _currentSurface = SurfaceType.Ground;
|
private SurfaceType _currentSurface = SurfaceType.Ground;
|
||||||
private readonly Collider2D[] _groundBuffer = new Collider2D[4];
|
private readonly Collider2D[] _groundBuffer = new Collider2D[4];
|
||||||
@@ -213,6 +214,7 @@ namespace BaseGames.Player
|
|||||||
// ── 朝向 ──────────────────────────────────────────────────────────────
|
// ── 朝向 ──────────────────────────────────────────────────────────────
|
||||||
public void UpdateFacing()
|
public void UpdateFacing()
|
||||||
{
|
{
|
||||||
|
if (_facingLocked) return;
|
||||||
// 读取玩家输入速度(不含平台分量),避免平台横向运动驱动朝向翻转。
|
// 读取玩家输入速度(不含平台分量),避免平台横向运动驱动朝向翻转。
|
||||||
float vx = _inputVelocityX;
|
float vx = _inputVelocityX;
|
||||||
if (Mathf.Abs(vx) < 0.1f) return;
|
if (Mathf.Abs(vx) < 0.1f) return;
|
||||||
@@ -247,6 +249,13 @@ namespace BaseGames.Player
|
|||||||
transform.localScale = new Vector3(dir, 1f, 1f);
|
transform.localScale = new Vector3(dir, 1f, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 锁定/解锁自动朝向(UpdateFacing)。
|
||||||
|
/// 传入 true 后 UpdateFacing 不再根据输入速度覆盖朝向,
|
||||||
|
/// 直到传入 false 解锁。适用于抓墙、蹬墙跳等需要手动控制朝向的状态。
|
||||||
|
/// </summary>
|
||||||
|
public void LockFacing(bool locked) => _facingLocked = locked;
|
||||||
|
|
||||||
// ── 取消窗口 ──────────────────────────────────────────────────────────
|
// ── 取消窗口 ──────────────────────────────────────────────────────────
|
||||||
public void SetCancelWindowOpen(bool open) => _cancelWindowOpen = open;
|
public void SetCancelWindowOpen(bool open) => _cancelWindowOpen = open;
|
||||||
|
|
||||||
@@ -318,7 +327,8 @@ namespace BaseGames.Player
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void WallJumpAway(int wallDir)
|
public void WallJumpAway(int wallDir)
|
||||||
{
|
{
|
||||||
_rb.velocity = new Vector2(-wallDir * _config.WallJumpAwayForceX, _config.WallJumpAwayForceY);
|
_inputVelocityX = -wallDir * _config.WallJumpAwayForceX;
|
||||||
|
_rb.velocity = new Vector2(_inputVelocityX, _config.WallJumpAwayForceY);
|
||||||
_coyoteTimer = 0f;
|
_coyoteTimer = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +338,8 @@ namespace BaseGames.Player
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void WallJumpToward(int wallDir)
|
public void WallJumpToward(int wallDir)
|
||||||
{
|
{
|
||||||
_rb.velocity = new Vector2(wallDir * _config.WallJumpTowardForceX, _config.WallJumpTowardForceY);
|
_inputVelocityX = wallDir * _config.WallJumpTowardForceX;
|
||||||
|
_rb.velocity = new Vector2(_inputVelocityX, _config.WallJumpTowardForceY);
|
||||||
_coyoteTimer = 0f;
|
_coyoteTimer = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,18 @@ namespace BaseGames.Player
|
|||||||
OnDamaged?.Invoke();
|
OnDamaged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制即死,无视无敌帧(危险区域、深渊等环境击杀专用)。
|
||||||
|
/// GodMode 下仍然豁免。
|
||||||
|
/// </summary>
|
||||||
|
public void Kill()
|
||||||
|
{
|
||||||
|
if (_isGodMode || !IsAlive) return;
|
||||||
|
CurrentHP = 0;
|
||||||
|
_onHPChanged?.Raise(CurrentHP);
|
||||||
|
OnDamaged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
public void FullHeal()
|
public void FullHeal()
|
||||||
{
|
{
|
||||||
if (!IsAlive) return;
|
if (!IsAlive) return;
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ namespace BaseGames.Player.States
|
|||||||
|
|
||||||
public override void OnStateEnter()
|
public override void OnStateEnter()
|
||||||
{
|
{
|
||||||
|
// 蹬墙时解锁自动朝向(由 WallSlideState.OnStateExit 已解锁,这里保险再做一次)
|
||||||
|
Move?.LockFacing(false);
|
||||||
|
|
||||||
// 施加对应类型的速度
|
// 施加对应类型的速度
|
||||||
if (_isAwayJump)
|
if (_isAwayJump)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ namespace BaseGames.Player.States
|
|||||||
_lastGrabDir = _wallDir;
|
_lastGrabDir = _wallDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 锁定自动朝向,防止 LateUpdate 的 UpdateFacing 覆盖手动设置的朝向
|
||||||
|
Move?.LockFacing(true);
|
||||||
|
// 抓墙时始终面朝墙壁,确保背墙跳的 FlipFacing(-_wallDir) 能正确翻转朝向
|
||||||
|
Move?.FlipFacing(_wallDir);
|
||||||
|
|
||||||
// 计算当前是否处于正常模式
|
// 计算当前是否处于正常模式
|
||||||
UpdateCanJump();
|
UpdateCanJump();
|
||||||
|
|
||||||
@@ -76,6 +81,7 @@ namespace BaseGames.Player.States
|
|||||||
|
|
||||||
public override void OnStateExit()
|
public override void OnStateExit()
|
||||||
{
|
{
|
||||||
|
Move?.LockFacing(false);
|
||||||
Input.JumpStartedEvent -= OnJumpPressed;
|
Input.JumpStartedEvent -= OnJumpPressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +104,25 @@ namespace BaseGames.Player.States
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 抓墙攻击:优先于方向键脱离检测,朝离墙方向翻转后进入空中攻击(无土狼时间)──
|
||||||
|
if (Buffer.ConsumeAttack())
|
||||||
|
{
|
||||||
|
Move.FlipFacing(-_wallDir);
|
||||||
|
Owner.TransitionTo(Owner.GetState<AirAttackState>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 抓墙冲刺:优先于方向键脱离检测,朝离墙方向翻转后冲出(无土狼时间)──────────
|
||||||
|
var ds = Owner.GetState<DashState>();
|
||||||
|
if (ds != null && ds.CanDashMidAir
|
||||||
|
&& Stats != null && Stats.HasAbility(AbilityType.Dash)
|
||||||
|
&& Buffer.ConsumeDash())
|
||||||
|
{
|
||||||
|
Move.FlipFacing(-_wallDir);
|
||||||
|
Owner.TransitionTo(ds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 按下方向键 → 启动墙壁土狼时间后主动脱离,自然下落
|
// 按下方向键 → 启动墙壁土狼时间后主动脱离,自然下落
|
||||||
if (Input.MoveInput.y < -0.5f)
|
if (Input.MoveInput.y < -0.5f)
|
||||||
{
|
{
|
||||||
@@ -107,7 +132,6 @@ namespace BaseGames.Player.States
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 按反方向键 → 启动墙壁土狼时间后脱离
|
// 按反方向键 → 启动墙壁土狼时间后脱离
|
||||||
// wall coyote 存在时,离墙后短窗口内仍可触发蹬墙跳,不会误双跳
|
|
||||||
float mx = Input.MoveInput.x;
|
float mx = Input.MoveInput.x;
|
||||||
if (Mathf.Abs(mx) > 0.1f && (mx > 0f ? 1 : -1) != _wallDir)
|
if (Mathf.Abs(mx) > 0.1f && (mx > 0f ? 1 : -1) != _wallDir)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ namespace BaseGames.World
|
|||||||
[SerializeField] private SceneFeedback _crumbleFeedback; // 预警震动 + 碎裂粒子 + 音效
|
[SerializeField] private SceneFeedback _crumbleFeedback; // 预警震动 + 碎裂粒子 + 音效
|
||||||
[SerializeField] private BoxCollider2D _passengerSensor; // IsTrigger,检测玩家踩踏
|
[SerializeField] private BoxCollider2D _passengerSensor; // IsTrigger,检测玩家踩踏
|
||||||
|
|
||||||
|
[Header("持久化(_isOneShot = true 时生效)")]
|
||||||
|
[Tooltip("平台唯一 ID。_isOneShot=true 时碎裂状态写入 WorldStateRegistry,重载场景后不复原。留空则不持久化。")]
|
||||||
|
[SerializeField] private string _platformId;
|
||||||
|
[SerializeField] private WorldStateRegistry _worldState;
|
||||||
|
|
||||||
private BoxCollider2D _col;
|
private BoxCollider2D _col;
|
||||||
private SpriteRenderer _sr;
|
private SpriteRenderer _sr;
|
||||||
private bool _isCrumbling;
|
private bool _isCrumbling;
|
||||||
@@ -28,6 +33,18 @@ namespace BaseGames.World
|
|||||||
_sr = GetComponent<SpriteRenderer>();
|
_sr = GetComponent<SpriteRenderer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
// 读档恢复:_isOneShot 平台已碎裂则直接禁用,无需等待触发
|
||||||
|
if (!_isOneShot || string.IsNullOrEmpty(_platformId) || _worldState == null) return;
|
||||||
|
if (!_worldState.HasFlag("crumble_" + _platformId)) return;
|
||||||
|
|
||||||
|
_isCrumbling = true;
|
||||||
|
_col.enabled = false;
|
||||||
|
_sr.enabled = false;
|
||||||
|
if (_passengerSensor != null) _passengerSensor.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnTriggerEnter2D(Collider2D other)
|
private void OnTriggerEnter2D(Collider2D other)
|
||||||
{
|
{
|
||||||
if (_isCrumbling) return;
|
if (_isCrumbling) return;
|
||||||
@@ -53,7 +70,12 @@ namespace BaseGames.World
|
|||||||
_passengerSensor.enabled = false;
|
_passengerSensor.enabled = false;
|
||||||
|
|
||||||
if (_isOneShot || _respawnDelay <= 0f)
|
if (_isOneShot || _respawnDelay <= 0f)
|
||||||
yield break; // 永久消失
|
{
|
||||||
|
// 持久化一次性碎裂状态,场景重载后不复原
|
||||||
|
if (_isOneShot && !string.IsNullOrEmpty(_platformId))
|
||||||
|
_worldState?.SetFlag("crumble_" + _platformId);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Respawn
|
// 4. Respawn
|
||||||
yield return new WaitForSeconds(_respawnDelay);
|
yield return new WaitForSeconds(_respawnDelay);
|
||||||
|
|||||||
@@ -51,10 +51,15 @@ namespace BaseGames.World
|
|||||||
|
|
||||||
// ── Physics Triggers ──────────────────────────────────────────────────
|
// ── Physics Triggers ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断碰撞体是否为有效触发来源。子类可覆写以扩展触发主体(如幻影身体)。
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool IsValidTriggerBody(Collider2D col) => col.CompareTag("Player");
|
||||||
|
|
||||||
private void OnTriggerEnter2D(Collider2D other)
|
private void OnTriggerEnter2D(Collider2D other)
|
||||||
{
|
{
|
||||||
if (_triggerCondition != TriggerCondition.PlayerBody) return;
|
if (_triggerCondition != TriggerCondition.PlayerBody) return;
|
||||||
if (!other.CompareTag("Player")) return;
|
if (!IsValidTriggerBody(other)) return;
|
||||||
if (!CheckSide(other.transform.position)) return;
|
if (!CheckSide(other.transform.position)) return;
|
||||||
TryActivate();
|
TryActivate();
|
||||||
}
|
}
|
||||||
@@ -62,7 +67,7 @@ namespace BaseGames.World
|
|||||||
private void OnTriggerExit2D(Collider2D other)
|
private void OnTriggerExit2D(Collider2D other)
|
||||||
{
|
{
|
||||||
if (_triggerCondition != TriggerCondition.PlayerBody) return;
|
if (_triggerCondition != TriggerCondition.PlayerBody) return;
|
||||||
if (!other.CompareTag("Player") || _isOneShot) return;
|
if (!IsValidTriggerBody(other) || _isOneShot) return;
|
||||||
_activated = false;
|
_activated = false;
|
||||||
_deactivationChannel?.Raise();
|
_deactivationChannel?.Raise();
|
||||||
}
|
}
|
||||||
@@ -106,13 +111,14 @@ namespace BaseGames.World
|
|||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
// 读档恢复:若机关已激活则静默还原
|
// 读档恢复:仅标记 _activated = true,不广播激活事件。
|
||||||
|
// 下游组件(PuzzleReceiver / 动画门等)已通过各自的 WorldStateRegistry 检查在 Start 中自行恢复,
|
||||||
|
// 重复广播会导致它们再次播放开门动画等副作用。
|
||||||
if (_isOneShot && !string.IsNullOrEmpty(_interactableId)
|
if (_isOneShot && !string.IsNullOrEmpty(_interactableId)
|
||||||
&& _worldState != null
|
&& _worldState != null
|
||||||
&& _worldState.HasFlag("mechanism_" + _interactableId))
|
&& _worldState.HasFlag("mechanism_" + _interactableId))
|
||||||
{
|
{
|
||||||
_activated = true;
|
_activated = true;
|
||||||
_activationChannel?.Raise();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,35 +4,66 @@ using UnityEngine;
|
|||||||
namespace BaseGames.World
|
namespace BaseGames.World
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 危险区域。玩家进入时触发即死或持续伤害。
|
/// 危险区域。玩家进入时触发即死(调用 Kill(),无视无敌帧)或持续伤害。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RequireComponent(typeof(Collider2D))]
|
[RequireComponent(typeof(Collider2D))]
|
||||||
public class HazardZone : MonoBehaviour
|
public class HazardZone : MonoBehaviour
|
||||||
{
|
{
|
||||||
[SerializeField] private bool _isInstantKill = true;
|
[SerializeField] private bool _isInstantKill = true;
|
||||||
[SerializeField] private int _damage = 9999;
|
[SerializeField] private int _damage = 1;
|
||||||
|
[Tooltip("持续伤害模式下每次造成伤害的间隔(秒)。仅 _isInstantKill = false 时生效。")]
|
||||||
|
[SerializeField] private float _damageInterval = 0.5f;
|
||||||
|
|
||||||
|
private PlayerStats _cachedStats;
|
||||||
|
private float _damageTimer;
|
||||||
|
private bool _playerInside;
|
||||||
|
|
||||||
private void OnTriggerEnter2D(Collider2D other)
|
private void OnTriggerEnter2D(Collider2D other)
|
||||||
{
|
{
|
||||||
if (!other.CompareTag("Player")) return;
|
if (!other.CompareTag("Player")) return;
|
||||||
|
_cachedStats = other.GetComponentInParent<PlayerStats>();
|
||||||
|
if (_cachedStats == null) return;
|
||||||
|
|
||||||
var stats = other.GetComponentInParent<PlayerStats>();
|
_playerInside = true;
|
||||||
if (stats == null) return;
|
ApplyDamage();
|
||||||
|
_damageTimer = _damageInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTriggerExit2D(Collider2D other)
|
||||||
|
{
|
||||||
|
if (!other.CompareTag("Player")) return;
|
||||||
|
_playerInside = false;
|
||||||
|
_cachedStats = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (!_playerInside || _isInstantKill || _cachedStats == null) return;
|
||||||
|
|
||||||
|
_damageTimer -= Time.deltaTime;
|
||||||
|
if (_damageTimer <= 0f)
|
||||||
|
{
|
||||||
|
ApplyDamage();
|
||||||
|
_damageTimer = _damageInterval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyDamage()
|
||||||
|
{
|
||||||
|
if (_cachedStats == null) return;
|
||||||
if (_isInstantKill)
|
if (_isInstantKill)
|
||||||
stats.TakeDamage(stats.MaxHP * 2); // 确保即死(超过最大血量)
|
_cachedStats.Kill();
|
||||||
else
|
else
|
||||||
stats.TakeDamage(_damage);
|
_cachedStats.TakeDamage(_damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDrawGizmos()
|
private void OnDrawGizmos()
|
||||||
{
|
{
|
||||||
Gizmos.color = new Color(1f, 0f, 0f, 0.3f);
|
|
||||||
var col = GetComponent<Collider2D>();
|
var col = GetComponent<Collider2D>();
|
||||||
if (col != null)
|
if (col == null) return;
|
||||||
|
Gizmos.color = new Color(1f, 0f, 0f, 0.3f);
|
||||||
Gizmos.DrawCube(transform.position, col.bounds.size);
|
Gizmos.DrawCube(transform.position, col.bounds.size);
|
||||||
Gizmos.color = new Color(1f, 0f, 0f, 0.8f);
|
Gizmos.color = new Color(1f, 0f, 0f, 0.8f);
|
||||||
if (col != null)
|
|
||||||
Gizmos.DrawWireCube(transform.position, col.bounds.size);
|
Gizmos.DrawWireCube(transform.position, col.bounds.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using BaseGames.Core.Events;
|
using BaseGames.Core.Events;
|
||||||
using BaseGames.Input;
|
using BaseGames.Input;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -22,8 +23,19 @@ namespace BaseGames.World
|
|||||||
// 预分配检测缓冲区,避免 OverlapCircleAll 每帧 GC 分配
|
// 预分配检测缓冲区,避免 OverlapCircleAll 每帧 GC 分配
|
||||||
private readonly Collider2D[] _overlapBuffer = new Collider2D[16];
|
private readonly Collider2D[] _overlapBuffer = new Collider2D[16];
|
||||||
|
|
||||||
private void OnEnable() => _inputReader.InteractEvent += TryInteract;
|
// Collider → IInteractable 缓存,避免 FindNearest 每帧重复 GetComponentInParent
|
||||||
private void OnDisable() => _inputReader.InteractEvent -= TryInteract;
|
private readonly Dictionary<Collider2D, IInteractable> _componentCache = new();
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
_inputReader.InteractEvent += TryInteract;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
_inputReader.InteractEvent -= TryInteract;
|
||||||
|
_componentCache.Clear(); // 清理缓存,防止跨场景持有旧引用
|
||||||
|
}
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
@@ -58,18 +70,27 @@ namespace BaseGames.World
|
|||||||
private IInteractable FindNearest(Collider2D[] hits, int count)
|
private IInteractable FindNearest(Collider2D[] hits, int count)
|
||||||
{
|
{
|
||||||
IInteractable best = null;
|
IInteractable best = null;
|
||||||
float bestDist = float.MaxValue;
|
float bestSqrDist = float.MaxValue;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var col = hits[i];
|
var col = hits[i];
|
||||||
var interactable = col.GetComponentInParent<IInteractable>();
|
if (col == null) continue;
|
||||||
|
|
||||||
|
// 查缓存,未命中时才调用 GetComponentInParent(避免每帧反射开销)
|
||||||
|
if (!_componentCache.TryGetValue(col, out var interactable))
|
||||||
|
{
|
||||||
|
interactable = col.GetComponentInParent<IInteractable>();
|
||||||
|
_componentCache[col] = interactable;
|
||||||
|
}
|
||||||
|
|
||||||
if (interactable == null || !interactable.CanInteract) continue;
|
if (interactable == null || !interactable.CanInteract) continue;
|
||||||
|
|
||||||
float dist = Vector2.Distance(transform.position, col.transform.position);
|
// 用 sqrMagnitude 比较距离,省去 Distance 的 sqrt 开销
|
||||||
if (dist < bestDist)
|
float sqrDist = ((Vector2)transform.position - (Vector2)col.transform.position).sqrMagnitude;
|
||||||
|
if (sqrDist < bestSqrDist)
|
||||||
{
|
{
|
||||||
bestDist = dist;
|
bestSqrDist = sqrDist;
|
||||||
best = interactable;
|
best = interactable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ namespace BaseGames.World
|
|||||||
|
|
||||||
if (_moveType == MoveType.LinearAB)
|
if (_moveType == MoveType.LinearAB)
|
||||||
{
|
{
|
||||||
|
if (_wayPoints.Length < 2) return; // 至少需要两个路径点
|
||||||
_movingForward = !_movingForward;
|
_movingForward = !_movingForward;
|
||||||
_waypointIndex = _movingForward ? 1 : 0;
|
_waypointIndex = _movingForward ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace BaseGames.World
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 幻影可交互机关。继承 DirectionalInteractable,额外响应 PhantomBody 层(太虚斩形态)。
|
/// 幻影可交互机关。继承 DirectionalInteractable,额外响应 PhantomBody 层(太虚斩形态)。
|
||||||
|
/// 通过覆写 IsValidTriggerBody 扩展触发主体,_triggerCondition 条件检查由父类统一处理。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PhantomInteractable : DirectionalInteractable
|
public class PhantomInteractable : DirectionalInteractable
|
||||||
{
|
{
|
||||||
@@ -11,13 +12,7 @@ namespace BaseGames.World
|
|||||||
|
|
||||||
private void Awake() => _phantomBodyLayer = LayerMask.NameToLayer("PhantomBody");
|
private void Awake() => _phantomBodyLayer = LayerMask.NameToLayer("PhantomBody");
|
||||||
|
|
||||||
private void OnTriggerEnter2D(Collider2D other)
|
protected override bool IsValidTriggerBody(Collider2D col)
|
||||||
{
|
=> col.CompareTag("Player") || col.gameObject.layer == _phantomBodyLayer;
|
||||||
bool isPlayer = other.CompareTag("Player");
|
|
||||||
bool isPhantom = other.gameObject.layer == _phantomBodyLayer;
|
|
||||||
|
|
||||||
if (!isPlayer && !isPhantom) return;
|
|
||||||
TryActivate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Animancer;
|
using Animancer;
|
||||||
using BaseGames.Feedback;
|
using BaseGames.Feedback;
|
||||||
|
using BaseGames.Input;
|
||||||
using BaseGames.World;
|
using BaseGames.World;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -38,6 +39,9 @@ namespace BaseGames.Puzzle
|
|||||||
[Header("持久化(SO 注入,非 Instance 单例)")]
|
[Header("持久化(SO 注入,非 Instance 单例)")]
|
||||||
[SerializeField] private WorldStateRegistry _worldState;
|
[SerializeField] private WorldStateRegistry _worldState;
|
||||||
|
|
||||||
|
[Header("Hold 模式输入(SwitchTriggerMode.Hold 时必填)")]
|
||||||
|
[SerializeField] private InputReaderSO _inputReader;
|
||||||
|
|
||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
|
|
||||||
public bool IsActive => _isActive;
|
public bool IsActive => _isActive;
|
||||||
@@ -59,16 +63,41 @@ namespace BaseGames.Puzzle
|
|||||||
|
|
||||||
// ── IInteractable ────────────────────────────────────────────────────
|
// ── IInteractable ────────────────────────────────────────────────────
|
||||||
public string InteractPrompt => _mode == SwitchTriggerMode.Hold ? "按住交互" : "交互";
|
public string InteractPrompt => _mode == SwitchTriggerMode.Hold ? "按住交互" : "交互";
|
||||||
public bool CanInteract => true;
|
|
||||||
|
/// <summary>
|
||||||
|
/// 压板模式不需要交互提示(物理触发);InteractOnce 已激活后隐藏提示。
|
||||||
|
/// </summary>
|
||||||
|
public bool CanInteract => _mode switch
|
||||||
|
{
|
||||||
|
SwitchTriggerMode.InteractOnce => !_isActive,
|
||||||
|
SwitchTriggerMode.InteractToggle => true,
|
||||||
|
SwitchTriggerMode.Hold => true,
|
||||||
|
SwitchTriggerMode.Pressure => false,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
public void Interact(Transform player)
|
public void Interact(Transform player)
|
||||||
{
|
{
|
||||||
|
// Hold 模式通过 OnPlayerEnterRange 订阅输入事件处理,此处不响应
|
||||||
|
if (_mode == SwitchTriggerMode.Hold) return;
|
||||||
if (_mode == SwitchTriggerMode.InteractOnce && _isActive) return;
|
if (_mode == SwitchTriggerMode.InteractOnce && _isActive) return;
|
||||||
SetState(!_isActive);
|
SetState(!_isActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnPlayerEnterRange(Transform player) { }
|
public void OnPlayerEnterRange(Transform player)
|
||||||
public void OnPlayerExitRange() { }
|
{
|
||||||
|
if (_mode != SwitchTriggerMode.Hold || _inputReader == null) return;
|
||||||
|
_inputReader.InteractEvent += OnHoldStarted;
|
||||||
|
_inputReader.InteractCancelledEvent += OnHoldCancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPlayerExitRange()
|
||||||
|
{
|
||||||
|
UnsubscribeHold();
|
||||||
|
// 玩家离开范围时停用 Hold 开关
|
||||||
|
if (_mode == SwitchTriggerMode.Hold && _isActive)
|
||||||
|
SetState(false);
|
||||||
|
}
|
||||||
|
|
||||||
// ── ISwitchable ──────────────────────────────────────────────────────
|
// ── ISwitchable ──────────────────────────────────────────────────────
|
||||||
public void ForceState(bool active) => SetState(active);
|
public void ForceState(bool active) => SetState(active);
|
||||||
@@ -106,5 +135,19 @@ namespace BaseGames.Puzzle
|
|||||||
else _worldState?.ClearFlag("switch_" + _switchId);
|
else _worldState?.ClearFlag("switch_" + _switchId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Hold 模式辅助 ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void OnHoldStarted() => SetState(true);
|
||||||
|
private void OnHoldCancelled() => SetState(false);
|
||||||
|
|
||||||
|
private void UnsubscribeHold()
|
||||||
|
{
|
||||||
|
if (_inputReader == null) return;
|
||||||
|
_inputReader.InteractEvent -= OnHoldStarted;
|
||||||
|
_inputReader.InteractCancelledEvent -= OnHoldCancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy() => UnsubscribeHold();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user