Compare commits

..
14 Commits
Author SHA1 Message Date
joywayer c7dd125bc0 添加资源 2026-06-12 12:05:30 +08:00
joywayerandClaude Fable 5 f7eb2d4368 fix(editor): WeaponHitBoxWizard 补挂 WeaponFeedback——武器本地反馈链路接通
向导生成武器 HitBox Prefab 时在根节点添加 WeaponFeedback
(WeaponHitBoxInstance.Awake 经 GetComponentInChildren 获取)。
此前组件缺失,武器级命中/破风反馈静默落到 NullFeedbackPlayer。
MMF_Player 反馈链内容仍留待 Inspector 编排。存量
WPN_WPN_DiHun_HitBox.prefab 已另行补挂修正。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:59:35 +08:00
joywayerandClaude Fable 5 09bdda970a feat(feedback): 命中/受击反馈支持固定位置与击中位置两种生成模式
DamageInfo 新增运行时字段 HitPoint:HitBox 结算时写入精确接触点
(ClosestPoint),HurtBox.ReceiveDamage 以解析后命中点兜底盖戳,
陷阱/环境等直调路径同样可用。IFeedbackPlayer.PlayHit/PlayTakeHit
增加命中点参数,全链路(武器实例→PlayerCombat→HurtState→
EnemyFeedback)透传。各 Feedback 组件命中/受击槽位新增
FeedbackPositionMode(Fixed=反馈链编排的固定位置 /
HitPoint=传入命中点,启用位置选项的模块在该点生成),由编排
反馈链的开发者在 Inspector 按表现选择;武器命中默认 HitPoint
(火花贴点),角色级默认 Fixed(震屏/振动与位置无关)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:48:37 +08:00
joywayerandClaude Fable 5 5ccab35948 feat(world): 检查点复活位置由 RespawnPoint 子节点显式配置
CheckpointMarker 新增 _respawnPoint 引用,注册的检查点位置改为
该子节点的位置——触发区与出生点解耦,触发区可横跨通道、出生点
精确落在安全平台上。漏配时 Awake 显式报错且不注册(不以标记
自身位置兜底)。Gizmos 增画复活点(青色)与触发区连线,漏配
画红叉警示。PlaceCheckpointMarker 脚手架自动创建并绑定
RespawnPoint 子节点。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:26:02 +08:00
joywayerandClaude Fable 5 bedb0f8ff4 fix(combat): 地刺死亡判定改从 HurtBox 向上查找——死亡不再误传回检查点
此前经 other.transform.root 查 IDamageable,玩家被临时挂到其他
父物体(如移动平台)下时 root 非玩家根节点而漏判死亡,导致致死
触碰仍 Raise EVT_CheckpointRespawn 与死亡流程撞车。改为
other.GetComponentInParent 从 HurtBox 自身向上查找,任何层级
结构下死亡均只走死亡流程,存活才回溯检查点。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:20:10 +08:00
joywayerandClaude Fable 5 01787e9c34 fix(world): 检查点标记闩锁随接触周期重置——折返重经标记可覆盖注册
CheckpointMarker._isActivated 此前一次性置位,玩家折返重新经过
早先的标记不会再注册,"最近经过的为准"对回头路失效。改为
OnTriggerExit2D 重置闩锁:每次重新进入触发区都覆盖注册最近
检查点,同一次接触内仍只注册/广播一次。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:16:08 +08:00
joywayerandClaude Fable 5 613f93c13e fix(combat): 地刺闩锁随接触周期重置——检查点回溯后陷阱恢复生效
LethalTrap._triggered 此前仅在 OnEnable 重置,存活回检查点路径
不重载场景,首次触发后陷阱永久失效。改为 OnTriggerExit2D(玩家
HurtBox 离开触发区,含传送移位)重置闩锁:同一次接触内仍只结算
一次,下一次触碰重新生效;死亡路径仍经场景重载由 OnEnable 重置。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:10:51 +08:00
joywayerandClaude Fable 5 64822595b3 fix(player): 补挂 CheckpointRespawnHandler——存活回检查点链路接通
PlacePlayer 脚手架新增 CheckpointRespawnHandler 并自动绑定
EVT_CheckpointRespawn/EVT_FadeOutRequest/EVT_FadeInRequest;
Player.prefab 同步补挂修正。此前 LethalTrap 存活路径 Raise 的
EVT_CheckpointRespawn 无任何监听者,玩家不会被传送回检查点。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:04:33 +08:00
joywayerandClaude Opus 4.8 862a1e5899 fix(combat): 弹反阵营感知——仅玩家弹反才翻转投射物阵营与目标层
新增 Projectile.ReflectBy(parrier):按弹反者根节点 Tag 区分阵营。玩家弹反走原 ReflectAsPlayerProjectile(切 PlayerProjectile 层+切换伤害目标层);敌人弹反敌人投射物时阵营层与目标层均保持不变(仍是敌方投射物、仍打玩家侧),仅反转方向并重置命中记录与预算。HurtBox 弹反分支改传弹反者 Transform;ParryableProjectile 手写弹反分支同步加阵营判断。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:49:23 +08:00
joywayerandClaude Opus 4.8 5922ef373d feat(combat): 弹反投射物伤害目标层改为显式配置
ProjectileConfigSO 新增 ReflectedTargetLayers:弹反后写入 HitBox.TargetLayers 的目标层显式配置;留空(Nothing)有明确缺省语义=自动翻转(PlayerHurtBox 位换 EnemyHurtBox 位、其余位保留)。Projectile/ParryableProjectile 两条弹反路径统一走 ApplyReflectedTargetLayers。现有 6 个投射物配置资产已显式配为 EnemyHurtBox。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:43:57 +08:00
joywayerandClaude Opus 4.8 1866f323e4 feat(combat): 投射物接入伤害目标层过滤,弹反阵营/目标同步翻转
HitBox 暴露 TargetLayers 运行时读写。Projectile 缓存预制体初始目标层并在 Initialize 还原(对象池复用不被上一发弹反污染);ReflectAsPlayerProjectile 时目标层随阵营翻转(PlayerHurtBox→EnemyHurtBox,保留可破坏物等其他位)。

ParryableProjectile 绕过 HitBox 自行判定,补上同样的目标层过滤;并修复其弹反分支不切 PlayerProjectile 层的问题——原先反射后仍留在 EnemyProjectile 层,碰撞矩阵 EnemyProjectile↔EnemyHurtBox 不碰撞,反射弹永远打不中敌人。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:34:43 +08:00
joywayerandClaude Opus 4.8 ef7ff63b67 feat(combat): HitBox 新增 _targetLayers 实例级伤害目标过滤
Physics2D 碰撞矩阵只决定哪些层之间产生 Trigger 事件(全局门),无法表达单个判定盒的伤害目标差异。新增 _targetLayers 掩码:事件发生后仅对配置层结算伤害,拼刀检测独立于该过滤;Enter 入口同步过滤,非目标不进入命中/占用跟踪。默认 Everything,存量资产行为不变。

ENM_CaoZhi 接触伤害盒按规范配置 _targetLayers=PlayerHurtBox(不误伤友军),并清掉误配为 Player 层的 _rivalHitBoxMask(身体接触不参与拼刀)。已实机验证收紧后接触伤害正常。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:15:27 +08:00
joywayerandClaude Opus 4.8 8515dfa7ab feat(combat): 下劈弹跳完善——可破坏物可弹跳+独立弹跳力+下劈保留空中操控
- HitBox 新增 OnBreakableHitConfirmed:命中 IBreakable 也发命中确认,
  下劈打可破坏物同样弹跳;不走 OnHitConfirmed,灵力仍只来自敌人
- 新增 PogoBounceForce 配置(默认 15,略低于 JumpForce=18)+
  PlayerMovement.PogoBounce(),弹跳高度独立可调、固定不可截断
- DownAttackState 增加 OnStateFixedUpdate:下劈期间保留完整空中
  水平操控(含与 FallState 同款贴墙保护)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 09:51:17 +08:00
