多轮审查和修复
This commit is contained in:
15
Assets/Data/Events/Boss/EVT_BossFightEnded.asset
Normal file
15
Assets/Data/Events/Boss/EVT_BossFightEnded.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: d3e424c1787e5be4fa918201b1830192, type: 3}
|
||||||
|
m_Name: EVT_BossFightEnded
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/Boss/EVT_BossFightEnded.asset.meta
Normal file
8
Assets/Data/Events/Boss/EVT_BossFightEnded.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6f942c86e6b23b24ca575bc0626ab022
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Boss/EVT_BossFightStarted.asset
Normal file
15
Assets/Data/Events/Boss/EVT_BossFightStarted.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 23dad55c2f7bcc54a92ed61cc6f27c5b, type: 3}
|
||||||
|
m_Name: EVT_BossFightStarted
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/Boss/EVT_BossFightStarted.asset.meta
Normal file
8
Assets/Data/Events/Boss/EVT_BossFightStarted.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 155af2621b0a923468c62edf4fadbc21
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Combat/EVT_DeathScreenConfirmed.asset
Normal file
15
Assets/Data/Events/Combat/EVT_DeathScreenConfirmed.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 07c5881d0d5ca3c42949a79f40939c3e, type: 3}
|
||||||
|
m_Name: EVT_DeathScreenConfirmed
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0f5ae456aa9eccd49975a9ff609705f6
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Combat/EVT_PlayerRespawned.asset
Normal file
15
Assets/Data/Events/Combat/EVT_PlayerRespawned.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 07c5881d0d5ca3c42949a79f40939c3e, type: 3}
|
||||||
|
m_Name: EVT_PlayerRespawned
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/Combat/EVT_PlayerRespawned.asset.meta
Normal file
8
Assets/Data/Events/Combat/EVT_PlayerRespawned.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f5e5756bf4809cf41abc48cf331dc354
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Combat/EVT_RespawnCompleted.asset
Normal file
15
Assets/Data/Events/Combat/EVT_RespawnCompleted.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 07c5881d0d5ca3c42949a79f40939c3e, type: 3}
|
||||||
|
m_Name: EVT_RespawnCompleted
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ca2f6b7e523ce5b4793f42d48fa600ba
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Combat/EVT_RespawnStarted.asset
Normal file
15
Assets/Data/Events/Combat/EVT_RespawnStarted.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 07c5881d0d5ca3c42949a79f40939c3e, type: 3}
|
||||||
|
m_Name: EVT_RespawnStarted
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/Combat/EVT_RespawnStarted.asset.meta
Normal file
8
Assets/Data/Events/Combat/EVT_RespawnStarted.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5945b07508ca07646ba9cbf9a39b7683
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Core/EVT_FadeInRequest.asset
Normal file
15
Assets/Data/Events/Core/EVT_FadeInRequest.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 07c5881d0d5ca3c42949a79f40939c3e, type: 3}
|
||||||
|
m_Name: EVT_FadeInRequest
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/Core/EVT_FadeInRequest.asset.meta
Normal file
8
Assets/Data/Events/Core/EVT_FadeInRequest.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d00a4fdb64a17324bb699756f5f0006c
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Core/EVT_FadeOutRequest.asset
Normal file
15
Assets/Data/Events/Core/EVT_FadeOutRequest.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 07c5881d0d5ca3c42949a79f40939c3e, type: 3}
|
||||||
|
m_Name: EVT_FadeOutRequest
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/Core/EVT_FadeOutRequest.asset.meta
Normal file
8
Assets/Data/Events/Core/EVT_FadeOutRequest.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e0ce042bacf58e24ba78ba34fc16394b
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Core/EVT_GameStateChanged.asset
Normal file
15
Assets/Data/Events/Core/EVT_GameStateChanged.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: c060c20fe37837c408cc5a628f6d8863, type: 3}
|
||||||
|
m_Name: EVT_GameStateChanged
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/Core/EVT_GameStateChanged.asset.meta
Normal file
8
Assets/Data/Events/Core/EVT_GameStateChanged.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6c0a62225f8da694c94cd934fc29b72e
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/Core/EVT_SceneLoaded.asset
Normal file
15
Assets/Data/Events/Core/EVT_SceneLoaded.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 23dad55c2f7bcc54a92ed61cc6f27c5b, type: 3}
|
||||||
|
m_Name: EVT_SceneLoaded
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/Core/EVT_SceneLoaded.asset.meta
Normal file
8
Assets/Data/Events/Core/EVT_SceneLoaded.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9cd668f69c1c8be4e9fdda565b86df0c
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/UI/EVT_FastTravelOpen.asset
Normal file
15
Assets/Data/Events/UI/EVT_FastTravelOpen.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 07c5881d0d5ca3c42949a79f40939c3e, type: 3}
|
||||||
|
m_Name: EVT_FastTravelOpen
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/UI/EVT_FastTravelOpen.asset.meta
Normal file
8
Assets/Data/Events/UI/EVT_FastTravelOpen.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ec4958dcfb565ae4481c47e64838a05d
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/UI/EVT_MapOpen.asset
Normal file
15
Assets/Data/Events/UI/EVT_MapOpen.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 07c5881d0d5ca3c42949a79f40939c3e, type: 3}
|
||||||
|
m_Name: EVT_MapOpen
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/UI/EVT_MapOpen.asset.meta
Normal file
8
Assets/Data/Events/UI/EVT_MapOpen.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c2e3392d42dd3e046819a949ab861b17
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/UI/EVT_ShopOpen.asset
Normal file
15
Assets/Data/Events/UI/EVT_ShopOpen.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 23dad55c2f7bcc54a92ed61cc6f27c5b, type: 3}
|
||||||
|
m_Name: EVT_ShopOpen
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
8
Assets/Data/Events/UI/EVT_ShopOpen.asset.meta
Normal file
8
Assets/Data/Events/UI/EVT_ShopOpen.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8ef6f571506099c49af09e36ba74f97e
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Data/Events/World/EVT_SavePointActivated.asset
Normal file
15
Assets/Data/Events/World/EVT_SavePointActivated.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 23dad55c2f7bcc54a92ed61cc6f27c5b, type: 3}
|
||||||
|
m_Name: EVT_SavePointActivated
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
description:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2f6be6329d5d8cd4e956d00cd29cab96
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Data/Input.meta
Normal file
8
Assets/Data/Input.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 194d26dc1491bb84c8304ad81f146308
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
16
Assets/Data/Input/InputReader.asset
Normal file
16
Assets/Data/Input/InputReader.asset
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
%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: 3945955c08d2670458e14d41e1236946, type: 3}
|
||||||
|
m_Name: InputReader
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
_inputActions: {fileID: -944628639613478452, guid: 6d0341a640ba64043a8cd70f771b962d, type: 3}
|
||||||
|
_onPauseRequested: {fileID: 11400000, guid: 032b9e347985f7a46ac37371acd2f619, type: 2}
|
||||||
8
Assets/Data/Input/InputReader.asset.meta
Normal file
8
Assets/Data/Input/InputReader.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d3d7b464ae0409a47a5b4b627c6b3ca6
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
29
Assets/Data/Player/PLY_PlayerAnimationConfig.asset
Normal file
29
Assets/Data/Player/PLY_PlayerAnimationConfig.asset
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
%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: 5ec15df6b0d345c4f92ba459e89dc02f, type: 3}
|
||||||
|
m_Name: PLY_PlayerAnimationConfig
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
Idle: {fileID: 0}
|
||||||
|
Run: {fileID: 0}
|
||||||
|
Jump: {fileID: 0}
|
||||||
|
Fall: {fileID: 0}
|
||||||
|
Dash: {fileID: 0}
|
||||||
|
WallSlide: {fileID: 0}
|
||||||
|
Hurt: {fileID: 0}
|
||||||
|
Dead: {fileID: 0}
|
||||||
|
UseSpring: {fileID: 0}
|
||||||
|
GroundAttacks: []
|
||||||
|
AirAttack: {fileID: 0}
|
||||||
|
UpAttack: {fileID: 0}
|
||||||
|
DownAttack: {fileID: 0}
|
||||||
|
ParryStart: {fileID: 0}
|
||||||
|
ParrySuccess: {fileID: 0}
|
||||||
8
Assets/Data/Player/PLY_PlayerAnimationConfig.asset.meta
Normal file
8
Assets/Data/Player/PLY_PlayerAnimationConfig.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b8fe5871cf6cc774aac57d984e29e2d9
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
36
Assets/Data/Player/PLY_PlayerMovementConfig.asset
Normal file
36
Assets/Data/Player/PLY_PlayerMovementConfig.asset
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
%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: 81da55e0fcf99d34693cbc5a348225c3, type: 3}
|
||||||
|
m_Name: PLY_PlayerMovementConfig
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
RunSpeed: 7
|
||||||
|
Acceleration: 50
|
||||||
|
Deceleration: 80
|
||||||
|
JumpForce: 18
|
||||||
|
CoyoteTime: 0.12
|
||||||
|
FallGravityMult: 2.5
|
||||||
|
MaxFallSpeed: 20
|
||||||
|
DashSpeed: 20
|
||||||
|
DashDuration: 0.18
|
||||||
|
DashCooldown: 0.4
|
||||||
|
MaxAerialDashes: 1
|
||||||
|
WallSlideSpeed: 2
|
||||||
|
WallJumpForceX: 12
|
||||||
|
WallJumpForceY: 16
|
||||||
|
WallRayLength: 0.55
|
||||||
|
WallRayOffsetY: 0.2
|
||||||
|
WallGrabMaxHeightGain: 0.5
|
||||||
|
WallGrabReleaseDelay: 0.08
|
||||||
|
WallJumpBackForceX: 14
|
||||||
|
WallJumpAwayForceX: 10
|
||||||
|
WallJumpAwayForceY: 18
|
||||||
|
WallJumpInputLockDuration: 0.15
|
||||||
8
Assets/Data/Player/PLY_PlayerMovementConfig.asset.meta
Normal file
8
Assets/Data/Player/PLY_PlayerMovementConfig.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ed5e9f8d24bf223479a3bcbbb458294d
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
23
Assets/Data/Player/PLY_PlayerStats.asset
Normal file
23
Assets/Data/Player/PLY_PlayerStats.asset
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
%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: 31a9f22bef1315643bf5a49f2a6edd2b, type: 3}
|
||||||
|
m_Name: PLY_PlayerStats
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
MaxHP: 5
|
||||||
|
MaxSoulPower: 100
|
||||||
|
MaxSpiritPower: 100
|
||||||
|
SpiritRegenRate: 5
|
||||||
|
MaxSpringCharges: 3
|
||||||
|
SpringHealAmount: 2
|
||||||
|
SpringKillThreshold: 4
|
||||||
|
InvincibilityDuration: 0.6
|
||||||
|
InitialGeo: 0
|
||||||
8
Assets/Data/Player/PLY_PlayerStats.asset.meta
Normal file
8
Assets/Data/Player/PLY_PlayerStats.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e1705c75c3139e94a8b1c9e90002d364
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Resources/Art.meta
Normal file
8
Assets/Resources/Art.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 55368645c788ddc4994002d987e16ed8
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Resources/Art/Builds.meta
Normal file
8
Assets/Resources/Art/Builds.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8dc030a56b7172d40934e0f5c7f186a6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Resources/Art/Effects.meta
Normal file
8
Assets/Resources/Art/Effects.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ed95febcdefa7d74caf641da93e597dd
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Resources/Art/Effects/Textures.meta
Normal file
8
Assets/Resources/Art/Effects/Textures.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1de403e36134fbe4cb817afac9eb9f4d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Resources/Art/Effects/Textures/Fx_Ring03.png
Normal file
BIN
Assets/Resources/Art/Effects/Textures/Fx_Ring03.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
123
Assets/Resources/Art/Effects/Textures/Fx_Ring03.png.meta
Normal file
123
Assets/Resources/Art/Effects/Textures/Fx_Ring03.png.meta
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 17cf2fefc7891ab47a24bb35c6089635
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 12
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 1
|
||||||
|
sRGBTexture: 1
|
||||||
|
linearTexture: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapsPreserveCoverage: 0
|
||||||
|
alphaTestReferenceValue: 0.5
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: 0.25
|
||||||
|
normalMapFilter: 0
|
||||||
|
isReadable: 0
|
||||||
|
streamingMipmaps: 0
|
||||||
|
streamingMipmapsPriority: 0
|
||||||
|
vTOnly: 0
|
||||||
|
ignoreMasterTextureLimit: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 6
|
||||||
|
cubemapConvolution: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: 1
|
||||||
|
maxTextureSize: 2048
|
||||||
|
textureSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
filterMode: 1
|
||||||
|
aniso: 4
|
||||||
|
mipBias: 0
|
||||||
|
wrapU: 1
|
||||||
|
wrapV: 1
|
||||||
|
wrapW: 1
|
||||||
|
nPOTScale: 1
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 0
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: 0.5, y: 0.5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
spriteGenerateFallbackPhysicsShape: 1
|
||||||
|
alphaUsage: 1
|
||||||
|
alphaIsTransparency: 1
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 0
|
||||||
|
textureShape: 1
|
||||||
|
singleChannelComponent: 0
|
||||||
|
flipbookRows: 1
|
||||||
|
flipbookColumns: 1
|
||||||
|
maxTextureSizeSet: 0
|
||||||
|
compressionQualitySet: 0
|
||||||
|
textureFormatSet: 0
|
||||||
|
ignorePngGamma: 0
|
||||||
|
applyGammaDecoding: 0
|
||||||
|
cookieLightType: 1
|
||||||
|
platformSettings:
|
||||||
|
- serializedVersion: 3
|
||||||
|
buildTarget: DefaultTexturePlatform
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 3
|
||||||
|
buildTarget: Standalone
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 3
|
||||||
|
buildTarget: Server
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID:
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
spritePackingTag:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
pSDShowRemoveMatteOption: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 970b399c05a553947883805f7dad7770
|
guid: 6372e5b8e07d7ae4eb37a184fc8e912d
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
|
|||||||
2606
Assets/Scenes/TestRoom.unity
Normal file
2606
Assets/Scenes/TestRoom.unity
Normal file
File diff suppressed because it is too large
Load Diff
7
Assets/Scenes/TestRoom.unity.meta
Normal file
7
Assets/Scenes/TestRoom.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 970b399c05a553947883805f7dad7770
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Scenes/Testings.meta
Normal file
8
Assets/Scenes/Testings.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8d4bcc3c6c0b6b6469d1a35e35ba22be
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
789419
Assets/Scenes/Testings/Build01.prefab
Normal file
789419
Assets/Scenes/Testings/Build01.prefab
Normal file
File diff suppressed because it is too large
Load Diff
7
Assets/Scenes/Testings/Build01.prefab.meta
Normal file
7
Assets/Scenes/Testings/Build01.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 880352b798b1049499d1262ddc9d8b7f
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
286
Assets/Scenes/Testings/Test.prefab
Normal file
286
Assets/Scenes/Testings/Test.prefab
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &3419683162514475710
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 7063479915286433301}
|
||||||
|
- component: {fileID: 1913988771854760432}
|
||||||
|
- component: {fileID: 7242343237021918190}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Layer1
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &7063479915286433301
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3419683162514475710}
|
||||||
|
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: 8299643768118427544}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1839735485 &1913988771854760432
|
||||||
|
Tilemap:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3419683162514475710}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_Tiles:
|
||||||
|
- first: {x: -1, y: -2, z: 0}
|
||||||
|
second:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_TileIndex: 4
|
||||||
|
m_TileSpriteIndex: 4
|
||||||
|
m_TileMatrixIndex: 0
|
||||||
|
m_TileColorIndex: 0
|
||||||
|
m_TileObjectToInstantiateIndex: 65535
|
||||||
|
dummyAlignment: 0
|
||||||
|
m_AllTileFlags: 1073741825
|
||||||
|
- first: {x: 0, y: -2, z: 0}
|
||||||
|
second:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_TileIndex: 5
|
||||||
|
m_TileSpriteIndex: 5
|
||||||
|
m_TileMatrixIndex: 0
|
||||||
|
m_TileColorIndex: 0
|
||||||
|
m_TileObjectToInstantiateIndex: 65535
|
||||||
|
dummyAlignment: 0
|
||||||
|
m_AllTileFlags: 1073741825
|
||||||
|
- first: {x: -1, y: -1, z: 0}
|
||||||
|
second:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_TileIndex: 2
|
||||||
|
m_TileSpriteIndex: 2
|
||||||
|
m_TileMatrixIndex: 0
|
||||||
|
m_TileColorIndex: 0
|
||||||
|
m_TileObjectToInstantiateIndex: 65535
|
||||||
|
dummyAlignment: 0
|
||||||
|
m_AllTileFlags: 1073741825
|
||||||
|
- first: {x: 0, y: -1, z: 0}
|
||||||
|
second:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_TileIndex: 3
|
||||||
|
m_TileSpriteIndex: 3
|
||||||
|
m_TileMatrixIndex: 0
|
||||||
|
m_TileColorIndex: 0
|
||||||
|
m_TileObjectToInstantiateIndex: 65535
|
||||||
|
dummyAlignment: 0
|
||||||
|
m_AllTileFlags: 1073741825
|
||||||
|
- first: {x: -1, y: 0, z: 0}
|
||||||
|
second:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_TileIndex: 0
|
||||||
|
m_TileSpriteIndex: 0
|
||||||
|
m_TileMatrixIndex: 0
|
||||||
|
m_TileColorIndex: 0
|
||||||
|
m_TileObjectToInstantiateIndex: 65535
|
||||||
|
dummyAlignment: 0
|
||||||
|
m_AllTileFlags: 1073741825
|
||||||
|
- first: {x: 0, y: 0, z: 0}
|
||||||
|
second:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_TileIndex: 1
|
||||||
|
m_TileSpriteIndex: 1
|
||||||
|
m_TileMatrixIndex: 0
|
||||||
|
m_TileColorIndex: 0
|
||||||
|
m_TileObjectToInstantiateIndex: 65535
|
||||||
|
dummyAlignment: 0
|
||||||
|
m_AllTileFlags: 1073741825
|
||||||
|
m_AnimatedTiles: {}
|
||||||
|
m_TileAssetArray:
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: 11400000, guid: 4ca4f5908b7d5364cb0287c046251970, type: 2}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: 11400000, guid: c1a00617dda76b941b08860cf7752bda, type: 2}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: 11400000, guid: 40d643345af17ba4a8ae9853163f97bf, type: 2}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: 11400000, guid: a75c0ec63e8d0614ca0ec8f41dd73214, type: 2}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: 11400000, guid: 188b71f83e9bdfd48aad2a153881b1cb, type: 2}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: 11400000, guid: 41c28c158dd92564ea3cc8370f13763d, type: 2}
|
||||||
|
m_TileSpriteArray:
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: -1728761622, guid: fe57cc6797ae6534ca109ccaa9bb1a93, type: 3}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: -842361761, guid: fe57cc6797ae6534ca109ccaa9bb1a93, type: 3}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: -1180015184, guid: fe57cc6797ae6534ca109ccaa9bb1a93, type: 3}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: 428007125, guid: fe57cc6797ae6534ca109ccaa9bb1a93, type: 3}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: 1676777230, guid: fe57cc6797ae6534ca109ccaa9bb1a93, type: 3}
|
||||||
|
- m_RefCount: 1
|
||||||
|
m_Data: {fileID: -1797864058, guid: fe57cc6797ae6534ca109ccaa9bb1a93, type: 3}
|
||||||
|
m_TileMatrixArray:
|
||||||
|
- m_RefCount: 6
|
||||||
|
m_Data:
|
||||||
|
e00: 1
|
||||||
|
e01: 0
|
||||||
|
e02: 0
|
||||||
|
e03: 0
|
||||||
|
e10: 0
|
||||||
|
e11: 1
|
||||||
|
e12: 0
|
||||||
|
e13: 0
|
||||||
|
e20: 0
|
||||||
|
e21: 0
|
||||||
|
e22: 1
|
||||||
|
e23: 0
|
||||||
|
e30: 0
|
||||||
|
e31: 0
|
||||||
|
e32: 0
|
||||||
|
e33: 1
|
||||||
|
m_TileColorArray:
|
||||||
|
- m_RefCount: 6
|
||||||
|
m_Data: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_TileObjectToInstantiateArray: []
|
||||||
|
m_AnimationFrameRate: 1
|
||||||
|
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_Origin: {x: -1, y: -2, z: 0}
|
||||||
|
m_Size: {x: 2, y: 3, z: 1}
|
||||||
|
m_TileAnchor: {x: 0.5, y: 0.5, z: 0}
|
||||||
|
m_TileOrientation: 0
|
||||||
|
m_TileOrientationMatrix:
|
||||||
|
e00: 1
|
||||||
|
e01: 0
|
||||||
|
e02: 0
|
||||||
|
e03: 0
|
||||||
|
e10: 0
|
||||||
|
e11: 1
|
||||||
|
e12: 0
|
||||||
|
e13: 0
|
||||||
|
e20: 0
|
||||||
|
e21: 0
|
||||||
|
e22: 1
|
||||||
|
e23: 0
|
||||||
|
e30: 0
|
||||||
|
e31: 0
|
||||||
|
e32: 0
|
||||||
|
e33: 1
|
||||||
|
--- !u!483693784 &7242343237021918190
|
||||||
|
TilemapRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3419683162514475710}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 0
|
||||||
|
m_ReceiveShadows: 0
|
||||||
|
m_DynamicOccludee: 0
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 0
|
||||||
|
m_ReflectionProbeUsage: 0
|
||||||
|
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_ChunkSize: {x: 32, y: 32, z: 32}
|
||||||
|
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||||
|
m_MaxChunkCount: 16
|
||||||
|
m_MaxFrameAge: 16
|
||||||
|
m_SortOrder: 0
|
||||||
|
m_Mode: 0
|
||||||
|
m_DetectChunkCullingBounds: 0
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
--- !u!1 &6022835947839004827
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 8299643768118427544}
|
||||||
|
- component: {fileID: 6367259315250269812}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Test
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &8299643768118427544
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6022835947839004827}
|
||||||
|
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:
|
||||||
|
- {fileID: 7063479915286433301}
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!156049354 &6367259315250269812
|
||||||
|
Grid:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6022835947839004827}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CellSize: {x: 1, y: 1, z: 0}
|
||||||
|
m_CellGap: {x: 0, y: 0, z: 0}
|
||||||
|
m_CellLayout: 0
|
||||||
|
m_CellSwizzle: 0
|
||||||
|
--- !u!114 &8034214933598048606
|
||||||
|
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: 12395, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
m_Name: Palette Settings
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
cellSizing: 0
|
||||||
|
m_TransparencySortMode: 0
|
||||||
|
m_TransparencySortAxis: {x: 0, y: 0, z: 1}
|
||||||
7
Assets/Scenes/Testings/Test.prefab.meta
Normal file
7
Assets/Scenes/Testings/Test.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7029ac4d5ec09684ca31cc0c69db189a
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
31
Assets/Scripts/Animation/AnimationEventBinder.cs
Normal file
31
Assets/Scripts/Animation/AnimationEventBinder.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Animancer;
|
||||||
|
|
||||||
|
namespace BaseGames.Animation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 静态工具类:将 AnimationEventConfigSO 中声明的事件注入 Animancer ClipTransition。
|
||||||
|
/// 使用 Animancer Pro API:ClipTransition.Events.SetCallback(normalizedTime, Action)。
|
||||||
|
/// 调用时机:MonoBehaviour.Awake(在 Animancer 播放前完成绑定)。
|
||||||
|
/// </summary>
|
||||||
|
public static class AnimationEventBinder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将 config 中的所有事件回调注入到 clip 的 Animancer 事件系统。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clip">目标 Animancer ClipTransition。</param>
|
||||||
|
/// <param name="config">事件配置资产(null 时静默跳过)。</param>
|
||||||
|
/// <param name="receiver">事件接收者(通常是同一 MonoBehaviour)。</param>
|
||||||
|
public static void Bind(ClipTransition clip, AnimationEventConfigSO config, IAnimationEventHandler receiver)
|
||||||
|
{
|
||||||
|
if (clip == null || config == null || receiver == null) return;
|
||||||
|
|
||||||
|
foreach (var entry in config.SortedEvents)
|
||||||
|
{
|
||||||
|
// 捕获循环变量,避免闭包陷阱
|
||||||
|
var captured = entry;
|
||||||
|
clip.Events.Add(captured.normalizedTime, () =>
|
||||||
|
receiver.HandleEvent(captured.eventType, captured.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: c7d6fe0521388084e83314560a394951
|
guid: e9c16d7d7a6978a4c98a745261fbbf4f
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
64
Assets/Scripts/Animation/AnimationEventConfigSO.cs
Normal file
64
Assets/Scripts/Animation/AnimationEventConfigSO.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace BaseGames.Animation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 动画事件配置资产(架构 §AnimationModule)。
|
||||||
|
/// 为指定 AnimationClip 声明一组事件时机,由 AnimationEventBinder 在运行时注入 Animancer 回调。
|
||||||
|
///
|
||||||
|
/// 使用方式:
|
||||||
|
/// 1. 在 Inspector 中创建此资产(菜单:Animation/EventConfig)。
|
||||||
|
/// 2. 配置 TargetClip 和 Events 列表。
|
||||||
|
/// 3. 在 MonoBehaviour.Awake 中:AnimationEventBinder.Bind(clip, config, this)。
|
||||||
|
/// </summary>
|
||||||
|
[CreateAssetMenu(menuName = "Animation/EventConfig")]
|
||||||
|
public class AnimationEventConfigSO : ScriptableObject
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public struct EventEntry
|
||||||
|
{
|
||||||
|
public AnimationEventType eventType;
|
||||||
|
|
||||||
|
[Range(0f, 1f)]
|
||||||
|
[Tooltip("事件在动画中的归一化时间(0 = 开始,1 = 结束)。")]
|
||||||
|
public float normalizedTime;
|
||||||
|
|
||||||
|
[Tooltip("附加字符串数据(可空)。用于 payload 参数。")]
|
||||||
|
public string data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Header("绑定的动画片段")]
|
||||||
|
public AnimationClip targetClip;
|
||||||
|
|
||||||
|
[Header("事件时机列表(归一化时间)")]
|
||||||
|
public EventEntry[] events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于编辑器工具验证:记录创建 Config 时 Clip 的实际帧数,
|
||||||
|
/// 若 Clip 被替换(长度漂移 >5 帧)则 EventConfigEditor 显示警告。
|
||||||
|
/// </summary>
|
||||||
|
[HideInInspector] public float ExpectedClipLength = -1f;
|
||||||
|
|
||||||
|
// ── 运行时查询 ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>按归一化时间升序返回所有事件(保证 SetCallback 顺序稳定)。</summary>
|
||||||
|
public IEnumerable<EventEntry> SortedEvents =>
|
||||||
|
events != null
|
||||||
|
? events.OrderBy(e => e.normalizedTime)
|
||||||
|
: Enumerable.Empty<EventEntry>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回第一个匹配类型的归一化时间;未找到时返回 -1。
|
||||||
|
/// </summary>
|
||||||
|
public float GetNormalizedTime(AnimationEventType eventType)
|
||||||
|
{
|
||||||
|
if (events == null) return -1f;
|
||||||
|
foreach (var e in events)
|
||||||
|
if (e.eventType == eventType) return e.normalizedTime;
|
||||||
|
return -1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 1dfc988231a6ac14a9aa035ba1719ab0
|
guid: 3dc3625236c5f8747ae7352e78c01137
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
49
Assets/Scripts/Animation/AnimationEventType.cs
Normal file
49
Assets/Scripts/Animation/AnimationEventType.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
namespace BaseGames.Animation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 动画事件类型枚举(架构 §AnimationModule)。
|
||||||
|
/// 用作 AnimationEventConfigSO 中事件时机的分类标签,
|
||||||
|
/// 与 AnimationEventBinder 配合将 Animancer ClipTransition 回调路由到 IAnimationEventHandler。
|
||||||
|
/// </summary>
|
||||||
|
public enum AnimationEventType
|
||||||
|
{
|
||||||
|
// ── 命中判定 ──────────────────────────────────────────────────────
|
||||||
|
EnableHitBox,
|
||||||
|
DisableHitBox,
|
||||||
|
AttackImpact, // 攻击命中瞬间反馈(不依赖特定 HitBox)
|
||||||
|
|
||||||
|
// ── 招架窗口 ──────────────────────────────────────────────────────
|
||||||
|
EnableParryWindow,
|
||||||
|
DisableParryWindow,
|
||||||
|
|
||||||
|
// ── 无敌帧 ────────────────────────────────────────────────────────
|
||||||
|
EnableIFrame,
|
||||||
|
DisableIFrame,
|
||||||
|
|
||||||
|
// ── 移动反馈 ──────────────────────────────────────────────────────
|
||||||
|
Footstep,
|
||||||
|
LandImpact,
|
||||||
|
JumpLaunch,
|
||||||
|
|
||||||
|
// ── 交互 ──────────────────────────────────────────────────────────
|
||||||
|
EnableInteract,
|
||||||
|
|
||||||
|
// ── 通用反馈 ──────────────────────────────────────────────────────
|
||||||
|
TriggerFeedback, // payload:FeedbackPreset 名称
|
||||||
|
PlaySFX, // payload:SFX Id
|
||||||
|
|
||||||
|
// ── 敌方专用 ──────────────────────────────────────────────────────
|
||||||
|
SpawnProjectile, // payload:弹幕配置 Id
|
||||||
|
RoarStart,
|
||||||
|
RoarEnd,
|
||||||
|
PhaseTwoStart,
|
||||||
|
|
||||||
|
// ── 玩家取消窗口 ──────────────────────────────────────────────────
|
||||||
|
CancelWindowOpen,
|
||||||
|
CancelWindowClose,
|
||||||
|
|
||||||
|
// ── 状态机钩子 ────────────────────────────────────────────────────
|
||||||
|
StateTransition, // payload:目标状态 Id
|
||||||
|
AnimationComplete, // payload:上下文字符串(可空)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 14d8a3e3d7371e54eb25a5d4dded4645
|
guid: 97412b5f50276484fa226d97b8c3769b
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -9,7 +9,12 @@
|
|||||||
"rootNamespace": "BaseGames.Animation",
|
"rootNamespace": "BaseGames.Animation",
|
||||||
"references": [
|
"references": [
|
||||||
"BaseGames.Core.Events",
|
"BaseGames.Core.Events",
|
||||||
"Kybernetik.Animancer"
|
"Kybernetik.Animancer",
|
||||||
|
"BaseGames.Combat",
|
||||||
|
"BaseGames.Parry",
|
||||||
|
"BaseGames.Feedback",
|
||||||
|
"BaseGames.Player",
|
||||||
|
"BaseGames.Enemies"
|
||||||
],
|
],
|
||||||
"autoReferenced": true,
|
"autoReferenced": true,
|
||||||
"overrideReferences": false,
|
"overrideReferences": false,
|
||||||
|
|||||||
111
Assets/Scripts/Animation/EnemyAnimationEvents.cs
Normal file
111
Assets/Scripts/Animation/EnemyAnimationEvents.cs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
using System;
|
||||||
|
using Animancer;
|
||||||
|
using BaseGames.Combat;
|
||||||
|
using BaseGames.Enemies;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace BaseGames.Animation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人动画事件接收器(架构 §AnimationModule)。
|
||||||
|
/// 挂载于敌人 Prefab 根节点。负责将 Animancer 动画时间点回调路由到
|
||||||
|
/// HitBox 激活、弹幕生成、嘶吼状态、二阶段切换等系统。
|
||||||
|
///
|
||||||
|
/// 使用方式:
|
||||||
|
/// 1. 在 Inspector 中填充 _hitBoxes、_enemy。
|
||||||
|
/// 2. 将每条 ClipTransition + AnimationEventConfigSO 配对添加到 _bindings。
|
||||||
|
/// 3. Awake 中 AnimationEventBinder.Bind 自动完成注入。
|
||||||
|
/// </summary>
|
||||||
|
public class EnemyAnimationEvents : MonoBehaviour, IAnimationEventHandler
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public struct EventBinding
|
||||||
|
{
|
||||||
|
[Tooltip("Animancer ClipTransition(需与动画组件中同一引用绑定)。")]
|
||||||
|
public ClipTransition clip;
|
||||||
|
|
||||||
|
[Tooltip("对应的 AnimationEventConfigSO 资产。")]
|
||||||
|
public AnimationEventConfigSO config;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Header("子系统引用")]
|
||||||
|
[SerializeField] private HitBox[] _hitBoxes;
|
||||||
|
[SerializeField] private EnemyBase _enemy;
|
||||||
|
|
||||||
|
[Header("事件绑定(每个 Clip 对应一个配置资产)")]
|
||||||
|
[SerializeField] private EventBinding[] _bindings;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (_enemy == null)
|
||||||
|
_enemy = GetComponentInParent<EnemyBase>();
|
||||||
|
|
||||||
|
foreach (var b in _bindings)
|
||||||
|
AnimationEventBinder.Bind(b.clip, b.config, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── IAnimationEventHandler ─────────────────────────────────────────
|
||||||
|
|
||||||
|
public void HandleEvent(AnimationEventType type, string payload)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
// ── 命中判定 ──────────────────────────────────────────────
|
||||||
|
case AnimationEventType.EnableHitBox:
|
||||||
|
SetHitBoxActive(payload, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.DisableHitBox:
|
||||||
|
SetHitBoxActive(payload, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 弹幕 / 技能 ───────────────────────────────────────────
|
||||||
|
case AnimationEventType.SpawnProjectile:
|
||||||
|
_enemy?.SpawnProjectile(payload);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 嘶吼 ──────────────────────────────────────────────────
|
||||||
|
case AnimationEventType.RoarStart:
|
||||||
|
_enemy?.SetRoaring(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.RoarEnd:
|
||||||
|
_enemy?.SetRoaring(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 二阶段切换 ────────────────────────────────────────────
|
||||||
|
case AnimationEventType.PhaseTwoStart:
|
||||||
|
_enemy?.TriggerPhaseTwo();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 状态机钩子 ────────────────────────────────────────────
|
||||||
|
case AnimationEventType.AnimationComplete:
|
||||||
|
_enemy?.OnAnimationComplete(payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 私有辅助 ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按 Id 激活或停用 HitBox。
|
||||||
|
/// payload 为空时操作所有 HitBox;非空时仅操作 Id 匹配的 HitBox。
|
||||||
|
/// </summary>
|
||||||
|
private void SetHitBoxActive(string id, bool active)
|
||||||
|
{
|
||||||
|
if (_hitBoxes == null) return;
|
||||||
|
|
||||||
|
bool matchAll = string.IsNullOrEmpty(id);
|
||||||
|
|
||||||
|
foreach (var hb in _hitBoxes)
|
||||||
|
{
|
||||||
|
if (hb == null) continue;
|
||||||
|
if (matchAll || hb.Id == id)
|
||||||
|
{
|
||||||
|
if (active) hb.Activate();
|
||||||
|
else hb.Deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: b9c4356cd693b604bb0889f9538eb13e
|
guid: 13003b40ed7bd164d8d90eb58f0548ec
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
17
Assets/Scripts/Animation/IAnimationEventHandler.cs
Normal file
17
Assets/Scripts/Animation/IAnimationEventHandler.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace BaseGames.Animation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 动画事件接收接口(架构 §AnimationModule)。
|
||||||
|
/// 由 PlayerAnimationEvents / EnemyAnimationEvents 实现。
|
||||||
|
/// AnimationEventBinder 将 Animancer 回调路由至此接口,避免硬耦合。
|
||||||
|
/// </summary>
|
||||||
|
public interface IAnimationEventHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 处理由 Animancer ClipTransition 触发的动画事件。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">事件类型。</param>
|
||||||
|
/// <param name="payload">附加字符串数据(可为 null 或空)。</param>
|
||||||
|
void HandleEvent(AnimationEventType type, string payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Animation/IAnimationEventHandler.cs.meta
Normal file
11
Assets/Scripts/Animation/IAnimationEventHandler.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f2c57ff98093620448d1f7c8ce6d0511
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
149
Assets/Scripts/Animation/PlayerAnimationEvents.cs
Normal file
149
Assets/Scripts/Animation/PlayerAnimationEvents.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
using System;
|
||||||
|
using Animancer;
|
||||||
|
using BaseGames.Combat;
|
||||||
|
using BaseGames.Feedback;
|
||||||
|
using BaseGames.Parry;
|
||||||
|
using BaseGames.Player;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace BaseGames.Animation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家动画事件接收器(架构 §AnimationModule)。
|
||||||
|
/// 挂载于玩家 Prefab 根节点。负责将 Animancer 动画时间点回调路由到
|
||||||
|
/// HitBox 激活、招架窗口、无敌帧、移动取消窗口、反馈等系统。
|
||||||
|
///
|
||||||
|
/// 使用方式:
|
||||||
|
/// 1. 在 Inspector 中填充 _hitBoxes、_hurtBox、_mover、_parrySystem。
|
||||||
|
/// 2. 将每条 ClipTransition + AnimationEventConfigSO 配对添加到 _bindings。
|
||||||
|
/// 3. Awake 中 AnimationEventBinder.Bind 自动完成注入。
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerAnimationEvents : MonoBehaviour, IAnimationEventHandler
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public struct EventBinding
|
||||||
|
{
|
||||||
|
[Tooltip("Animancer ClipTransition(需与动画组件中同一引用绑定)。")]
|
||||||
|
public ClipTransition clip;
|
||||||
|
|
||||||
|
[Tooltip("对应的 AnimationEventConfigSO 资产。")]
|
||||||
|
public AnimationEventConfigSO config;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Header("子系统引用")]
|
||||||
|
[SerializeField] private HitBox[] _hitBoxes;
|
||||||
|
[SerializeField] private HurtBox _hurtBox;
|
||||||
|
[SerializeField] private PlayerMovement _mover;
|
||||||
|
[SerializeField] private ParrySystem _parrySystem;
|
||||||
|
|
||||||
|
[Header("事件绑定(每个 Clip 对应一个配置资产)")]
|
||||||
|
[SerializeField] private EventBinding[] _bindings;
|
||||||
|
|
||||||
|
private IFeedbackPlayer _feedback;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
// IFeedbackPlayer 由父层或同层实现(PlayerFeedback / NullFeedbackPlayer)
|
||||||
|
_feedback = GetComponentInParent<IFeedbackPlayer>()
|
||||||
|
?? NullFeedbackPlayer.Instance;
|
||||||
|
|
||||||
|
foreach (var b in _bindings)
|
||||||
|
AnimationEventBinder.Bind(b.clip, b.config, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── IAnimationEventHandler ─────────────────────────────────────────
|
||||||
|
|
||||||
|
public void HandleEvent(AnimationEventType type, string payload)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
// ── 命中判定 ──────────────────────────────────────────────
|
||||||
|
case AnimationEventType.EnableHitBox:
|
||||||
|
SetHitBoxActive(payload, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.DisableHitBox:
|
||||||
|
SetHitBoxActive(payload, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.AttackImpact:
|
||||||
|
_feedback.PlayAttackWhoosh();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 招架窗口 ──────────────────────────────────────────────
|
||||||
|
case AnimationEventType.EnableParryWindow:
|
||||||
|
_parrySystem?.OpenParryWindow();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.DisableParryWindow:
|
||||||
|
_parrySystem?.CloseParryWindow();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 无敌帧 ────────────────────────────────────────────────
|
||||||
|
case AnimationEventType.EnableIFrame:
|
||||||
|
_hurtBox?.SetInvincible(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.DisableIFrame:
|
||||||
|
_hurtBox?.SetInvincible(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 移动反馈 ──────────────────────────────────────────────
|
||||||
|
case AnimationEventType.LandImpact:
|
||||||
|
_feedback.PlayLandImpact();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.JumpLaunch:
|
||||||
|
_feedback.PlayJumpLaunch();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.Footstep:
|
||||||
|
_feedback.PlayFootstep();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 取消窗口 ──────────────────────────────────────────────
|
||||||
|
case AnimationEventType.CancelWindowOpen:
|
||||||
|
_mover?.SetCancelWindowOpen(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.CancelWindowClose:
|
||||||
|
_mover?.SetCancelWindowOpen(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ── 通用反馈 ──────────────────────────────────────────────
|
||||||
|
case AnimationEventType.TriggerFeedback:
|
||||||
|
if (!string.IsNullOrEmpty(payload))
|
||||||
|
_feedback.TriggerPreset(payload);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AnimationEventType.PlaySFX:
|
||||||
|
if (!string.IsNullOrEmpty(payload))
|
||||||
|
_feedback.PlaySFXById(payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 私有辅助 ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按 Id 激活或停用 HitBox。
|
||||||
|
/// payload 为空时操作所有 HitBox;非空时仅操作 Id 匹配的 HitBox。
|
||||||
|
/// </summary>
|
||||||
|
private void SetHitBoxActive(string id, bool active)
|
||||||
|
{
|
||||||
|
if (_hitBoxes == null) return;
|
||||||
|
|
||||||
|
bool matchAll = string.IsNullOrEmpty(id);
|
||||||
|
|
||||||
|
foreach (var hb in _hitBoxes)
|
||||||
|
{
|
||||||
|
if (hb == null) continue;
|
||||||
|
if (matchAll || hb.Id == id)
|
||||||
|
{
|
||||||
|
if (active) hb.Activate();
|
||||||
|
else hb.Deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Animation/PlayerAnimationEvents.cs.meta
Normal file
11
Assets/Scripts/Animation/PlayerAnimationEvents.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4e3a90eca47d12a49a07be7f38a376e8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
// Placeholder to prevent asmdef-no-scripts warning.
|
|
||||||
namespace BaseGames.Animation { }
|
|
||||||
|
|
||||||
52
Assets/Scripts/Audio/AudioEventSO.cs
Normal file
52
Assets/Scripts/Audio/AudioEventSO.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Audio;
|
||||||
|
|
||||||
|
namespace BaseGames.Audio
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 音效事件 ScriptableObject。随机从 Clips 中选取一条播放,
|
||||||
|
/// 并在 Volume / Pitch 区间内随机变化,增强音效多样性。
|
||||||
|
/// </summary>
|
||||||
|
[CreateAssetMenu(menuName = "Audio/AudioEvent")]
|
||||||
|
public class AudioEventSO : ScriptableObject
|
||||||
|
{
|
||||||
|
[SerializeField] private AudioClip[] _clips;
|
||||||
|
[SerializeField] private float _volumeMin = 0.9f;
|
||||||
|
[SerializeField] private float _volumeMax = 1.0f;
|
||||||
|
[SerializeField] private float _pitchMin = 0.95f;
|
||||||
|
[SerializeField] private float _pitchMax = 1.05f;
|
||||||
|
[SerializeField] private AudioMixerGroup _mixerGroup;
|
||||||
|
|
||||||
|
public AudioClip PickClip()
|
||||||
|
{
|
||||||
|
if (_clips == null || _clips.Length == 0) return null;
|
||||||
|
return _clips[Random.Range(0, _clips.Length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public float PickVolume() => Random.Range(_volumeMin, _volumeMax);
|
||||||
|
|
||||||
|
/// <summary>通过已有的 AudioSource 播放(适用于场景内固定位置音效)。</summary>
|
||||||
|
public void Play(AudioSource source)
|
||||||
|
{
|
||||||
|
var clip = PickClip();
|
||||||
|
if (clip == null || source == null) return;
|
||||||
|
|
||||||
|
source.clip = clip;
|
||||||
|
source.volume = PickVolume();
|
||||||
|
source.pitch = Random.Range(_pitchMin, _pitchMax);
|
||||||
|
source.outputAudioMixerGroup = _mixerGroup;
|
||||||
|
source.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>通过 AudioSource.PlayOneShot 播放(不打断当前音效)。</summary>
|
||||||
|
public void PlayOneShot(AudioSource source)
|
||||||
|
{
|
||||||
|
var clip = PickClip();
|
||||||
|
if (clip == null || source == null) return;
|
||||||
|
|
||||||
|
source.outputAudioMixerGroup = _mixerGroup;
|
||||||
|
source.pitch = Random.Range(_pitchMin, _pitchMax);
|
||||||
|
source.PlayOneShot(clip, PickVolume());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Audio/AudioEventSO.cs.meta
Normal file
11
Assets/Scripts/Audio/AudioEventSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 356d306cd9d1eaa46b9d283761bcba54
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -22,8 +22,22 @@ namespace BaseGames.Audio
|
|||||||
[SerializeField] private AudioSource _bgmSourceB;
|
[SerializeField] private AudioSource _bgmSourceB;
|
||||||
|
|
||||||
[Header("SFX Pool(建议 6 个,均路由到 SFX MixerGroup)")]
|
[Header("SFX Pool(建议 6 个,均路由到 SFX MixerGroup)")]
|
||||||
|
[Tooltip("轮转池大小:同帧触发数超过此值时最旧的音效会被打断。建议 6~8 个。")]
|
||||||
[SerializeField] private AudioSource[] _sfxSources;
|
[SerializeField] private AudioSource[] _sfxSources;
|
||||||
|
|
||||||
|
[Header("Audio Config(BGM 映射)")]
|
||||||
|
[SerializeField] private AudioConfigSO _audioConfig;
|
||||||
|
|
||||||
|
[Header("SFX 注册表(key → AudioEventSO)")]
|
||||||
|
[SerializeField] private AudioEventEntry[] _sfxRegistry;
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public struct AudioEventEntry
|
||||||
|
{
|
||||||
|
public string Key;
|
||||||
|
public AudioEventSO Event;
|
||||||
|
}
|
||||||
|
|
||||||
[Header("Event Channels - Subscribe")]
|
[Header("Event Channels - Subscribe")]
|
||||||
[SerializeField] private VoidEventChannelSO _onPlayerDied;
|
[SerializeField] private VoidEventChannelSO _onPlayerDied;
|
||||||
|
|
||||||
@@ -31,49 +45,61 @@ namespace BaseGames.Audio
|
|||||||
private AudioSource _inactiveBGMSource;
|
private AudioSource _inactiveBGMSource;
|
||||||
private Coroutine _crossfadeCoroutine;
|
private Coroutine _crossfadeCoroutine;
|
||||||
private int _sfxRoundRobin;
|
private int _sfxRoundRobin;
|
||||||
|
private Dictionary<string, AudioEventSO> _sfxLookup;
|
||||||
// ── 遗留单例(已废弃;新代码请使用 ServiceLocator.Get<IAudioService>())────────────
|
private readonly CompositeDisposable _subs = new();
|
||||||
[System.Obsolete("Use ServiceLocator.Get<IAudioService>() instead.")]
|
|
||||||
public static AudioManager Instance { get; private set; }
|
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0618
|
if (ServiceLocator.GetOrDefault<IAudioService>() != null) { Destroy(gameObject); return; }
|
||||||
if (Instance != null) { Destroy(gameObject); return; }
|
|
||||||
Instance = this;
|
Debug.Assert(_audioConfig != null, "[AudioManager] _audioConfig 未赋值,请在 Inspector 中指定 AudioConfigSO。", this);
|
||||||
#pragma warning restore CS0618
|
|
||||||
|
|
||||||
_activeBGMSource = _bgmSourceA;
|
_activeBGMSource = _bgmSourceA;
|
||||||
_inactiveBGMSource = _bgmSourceB;
|
_inactiveBGMSource = _bgmSourceB;
|
||||||
|
|
||||||
// ServiceLocator 注册(覆盖 GameServiceRegistrar 的 NullAudioService 兜底)
|
|
||||||
ServiceLocator.Register<IAudioService>(this);
|
ServiceLocator.Register<IAudioService>(this);
|
||||||
|
BuildSFXLookup();
|
||||||
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
if (_onPlayerDied != null)
|
_onPlayerDied?.Subscribe(HandlePlayerDied).AddTo(_subs);
|
||||||
_onPlayerDied.OnEventRaised += HandlePlayerDied;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
if (_onPlayerDied != null)
|
_subs.Clear();
|
||||||
_onPlayerDied.OnEventRaised -= HandlePlayerDied;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── IAudioService string-key API(Phase 2 接入 AudioEventSO 后完整实现)─────────────
|
private void OnDestroy()
|
||||||
/// <summary>
|
{
|
||||||
/// 按 Addressable key 播放 BGM。Phase 2 接入 AudioEventSO 前为占位警告。
|
ServiceLocator.Unregister<IAudioService>(this);
|
||||||
/// </summary>
|
}
|
||||||
public void PlayBGM(string key)
|
|
||||||
=> Debug.LogWarning($"[AudioManager] PlayBGM(key) 尚未接入 AudioEventSO(Phase 2)。key={key}");
|
|
||||||
|
|
||||||
/// <summary>
|
// ── IAudioService string-key API ────────────────────────────────────────────
|
||||||
/// 按 Addressable key 播放 SFX。Phase 2 接入 AudioEventSO 前为占位警告。
|
/// <summary>按 Zone/Boss key 查 AudioConfigSO 播放 BGM。</summary>
|
||||||
/// </summary>
|
public void PlayBGM(string key)
|
||||||
|
{
|
||||||
|
var clip = _audioConfig.GetZoneBGM(key) ?? _audioConfig.GetBossBGM(key);
|
||||||
|
if (clip == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[AudioManager] BGM key '{key}' 在 AudioConfigSO 中未找到。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PlayBGM(clip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>按 key 查 SFX 注册表播放 AudioEventSO。</summary>
|
||||||
public void PlaySFX(string key)
|
public void PlaySFX(string key)
|
||||||
=> Debug.LogWarning($"[AudioManager] PlaySFX(key) 尚未接入 AudioEventSO(Phase 2)。key={key}");
|
{
|
||||||
|
if (_sfxLookup != null && _sfxLookup.TryGetValue(key, out var evt))
|
||||||
|
{
|
||||||
|
evt?.PlayOneShot(NextSFXSource());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Debug.LogWarning($"[AudioManager] SFX key '{key}' 未在注册表中找到。");
|
||||||
|
}
|
||||||
|
|
||||||
// ── 音量控制 ─────────────────────────────────────────────────────────────
|
// ── 音量控制 ─────────────────────────────────────────────────────────────
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -83,10 +109,15 @@ namespace BaseGames.Audio
|
|||||||
public void SetVolume(string exposedParam, float linear)
|
public void SetVolume(string exposedParam, float linear)
|
||||||
=> _mixer.SetFloat(exposedParam, LinearToDecibel(linear));
|
=> _mixer.SetFloat(exposedParam, LinearToDecibel(linear));
|
||||||
|
|
||||||
/// <summary>读取 GlobalSettings 并应用所有音量初始值。</summary>
|
/// <summary>读取 SettingsManager 已加载的设置数据并应用四路音量到 AudioMixer。</summary>
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
// TODO: 从 SettingsManager / PlayerPrefs 读取保存的音量值并应用
|
var settings = ServiceLocator.GetOrDefault<ISettingsService>();
|
||||||
|
GlobalSettingsData data = settings?.Current ?? new GlobalSettingsData();
|
||||||
|
SetVolume(AudioMixerKeys.Master, data.MasterVolume);
|
||||||
|
SetVolume(AudioMixerKeys.BGM, data.BGMVolume);
|
||||||
|
SetVolume(AudioMixerKeys.SFX, data.SFXVolume);
|
||||||
|
SetVolume(AudioMixerKeys.Ambient, data.AmbientVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── BGM ──────────────────────────────────────────────────────────────────
|
// ── BGM ──────────────────────────────────────────────────────────────────
|
||||||
@@ -113,6 +144,7 @@ namespace BaseGames.Audio
|
|||||||
{
|
{
|
||||||
if (clip == null) return;
|
if (clip == null) return;
|
||||||
var src = NextSFXSource();
|
var src = NextSFXSource();
|
||||||
|
if (src == null) return;
|
||||||
src.volume = volumeScale;
|
src.volume = volumeScale;
|
||||||
src.PlayOneShot(clip);
|
src.PlayOneShot(clip);
|
||||||
}
|
}
|
||||||
@@ -138,9 +170,22 @@ namespace BaseGames.Audio
|
|||||||
TransitionToSnapshot("Dead", 1.5f);
|
TransitionToSnapshot("Dead", 1.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BuildSFXLookup()
|
||||||
|
{
|
||||||
|
_sfxLookup = new Dictionary<string, AudioEventSO>(_sfxRegistry?.Length ?? 0);
|
||||||
|
if (_sfxRegistry == null) return;
|
||||||
|
foreach (var entry in _sfxRegistry)
|
||||||
|
if (!string.IsNullOrEmpty(entry.Key) && entry.Event != null)
|
||||||
|
_sfxLookup[entry.Key] = entry.Event;
|
||||||
|
}
|
||||||
|
|
||||||
private AudioSource NextSFXSource()
|
private AudioSource NextSFXSource()
|
||||||
{
|
{
|
||||||
if (_sfxSources == null || _sfxSources.Length == 0) return _bgmSourceA;
|
if (_sfxSources == null || _sfxSources.Length == 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("[AudioManager] SFX Source 池为空,请在 Inspector 中为 _sfxSources 赋值。");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return _sfxSources[_sfxRoundRobin++ % _sfxSources.Length];
|
return _sfxSources[_sfxRoundRobin++ % _sfxSources.Length];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ MonoImporter:
|
|||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
defaultReferences: []
|
defaultReferences: []
|
||||||
executionOrder: 0
|
executionOrder: -500
|
||||||
icon: {instanceID: 0}
|
icon: {instanceID: 0}
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
|
|||||||
@@ -30,19 +30,23 @@ namespace BaseGames.Audio
|
|||||||
|
|
||||||
private MusicState _musicState = MusicState.Exploration;
|
private MusicState _musicState = MusicState.Exploration;
|
||||||
private string _currentRegion = string.Empty;
|
private string _currentRegion = string.Empty;
|
||||||
|
private readonly CompositeDisposable _subscriptions = new();
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
Debug.Assert(_config != null, "[BGMController] _config 未赋值,请在 Inspector 中指定 AudioConfigSO。", this);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
if (_onBossFightToggled != null) _onBossFightToggled.OnEventRaised += OnBossFightToggled;
|
_onBossFightToggled?.Subscribe(OnBossFightToggled).AddTo(_subscriptions);
|
||||||
if (_onRegionEntered != null) _onRegionEntered.OnEventRaised += OnRegionEntered;
|
_onRegionEntered?.Subscribe(OnRegionEntered).AddTo(_subscriptions);
|
||||||
if (_onGameStateChanged != null) _onGameStateChanged.OnEventRaised += HandleStateChanged;
|
_onGameStateChanged?.Subscribe(HandleStateChanged).AddTo(_subscriptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
if (_onBossFightToggled != null) _onBossFightToggled.OnEventRaised -= OnBossFightToggled;
|
_subscriptions.Clear();
|
||||||
if (_onRegionEntered != null) _onRegionEntered.OnEventRaised -= OnRegionEntered;
|
|
||||||
if (_onGameStateChanged != null) _onGameStateChanged.OnEventRaised -= HandleStateChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBossFightToggled(bool started)
|
private void OnBossFightToggled(bool started)
|
||||||
@@ -50,7 +54,7 @@ namespace BaseGames.Audio
|
|||||||
if (started)
|
if (started)
|
||||||
{
|
{
|
||||||
_musicState = MusicState.Boss;
|
_musicState = MusicState.Boss;
|
||||||
var clip = _config != null ? _config.GetBossBGM(_currentRegion) : null;
|
var clip = _config.GetBossBGM(_currentRegion);
|
||||||
_audioManager.PlayBGM(clip, fadeOutDur: 1f, fadeInDur: 0.5f);
|
_audioManager.PlayBGM(clip, fadeOutDur: 1f, fadeInDur: 0.5f);
|
||||||
_audioManager.TransitionToSnapshot("BossFight", 0.5f);
|
_audioManager.TransitionToSnapshot("BossFight", 0.5f);
|
||||||
}
|
}
|
||||||
@@ -63,9 +67,9 @@ namespace BaseGames.Audio
|
|||||||
private IEnumerator PlayVictoryThenRestore()
|
private IEnumerator PlayVictoryThenRestore()
|
||||||
{
|
{
|
||||||
_musicState = MusicState.Victory;
|
_musicState = MusicState.Victory;
|
||||||
_audioManager.PlayBGM(_config != null ? _config.VictoryStingBGM : null,
|
_audioManager.PlayBGM(_config.VictoryStingBGM,
|
||||||
fadeOutDur: 0.3f, fadeInDur: 0.1f);
|
fadeOutDur: 0.3f, fadeInDur: 0.1f);
|
||||||
float dur = _config != null ? _config.VictoryStingDuration : 4f;
|
float dur = _config.VictoryStingDuration;
|
||||||
yield return new WaitForSecondsRealtime(dur);
|
yield return new WaitForSecondsRealtime(dur);
|
||||||
_musicState = MusicState.Exploration;
|
_musicState = MusicState.Exploration;
|
||||||
OnRegionEntered(_currentRegion);
|
OnRegionEntered(_currentRegion);
|
||||||
@@ -78,16 +82,15 @@ namespace BaseGames.Audio
|
|||||||
_currentRegion = regionId;
|
_currentRegion = regionId;
|
||||||
if (_musicState == MusicState.Exploration)
|
if (_musicState == MusicState.Exploration)
|
||||||
{
|
{
|
||||||
var clip = _config != null ? _config.GetZoneBGM(regionId) : null;
|
var clip = _config.GetZoneBGM(regionId);
|
||||||
_audioManager.PlayBGM(clip, fadeOutDur: 1f, fadeInDur: 1f);
|
_audioManager.PlayBGM(clip, fadeOutDur: 1f, fadeInDur: 1f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleStateChanged(GameStateId state)
|
private void HandleStateChanged(GameStateId state)
|
||||||
{
|
{
|
||||||
// ⚠️ GameStateId 是 struct,不能用 switch;使用 if/else + GameStates 常量
|
|
||||||
if (state == GameStates.MainMenu)
|
if (state == GameStates.MainMenu)
|
||||||
_audioManager.PlayBGM(_config != null ? _config.MainMenuBGM : null,
|
_audioManager.PlayBGM(_config.MainMenuBGM,
|
||||||
fadeOutDur: 0.5f, fadeInDur: 1.0f);
|
fadeOutDur: 0.5f, fadeInDur: 1.0f);
|
||||||
else if (state == GameStates.Paused)
|
else if (state == GameStates.Paused)
|
||||||
_audioManager.TransitionToSnapshot("Paused", 0.2f);
|
_audioManager.TransitionToSnapshot("Paused", 0.2f);
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ using BaseGames.Combat;
|
|||||||
namespace BaseGames.Audio
|
namespace BaseGames.Audio
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 订阅战斗/死亡事件,通过 AudioManager 播放对应 SFX。
|
/// 订阅战斗/死亡事件,通过 GlobalSFXPlayer 播放对应 AudioEventSO 音效。
|
||||||
/// 挂载在 Persistent 场景的 [Systems] GameObject 上。
|
/// 挂载在 Persistent 场景的 [Systems] GameObject 上。
|
||||||
|
/// 使用 AudioEventSO 替代裸 AudioClip,支持随机音量 / 音调 / 多片段。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CombatSFXController : MonoBehaviour
|
public class CombatSFXController : MonoBehaviour
|
||||||
{
|
{
|
||||||
@@ -15,57 +16,52 @@ namespace BaseGames.Audio
|
|||||||
[SerializeField] private VoidEventChannelSO _onPlayerDied;
|
[SerializeField] private VoidEventChannelSO _onPlayerDied;
|
||||||
|
|
||||||
[Header("Default Hit SFX")]
|
[Header("Default Hit SFX")]
|
||||||
[SerializeField] private AudioClip _defaultHitSFX;
|
[SerializeField] private AudioEventSO _defaultHitSFX;
|
||||||
|
|
||||||
[Header("Per-Type Hit SFX (optional, overrides default)")]
|
[Header("Per-Type Hit SFX (optional, overrides default)")]
|
||||||
[SerializeField] private AudioClip _sparkHitSFX;
|
[SerializeField] private AudioEventSO _sparkHitSFX;
|
||||||
[SerializeField] private AudioClip _slashHitSFX;
|
[SerializeField] private AudioEventSO _slashHitSFX;
|
||||||
[SerializeField] private AudioClip _bloodHitSFX;
|
[SerializeField] private AudioEventSO _bloodHitSFX;
|
||||||
[SerializeField] private AudioClip _magicHitSFX;
|
[SerializeField] private AudioEventSO _magicHitSFX;
|
||||||
[SerializeField] private AudioClip _heavyHitSFX;
|
[SerializeField] private AudioEventSO _heavyHitSFX;
|
||||||
[SerializeField] private AudioClip _critHitSFX;
|
[SerializeField] private AudioEventSO _critHitSFX;
|
||||||
[SerializeField] private AudioClip _parryHitSFX;
|
[SerializeField] private AudioEventSO _parryHitSFX;
|
||||||
[SerializeField] private AudioClip _fireHitSFX;
|
[SerializeField] private AudioEventSO _fireHitSFX;
|
||||||
[SerializeField] private AudioClip _iceHitSFX;
|
[SerializeField] private AudioEventSO _iceHitSFX;
|
||||||
|
|
||||||
[Header("Death SFX")]
|
[Header("Death SFX")]
|
||||||
[SerializeField] private AudioClip _playerDeathSFX;
|
[SerializeField] private AudioEventSO _playerDeathSFX;
|
||||||
|
|
||||||
|
private readonly CompositeDisposable _subs = new();
|
||||||
|
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
if (_onHitConfirmed != null)
|
_onHitConfirmed?.Subscribe(HandleHit).AddTo(_subs);
|
||||||
_onHitConfirmed.OnEventRaised += HandleHit;
|
_onPlayerDied?.Subscribe(HandlePlayerDied).AddTo(_subs);
|
||||||
|
|
||||||
if (_onPlayerDied != null)
|
|
||||||
_onPlayerDied.OnEventRaised += HandlePlayerDied;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
if (_onHitConfirmed != null)
|
_subs.Clear();
|
||||||
_onHitConfirmed.OnEventRaised -= HandleHit;
|
|
||||||
|
|
||||||
if (_onPlayerDied != null)
|
|
||||||
_onPlayerDied.OnEventRaised -= HandlePlayerDied;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleHit(HitInfo info)
|
private void HandleHit(HitInfo info)
|
||||||
{
|
{
|
||||||
AudioClip clip = ResolveHitClip(info.DamageInfo.FxType);
|
AudioEventSO sfx = ResolveHitSFX(info.DamageInfo.FxType);
|
||||||
if (clip == null) return;
|
if (sfx == null) return;
|
||||||
|
|
||||||
AudioManager.Instance.PlaySFXAtPosition(clip, info.HitPoint);
|
GlobalSFXPlayer.Play(sfx, info.HitPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandlePlayerDied()
|
private void HandlePlayerDied()
|
||||||
{
|
{
|
||||||
if (_playerDeathSFX == null) return;
|
if (_playerDeathSFX == null) return;
|
||||||
AudioManager.Instance.PlaySFX(_playerDeathSFX);
|
GlobalSFXPlayer.Play(_playerDeathSFX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AudioClip ResolveHitClip(HitFxType fxType)
|
private AudioEventSO ResolveHitSFX(HitFxType fxType)
|
||||||
{
|
{
|
||||||
AudioClip perType = fxType switch
|
AudioEventSO perType = fxType switch
|
||||||
{
|
{
|
||||||
HitFxType.Spark => _sparkHitSFX,
|
HitFxType.Spark => _sparkHitSFX,
|
||||||
HitFxType.Slash => _slashHitSFX,
|
HitFxType.Slash => _slashHitSFX,
|
||||||
|
|||||||
30
Assets/Scripts/Audio/FootstepAudioConfigSO.cs
Normal file
30
Assets/Scripts/Audio/FootstepAudioConfigSO.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Assets/Scripts/Audio/FootstepAudioConfigSO.cs
|
||||||
|
// 脚步声音效配置(Architecture 21_LiquidPuzzleModule §3.3)
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace BaseGames.Audio
|
||||||
|
{
|
||||||
|
[CreateAssetMenu(menuName = "BaseGames/Audio/FootstepAudioConfig")]
|
||||||
|
public class FootstepAudioConfigSO : ScriptableObject
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public struct MaterialEntry
|
||||||
|
{
|
||||||
|
public FootstepMaterial material;
|
||||||
|
public AudioClip[] clips; // 随机选一个,防止重复感
|
||||||
|
[Range(0f, 1f)] public float volume;
|
||||||
|
[Range(0.8f, 1.2f)] public float pitchVariance; // 每次随机 pitch 偏移范围
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaterialEntry[] entries;
|
||||||
|
|
||||||
|
public MaterialEntry? GetEntry(FootstepMaterial mat)
|
||||||
|
{
|
||||||
|
if (entries == null) return null;
|
||||||
|
foreach (var e in entries)
|
||||||
|
if (e.material == mat) return e;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Audio/FootstepAudioConfigSO.cs.meta
Normal file
11
Assets/Scripts/Audio/FootstepAudioConfigSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 902dc34e76d63a041a7e9a564b89a5e0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
16
Assets/Scripts/Audio/FootstepMaterial.cs
Normal file
16
Assets/Scripts/Audio/FootstepMaterial.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Assets/Scripts/Audio/FootstepMaterial.cs
|
||||||
|
// 脚步声材质枚举(Architecture 21_LiquidPuzzleModule §3.3)
|
||||||
|
namespace BaseGames.Audio
|
||||||
|
{
|
||||||
|
public enum FootstepMaterial
|
||||||
|
{
|
||||||
|
Stone, // 石板地(默认)
|
||||||
|
Dirt, // 泥土/草地
|
||||||
|
Wood, // 木板
|
||||||
|
Metal, // 金属格栅
|
||||||
|
Water, // 浅水区(溅水声)
|
||||||
|
Sand, // 沙地
|
||||||
|
Grass, // 草丛
|
||||||
|
Cave, // 洞穴(回响加强)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Audio/FootstepMaterial.cs.meta
Normal file
11
Assets/Scripts/Audio/FootstepMaterial.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3dc331b476a760a49b238b6645aae052
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
16
Assets/Scripts/Audio/FootstepMaterialMarker.cs
Normal file
16
Assets/Scripts/Audio/FootstepMaterialMarker.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Assets/Scripts/Audio/FootstepMaterialMarker.cs
|
||||||
|
// 挂载到地面碰撞体所在 GameObject,标记该地面的脚步声材质
|
||||||
|
// (Architecture 21_LiquidPuzzleModule §3.3)
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace BaseGames.Audio
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 挂载到地面碰撞体所在 GameObject(Tilemap 图层 or 单体地形 Prefab)。
|
||||||
|
/// 若玩家脚下 GameObject 无此组件,默认使用 FootstepMaterial.Stone。
|
||||||
|
/// </summary>
|
||||||
|
public class FootstepMaterialMarker : MonoBehaviour
|
||||||
|
{
|
||||||
|
public FootstepMaterial material = FootstepMaterial.Stone;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Audio/FootstepMaterialMarker.cs.meta
Normal file
11
Assets/Scripts/Audio/FootstepMaterialMarker.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9bed4b6cdf4d7064793fe22fefccce43
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
44
Assets/Scripts/Audio/GlobalSFXPlayer.cs
Normal file
44
Assets/Scripts/Audio/GlobalSFXPlayer.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using BaseGames.Core;
|
||||||
|
|
||||||
|
namespace BaseGames.Audio
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 全局 SFX 播放入口(单例 MonoBehaviour)。
|
||||||
|
/// 通过 <see cref="Play"/> 静态方法播放 <see cref="AudioEventSO"/>,
|
||||||
|
/// 可选传入世界坐标以在指定位置 3D 播放。
|
||||||
|
/// </summary>
|
||||||
|
public class GlobalSFXPlayer : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static GlobalSFXPlayer _instance;
|
||||||
|
|
||||||
|
[SerializeField] private AudioSource _globalSFXSource;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (_instance != null) { Destroy(gameObject); return; }
|
||||||
|
_instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 播放一个音效事件。
|
||||||
|
/// <para>若传入 <paramref name="worldPos"/>,则在该位置 3D 播放;否则使用全局 AudioSource 2D 播放。</para>
|
||||||
|
/// </summary>
|
||||||
|
public static void Play(AudioEventSO audioEvent, Vector2? worldPos = null)
|
||||||
|
{
|
||||||
|
if (audioEvent == null || _instance == null) return;
|
||||||
|
|
||||||
|
if (worldPos.HasValue)
|
||||||
|
{
|
||||||
|
var clip = audioEvent.PickClip();
|
||||||
|
if (clip != null)
|
||||||
|
ServiceLocator.GetOrDefault<IAudioService>()
|
||||||
|
?.PlaySFXAtPosition(clip, worldPos.Value, audioEvent.PickVolume());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
audioEvent.Play(_instance._globalSFXSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Audio/GlobalSFXPlayer.cs.meta
Normal file
11
Assets/Scripts/Audio/GlobalSFXPlayer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8275d76dd985c4c419f4c477b9275de3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
30
Assets/Scripts/Audio/UnderwaterAudioController.cs
Normal file
30
Assets/Scripts/Audio/UnderwaterAudioController.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Assets/Scripts/Audio/UnderwaterAudioController.cs
|
||||||
|
// 进入 LiquidZone 时切换水下 DSP 处理(Architecture 21_LiquidPuzzleModule §3.4)
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Audio;
|
||||||
|
|
||||||
|
namespace BaseGames.Audio
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 挂载于 PlayerController 所在 GameObject。
|
||||||
|
/// 由 LiquidZone.OnTriggerEnter2D / OnTriggerExit2D 直接调用。
|
||||||
|
/// 切换 AudioMixer Snapshot 以应用/解除水下 DSP 处理。
|
||||||
|
/// </summary>
|
||||||
|
public class UnderwaterAudioController : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] private AudioMixer _mixer;
|
||||||
|
[SerializeField] private float _transitionDuration = 0.3f;
|
||||||
|
|
||||||
|
/// <summary>玩家进入 Water 类型液体时调用。</summary>
|
||||||
|
public void EnterWater()
|
||||||
|
{
|
||||||
|
_mixer?.FindSnapshot("Underwater")?.TransitionTo(_transitionDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>玩家离开液体时调用。</summary>
|
||||||
|
public void ExitWater()
|
||||||
|
{
|
||||||
|
_mixer?.FindSnapshot("Default")?.TransitionTo(_transitionDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Audio/UnderwaterAudioController.cs.meta
Normal file
11
Assets/Scripts/Audio/UnderwaterAudioController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9d32189c7a2ecbe4caf2bf3d8aa174f2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
// Placeholder to prevent asmdef-no-scripts warning.
|
|
||||||
namespace BaseGames.Audio { }
|
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Unity.Cinemachine;
|
using Unity.Cinemachine;
|
||||||
|
using BaseGames.Core;
|
||||||
|
|
||||||
namespace BaseGames.Camera
|
namespace BaseGames.Camera
|
||||||
{
|
{
|
||||||
@@ -9,10 +10,8 @@ namespace BaseGames.Camera
|
|||||||
/// 须放置在持久化场景中。
|
/// 须放置在持久化场景中。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DefaultExecutionOrder(-100)]
|
[DefaultExecutionOrder(-100)]
|
||||||
public class CameraStateController : MonoBehaviour
|
public class CameraStateController : MonoBehaviour, ICameraService
|
||||||
{
|
{
|
||||||
public static CameraStateController Instance { get; private set; }
|
|
||||||
|
|
||||||
[Header("引用")]
|
[Header("引用")]
|
||||||
[SerializeField] private CinemachineBrain _brain;
|
[SerializeField] private CinemachineBrain _brain;
|
||||||
[SerializeField] private CinemachineImpulseSource _impulseSource;
|
[SerializeField] private CinemachineImpulseSource _impulseSource;
|
||||||
@@ -27,17 +26,13 @@ namespace BaseGames.Camera
|
|||||||
// ── Unity Lifecycle ───────────────────────────────────────────────────
|
// ── Unity Lifecycle ───────────────────────────────────────────────────
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
if (Instance != null && Instance != this)
|
if (ServiceLocator.GetOrDefault<ICameraService>() != null) { Destroy(gameObject); return; }
|
||||||
{
|
ServiceLocator.Register<ICameraService>(this);
|
||||||
Destroy(gameObject);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Instance = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
if (Instance == this) Instance = null;
|
ServiceLocator.Unregister<ICameraService>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 公开 API ──────────────────────────────────────────────────────────
|
// ── 公开 API ──────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using BaseGames.Core;
|
||||||
|
|
||||||
namespace BaseGames.Camera
|
namespace BaseGames.Camera
|
||||||
{
|
{
|
||||||
@@ -26,7 +27,7 @@ namespace BaseGames.Camera
|
|||||||
if (!Application.isPlaying) return;
|
if (!Application.isPlaying) return;
|
||||||
if (!other.CompareTag(_playerTag)) return;
|
if (!other.CompareTag(_playerTag)) return;
|
||||||
if (_targetCamera != null)
|
if (_targetCamera != null)
|
||||||
CameraStateController.Instance?.SwitchRoom(_targetCamera);
|
ServiceLocator.GetOrDefault<ICameraService>()?.SwitchRoom(_targetCamera);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDrawGizmos()
|
private void OnDrawGizmos()
|
||||||
|
|||||||
18
Assets/Scripts/Camera/ICameraService.cs
Normal file
18
Assets/Scripts/Camera/ICameraService.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace BaseGames.Camera
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 相机服务接口。供 RoomController / CameraTriggerZone 等调用,
|
||||||
|
/// 通过 ServiceLocator.Get<ICameraService>() 访问,无需直接依赖 CameraStateController。
|
||||||
|
/// </summary>
|
||||||
|
public interface ICameraService
|
||||||
|
{
|
||||||
|
/// <summary>切换到目标房间相机。</summary>
|
||||||
|
void SwitchRoom(RoomCamera targetCamera);
|
||||||
|
|
||||||
|
/// <summary>注册一个房间相机到控制器注册表。</summary>
|
||||||
|
void RegisterRoomCamera(RoomCamera camera);
|
||||||
|
|
||||||
|
/// <summary>注销一个房间相机。</summary>
|
||||||
|
void UnregisterRoomCamera(RoomCamera camera);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Camera/ICameraService.cs.meta
Normal file
11
Assets/Scripts/Camera/ICameraService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 94f7141340a22a54aa504d7c6a09eeb3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
27
Assets/Scripts/Combat/ArcProjectile.cs
Normal file
27
Assets/Scripts/Combat/ArcProjectile.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace BaseGames.Combat
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 抛物线抛射物。以 <see cref="ProjectileConfigSO.LaunchAngleDeg"/> 指定抛射角,
|
||||||
|
/// 并启用重力缩放,使子弹呈弧线飞行。
|
||||||
|
/// </summary>
|
||||||
|
public class ArcProjectile : Projectile
|
||||||
|
{
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
float rad = _config.LaunchAngleDeg * Mathf.Deg2Rad;
|
||||||
|
float vX = Mathf.Cos(rad) * _config.Speed * Mathf.Sign(Direction.x == 0f ? 1f : Direction.x);
|
||||||
|
float vY = Mathf.Sin(rad) * _config.Speed;
|
||||||
|
|
||||||
|
_rb.velocity = new Vector2(vX, vY);
|
||||||
|
_rb.gravityScale = _config.GravityScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisable()
|
||||||
|
{
|
||||||
|
base.OnDisable();
|
||||||
|
_rb.gravityScale = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Combat/ArcProjectile.cs.meta
Normal file
11
Assets/Scripts/Combat/ArcProjectile.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f43e5039135c2f84682862a9249e2688
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
"versionDefines": [],
|
"versionDefines": [],
|
||||||
"rootNamespace": "BaseGames.Combat",
|
"rootNamespace": "BaseGames.Combat",
|
||||||
"references": [
|
"references": [
|
||||||
|
"BaseGames.Core",
|
||||||
"BaseGames.Core.Events",
|
"BaseGames.Core.Events",
|
||||||
"BaseGames.Parry"
|
"BaseGames.Parry"
|
||||||
],
|
],
|
||||||
|
|||||||
24
Assets/Scripts/Combat/ClashConfigSO.cs
Normal file
24
Assets/Scripts/Combat/ClashConfigSO.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace BaseGames.Combat
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 拼刀系统参数配置(架构 06_CombatModule §15)。
|
||||||
|
/// 资产路径: Assets/ScriptableObjects/Config/Combat/ClashConfig.asset
|
||||||
|
/// </summary>
|
||||||
|
[CreateAssetMenu(menuName = "Combat/ClashConfig")]
|
||||||
|
public class ClashConfigSO : ScriptableObject
|
||||||
|
{
|
||||||
|
[Header("HitStop")]
|
||||||
|
[Tooltip("拼刀冻帧数(比普通命中的 2 帧更短)")]
|
||||||
|
public int ClashFreezeFrames = 1;
|
||||||
|
|
||||||
|
[Header("弹开")]
|
||||||
|
[Tooltip("拼刀弹开力度(Impulse 模式)")]
|
||||||
|
public float ClashKnockbackForce = 6.0f;
|
||||||
|
|
||||||
|
[Header("Camera Impulse")]
|
||||||
|
[Tooltip("Cinemachine Impulse 强度(轻微震感)")]
|
||||||
|
public float ClashImpulseStrength = 0.3f;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Combat/ClashConfigSO.cs.meta
Normal file
11
Assets/Scripts/Combat/ClashConfigSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d2dce4002cf4f99429388b2038fa2f60
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
70
Assets/Scripts/Combat/ClashResolver.cs
Normal file
70
Assets/Scripts/Combat/ClashResolver.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using BaseGames.Core;
|
||||||
|
using BaseGames.Core.Events;
|
||||||
|
|
||||||
|
namespace BaseGames.Combat
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 拼刀系统单例服务(架构 06_CombatModule §15)。
|
||||||
|
/// 常驻 Persistent 场景,由 GameManager 持有。
|
||||||
|
/// 当玩家与敌人的近战 HitBox 同时激活并相互重叠时触发拼刀:双方均不扣血,各自弹开。
|
||||||
|
/// </summary>
|
||||||
|
[DefaultExecutionOrder(-500)]
|
||||||
|
public class ClashResolver : MonoBehaviour, IClashService
|
||||||
|
{
|
||||||
|
[SerializeField] private VoidEventChannelSO _onNailClash; // EVT_NailClash
|
||||||
|
[SerializeField] private ClashConfigSO _config;
|
||||||
|
|
||||||
|
// 防止同一碰撞在同帧被双方 HitBox 各触发一次(去重)
|
||||||
|
// Key = (min(idA,idB), max(idA,idB)),顺序无关且无 XOR 哈希碰撞风险
|
||||||
|
private readonly HashSet<(int, int)> _processedThisFrame = new();
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (ServiceLocator.GetOrDefault<IClashService>() != null)
|
||||||
|
{
|
||||||
|
Destroy(gameObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ServiceLocator.Register<IClashService>(this);
|
||||||
|
Debug.Assert(_config != null, "[ClashResolver] _config 未赋值,请在 Inspector 中指定 ClashConfigSO。", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LateUpdate() => _processedThisFrame.Clear();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 由 HitBox.OnTriggerEnter2D 调用。
|
||||||
|
/// 对同一对 HitBox 每帧只处理一次(HashSet 去重)。
|
||||||
|
/// </summary>
|
||||||
|
public void ResolveClash(HitBox hitBoxA, HitBox hitBoxB)
|
||||||
|
{
|
||||||
|
int idA = hitBoxA.GetInstanceID();
|
||||||
|
int idB = hitBoxB.GetInstanceID();
|
||||||
|
(int, int) key = (System.Math.Min(idA, idB), System.Math.Max(idA, idB));
|
||||||
|
if (!_processedThisFrame.Add(key)) return;
|
||||||
|
|
||||||
|
// 1. 拼刀冻帧(比普通命中的 2 帧更短,强化拼刀手感)
|
||||||
|
ServiceLocator.GetOrDefault<IHitStopService>()?.FreezeFrames(_config.ClashFreezeFrames);
|
||||||
|
|
||||||
|
// 2. 双方弹开
|
||||||
|
ApplyClashKnockback(hitBoxA.OwnerRigidbody, hitBoxB.transform.position);
|
||||||
|
ApplyClashKnockback(hitBoxB.OwnerRigidbody, hitBoxA.transform.position);
|
||||||
|
|
||||||
|
// 3. 广播事件(VFX / Audio / CameraImpulse 订阅)
|
||||||
|
_onNailClash?.Raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyClashKnockback(Rigidbody2D rb, Vector2 oppositePos)
|
||||||
|
{
|
||||||
|
if (rb == null) return;
|
||||||
|
Vector2 dir = ((Vector2)rb.transform.position - oppositePos).normalized;
|
||||||
|
rb.AddForce(dir * _config.ClashKnockbackForce, ForceMode2D.Impulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
ServiceLocator.Unregister<IClashService>(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Combat/ClashResolver.cs.meta
Normal file
11
Assets/Scripts/Combat/ClashResolver.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: df7a93b79e446e24dbb27d2971c85f39
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -5,12 +5,13 @@ namespace BaseGames.Combat
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 单次伤害信息。流水线:RawDamage → Amount(护盾修改)→ FinalDamage(防御减免后)。
|
/// 单次伤害信息。流水线:RawDamage → Amount(护盾修改)→ FinalDamage(防御减免后)。
|
||||||
/// ⚠️ 非 readonly struct — Builder 就地写入字段。
|
/// ⚠️ 保留为可变 struct:HurtBox 流水线需要在方法内修改本地副本的 Amount / FinalDamage。
|
||||||
|
/// Builder 通过独立字段构造,不直接修改 DamageInfo 实例。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public struct DamageInfo
|
public struct DamageInfo
|
||||||
{
|
{
|
||||||
public int RawDamage; // HitBox 设定的原始值(Builder.SetRaw 写入一次)
|
public int RawDamage; // HitBox 设定的原始值(工厂/Builder 写入一次)
|
||||||
public int Amount; // 流水线中被护盾/防御修改
|
public int Amount; // 流水线中被护盾/防御修改
|
||||||
public int FinalDamage; // HurtBox 写入,最终 HP 扣除量
|
public int FinalDamage; // HurtBox 写入,最终 HP 扣除量
|
||||||
public Vector2 KnockbackDirection;
|
public Vector2 KnockbackDirection;
|
||||||
@@ -28,50 +29,92 @@ namespace BaseGames.Combat
|
|||||||
public string SkillId;
|
public string SkillId;
|
||||||
|
|
||||||
// ── Builder ──────────────────────────────────────────────────────────
|
// ── Builder ──────────────────────────────────────────────────────────
|
||||||
|
/// <summary>
|
||||||
|
/// 通过独立字段构造 DamageInfo,避免直接持有可变 DamageInfo 实例。
|
||||||
|
/// </summary>
|
||||||
public class Builder
|
public class Builder
|
||||||
{
|
{
|
||||||
private DamageInfo _d;
|
private int _raw;
|
||||||
|
private DamageType _type;
|
||||||
|
private DamageCategory _category;
|
||||||
|
private DamageFlags _flags;
|
||||||
|
private DamageTags _tags;
|
||||||
|
private string _skillId;
|
||||||
|
private string _sourceId;
|
||||||
|
private Vector2 _knockbackDirection;
|
||||||
|
private float _knockbackForce;
|
||||||
|
private float _hitStunDuration;
|
||||||
|
private HitFxType _fxType;
|
||||||
|
private BreakLevel _break;
|
||||||
|
private Vector2 _sourcePosition;
|
||||||
|
private int _sourceLayer;
|
||||||
|
|
||||||
public Builder() { }
|
public Builder() { }
|
||||||
|
|
||||||
// SetRaw 同步初始化 Amount(Amount 始终以 RawDamage 为起点)
|
// SetRaw 同步初始化 Amount(Amount 始终以 RawDamage 为起点)
|
||||||
public Builder SetRaw(int v) { _d.RawDamage = v; _d.Amount = v; return this; }
|
public Builder SetRaw(int v) { _raw = v; return this; }
|
||||||
public Builder SetType(DamageType v) { _d.Type = v; return this; }
|
public Builder SetType(DamageType v) { _type = v; return this; }
|
||||||
public Builder SetCategory(DamageCategory v){ _d.Category = v; return this; }
|
public Builder SetCategory(DamageCategory v) { _category = v; return this; }
|
||||||
public Builder SetFlags(DamageFlags v) { _d.Flags = v; return this; }
|
public Builder SetFlags(DamageFlags v) { _flags = v; return this; }
|
||||||
public Builder SetTags(DamageTags v) { _d.Tags = v; return this; }
|
public Builder SetTags(DamageTags v) { _tags = v; return this; }
|
||||||
public Builder SetSkillId(string v) { _d.SkillId = v; return this; }
|
public Builder SetSkillId(string v) { _skillId = v; return this; }
|
||||||
public Builder SetSourceId(string v) { _d.SourceId = v; return this; }
|
public Builder SetSourceId(string v) { _sourceId = v; return this; }
|
||||||
public Builder SetKnockback(Vector2 dir, float force)
|
public Builder SetKnockback(Vector2 dir, float force) { _knockbackDirection = dir; _knockbackForce = force; return this; }
|
||||||
{ _d.KnockbackDirection = dir; _d.KnockbackForce = force; return this; }
|
public Builder SetStun(float dur) { _hitStunDuration = dur; return this; }
|
||||||
public Builder SetStun(float dur) { _d.HitStunDuration = dur; return this; }
|
public Builder SetFx(HitFxType v) { _fxType = v; return this; }
|
||||||
public Builder SetFx(HitFxType v) { _d.FxType = v; return this; }
|
public Builder SetBreak(BreakLevel v) { _break = v; return this; }
|
||||||
public Builder SetBreak(BreakLevel v) { _d.Break = v; return this; }
|
public Builder SetSourcePos(Vector2 v) { _sourcePosition = v; return this; }
|
||||||
public Builder SetSourcePos(Vector2 v) { _d.SourcePosition = v; return this; }
|
public Builder SetLayer(int v) { _sourceLayer = v; return this; }
|
||||||
public Builder SetLayer(int v) { _d.SourceLayer = v; return this; }
|
|
||||||
public DamageInfo Build() => _d;
|
public DamageInfo Build() => new DamageInfo
|
||||||
|
{
|
||||||
|
RawDamage = _raw,
|
||||||
|
Amount = _raw,
|
||||||
|
Type = _type,
|
||||||
|
Category = _category,
|
||||||
|
Flags = _flags,
|
||||||
|
Tags = _tags,
|
||||||
|
SkillId = _skillId,
|
||||||
|
SourceId = _sourceId,
|
||||||
|
KnockbackDirection = _knockbackDirection,
|
||||||
|
KnockbackForce = _knockbackForce,
|
||||||
|
HitStunDuration = _hitStunDuration,
|
||||||
|
FxType = _fxType,
|
||||||
|
Break = _break,
|
||||||
|
SourcePosition = _sourcePosition,
|
||||||
|
SourceLayer = _sourceLayer,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ⚡ 零堆分配工厂(热路径首选)。直接从 DamageSourceSO 填入基础字段。
|
/// ⚡ 零堆分配工厂(热路径首选)。从 DamageSourceSO 填入所有静态字段;
|
||||||
/// KnockbackDirection / SourcePosition / SourceLayer 等运行时字段由调用方就地赋值。
|
/// 可选传入运行时字段(knockbackDir、sourcePos、sourceLayer),
|
||||||
|
/// 无需调用方事后就地赋值。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static DamageInfo From(DamageSourceSO so)
|
public static DamageInfo From(
|
||||||
|
DamageSourceSO so,
|
||||||
|
Vector2 knockbackDir = default,
|
||||||
|
Vector2 sourcePos = default,
|
||||||
|
int sourceLayer = 0)
|
||||||
{
|
{
|
||||||
int baseAmt = Mathf.RoundToInt(so.BaseDamage * so.DamageMultiplier);
|
int baseAmt = Mathf.RoundToInt(so.BaseDamage * so.DamageMultiplier);
|
||||||
return new DamageInfo
|
return new DamageInfo
|
||||||
{
|
{
|
||||||
RawDamage = baseAmt,
|
RawDamage = baseAmt,
|
||||||
Amount = baseAmt,
|
Amount = baseAmt,
|
||||||
Type = so.Type,
|
Type = so.Type,
|
||||||
Category = so.Category,
|
Category = so.Category,
|
||||||
Flags = so.Flags,
|
Flags = so.Flags,
|
||||||
Tags = so.Tags,
|
Tags = so.Tags,
|
||||||
HitStunDuration = so.HitStunDuration,
|
HitStunDuration = so.HitStunDuration,
|
||||||
FxType = so.FxType,
|
FxType = so.FxType,
|
||||||
Break = so.BreakLevel,
|
Break = so.BreakLevel,
|
||||||
SourceId = so.sourceId,
|
SourceId = so.sourceId,
|
||||||
SkillId = so.skillId,
|
SkillId = so.skillId,
|
||||||
|
KnockbackDirection = knockbackDir,
|
||||||
|
KnockbackForce = so.KnockbackForce,
|
||||||
|
SourcePosition = sourcePos,
|
||||||
|
SourceLayer = sourceLayer,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using BaseGames.Core;
|
||||||
|
|
||||||
namespace BaseGames.Combat
|
namespace BaseGames.Combat
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 攻击判定盒。挂载在武器 Prefab 或技能 HitBox Prefab 的子节点上。
|
/// 攻击判定盒。挂载在武器 Prefab 或技能 HitBox Prefab 的子节点上。
|
||||||
/// Phase 1 简化:直接挂在 Player Prefab 子节点 [HitBoxGround/Up/Down/Air]。
|
/// 直接挂在 Player Prefab 子节点 [HitBoxGround/Up/Down/Air]。
|
||||||
/// Collider2D 需设 IsTrigger = true,Layer = PlayerHitBox 或 EnemyHitBox。
|
/// Collider2D 需设 IsTrigger = true,Layer = PlayerHitBox 或 EnemyHitBox。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RequireComponent(typeof(Collider2D))]
|
[RequireComponent(typeof(Collider2D))]
|
||||||
@@ -14,12 +15,38 @@ namespace BaseGames.Combat
|
|||||||
[SerializeField] private DamageSourceSO _defaultSource;
|
[SerializeField] private DamageSourceSO _defaultSource;
|
||||||
[SerializeField] private float _hitCooldown = 0.1f;
|
[SerializeField] private float _hitCooldown = 0.1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HitBox 标识符,供 PlayerAnimationEvents / EnemyAnimationEvents 按名称精确激活特定判定盒。
|
||||||
|
/// 留空表示"无 Id";事件 payload 为空时将操作所有 HitBox。
|
||||||
|
/// </summary>
|
||||||
|
[SerializeField] private string _id = "";
|
||||||
|
public string Id => _id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对立阵营 HitBox 所在的 Layer 掩码(用于拼刀检测)。
|
||||||
|
/// Inspector 中将 PlayerHitBox 与 EnemyHitBox 两个 Layer 均勾选。
|
||||||
|
/// </summary>
|
||||||
|
[SerializeField] private LayerMask _rivalHitBoxMask;
|
||||||
|
|
||||||
private DamageSourceSO _currentSource;
|
private DamageSourceSO _currentSource;
|
||||||
private Transform _attackerTransform;
|
private Transform _attackerTransform;
|
||||||
|
private Rigidbody2D _ownerRigidbody;
|
||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
|
private IClashService _clashService;
|
||||||
|
|
||||||
|
/// <summary>HitBox 当前是否激活(供 ClashResolver 查询)。</summary>
|
||||||
|
public bool IsActive => _isActive;
|
||||||
|
|
||||||
|
/// <summary>当前 Source 是否携带 CanClash 标记(供 ClashResolver 查询)。</summary>
|
||||||
|
public bool CanClash => _currentSource != null && _currentSource.Flags.HasFlag(DamageFlags.CanClash);
|
||||||
|
|
||||||
|
/// <summary>宿主角色的 Rigidbody2D(用于拼刀弹开力计算)。</summary>
|
||||||
|
public Rigidbody2D OwnerRigidbody => _ownerRigidbody;
|
||||||
|
|
||||||
|
// 拼刀检测所需的对立层掩码(Inspector 配置)
|
||||||
|
|
||||||
/// <summary>命中确认委托(PlayerCombat / EnemyCombat 订阅)。</summary>
|
/// <summary>命中确认委托(PlayerCombat / EnemyCombat 订阅)。</summary>
|
||||||
public System.Action<DamageInfo> OnHitConfirmed;
|
public event System.Action<DamageInfo> OnHitConfirmed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 激活 HitBox。source/attacker 均可选,未传则使用 Inspector 默认值。
|
/// 激活 HitBox。source/attacker 均可选,未传则使用 Inspector 默认值。
|
||||||
@@ -30,9 +57,25 @@ namespace BaseGames.Combat
|
|||||||
_currentSource = source ?? _defaultSource;
|
_currentSource = source ?? _defaultSource;
|
||||||
_attackerTransform = attacker ?? transform;
|
_attackerTransform = attacker ?? transform;
|
||||||
_isActive = true;
|
_isActive = true;
|
||||||
|
// 缓存宿主 Rigidbody2D(沿父层级向上查找)
|
||||||
|
_ownerRigidbody = _attackerTransform.GetComponentInParent<Rigidbody2D>();
|
||||||
|
// 每次激活清空当前激活期已命中目标集合(防止连击连段导致同一阶段多次命中目标)
|
||||||
|
_hitThisActivation.Clear();
|
||||||
|
_hitCooldownTimers.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Deactivate() => _isActive = false;
|
public void Deactivate()
|
||||||
|
{
|
||||||
|
_isActive = false;
|
||||||
|
_hitThisActivation.Clear();
|
||||||
|
_hitCooldownTimers.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>仅替换当前 DamageSource(不改变激活状态,供 PlayerCombat 连击段切换)。</summary>
|
||||||
|
public void SetDamageSource(DamageSourceSO source)
|
||||||
|
{
|
||||||
|
if (source != null) _currentSource = source;
|
||||||
|
}
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
@@ -40,35 +83,60 @@ namespace BaseGames.Combat
|
|||||||
var col = GetComponent<Collider2D>();
|
var col = GetComponent<Collider2D>();
|
||||||
if (!col.isTrigger)
|
if (!col.isTrigger)
|
||||||
Debug.LogWarning($"[HitBox] {name}: Collider2D.isTrigger 应为 true。", this);
|
Debug.LogWarning($"[HitBox] {name}: Collider2D.isTrigger 应为 true。", this);
|
||||||
|
// 缓存 IClashService:OnTriggerEnter2D 为物理热路径,避免每次调用 Dictionary 查找
|
||||||
|
_clashService = ServiceLocator.GetOrDefault<IClashService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
|
_hitThisActivation.Clear();
|
||||||
_hitCooldownTimers.Clear();
|
_hitCooldownTimers.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTriggerEnter2D(Collider2D other)
|
private void OnTriggerExit2D(Collider2D other)
|
||||||
{
|
{
|
||||||
|
// 目标离开判定区域时清除其冷却记录,防止持续激活的 HitBox(环境危险等)
|
||||||
|
// 因有效目标持续流动而无限积累已离场对象。
|
||||||
|
// 注意:_hitThisActivation 刻意保留,确保同一激活期内不重复命中。
|
||||||
|
_hitCooldownTimers.Remove(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTriggerEnter2D(Collider2D other) {
|
||||||
if (!_isActive) return;
|
if (!_isActive) return;
|
||||||
if (_currentSource == null)
|
if (_currentSource == null)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"[HitBox] {name}: 无 DamageSourceSO,跳过命中。", this);
|
Debug.LogWarning($"[HitBox] {name}: 无 DamageSourceSO,跳过命中。", this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 同一激活期防止对同一 Collider 重复命中(一次攻击每个目标至多命中一次)
|
||||||
|
if (!_hitThisActivation.Add(other)) return;
|
||||||
if (!CheckCooldown(other)) return;
|
if (!CheckCooldown(other)) return;
|
||||||
|
|
||||||
Vector2 knockDir = ((Vector2)other.bounds.center
|
Vector2 knockDir = ((Vector2)other.bounds.center
|
||||||
- (Vector2)_attackerTransform.position).normalized;
|
- (Vector2)_attackerTransform.position).normalized;
|
||||||
|
|
||||||
// ⚡ 零 GC:struct 工厂,就地赋值运行时字段
|
// ⚡ 零 GC:struct 工厂,运行时字段内联传入
|
||||||
var info = DamageInfo.From(_currentSource);
|
var info = DamageInfo.From(
|
||||||
info.KnockbackDirection = knockDir;
|
_currentSource,
|
||||||
info.KnockbackForce = _currentSource.KnockbackForce;
|
knockDir,
|
||||||
info.SourcePosition = _attackerTransform.position;
|
_attackerTransform.position,
|
||||||
info.SourceLayer = _attackerTransform.gameObject.layer;
|
_attackerTransform.gameObject.layer);
|
||||||
|
|
||||||
// ① 命中 HurtBox
|
// ① 拼刀检测:当前 HitBox 携带 CanClash 标记,且碰到对立阵营的 HitBox 层
|
||||||
|
int otherLayer = other.gameObject.layer;
|
||||||
|
bool isRivalHitBoxLayer = (_rivalHitBoxMask.value & (1 << otherLayer)) != 0;
|
||||||
|
if (isRivalHitBoxLayer && CanClash)
|
||||||
|
{
|
||||||
|
var rivalHitBox = other.GetComponent<HitBox>();
|
||||||
|
if (rivalHitBox != null && rivalHitBox.IsActive && rivalHitBox.CanClash)
|
||||||
|
{
|
||||||
|
_clashService?.ResolveClash(this, rivalHitBox);
|
||||||
|
return; // 拼刀,中止伤害流水线
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ② 命中 HurtBox
|
||||||
var hurtBox = other.GetComponent<HurtBox>();
|
var hurtBox = other.GetComponent<HurtBox>();
|
||||||
if (hurtBox != null)
|
if (hurtBox != null)
|
||||||
{
|
{
|
||||||
@@ -77,12 +145,14 @@ namespace BaseGames.Combat
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ② 命中 IBreakable(机关/障碍物)
|
// ③ 命中 IBreakable(机关/障碍物)
|
||||||
other.GetComponent<IBreakable>()?.TryInteract(info);
|
other.GetComponent<IBreakable>()?.TryInteract(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 同目标多帧命中冷却 ────────────────────────────────────────────────
|
// ── 当前激活期已命中目标集合(防止复合子 Collider 导致同帧多次命中)────────────
|
||||||
private readonly Dictionary<Collider2D, float> _hitCooldownTimers = new();
|
private readonly HashSet<Collider2D> _hitThisActivation = new(8);
|
||||||
|
// ── 同目标多帧命中冷却(防止 Trigger 处于重叠状态时重入等殊情导致的连击)──
|
||||||
|
private readonly Dictionary<Collider2D, float> _hitCooldownTimers = new(8);
|
||||||
|
|
||||||
private bool CheckCooldown(Collider2D other)
|
private bool CheckCooldown(Collider2D other)
|
||||||
{
|
{
|
||||||
@@ -92,5 +162,20 @@ namespace BaseGames.Combat
|
|||||||
_hitCooldownTimers[other] = now;
|
_hitCooldownTimers[other] = now;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
var col = GetComponent<Collider2D>();
|
||||||
|
if (col == null) return;
|
||||||
|
// 激活时显示橙色判定框,非激活时显示极淡轮廓
|
||||||
|
Gizmos.color = _isActive
|
||||||
|
? new UnityEngine.Color(1f, 0.5f, 0f, 0.55f)
|
||||||
|
: new UnityEngine.Color(1f, 0.5f, 0f, 0.1f);
|
||||||
|
Gizmos.DrawCube(col.bounds.center, col.bounds.size);
|
||||||
|
Gizmos.color = new UnityEngine.Color(1f, 0.5f, 0f, 0.9f);
|
||||||
|
Gizmos.DrawWireCube(col.bounds.center, col.bounds.size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
102
Assets/Scripts/Combat/HitStopManager.cs
Normal file
102
Assets/Scripts/Combat/HitStopManager.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using UnityEngine;
|
||||||
|
using BaseGames.Core;
|
||||||
|
|
||||||
|
namespace BaseGames.Combat
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 命中冻帧服务接口。
|
||||||
|
/// </summary>
|
||||||
|
public interface IHitStopService
|
||||||
|
{
|
||||||
|
/// <summary>冻帧 <paramref name="frames"/> 帧(以 fixedDeltaTime 换算为实际时长)。</summary>
|
||||||
|
void FreezeFrames(int frames);
|
||||||
|
/// <summary>冻帧指定时长(Unscaled 秒)。</summary>
|
||||||
|
void FreezeDuration(float unscaledSeconds);
|
||||||
|
/// <summary>游戏正常时间缩放(默认 1);子弹时间等功能修改此属性。</summary>
|
||||||
|
float BaseTimeScale { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 命中冻帧服务(HitStop)(架构 06_CombatModule §16)。
|
||||||
|
/// 通过短暂将 Time.timeScale 设为 0 实现"冻帧"效果,强化打击感。
|
||||||
|
/// 常驻 Persistent 场景,由 GameManager 持有;通过 ServiceLocator 注册访问。
|
||||||
|
///
|
||||||
|
/// 设计说明:
|
||||||
|
/// - 多次并发请求取最长持续时间(StopCoroutine + 重启)
|
||||||
|
/// - 使用 WaitForSecondsRealtime 确保 timeScale=0 时协程仍能恢复
|
||||||
|
/// - OnDestroy 强制还原 timeScale,防止异常退出导致游戏卡死
|
||||||
|
/// </summary>
|
||||||
|
[DefaultExecutionOrder(-400)]
|
||||||
|
public class HitStopManager : MonoBehaviour, IHitStopService
|
||||||
|
{
|
||||||
|
/// <summary>游戏正常时间缩放(默认 1);通过属性读取以便外部修改子弹时间时保留基准值。</summary>
|
||||||
|
public float BaseTimeScale
|
||||||
|
{
|
||||||
|
get => _baseTimeScale;
|
||||||
|
set => _baseTimeScale = Mathf.Clamp(value, 0.01f, 10f);
|
||||||
|
}
|
||||||
|
private float _baseTimeScale = 1f;
|
||||||
|
|
||||||
|
private Coroutine _activeRoutine;
|
||||||
|
private float _freezeEndTime;
|
||||||
|
|
||||||
|
// ── 生命周期 ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (ServiceLocator.GetOrDefault<IHitStopService>() != null) { Destroy(gameObject); return; }
|
||||||
|
ServiceLocator.Register<IHitStopService>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
// 安全恢复:防止场景卸载/异常退出时 timeScale 永久为 0
|
||||||
|
Time.timeScale = _baseTimeScale;
|
||||||
|
ServiceLocator.Unregister<IHitStopService>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 公共 API ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冻帧 <paramref name="frames"/> 帧(以 fixedDeltaTime 换算为实际时长)。
|
||||||
|
/// 若已有冻帧进行中,取两者中持续时间较长的(避免短请求截断较长的冻帧)。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="frames">冻帧帧数(fixedDeltaTime 单位)。0 或负数无效。</param>
|
||||||
|
public void FreezeFrames(int frames)
|
||||||
|
{
|
||||||
|
if (frames <= 0) return;
|
||||||
|
FreezeDuration(frames * Time.fixedDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冻帧指定时长(Unscaled 实际秒数)。
|
||||||
|
/// 若已有冻帧进行中,取两者中较长的。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unscaledSeconds">实际时长(秒),不受 timeScale 影响。</param>
|
||||||
|
public void FreezeDuration(float unscaledSeconds)
|
||||||
|
{
|
||||||
|
if (unscaledSeconds <= 0f) return;
|
||||||
|
|
||||||
|
float newEndTime = Time.unscaledTime + unscaledSeconds;
|
||||||
|
// 已有更长的冻帧进行中,放弃短请求,避免截断
|
||||||
|
if (_activeRoutine != null && newEndTime <= _freezeEndTime) return;
|
||||||
|
|
||||||
|
_freezeEndTime = newEndTime;
|
||||||
|
if (_activeRoutine != null)
|
||||||
|
StopCoroutine(_activeRoutine);
|
||||||
|
|
||||||
|
_activeRoutine = StartCoroutine(FreezeRoutine(unscaledSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 内部实现 ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private IEnumerator FreezeRoutine(float unscaledSeconds)
|
||||||
|
{
|
||||||
|
Time.timeScale = 0f;
|
||||||
|
yield return new WaitForSecondsRealtime(unscaledSeconds);
|
||||||
|
Time.timeScale = _baseTimeScale;
|
||||||
|
_activeRoutine = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Combat/HitStopManager.cs.meta
Normal file
11
Assets/Scripts/Combat/HitStopManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9aace2ae37bf9d0459a4cdeb34595885
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user