joywayerandClaude Opus 4.8 3bafc4cbaa fix(combat): 下劈去除向下俯冲位移,回归纯向下攻击+命中弹跳
下劈原在 OnStateEnter 强加 -18 向下速度,使手感变成下冲/俯冲。
删除该俯冲位移,角色保持自然下落、纯向下挥击;命中弹跳与
重置空中跳跃/冲刺次数逻辑(OnDownHitConfirmed)保持不变。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 02:40:00 +08:00
44 changed files with 1237 additions and 81 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"enabledPlugins": {
"superpowers@claude-plugins-official": true,
"ralph-loop@claude-plugins-official": true
}
}
+8 -3
View File
@@ -1,11 +1,16 @@
{
"permissions": {
"dangerouslySkipPermissions": true,
"allow": [
"Read(*)",
"Write(*)",
"Bash(*)",
"mcp__unity-mcp__*"
]
"mcp__unity-mcp__*",
"PowerShell(Select-String -Path \"E:\\\\Works\\\\Unity\\\\Projects\\\\zeling_v2\\\\Assets\\\\_Game\\\\Prefabs\\\\Player\\\\Player.prefab\" -Pattern \"7ca41f67644b6b843ba7ef65e78b13e5\" -Context 2,8 | ForEach-Object { $_.Context.PreContext + $_.Line + $_.Context.PostContext })",
"PowerShell(foreach \\($f in @\\(\"Combat\\\\EVT_CheckpointRespawn\",\"Core\\\\EVT_FadeOutRequest\",\"Core\\\\EVT_FadeInRequest\"\\)\\) { $g = \\(Get-Content \"E:\\\\Works\\\\Unity\\\\Projects\\\\zeling_v2\\\\Assets\\\\_Game\\\\Data\\\\Events\\\\$f.asset.meta\" | Select-String 'guid: \\(\\\\w+\\)'\\).Matches[0].Groups[1].Value; \"$f -> $g\" })",
"PowerShell(Select-String *)",
"PowerShell(rtk git *)",
"PowerShell(Select-String -Path \"E:\\\\Works\\\\Unity\\\\Projects\\\\zeling_v2\\\\Assets\\\\_Game\\\\Prefabs\\\\Weapons\\\\WPN_WPN_DiHun_HitBox.prefab\" -Pattern \"adb4af2f574f356449634bc130f94592\" -Context 0,7 | ForEach-Object { $_.Line; $_.Context.PostContext })"
],
"dangerouslySkipPermissions": true
}
}
Submodule .claude/skills/planning-with-files added at 5f37e662b0
@@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96b10c11e6173394a8fa8d9c614b0035, type: 3}
m_Name: CMB_Player_ATK_Air1
m_EditorClassIdentifier:
sourceId:
skillId:
BaseDamage: 10
DamageMultiplier: 1
Type: 0
Category: 0
Flags: 2
Tags: 1
KnockbackForce: 5
HitStunDuration: 0.1
BreakLevel: 1
FxType: 1
ComboWindowDuration: 0.4
CancelWindowEnd: 0.5
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 88812fa8d3e028a41b8a20f2ca90c9fa
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96b10c11e6173394a8fa8d9c614b0035, type: 3}
m_Name: CMB_Player_ATK_Air2
m_EditorClassIdentifier:
sourceId:
skillId:
BaseDamage: 10
DamageMultiplier: 1
Type: 0
Category: 0
Flags: 2
Tags: 1
KnockbackForce: 5
HitStunDuration: 0.1
BreakLevel: 1
FxType: 1
ComboWindowDuration: 0.4
CancelWindowEnd: 0.5
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 967b72666e8bb2f498e4f7a03719e7c9
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96b10c11e6173394a8fa8d9c614b0035, type: 3}
m_Name: CMB_Player_ATK_Down
m_EditorClassIdentifier:
sourceId:
skillId:
BaseDamage: 10
DamageMultiplier: 1
Type: 0
Category: 0
Flags: 2
Tags: 1
KnockbackForce: 5
HitStunDuration: 0.1
BreakLevel: 1
FxType: 1
ComboWindowDuration: 0.4
CancelWindowEnd: 0.5
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6c81ccb5ed489a444b02709ec4506c5e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96b10c11e6173394a8fa8d9c614b0035, type: 3}
m_Name: CMB_Player_ATK_Up
m_EditorClassIdentifier:
sourceId:
skillId:
BaseDamage: 10
DamageMultiplier: 1
Type: 0
Category: 0
Flags: 2
Tags: 1
KnockbackForce: 5
HitStunDuration: 0.1
BreakLevel: 1
FxType: 1
ComboWindowDuration: 0.4
CancelWindowEnd: 0.5
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ef2a60dea66f7e44c8659264b94a9c38
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -13,6 +13,7 @@ MonoBehaviour:
m_Name: PROJ_Boomerang_Config
m_EditorClassIdentifier:
DamageSource: {fileID: 11400000, guid: caae9c7600281fe4e8d8637fa3fd2ca1, type: 2}
MaxHits: 1
Speed: 10
Lifetime: 6
LaunchAngleDeg: 0
@@ -21,3 +22,6 @@ MonoBehaviour:
PoolKey: PROJ_Boomerang
ParrySpeedMultiplier: 1.2
ParryDamageMultiplier: 2
ReflectedTargetLayers:
serializedVersion: 2
m_Bits: 134217728
@@ -13,6 +13,7 @@ MonoBehaviour:
m_Name: PROJ_FeiZhi_Acid_Config
m_EditorClassIdentifier:
DamageSource: {fileID: 11400000, guid: caae9c7600281fe4e8d8637fa3fd2ca1, type: 2}
MaxHits: 1
Speed: 9
Lifetime: 4
LaunchAngleDeg: 45
@@ -21,3 +22,6 @@ MonoBehaviour:
PoolKey: PROJ_FeiZhi_Acid
ParrySpeedMultiplier: 1.2
ParryDamageMultiplier: 2
ReflectedTargetLayers:
serializedVersion: 2
m_Bits: 134217728
@@ -13,6 +13,7 @@ MonoBehaviour:
m_Name: PROJ_TornadoLarge_Config
m_EditorClassIdentifier:
DamageSource: {fileID: 11400000, guid: caae9c7600281fe4e8d8637fa3fd2ca1, type: 2}
MaxHits: 1
Speed: 0
Lifetime: 5
LaunchAngleDeg: 0
@@ -21,3 +22,6 @@ MonoBehaviour:
PoolKey: PROJ_TornadoLarge
ParrySpeedMultiplier: 1.2
ParryDamageMultiplier: 2
ReflectedTargetLayers:
serializedVersion: 2
m_Bits: 134217728
@@ -13,6 +13,7 @@ MonoBehaviour:
m_Name: PROJ_TornadoSmall_Config
m_EditorClassIdentifier:
DamageSource: {fileID: 11400000, guid: caae9c7600281fe4e8d8637fa3fd2ca1, type: 2}
MaxHits: 1
Speed: 7
Lifetime: 4
LaunchAngleDeg: 0
@@ -21,3 +22,6 @@ MonoBehaviour:
PoolKey: PROJ_TornadoSmall
ParrySpeedMultiplier: 1.2
ParryDamageMultiplier: 2
ReflectedTargetLayers:
serializedVersion: 2
m_Bits: 134217728
@@ -13,6 +13,7 @@ MonoBehaviour:
m_Name: PROJ_WindStone_Config
m_EditorClassIdentifier:
DamageSource: {fileID: 11400000, guid: caae9c7600281fe4e8d8637fa3fd2ca1, type: 2}
MaxHits: 1
Speed: 6
Lifetime: 4
LaunchAngleDeg: -90
@@ -21,3 +22,6 @@ MonoBehaviour:
PoolKey: PROJ_WindStone
ParrySpeedMultiplier: 1.2
ParryDamageMultiplier: 2
ReflectedTargetLayers:
serializedVersion: 2
m_Bits: 134217728
@@ -13,6 +13,7 @@ MonoBehaviour:
m_Name: PROJ_ZhiMu_Acid_Config
m_EditorClassIdentifier:
DamageSource: {fileID: 11400000, guid: caae9c7600281fe4e8d8637fa3fd2ca1, type: 2}
MaxHits: 1
Speed: 9
Lifetime: 4
LaunchAngleDeg: 45
@@ -21,3 +22,6 @@ MonoBehaviour:
PoolKey: PROJ_ZhiMu_Acid
ParrySpeedMultiplier: 1.2
ParryDamageMultiplier: 2
ReflectedTargetLayers:
serializedVersion: 2
m_Bits: 134217728
@@ -63,7 +63,7 @@ MonoBehaviour:
_Names: []
_Clip: {fileID: 7400000, guid: bde6a54ec8fc04f45a424f9bcd7845df, type: 2}
_NormalizedStartTime: NaN
damageSource: {fileID: 0}
damageSource: {fileID: 11400000, guid: 88812fa8d3e028a41b8a20f2ca90c9fa, type: 2}
hitBoxEnter: 0.1
hitBoxExit: 0.8
comboInputOpen: 0
@@ -81,7 +81,7 @@ MonoBehaviour:
_Names: []
_Clip: {fileID: 7400000, guid: c0ef319f521c2a2448022cebcf9c9266, type: 2}
_NormalizedStartTime: NaN
damageSource: {fileID: 0}
damageSource: {fileID: 11400000, guid: 967b72666e8bb2f498e4f7a03719e7c9, type: 2}
hitBoxEnter: 0.1
hitBoxExit: 0.8
comboInputOpen: 0.026
@@ -100,7 +100,7 @@ MonoBehaviour:
_Names: []
_Clip: {fileID: 7400000, guid: baae11fb27958444d911f8d637448184, type: 2}
_NormalizedStartTime: NaN
damageSource: {fileID: 0}
damageSource: {fileID: 11400000, guid: ef2a60dea66f7e44c8659264b94a9c38, type: 2}
hitBoxEnter: 0.2
hitBoxExit: 0.7
comboInputOpen: 0
@@ -119,7 +119,7 @@ MonoBehaviour:
_Names: []
_Clip: {fileID: 7400000, guid: d199f06821d6b92478f9fd7c3f0cd11d, type: 2}
_NormalizedStartTime: NaN
damageSource: {fileID: 0}
damageSource: {fileID: 11400000, guid: 6c81ccb5ed489a444b02709ec4506c5e, type: 2}
hitBoxEnter: 0.1
hitBoxExit: 0.9
comboInputOpen: 0
@@ -29,6 +29,7 @@ MonoBehaviour:
DashCooldown: 0.4
DashInvincibilityDuration: 0.2
DashInvincibilityCooldown: 0.9
PogoBounceForce: 15
DownDashSpeed: 22
DownDashDuration: 0.25
WallSlideSpeed: 3
@@ -21,4 +21,4 @@ MonoBehaviour:
SpringKillThreshold: 4
InvincibilityDuration: 0.6
InitialLingZhu: 0
InitialAbilities: 4194295
InitialAbilities: 4194167
@@ -657,6 +657,11 @@ MonoBehaviour:
m_EditorClassIdentifier:
_defaultSource: {fileID: 11400000, guid: caae9c7600281fe4e8d8637fa3fd2ca1, type: 2}
_hitCooldown: 0.1
_hitMode: 0
_hitInterval: 0.5
_targetLayers:
serializedVersion: 2
m_Bits: 32768
_id:
_rivalHitBoxMask:
serializedVersion: 2
+17 -2
View File
@@ -248,6 +248,7 @@ GameObject:
- component: {fileID: 8116248334753210478}
- component: {fileID: 2674139131311821727}
- component: {fileID: 1348220184733719450}
- component: {fileID: 6289226623396459915}
m_Layer: 9
m_Name: Player
m_TagString: Player
@@ -777,8 +778,22 @@ MonoBehaviour:
serializedVersion: 2
m_Bits: 16777216
_inputReader: {fileID: 11400000, guid: 88fadef8bc554e04483edd7418d20aa2, type: 2}
_onShowInteractPrompt: {fileID: 11400000, guid: 18bb3dbe7d352dc4a9b62b13e068eb9d, type: 2}
_onHideInteractPrompt: {fileID: 11400000, guid: b8cfbee27c2493e4ea75b4158a74a7c4, type: 2}
--- !u!114 &6289226623396459915
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6834103521996502824}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7ca41f67644b6b843ba7ef65e78b13e5, type: 3}
m_Name:
m_EditorClassIdentifier:
_onCheckpointRespawn: {fileID: 11400000, guid: eef5aef659932dd448be6d91119082e6, type: 2}
_onFadeOutRequest: {fileID: 11400000, guid: a17901d6793dcf2409e2672ffb383208, type: 2}
_onFadeInRequest: {fileID: 11400000, guid: f8d520fe699782b4184ff72ce5200c25, type: 2}
_fadeHalfDuration: 0.2
--- !u!1001 &5247753868797769228
PrefabInstance:
m_ObjectHideFlags: 0
@@ -27,7 +27,7 @@ Transform:
m_GameObject: {fileID: 1932889250901504761}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalPosition: {x: 0, y: -1.099, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
@@ -65,7 +65,7 @@ BoxCollider2D:
m_IsTrigger: 1
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_Offset: {x: 0.15452856, y: -0.38786495}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0, y: 0}
@@ -76,7 +76,7 @@ BoxCollider2D:
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 1, y: 0.5}
m_Size: {x: 1.6900157, y: 1.2757299}
m_EdgeRadius: 0
--- !u!114 &6478051166999031478
MonoBehaviour:
@@ -92,6 +92,11 @@ MonoBehaviour:
m_EditorClassIdentifier:
_defaultSource: {fileID: 0}
_hitCooldown: 0.1
_hitMode: 0
_hitInterval: 0.5
_targetLayers:
serializedVersion: 2
m_Bits: 134217728
_id:
_rivalHitBoxMask:
serializedVersion: 2
@@ -161,7 +166,7 @@ BoxCollider2D:
m_IsTrigger: 1
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_Offset: {x: 0.84977627, y: -0.11744261}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0, y: 0}
@@ -172,7 +177,7 @@ BoxCollider2D:
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 1, y: 0.5}
m_Size: {x: 1.1095867, y: 1.4387312}
m_EdgeRadius: 0
--- !u!114 &4757677899241504248
MonoBehaviour:
@@ -188,6 +193,11 @@ MonoBehaviour:
m_EditorClassIdentifier:
_defaultSource: {fileID: 0}
_hitCooldown: 0.1
_hitMode: 0
_hitInterval: 0.5
_targetLayers:
serializedVersion: 2
m_Bits: 134217728
_id:
_rivalHitBoxMask:
serializedVersion: 2
@@ -219,7 +229,7 @@ Transform:
m_GameObject: {fileID: 4335406389674002762}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalPosition: {x: 0, y: 1.349, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
@@ -257,7 +267,7 @@ BoxCollider2D:
m_IsTrigger: 1
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_Offset: {x: 0.1258812, y: -0.178514}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0, y: 0}
@@ -268,7 +278,7 @@ BoxCollider2D:
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 0.5, y: 1}
m_Size: {x: 1.5816574, y: 0.7235365}
m_EdgeRadius: 0
--- !u!114 &1392799324577637263
MonoBehaviour:
@@ -284,6 +294,11 @@ MonoBehaviour:
m_EditorClassIdentifier:
_defaultSource: {fileID: 0}
_hitCooldown: 0.1
_hitMode: 0
_hitInterval: 0.5
_targetLayers:
serializedVersion: 2
m_Bits: 134217728
_id:
_rivalHitBoxMask:
serializedVersion: 2
@@ -298,6 +313,7 @@ GameObject:
m_Component:
- component: {fileID: 8975424752584779179}
- component: {fileID: 3691925044832415471}
- component: {fileID: 7280738434496013396}
m_Layer: 0
m_Name: WPN_WPN_DiHun_HitBox
m_TagString: Untagged
@@ -340,6 +356,24 @@ MonoBehaviour:
_hitBoxUp: {fileID: 1392799324577637263}
_hitBoxDown: {fileID: 6478051166999031478}
_hitBoxAir: {fileID: 1382006829078153708}
--- !u!114 &7280738434496013396
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4821376343125962025}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: adb4af2f574f356449634bc130f94592, type: 3}
m_Name:
m_EditorClassIdentifier:
_onHitLight: {fileID: 0}
_onHitMedium: {fileID: 0}
_onHitHeavy: {fileID: 0}
_hitPositionMode: 1
_onAttackWhoosh: {fileID: 0}
_namedPresets: []
--- !u!1 &6434981771063321190
GameObject:
m_ObjectHideFlags: 0
@@ -405,7 +439,7 @@ BoxCollider2D:
m_IsTrigger: 1
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_Offset: {x: 0.84977627, y: -0.11744261}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0, y: 0}
@@ -416,7 +450,7 @@ BoxCollider2D:
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 0.5, y: 1}
m_Size: {x: 1.1095867, y: 1.4387312}
m_EdgeRadius: 0
--- !u!114 &1382006829078153708
MonoBehaviour:
@@ -432,6 +466,11 @@ MonoBehaviour:
m_EditorClassIdentifier:
_defaultSource: {fileID: 0}
_hitCooldown: 0.1
_hitMode: 0
_hitInterval: 0.5
_targetLayers:
serializedVersion: 2
m_Bits: 134217728
_id:
_rivalHitBoxMask:
serializedVersion: 2
+633 -2
View File
@@ -9203,6 +9203,37 @@ BoxCollider2D:
serializedVersion: 2
m_Size: {x: 0.1, y: 4}
m_EdgeRadius: 0
--- !u!1 &505494683
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 505494684}
m_Layer: 0
m_Name: RespawnPoint
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &505494684
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 505494683}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 1.7, y: -0.48, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2146472069}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &510769166
GameObject:
m_ObjectHideFlags: 0
@@ -15949,6 +15980,102 @@ Transform:
m_Children: []
m_Father: {fileID: 1328380148}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &960547552
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 960547555}
- component: {fileID: 960547554}
- component: {fileID: 960547553}
m_Layer: 24
m_Name: CheckpointMarker_2
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &960547553
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 960547552}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 81b8a6464ad9a90459fa28f2a0e1ce02, type: 3}
m_Name:
m_EditorClassIdentifier:
_playerLayers:
serializedVersion: 2
m_Bits: 512
_respawnPoint: {fileID: 1476535863}
_onCheckpointReached: {fileID: 11400000, guid: 172e9bc777b1b4b4b99f47e43cbaedb1, type: 2}
--- !u!61 &960547554
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 960547552}
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, y: 0}
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, y: 2}
m_EdgeRadius: 0
--- !u!4 &960547555
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 960547552}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -34.06, y: 2.2, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1476535863}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &985676459
GameObject:
m_ObjectHideFlags: 0
@@ -22824,6 +22951,157 @@ PolygonCollider2D:
- {x: -13, y: 14}
- {x: -13, y: -17}
m_UseDelaunayMesh: 0
--- !u!1 &1288627275
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1288627279}
- component: {fileID: 1288627278}
- component: {fileID: 1288627277}
- component: {fileID: 1288627276}
m_Layer: 25
m_Name: LethalTrap
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1288627276
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1288627275}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 03c34fc042c310942a02cfa075dcd981, type: 3}
m_Name:
m_EditorClassIdentifier:
_damage: 1
_playerLayers:
serializedVersion: 2
m_Bits: 32768
_onPlayerDied: {fileID: 11400000, guid: 7e1624bde83886d40b32f43e84a65a75, type: 2}
_onCheckpointRespawn: {fileID: 11400000, guid: eef5aef659932dd448be6d91119082e6, type: 2}
_canPogo: 1
--- !u!212 &1288627277
SpriteRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1288627275}
m_Enabled: 1
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: 6c1a7b756ba1d4646a405f7f6e0833ad, 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 &1288627278
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1288627275}
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, 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: 2, y: 0.5}
m_EdgeRadius: 0
--- !u!4 &1288627279
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1288627275}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -30.57, y: 1.49, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2040911888}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1295644709
GameObject:
m_ObjectHideFlags: 0
@@ -25323,10 +25601,13 @@ MonoBehaviour:
_hitCooldown: 0.1
_hitMode: 1
_hitInterval: 0.5
_targetLayers:
serializedVersion: 2
m_Bits: 32768
_id:
_rivalHitBoxMask:
serializedVersion: 2
m_Bits: 512
m_Bits: 0
--- !u!58 &1380656263
CircleCollider2D:
m_ObjectHideFlags: 0
@@ -26015,7 +26296,7 @@ MonoBehaviour:
_edgeCheckDownLen: 0.6
_groundMask:
serializedVersion: 2
m_Bits: 4294967295
m_Bits: 256
--- !u!114 &1409939195
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -28100,6 +28381,37 @@ Transform:
m_Children: []
m_Father: {fileID: 368251301}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1476535862
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1476535863}
m_Layer: 0
m_Name: RespawnPoint
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1476535863
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1476535862}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -2.04, y: -0.69, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 960547555}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1476942765
GameObject:
m_ObjectHideFlags: 0
@@ -208662,6 +208974,37 @@ Transform:
m_Children: []
m_Father: {fileID: 1223440397}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1608402817
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1608402818}
m_Layer: 0
m_Name: RespawnPoint
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1608402818
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1608402817}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 6.3, y: -0.31, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2124924423}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1611181745
GameObject:
m_ObjectHideFlags: 0
@@ -215691,6 +216034,98 @@ PolygonCollider2D:
- {x: -19.5, y: 7.5}
- {x: -19.5, y: -2.5}
m_UseDelaunayMesh: 0
--- !u!1 &2040911887
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2040911888}
- component: {fileID: 2040911890}
- component: {fileID: 2040911889}
m_Layer: 27
m_Name: HurtBox
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2040911888
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2040911887}
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: 1288627279}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2040911889
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2040911887}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d7b7a233d7f70aa4f86b473412b826de, type: 3}
m_Name:
m_EditorClassIdentifier:
_onDamageDealt: {fileID: 0}
_onHitConfirmed: {fileID: 0}
--- !u!61 &2040911890
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2040911887}
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, y: 0}
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: 2, y: 0.3}
m_EdgeRadius: 0
--- !u!1 &2043137490
GameObject:
m_ObjectHideFlags: 0
@@ -216962,6 +217397,102 @@ Transform:
m_Children: []
m_Father: {fileID: 432776958}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2124924420
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2124924423}
- component: {fileID: 2124924422}
- component: {fileID: 2124924421}
m_Layer: 24
m_Name: CheckpointMarker_1
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &2124924421
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2124924420}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 81b8a6464ad9a90459fa28f2a0e1ce02, type: 3}
m_Name:
m_EditorClassIdentifier:
_playerLayers:
serializedVersion: 2
m_Bits: 512
_respawnPoint: {fileID: 1608402818}
_onCheckpointReached: {fileID: 11400000, guid: 172e9bc777b1b4b4b99f47e43cbaedb1, type: 2}
--- !u!61 &2124924422
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2124924420}
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, y: 0}
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, y: 2}
m_EdgeRadius: 0
--- !u!4 &2124924423
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2124924420}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -20.24, y: 3.03, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1608402818}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2127047899
GameObject:
m_ObjectHideFlags: 0
@@ -217340,6 +217871,102 @@ Transform:
m_Children: []
m_Father: {fileID: 81962682}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2146472066
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2146472069}
- component: {fileID: 2146472068}
- component: {fileID: 2146472067}
m_Layer: 24
m_Name: CheckpointMarker
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &2146472067
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2146472066}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 81b8a6464ad9a90459fa28f2a0e1ce02, type: 3}
m_Name:
m_EditorClassIdentifier:
_playerLayers:
serializedVersion: 2
m_Bits: 512
_respawnPoint: {fileID: 505494684}
_onCheckpointReached: {fileID: 11400000, guid: 172e9bc777b1b4b4b99f47e43cbaedb1, type: 2}
--- !u!61 &2146472068
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2146472066}
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, y: 0}
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, y: 2}
m_EdgeRadius: 0
--- !u!4 &2146472069
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2146472066}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -24.6, y: 2.05, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 505494684}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
@@ -217348,3 +217975,7 @@ SceneRoots:
- {fileID: 1354690328}
- {fileID: 783576435}
- {fileID: 1409939202}
- {fileID: 1288627279}
- {fileID: 2146472069}
- {fileID: 2124924423}
- {fileID: 960547555}
+8 -1
View File
@@ -34,8 +34,15 @@ namespace BaseGames.Combat
/// </summary>
[System.NonSerialized] public uint HitActivationId;
/// <summary>
/// 命中点世界坐标。HitBox 结算时写入精确接触点(ClosestPoint);
/// 直接调用 HurtBox.ReceiveDamage 的路径由 HurtBox 以解析后的命中点兜底写入。
/// 供"击中位置"模式的反馈在该点生成(FeedbackPositionMode.HitPoint)。
/// [NonSerialized]:运行时逐次命中生成,不需要序列化。
/// </summary>
[System.NonSerialized] public Vector3 HitPoint;
/// <summary>
/// 攻击来源投射物(仅当攻击方是 Projectile 时非 null)。
/// 用于弹反成功时调用 ReflectAsPlayerProjectile() 翻转阵营。
/// 用于弹反成功时调用 ReflectBy(parrier) 按弹反者阵营反射(玩家弹反才翻转阵营
/// [NonSerialized]MonoBehaviour 引用不参与 Unity 资产序列化。
/// </summary>
[System.NonSerialized] public Projectile SourceProjectile;
+58 -18
View File
@@ -34,6 +34,12 @@ namespace BaseGames.Combat
[Tooltip("Interval 模式下,对同一目标重复造成伤害的间隔(秒)。")]
[SerializeField] private float _hitInterval = 0.5f;
[Header("伤害目标")]
[Tooltip("本判定盒可造成伤害的 Layer(实例级过滤,叠加在 Physics2D 碰撞矩阵之上)。" +
"矩阵决定哪些层之间会产生 Trigger 事件(全局),此掩码决定事件发生后是否实际结算伤害(单盒)。" +
"默认 Everything = 行为与仅靠矩阵时一致。典型配置:EnemyHitBox 盒勾 PlayerHurtBox(不误伤友军则不勾 EnemyHurtBox)。")]
[SerializeField] private LayerMask _targetLayers = ~0;
/// <summary>
/// HitBox 标识符,供 PlayerAnimationEvents / EnemyAnimationEvents 按名称精确激活特定判定盒。
/// 留空表示"无 Id";事件 payload 为空时将操作所有 HitBox。
@@ -77,6 +83,13 @@ namespace BaseGames.Combat
/// <summary>命中确认委托(PlayerCombat / EnemyCombat 订阅)。</summary>
public event System.Action<DamageInfo> OnHitConfirmed;
/// <summary>
/// 命中可破坏物(IBreakable)确认事件。
/// 与 OnHitConfirmed 区分:不走 HurtBox 伤害流水线,不参与灵力获取;
/// 供下劈弹跳等"打到实体即生效"的逻辑订阅。
/// </summary>
public event System.Action<DamageInfo> OnBreakableHitConfirmed;
// 宿主投射物缓存(Activate 时填入,DamageInfo.SourceProjectile 写入用)
private Projectile _ownerProjectile;
@@ -196,6 +209,13 @@ namespace BaseGames.Combat
Debug.LogWarning($"[HitBox] {name}: 无 DamageSourceSO,跳过命中。", this);
return;
}
// 目标层过滤:既非伤害目标层、也非拼刀对象层时直接忽略,
// 不进入命中/占用跟踪(矩阵管"事件是否发生",此掩码管"本盒是否结算")
int enterLayer = other.gameObject.layer;
bool isDamageTarget = (_targetLayers.value & (1 << enterLayer)) != 0;
bool isClashRival = (_rivalHitBoxMask.value & (1 << enterLayer)) != 0 && CanClash;
if (!isDamageTarget && !isClashRival) return;
if (_hitMode == HitMode.Interval)
{
// 周期接触模式:记录占用并立即结算一次;后续由 Update 按间隔对仍停留的目标重判。
@@ -219,18 +239,6 @@ namespace BaseGames.Combat
// 排除自身:攻击方与受击方属于同一根 GameObject 时跳过(防止近战 EnemyHitBox 命中同一敌人的 EnemyHurtBox
if (other.transform.root == _attackerTransform.root) return;
Vector2 knockDir = ((Vector2)other.bounds.center
- (Vector2)_attackerTransform.position).normalized;
// ⚡ 零 GC:struct 工厂,运行时字段内联传入
var info = DamageInfo.From(
_currentSource,
knockDir,
_attackerTransform.position,
_attackerTransform.gameObject.layer,
_ownerProjectile);
info.HitActivationId = _currentActivationId;
// ① 拼刀检测:当前 HitBox 携带 CanClash 标记,且碰到对立阵营的 HitBox 层
int otherLayer = other.gameObject.layer;
bool isRivalHitBoxLayer = (_rivalHitBoxMask.value & (1 << otherLayer)) != 0;
@@ -244,15 +252,34 @@ namespace BaseGames.Combat
}
}
// 目标层过滤:拼刀之外,仅对 _targetLayers 内的层结算伤害。
// Enter 入口已过滤,这里再守一次:覆盖 Interval 占用期间目标 Layer 被运行时改变的情况(如投射物弹反换层)。
if ((_targetLayers.value & (1 << otherLayer)) == 0) return;
Vector2 knockDir = ((Vector2)other.bounds.center
- (Vector2)_attackerTransform.position).normalized;
// ⚡ 零 GC:struct 工厂,运行时字段内联传入
var info = DamageInfo.From(
_currentSource,
knockDir,
_attackerTransform.position,
_attackerTransform.gameObject.layer,
_ownerProjectile);
info.HitActivationId = _currentActivationId;
// hitPoint:优先使用触发命中的碰撞体中心在目标表面的最近点;
// 无 sourceCollider(直属碰撞体)时回退到 HitBox 节点坐标。
// 写入 info.HitPoint 供下游"击中位置"模式的反馈在该点生成。
Vector2 hitOrigin = sourceCollider != null
? (Vector2)sourceCollider.bounds.center
: (Vector2)transform.position;
Vector3 hitPoint = other.ClosestPoint(hitOrigin);
info.HitPoint = hitPoint;
// ② 命中 HurtBox
if (other.TryGetComponent<HurtBox>(out var hurtBox))
{
// hitPoint:优先使用触发命中的碰撞体中心在 HurtBox 表面的最近点;
// 无 sourceCollider(直属碰撞体)时回退到 HitBox 节点坐标。
Vector2 hitOrigin = sourceCollider != null
? (Vector2)sourceCollider.bounds.center
: (Vector2)transform.position;
Vector3 hitPoint = other.ClosestPoint(hitOrigin);
hurtBox.ReceiveDamage(info, hitPoint);
OnHitConfirmed?.Invoke(info);
return;
@@ -260,7 +287,10 @@ namespace BaseGames.Combat
// ③ 命中 IBreakable(机关/障碍物)
if (other.TryGetComponent<IBreakable>(out var breakable))
{
breakable.TryInteract(info);
OnBreakableHitConfirmed?.Invoke(info);
}
}
// ── 当前激活期已命中目标集合(防止复合子 Collider 导致同帧多次命中)────────────
@@ -272,6 +302,16 @@ namespace BaseGames.Combat
// Update 轮询时的临时缓冲(避免遍历字典时修改 + 降低 GC)
private readonly List<Collider2D> _intervalTickBuffer = new(8);
/// <summary>
/// 伤害目标层掩码(实例级过滤,叠加在 Physics2D 碰撞矩阵之上)。
/// 运行时可写:投射物弹反换阵营时由 Projectile 翻转目标侧。
/// </summary>
public LayerMask TargetLayers
{
get => _targetLayers;
set => _targetLayers = value;
}
/// <summary>
/// 配置为周期接触伤害模式(BodyContactDamage 等持续接触源在 OnEnable 时调用)。
/// 使持续接触的判定盒按 <paramref name="interval"/> 对停留目标重复造成伤害,
+6 -2
View File
@@ -61,6 +61,9 @@ namespace BaseGames.Combat
public void ReceiveDamage(DamageInfo info, Vector3? hitPoint = null)
{
Vector3 resolvedHitPoint = hitPoint ?? transform.position;
// 兜底盖戳:直接调用本方法的路径(陷阱/环境伤害)没有 HitBox 写入的精确接触点,
// 统一以解析后的命中点填充,保证下游"击中位置"模式反馈始终有效
info.HitPoint = resolvedHitPoint;
if (!_isActive || _owner == null) return;
// 所有者级去重:同一 HitBox 激活期内多个 HurtBox 子节点只处理首次命中(共享 HP)
if (_ownerGuard != null && !_ownerGuard.TryRegisterHit(info.HitActivationId)) return;
@@ -75,8 +78,9 @@ namespace BaseGames.Combat
{
if (_parrySystem.ConsumeParry())
{
// 若攻击来源是投射物,翻转其阵营 Layer 与飞行方向
info.SourceProjectile?.ReflectAsPlayerProjectile();
// 若攻击来源是投射物,按弹反者阵营反射:
// 玩家弹反翻转阵营 Layer 与伤害目标层;敌人弹反仅反转方向
info.SourceProjectile?.ReflectBy(transform);
return;
}
}
+16 -2
View File
@@ -47,6 +47,11 @@ namespace BaseGames.Combat
[Tooltip("true = 顶部有 HurtBox 子节点可下劈弹跳(需手动添加 Pogo Surface 子节点)")]
[SerializeField] private bool _canPogo = true;
/// <summary>
/// 单次接触闩锁:防止同一次触碰内重复结算(如淡出等待期间的物理回调)。
/// 玩家 HurtBox 离开触发区(OnTriggerExit2D,含检查点传送移位)后重置,
/// 使陷阱对下一次触碰再次生效;死亡路径则经场景重载由 OnEnable 重置。
/// </summary>
private bool _triggered;
private void Awake()
@@ -84,9 +89,12 @@ namespace BaseGames.Combat
hurtBox.ReceiveDamage(info);
// 检测伤害后玩家存活状态:
// 若已死亡,PlayerController.TakeDamage 已在同帧内 Raise EVT_PlayerDied
// 若已死亡,PlayerController.TakeDamage 已在同帧内 Raise EVT_PlayerDied
// 走完整死亡流程,本组件不得再 Raise 检查点回溯;
// 若存活,需由本组件 Raise 使其回到检查点。
var damageable = other.transform.root.GetComponentInParent<IDamageable>();
// 从 HurtBox 自身向上查找(而非 transform.root):玩家被临时挂到
// 其他父物体(如移动平台)下时 root 不是玩家根节点,会漏判死亡。
var damageable = other.GetComponentInParent<IDamageable>();
playerDiedFromDamage = damageable != null && !damageable.IsAlive;
}
}
@@ -102,6 +110,12 @@ namespace BaseGames.Combat
}
}
private void OnTriggerExit2D(Collider2D other)
{
if ((_playerLayers.value & (1 << other.gameObject.layer)) == 0) return;
_triggered = false;
}
#if UNITY_EDITOR
[UnityEngine.ContextMenu("打印 Pogo 提示")]
private void PrintPogoInfo()
@@ -34,6 +34,17 @@ namespace BaseGames.Combat
Direction = -Direction;
_rb.velocity = Direction * _config.Speed * _config.ParrySpeedMultiplier;
// 仅当弹反者是玩家时翻转阵营:切到 PlayerProjectile 层(否则碰撞矩阵
// EnemyProjectile↔EnemyHurtBox 不碰撞,反射后永远打不中敌人),
// 伤害目标层同步切换(优先取配置的 ReflectedTargetLayers)。
// 敌人弹反敌人投射物:阵营与目标层保持不变,仅上面已反转方向。
if (parrySystem.transform.root.CompareTag("Player"))
{
int playerProjLayer = LayerMask.NameToLayer("PlayerProjectile");
if (playerProjLayer >= 0) gameObject.layer = playerProjLayer;
ApplyReflectedTargetLayers();
}
if (_reflectSource != null)
DamageInfo = DamageInfo.From(_reflectSource);
return;
@@ -41,6 +52,8 @@ namespace BaseGames.Combat
}
// ── 正常命中 ─────────────────────────────────────────────────
// 本类绕过 HitBox 自行判定,需自行套用其伤害目标层过滤
if ((_hitBox.TargetLayers.value & (1 << other.gameObject.layer)) == 0) return;
var hurtBox = other.GetComponent<HurtBox>();
if (hurtBox != null)
{
+53 -2
View File
@@ -26,6 +26,8 @@ namespace BaseGames.Combat
private int _hitsRemaining;
// 弹反帧标记:弹反命中不计入命中预算(避免反射后立即被回收)
private bool _justReflected;
// 预制体上配置的初始伤害目标层:对象池复用时还原,避免上一发弹反翻转后的掩码污染下一发
private LayerMask _initialTargetLayers;
private PooledObject _pooledObject;
@@ -34,6 +36,7 @@ namespace BaseGames.Combat
_rb = GetComponent<Rigidbody2D>();
_hitBox = GetComponent<HitBox>();
_pooledObject = GetComponent<PooledObject>();
_initialTargetLayers = _hitBox.TargetLayers;
// 订阅命中确认:按命中预算决定何时回收(穿透 / 命中即消失)
_hitBox.OnHitConfirmed += HandleHitConfirmed;
}
@@ -49,6 +52,8 @@ namespace BaseGames.Combat
_maxHits = config.MaxHits;
_hitsRemaining = config.MaxHits;
_justReflected = false;
// 还原预制体配置的伤害目标层(清除上一次弹反翻转的影响)
_hitBox.TargetLayers = _initialTargetLayers;
SetFactionLayer(ownerLayer);
_hitBox.Activate(config.DamageSource);
@@ -66,9 +71,31 @@ namespace BaseGames.Combat
}
/// <summary>
/// 弹反:将投射物阵营从 EnemyProjectile 切换为 PlayerProjectile
/// 阵营感知弹反入口:根据弹反者阵营决定反射行为
/// 玩家弹反 → 切 PlayerProjectile 层并切换伤害目标层(走 <see cref="ReflectAsPlayerProjectile"/>);
/// 敌人弹反 → 阵营层与伤害目标层均保持不变(仍是敌方投射物、仍打玩家侧),仅反转方向并重置命中记录。
/// 由 HurtBox.ReceiveDamage() 在弹反成功后调用,传入弹反者 Transform。
/// </summary>
public virtual void ReflectBy(Transform parrier)
{
if (parrier != null && parrier.root.CompareTag("Player"))
{
ReflectAsPlayerProjectile();
return;
}
// ── 敌人弹反:不换阵营、不换目标层,仅反转飞行并重置判定 ──
Direction = -Direction;
_rb.velocity = -_rb.velocity;
_hitBox.Deactivate();
_hitBox.Activate(_config?.DamageSource);
_hitsRemaining = _maxHits;
_justReflected = true;
}
/// <summary>
/// 玩家弹反:将投射物阵营从 EnemyProjectile 切换为 PlayerProjectile。
/// 反转飞行方向,并重置 HitBox 命中记录使其能够命中新目标(敌人)。
/// 由 HurtBox.ReceiveDamage() 在弹反成功后调用。
/// </summary>
public virtual void ReflectAsPlayerProjectile()
{
@@ -82,12 +109,36 @@ namespace BaseGames.Combat
// 重置 HitBox 命中记录,确保反射后可命中新目标
_hitBox.Deactivate();
_hitBox.Activate(_config?.DamageSource);
// 伤害目标随阵营切换(优先取配置的显式目标层)
ApplyReflectedTargetLayers();
// 反射后重置命中预算,并跳过本次(弹反)命中的扣减,避免反射后立即被回收
_hitsRemaining = _maxHits;
_justReflected = true;
}
/// <summary>
/// 弹反换阵营后应用伤害目标层:
/// 优先使用 <see cref="ProjectileConfigSO.ReflectedTargetLayers"/> 的显式配置;
/// 未配置(Nothing)时自动翻转——仅交换 PlayerHurtBox→EnemyHurtBox 位,
/// 保留可破坏物等其他目标层。
/// </summary>
protected void ApplyReflectedTargetLayers()
{
if (_config != null && _config.ReflectedTargetLayers.value != 0)
{
_hitBox.TargetLayers = _config.ReflectedTargetLayers;
return;
}
int playerHurt = LayerMask.NameToLayer("PlayerHurtBox");
int enemyHurt = LayerMask.NameToLayer("EnemyHurtBox");
if (playerHurt < 0 || enemyHurt < 0) return; // Layer 尚未创建,保留现有掩码
int mask = _hitBox.TargetLayers.value;
mask &= ~(1 << playerHurt);
mask |= 1 << enemyHurt;
_hitBox.TargetLayers = mask;
}
/// <summary>HitBox 命中确认回调:按命中预算决定是否回收(穿透 / 命中即消失)。</summary>
private void HandleHitConfirmed(DamageInfo _)
{
@@ -26,5 +26,8 @@ namespace BaseGames.Combat
[Header("弹反")]
public float ParrySpeedMultiplier = 1.2f;
public float ParryDamageMultiplier = 2.0f;
[Tooltip("弹反后本投射物可造成伤害判定的 Layer(显式配置,写入 HitBox.TargetLayers)。" +
"留空(Nothing) = 自动翻转:在原目标层基础上 PlayerHurtBox 位换为 EnemyHurtBox 位、其余位保留。")]
public LayerMask ReflectedTargetLayers;
}
}
@@ -2,6 +2,7 @@ using System.IO;
using UnityEditor;
using UnityEngine;
using BaseGames.Combat;
using BaseGames.Feedback;
using BaseGames.Player;
namespace BaseGames.Editor.Combat
@@ -12,11 +13,14 @@ namespace BaseGames.Editor.Combat
///
/// 生成路径规范:Assets/_Game/Prefabs/Weapons/WPN_{weaponId}_HitBox.prefab
/// Prefab 结构:
/// [WPN_{weaponId}_HitBox] ← WeaponHitBoxInstance
/// [WPN_{weaponId}_HitBox] ← WeaponHitBoxInstance + WeaponFeedback(武器本地反馈:命中粒子/音效/破风)
/// ├── [HitBox_Ground] ← Collider2D(IsTrigger, 形状可选) + HitBox, Layer=PlayerHitBox
/// ├── [HitBox_Up]
/// ├── [HitBox_Down]
/// └── [HitBox_Air]
///
/// WeaponFeedback 的 MMF_Player 反馈链留待 Inspector 编排;
/// 缺失该组件时 WeaponHitBoxInstance 会静默落到 NullFeedbackPlayer(无武器级表现)。
/// </summary>
public class WeaponHitBoxWizard : ScriptableWizard
{
@@ -101,6 +105,9 @@ namespace BaseGames.Editor.Combat
// ── 构建 Prefab ────────────────────────────────────────────────
var root = new GameObject(prefabName);
var instance = root.AddComponent<WeaponHitBoxInstance>();
// 武器本地反馈(WeaponHitBoxInstance.Awake 经 GetComponentInChildren<IFeedbackPlayer>() 获取);
// MMF_Player 反馈链留待 Inspector 编排
root.AddComponent<WeaponFeedback>();
var so = new SerializedObject(instance);
void AddDirection(bool enabled, string nodeName, string fieldName, ColliderShape shape)
@@ -232,6 +232,13 @@ namespace BaseGames.Editor
if (inputReader != null)
AssignReference(interactDetector, "_inputReader", inputReader, report);
// ── 检查点回溯(LethalTrap 存活路径:淡出 → 传送至最近检查点 → 淡入)──
// 须与 Rigidbody2D 同节点(传送时直接写 rb.position)。
CheckpointRespawnHandler checkpointRespawn = GetOrAddComponent<CheckpointRespawnHandler>(root);
AssignAsset(checkpointRespawn, "_onCheckpointRespawn", report, false, "EVT_CheckpointRespawn");
AssignAsset(checkpointRespawn, "_onFadeOutRequest", report, false, "EVT_FadeOutRequest");
AssignAsset(checkpointRespawn, "_onFadeInRequest", report, false, "EVT_FadeInRequest");
if (animConfig == null) report.Add("★ 需创建并绑定:PlayerController._animConfigPLY_PlayerAnimationConfig");
if (statsConfig == null) report.Add("★ 需创建并绑定:PlayerStats._configPlayerStatsSO");
if (inputReader == null) report.Add("★ 需手动绑定:PlayerController._inputReader / FormController._input / SkillManager._inputInputReaderSO");
@@ -1629,8 +1636,14 @@ namespace BaseGames.Editor
AssignLayerMask(marker, "_playerLayers", "Player", report);
AssignAsset(marker, "_onCheckpointReached", report, false, "EVT_CheckpointReached");
// RespawnPoint 子节点:玩家回溯时的出生位置(触发区与出生点解耦)
Transform respawnT = GetOrCreateChild(go.transform, "RespawnPoint");
respawnT.localPosition = Vector3.zero;
AssignReference(marker, "_respawnPoint", respawnT, report);
report.Add("放置于跳跳乐段落的关键节点处;玩家经过后成为该房间最近检查点。");
report.Add("同一房间可放置多个,以最近经过的为准。");
report.Add("调整 RespawnPoint 子节点位置 = 玩家回溯出生位置(须落在安全平台上)。");
Selection.activeGameObject = go;
MarkDirtyAndLog("Checkpoint Marker", go, report);
@@ -15,13 +15,17 @@ namespace BaseGames.Enemies
[SerializeField] private MMF_Player _onHitLight;
[SerializeField] private MMF_Player _onHitMedium;
[SerializeField] private MMF_Player _onHitHeavy;
[Tooltip("命中反馈生成位置:Fixed = 反馈链编排的固定位置;HitPoint = 在命中点生成")]
[SerializeField] private FeedbackPositionMode _hitPositionMode = FeedbackPositionMode.Fixed;
[Header("受伤 / 死亡反馈")]
[SerializeField] private MMF_Player _onTakeHit;
[Tooltip("受击反馈生成位置:Fixed = 反馈链编排的固定位置;HitPoint = 在受击点生成(血粒子等贴点表现)")]
[SerializeField] private FeedbackPositionMode _takeHitPositionMode = FeedbackPositionMode.Fixed;
[SerializeField] private MMF_Player _onDeath;
// ── IFeedbackPlayer ──────────────────────────────────────────────────────
public void PlayHit(HitWeight weight)
public void PlayHit(HitWeight weight, Vector3 hitPoint)
{
var player = weight switch
{
@@ -30,10 +34,11 @@ namespace BaseGames.Enemies
HitWeight.Heavy => _onHitHeavy,
_ => _onHitLight,
};
player?.PlayFeedbacks();
FeedbackPlayback.Play(player, _hitPositionMode, hitPoint);
}
public void PlayTakeHit() => _onTakeHit?.PlayFeedbacks();
public void PlayTakeHit(Vector3 hitPoint)
=> FeedbackPlayback.Play(_onTakeHit, _takeHitPositionMode, hitPoint);
public void PlayDeath() => _onDeath?.PlayFeedbacks();
// 以下方法对敌人无意义,提供空实现保持接口完整
@@ -51,7 +56,7 @@ namespace BaseGames.Enemies
/// <summary>受到伤害时调用(由 EnemyBase 触发)。</summary>
public void OnHit(DamageInfo info)
{
PlayTakeHit();
PlayTakeHit(info.HitPoint);
}
/// <summary>死亡时调用(由 EnemyBase 触发)。</summary>
@@ -1,3 +1,6 @@
using UnityEngine;
using MoreMountains.Feedbacks;
namespace BaseGames.Feedback
{
/// <summary>
@@ -8,15 +11,22 @@ namespace BaseGames.Feedback
public interface IFeedbackPlayer
{
// ── 命中 ──────────────────────────────────────────────────────────────────
/// <summary>对目标造成命中时播放对应力度的反馈(摄像机震屏 + 控制器振动等)。</summary>
void PlayHit(HitWeight weight);
/// <summary>
/// 对目标造成命中时播放对应力度的反馈(摄像机震屏 + 控制器振动等)。
/// hitPoint = 命中点世界坐标(DamageInfo.HitPoint);
/// 实际生成位置取决于实现方各槽位的 FeedbackPositionMode 配置。
/// </summary>
void PlayHit(HitWeight weight, Vector3 hitPoint);
/// <summary>成功弹反(Parry)时播放反馈。</summary>
void PlayParrySuccess();
// ── 受伤 / 死亡 ──────────────────────────────────────────────────────────
/// <summary>角色受到伤害时播放反馈(闪白 + 轻微震屏等)。</summary>
void PlayTakeHit();
/// <summary>
/// 角色受到伤害时播放反馈(闪白 + 轻微震屏等)。
/// hitPoint = 受击点世界坐标(DamageInfo.HitPoint)。
/// </summary>
void PlayTakeHit(Vector3 hitPoint);
/// <summary>角色死亡时播放反馈(慢动作 + 震屏 + 音效)。</summary>
void PlayDeath();
@@ -53,6 +63,34 @@ namespace BaseGames.Feedback
/// <summary>命中力度。</summary>
public enum HitWeight { Light, Medium, Heavy }
/// <summary>
/// 反馈生成位置模式。反馈链本身由开发者在 Inspector 编排,
/// 此模式只决定播放时是否把命中点传入反馈链。
/// </summary>
public enum FeedbackPositionMode
{
/// <summary>固定位置:按反馈链各模块编排时设定的位置播放(不传入命中点)。</summary>
Fixed,
/// <summary>击中位置:把命中点传入反馈链,启用了播放位置选项的模块在命中点生成。</summary>
HitPoint,
}
/// <summary>
/// 反馈播放辅助:统一"按位置模式播放 MMF_Player"的实现,供各 IFeedbackPlayer 实现复用。
/// </summary>
public static class FeedbackPlayback
{
/// <summary>按位置模式播放。HitPoint 模式将命中点传给反馈链(仅启用位置选项的模块生效)。</summary>
public static void Play(MMF_Player player, FeedbackPositionMode mode, Vector3 hitPoint)
{
if (player == null) return;
if (mode == FeedbackPositionMode.HitPoint)
player.PlayFeedbacks(hitPoint);
else
player.PlayFeedbacks();
}
}
/// <summary>
/// 空对象模式实现:所有方法均为空操作,用于测试和不需要反馈的实体。
/// </summary>
@@ -60,9 +98,9 @@ namespace BaseGames.Feedback
{
public static readonly NullFeedbackPlayer Instance = new NullFeedbackPlayer();
public void PlayHit(HitWeight weight) { }
public void PlayHit(HitWeight weight, Vector3 hitPoint) { }
public void PlayParrySuccess() { }
public void PlayTakeHit() { }
public void PlayTakeHit(Vector3 hitPoint) { }
public void PlayDeath() { }
public void PlayHeal() { }
public void PlayLandImpact() { }
@@ -74,4 +112,3 @@ namespace BaseGames.Feedback
public void PlayFormSwitch(int formIndex) { }
}
}
@@ -15,10 +15,14 @@ namespace BaseGames.Feedback
[SerializeField] private MMF_Player _onHitLight;
[SerializeField] private MMF_Player _onHitMedium;
[SerializeField] private MMF_Player _onHitHeavy;
[Tooltip("命中反馈生成位置:Fixed = 反馈链编排的固定位置;HitPoint = 在命中点生成")]
[SerializeField] private FeedbackPositionMode _hitPositionMode = FeedbackPositionMode.Fixed;
[SerializeField] private MMF_Player _onParrySuccess;
[Header("受伤 / 死亡反馈")]
[SerializeField] private MMF_Player _onTakeHit;
[Tooltip("受击反馈生成位置:Fixed = 反馈链编排的固定位置;HitPoint = 在受击点生成")]
[SerializeField] private FeedbackPositionMode _takeHitPositionMode = FeedbackPositionMode.Fixed;
[SerializeField] private MMF_Player _onDeath;
[Header("恢复反馈")]
@@ -53,7 +57,7 @@ namespace BaseGames.Feedback
}
// ── IFeedbackPlayer ──────────────────────────────────────────────────────
public void PlayHit(HitWeight weight)
public void PlayHit(HitWeight weight, Vector3 hitPoint)
{
var player = weight switch
{
@@ -62,11 +66,12 @@ namespace BaseGames.Feedback
HitWeight.Heavy => _onHitHeavy,
_ => _onHitLight,
};
player?.PlayFeedbacks();
FeedbackPlayback.Play(player, _hitPositionMode, hitPoint);
}
public void PlayParrySuccess() => _onParrySuccess?.PlayFeedbacks();
public void PlayTakeHit() => _onTakeHit?.PlayFeedbacks();
public void PlayTakeHit(Vector3 hitPoint)
=> FeedbackPlayback.Play(_onTakeHit, _takeHitPositionMode, hitPoint);
public void PlayDeath() => _onDeath?.PlayFeedbacks();
public void PlayHeal() => _onHeal?.PlayFeedbacks();
public void PlayLandImpact() => _onLandImpact?.PlayFeedbacks();
@@ -14,6 +14,8 @@ namespace BaseGames.Feedback
[SerializeField] private MMF_Player _onHitLight;
[SerializeField] private MMF_Player _onHitMedium;
[SerializeField] private MMF_Player _onHitHeavy;
[Tooltip("命中反馈生成位置:Fixed = 反馈链编排的固定位置;HitPoint = 在命中点生成(命中火花等贴点表现)")]
[SerializeField] private FeedbackPositionMode _hitPositionMode = FeedbackPositionMode.HitPoint;
[Header("攻击破风")]
[SerializeField] private MMF_Player _onAttackWhoosh;
@@ -30,7 +32,7 @@ namespace BaseGames.Feedback
// ── IFeedbackPlayer 实现 ──────────────────────────────────────────────
public void PlayHit(HitWeight weight)
public void PlayHit(HitWeight weight, Vector3 hitPoint)
{
var player = weight switch
{
@@ -38,7 +40,7 @@ namespace BaseGames.Feedback
HitWeight.Heavy => _onHitHeavy,
_ => _onHitMedium,
};
player?.PlayFeedbacks();
FeedbackPlayback.Play(player, _hitPositionMode, hitPoint);
}
public void PlayAttackWhoosh() => _onAttackWhoosh?.PlayFeedbacks();
@@ -52,7 +54,7 @@ namespace BaseGames.Feedback
// ── 武器上不适用的反馈,空实现 ────────────────────────────────────────
public void PlayParrySuccess() { }
public void PlayTakeHit() { }
public void PlayTakeHit(Vector3 hitPoint){ }
public void PlayDeath() { }
public void PlayHeal() { }
public void PlayLandImpact() { }
+1 -1
View File
@@ -92,7 +92,7 @@ namespace BaseGames.Player
var weight = info.FinalDamage <= 5 ? HitWeight.Light
: info.FinalDamage <= 15 ? HitWeight.Medium
: HitWeight.Heavy;
_feedback.PlayHit(weight);
_feedback.PlayHit(weight, info.HitPoint);
// 攻击命中反嵈:向攻击反方向施加微小后退冲量,增强打击感
if (_movement?.Rb != null && info.KnockbackDirection.x != 0f)
@@ -214,6 +214,17 @@ namespace BaseGames.Player
_rb.velocity = new Vector2(_rb.velocity.x, _rb.velocity.y * _config.JumpCutMultiplier);
}
/// <summary>
/// 下劈命中弹跳(DownAttackState 调用)。覆盖当前垂直速度为 PogoBounceForce
/// 固定高度(JumpCancelledEvent 仅 JumpState 处理,弹跳不受变高跳截断影响)。
/// </summary>
public void PogoBounce()
{
_rb.velocity = new Vector2(_rb.velocity.x, _config.PogoBounceForce);
_coyoteTimer = 0f;
_slopeSnapDisabled = true;
}
/// <summary>
/// 二段跳。覆盖当前垂直速度为 DoubleJumpForce。
/// FallState / JumpState 在检测到 HasAbility(DoubleJump) && AirJumpsLeft > 0 时调用。
@@ -60,6 +60,10 @@ namespace BaseGames.Player
[Tooltip("无敌的独立冷却(秒)。CD 内再次冲刺不会获得无敌帧,防止连冲变相持续无敌(推荐 0.9s)。")]
public float DashInvincibilityCooldown = 0.9f;
[Header("下劈弹跳(Pogo")]
[Tooltip("下劈命中后的向上弹跳初速度。推荐略低于 JumpForce(如 JumpForce=18 时取 15),\n弹跳略矮于满跳;固定高度,不受变高跳截断影响。")]
public float PogoBounceForce = 15f;
[Header("下冲刺")]
[Tooltip("向下冲刺速度(单位/秒)。推荐 22,快速向下穿透空间。")]
public float DownDashSpeed = 22f;
@@ -42,9 +42,8 @@ namespace BaseGames.Player.States
OnClipEnd();
}
// 施加向下的速度(下劈冲击)
if (Move?.Rb != null)
Move.Rb.velocity = new Vector2(Move.Rb.velocity.x, -18f);
// 下劈为纯向下攻击:保持角色原有的自然下落,不额外施加向下俯冲位移
// (俯冲位移会让下劈手感变成下冲,命中弹跳逻辑见 OnDownHitConfirmed
}
public override void OnStateExit()
@@ -59,10 +58,10 @@ namespace BaseGames.Player.States
{
if (_hasHitEnemy) return;
_hasHitEnemy = true;
// Pogo 弹跳:命中敌人后向上弹起,同时重置空中能力(等同落地效果)
// Pogo 弹跳:命中后向上弹起(独立弹跳力,略矮于满跳),同时重置空中能力(等同落地效果)
Owner.ResetAirJumps();
Owner.GetState<DashState>()?.ResetDashCharge();
Move.Jump();
Move.PogoBounce();
}
public override void OnStateUpdate()
@@ -74,6 +73,25 @@ namespace BaseGames.Player.States
}
}
public override void OnStateFixedUpdate()
{
// 下劈期间保留完整空中水平操控(与 FallState 同款贴墙保护,防止压墙摩擦/卡角)
if (Mathf.Abs(Input.MoveInput.x) > 0.01f)
{
int inputDir = Input.MoveInput.x > 0 ? 1 : -1;
var wd = Owner.WallDetector;
bool currentFrameWall = inputDir > 0 ? Move.IsWallRight : Move.IsWallLeft;
if (currentFrameWall
|| (wd != null && (wd.IsTouchingWall && wd.WallDirection == inputDir
|| wd.HasPartialContact(inputDir))))
Move.ZeroHorizontalVelocity();
else
Move.Move(Input.MoveInput.x * Cfg.RunSpeed);
}
else
Move.ZeroHorizontalVelocity();
}
private void OnClipEnd()
{
if (_exited) return;
@@ -10,14 +10,16 @@ namespace BaseGames.Player.States
/// </summary>
public class HurtState : PlayerStateBase
{
private float _timer;
private bool _ended;
private float _timer;
private bool _ended;
private Vector3 _hitPoint;
public HurtState(PlayerController owner) : base(owner) { }
public void Initialize(BaseGames.Combat.DamageInfo info)
{
// 由 PlayerController.TakeDamage 传入伤害信息
_hitPoint = info.HitPoint;
if (info.KnockbackForce > 0.01f)
Move?.ApplyKnockback(info.KnockbackDirection, info.KnockbackForce);
}
@@ -28,7 +30,7 @@ namespace BaseGames.Player.States
_timer = Owner.AnimConfig?.HurtDuration ?? 0.4f;
_ended = false;
Stats?.BeginInvincibility();
Feedback.PlayTakeHit();
Feedback.PlayTakeHit(_hitPoint);
if (AnimCfg?.Hurt != null)
{
@@ -31,7 +31,7 @@ namespace BaseGames.Player
private AttackDirection _activeDir;
private IFeedbackPlayer _feedback;
/// <summary>下劈命中确认事件(供 DownAttackState Pogo 逻辑)。</summary>
/// <summary>下劈命中确认事件(供 DownAttackState Pogo 逻辑)。命中 HurtBox 或可破坏物均触发。</summary>
public event System.Action<DamageInfo> OnDownHitConfirmed;
/// <summary>任意 HitBox 命中确认事件(供 PlayerCombat 订阅通用命中反馈)。</summary>
@@ -41,7 +41,10 @@ namespace BaseGames.Player
{
_allHitBoxes = GetComponentsInChildren<HitBox>(true);
foreach (var hb in _allHitBoxes)
hb.OnHitConfirmed += OnAnyHitConfirmed;
{
hb.OnHitConfirmed += OnAnyHitConfirmed;
hb.OnBreakableHitConfirmed += OnAnyBreakableHitConfirmed;
}
_feedback = GetComponentInChildren<IFeedbackPlayer>() ?? NullFeedbackPlayer.Instance;
}
@@ -50,13 +53,24 @@ namespace BaseGames.Player
var weight = info.FinalDamage <= 5 ? HitWeight.Light
: info.FinalDamage <= 15 ? HitWeight.Medium
: HitWeight.Heavy;
_feedback.PlayHit(weight);
_feedback.PlayHit(weight, info.HitPoint);
OnHitConfirmed?.Invoke(info);
if (_activeDir == AttackDirection.Down)
OnDownHitConfirmed?.Invoke(info);
}
/// <summary>
/// 命中可破坏物:播放轻量打击反馈;下劈方向时同样触发弹跳。
/// 不转发 OnHitConfirmed(可破坏物不参与灵力获取)。
/// </summary>
private void OnAnyBreakableHitConfirmed(DamageInfo info)
{
_feedback.PlayHit(HitWeight.Light, info.HitPoint);
if (_activeDir == AttackDirection.Down)
OnDownHitConfirmed?.Invoke(info);
}
// ── 公共 API ──────────────────────────────────────────────────────────
/// <summary>
+39 -4
View File
@@ -9,9 +9,12 @@ namespace BaseGames.World
/// 玩家经过时自动激活,成为 LethalTrap 回溯的目标位置。
///
/// 设计要点:
/// - 同一房间可放置多个,取最近经过的一个
/// - 同一房间可放置多个,取最近经过的一个(折返重新经过早先的标记同样覆盖注册)
/// - 激活状态仅存于运行时,不持久化,换房间后重置
/// - 每个 CheckpointMarker 仅激活一次(重复进入同一触发区无效)
/// - 闩锁按接触周期:进入时注册并广播一次,离开触发区后重置,
/// 同一次接触内不重复结算
/// - 触发区与出生位置解耦:注册的是 _respawnPoint 子节点的位置,
/// 触发区可横跨通道,出生点精确落在安全平台上
/// </summary>
[RequireComponent(typeof(Collider2D))]
public class CheckpointMarker : MonoBehaviour
@@ -20,13 +23,17 @@ namespace BaseGames.World
[Tooltip("勾选 Player 层")]
[SerializeField] private LayerMask _playerLayers;
[Header("复活点")]
[Tooltip("玩家回溯时出现的位置(必配,脚手架自动创建 RespawnPoint 子节点)")]
[SerializeField] private Transform _respawnPoint;
[Header("事件")]
[Tooltip("EVT_CheckpointReached — 激活时广播,供 VFX/SFX 使用")]
[SerializeField] private VoidEventChannelSO _onCheckpointReached;
private bool _isActivated;
/// <summary>当前运行时是否已被激活。</summary>
/// <summary>本次接触是否已注册过(玩家离开触发区后重置)。</summary>
public bool IsActivated => _isActivated;
private void Awake()
@@ -37,18 +44,29 @@ namespace BaseGames.World
col.isTrigger = true;
Debug.LogWarning($"[CheckpointMarker] {name}: Collider2D.isTrigger 已自动设为 true。", this);
}
if (_respawnPoint == null)
Debug.LogError($"[CheckpointMarker] {name}: _respawnPoint 未绑定,检查点无法注册。" +
"请用脚手架(BaseGames/Scene/Place/Checkpoint Marker)创建,或手动补 RespawnPoint 子节点并绑定。", this);
}
private void OnTriggerEnter2D(Collider2D other)
{
if (_isActivated) return;
if ((_playerLayers.value & (1 << other.gameObject.layer)) == 0) return;
if (_respawnPoint == null) return; // 漏配已在 Awake 报错,此处不以标记自身位置兜底
_isActivated = true;
ServiceLocator.GetOrDefault<ICheckpointService>()?.RegisterCheckpoint(transform.position);
ServiceLocator.GetOrDefault<ICheckpointService>()?.RegisterCheckpoint(_respawnPoint.position);
_onCheckpointReached?.Raise();
}
private void OnTriggerExit2D(Collider2D other)
{
if ((_playerLayers.value & (1 << other.gameObject.layer)) == 0) return;
_isActivated = false;
}
private void OnDrawGizmos()
{
Gizmos.color = _isActivated
@@ -57,6 +75,23 @@ namespace BaseGames.World
Gizmos.DrawWireSphere(transform.position, 0.4f);
Gizmos.DrawLine(transform.position + Vector3.down * 0.4f,
transform.position + Vector3.up * 0.8f);
// 复活点:青色小球 + 落点竖线 + 与触发区的连线;漏配画红叉警示
if (_respawnPoint != null)
{
Gizmos.color = new Color(0f, 0.8f, 1f, 0.9f);
Gizmos.DrawWireSphere(_respawnPoint.position, 0.25f);
Gizmos.DrawLine(_respawnPoint.position + Vector3.down * 0.5f,
_respawnPoint.position + Vector3.up * 0.5f);
Gizmos.color = new Color(0f, 0.8f, 1f, 0.4f);
Gizmos.DrawLine(transform.position, _respawnPoint.position);
}
else
{
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position + new Vector3(-0.3f, -0.3f), transform.position + new Vector3(0.3f, 0.3f));
Gizmos.DrawLine(transform.position + new Vector3(-0.3f, 0.3f), transform.position + new Vector3(0.3f, -0.3f));
}
}
}
}