展前冒死重构
This commit is contained in:
@@ -1,24 +0,0 @@
|
||||
<linker>
|
||||
<assembly fullname="Unity.Addressables, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" preserve="all">
|
||||
<type fullname="UnityEngine.AddressableAssets.Addressables" preserve="all" />
|
||||
</assembly>
|
||||
<assembly fullname="Unity.Localization, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<type fullname="UnityEngine.Localization.Locale" preserve="all" />
|
||||
<type fullname="UnityEngine.Localization.Tables.SharedTableData" preserve="all" />
|
||||
<type fullname="UnityEngine.Localization.Tables.StringTable" preserve="all" />
|
||||
<type fullname="UnityEngine.Localization.LocaleIdentifier" preserve="nothing" serialized="true" />
|
||||
<type fullname="UnityEngine.Localization.Metadata.MetadataCollection" preserve="nothing" serialized="true" />
|
||||
<type fullname="UnityEngine.Localization.Tables.TableEntryData" preserve="nothing" serialized="true" />
|
||||
<type fullname="UnityEngine.Localization.Tables.DistributedUIDGenerator" preserve="nothing" serialized="true" />
|
||||
<type fullname="UnityEngine.Localization.Tables.SharedTableData/SharedTableEntry" preserve="nothing" serialized="true" />
|
||||
</assembly>
|
||||
<assembly fullname="Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" preserve="all">
|
||||
<type fullname="UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider" preserve="all" />
|
||||
<type fullname="UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider" preserve="all" />
|
||||
<type fullname="UnityEngine.ResourceManagement.ResourceProviders.InstanceProvider" preserve="all" />
|
||||
<type fullname="UnityEngine.ResourceManagement.ResourceProviders.SceneProvider" preserve="all" />
|
||||
</assembly>
|
||||
<assembly fullname="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<type fullname="UnityEngine.Object" preserve="all" />
|
||||
</assembly>
|
||||
</linker>
|
||||
@@ -721,7 +721,7 @@ MonoBehaviour:
|
||||
m_HandleRect: {fileID: 3809938165616928137}
|
||||
m_Direction: 0
|
||||
m_Value: 1
|
||||
m_Size: 1
|
||||
m_Size: 0.9999999
|
||||
m_NumberOfSteps: 0
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
@@ -3148,7 +3148,7 @@ RectTransform:
|
||||
m_Father: {fileID: 2889347862386961338}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 20, y: 20}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
@@ -4543,7 +4543,7 @@ MonoBehaviour:
|
||||
m_Top: 0
|
||||
m_Bottom: 0
|
||||
m_ChildAlignment: 3
|
||||
m_Spacing: 100
|
||||
m_Spacing: 15
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 1
|
||||
m_ChildControlWidth: 0
|
||||
|
||||
@@ -82,7 +82,7 @@ MonoBehaviour:
|
||||
levelProgressForThisDifficulty: 0
|
||||
experienceProvidedForThisDifficulty: 0
|
||||
- difficulty: 2
|
||||
difficultyLEVEL: 18
|
||||
difficultyLEVEL: 20
|
||||
enemyTotalHP_Multiplier: 40
|
||||
enemyConfigList:
|
||||
- enemyID: 31202
|
||||
@@ -96,7 +96,7 @@ MonoBehaviour:
|
||||
- enemyID: 31202
|
||||
hpPercentage: 29
|
||||
difficultyName:
|
||||
chartFile: {fileID: 4900000, guid: 1333012d19842c24c945e004b06c3be8, type: 3}
|
||||
chartFile: {fileID: 4900000, guid: 9b669f41f328e6b43a6375f90b02cfed, type: 3}
|
||||
difficultyDescription:
|
||||
lastScoreForThisDifficulty: 0
|
||||
chartPersonalRecordForThisDifficulty: 0
|
||||
|
||||
@@ -108,6 +108,6 @@ MonoBehaviour:
|
||||
current_levelProgress: 0
|
||||
_if_level_is_EASE: 0
|
||||
max_comboRecord: 0
|
||||
thisLevel_selectedDifficultyID: 2
|
||||
thisLevel_selectedDifficultyID: 0
|
||||
thisLevel_addDateString: 2026-01-01
|
||||
thisLevel_overallDescription: "\u201C\u54D7\u5566\u5566......\u201D\n\u7834\u788E\u7684\u955C\u9762\u6620\u51FA\u5979\u7684\u53CC\u76EE\uFF0C\u5728\u90A3\u4E00\u523B\uFF0C\u89C6\u91CE\u91CC\u51FA\u73B0\u4E86\u591A\u4E2A\u4EE5\u5979\u4E3A\u540D\u7684\u5076\u50CF......"
|
||||
|
||||
@@ -96,7 +96,7 @@ MonoBehaviour:
|
||||
- enemyID: 31202
|
||||
hpPercentage: 40
|
||||
difficultyName:
|
||||
chartFile: {fileID: 4900000, guid: dc27d9d67f3698646b7b7c3d926eea3c, type: 3}
|
||||
chartFile: {fileID: 4900000, guid: 8ab483e7c47c97b41b2fa048fa1d27f9, type: 3}
|
||||
difficultyDescription:
|
||||
lastScoreForThisDifficulty: 0
|
||||
chartPersonalRecordForThisDifficulty: 0
|
||||
|
||||
+10
-10
@@ -2212,7 +2212,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 36708243816153243, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 1
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3829753781754538968, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
@@ -17747,7 +17747,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 36708243816153243, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 1
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3829753781754538968, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
@@ -18848,7 +18848,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 36708243816153243, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 1
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3829753781754538968, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
@@ -23757,7 +23757,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 36708243816153243, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 1
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3829753781754538968, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
@@ -25527,7 +25527,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &887418840
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -26344,7 +26344,7 @@ MonoBehaviour:
|
||||
m_HandleRect: {fileID: 1616564962}
|
||||
m_Direction: 0
|
||||
m_Value: 0
|
||||
m_Size: 0.99999994
|
||||
m_Size: 1
|
||||
m_NumberOfSteps: 0
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
@@ -52681,7 +52681,7 @@ RectTransform:
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -200.95, y: 53.759}
|
||||
m_SizeDelta: {x: 282, y: 40}
|
||||
m_SizeDelta: {x: 279.75, y: 39.75}
|
||||
m_Pivot: {x: 0, y: 0.5}
|
||||
--- !u!114 &1826730530
|
||||
MonoBehaviour:
|
||||
@@ -54965,7 +54965,7 @@ RectTransform:
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: -0.000030517578}
|
||||
m_SizeDelta: {x: 0, y: 330}
|
||||
m_SizeDelta: {x: 0, y: 10}
|
||||
m_Pivot: {x: 0, y: 1}
|
||||
--- !u!114 &1887597342
|
||||
MonoBehaviour:
|
||||
@@ -55294,7 +55294,7 @@ MonoBehaviour:
|
||||
m_TargetGraphic: {fileID: 1219932954}
|
||||
m_HandleRect: {fileID: 1219932953}
|
||||
m_Direction: 0
|
||||
m_Value: 1
|
||||
m_Value: 0
|
||||
m_Size: 1
|
||||
m_NumberOfSteps: 0
|
||||
m_OnValueChanged:
|
||||
@@ -66242,7 +66242,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 36708243816153243, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 1
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3829753781754538968, guid: f18dd7043de95ee4381aadceab744d50, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
|
||||
@@ -899,7 +899,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 2658d847700da0541942ef5fdcc378b5, type: 3}
|
||||
m_Sprite: {fileID: 21300000, guid: 440e0141c6eb0144c9e9162f0f278d6b, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
@@ -2822,7 +2822,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: "\u7231\u7433\u8FBE\u96C5"
|
||||
m_text: "\u9ECE\u5999\u5999"
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 47babadf44fc728438ae9a924c42227e, type: 2}
|
||||
m_sharedMaterial: {fileID: 6312708493749940865, guid: 47babadf44fc728438ae9a924c42227e, type: 2}
|
||||
@@ -3772,7 +3772,7 @@ Transform:
|
||||
m_GameObject: {fileID: 154923898}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalPosition: {x: -150, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
@@ -12828,7 +12828,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &278391251
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -14063,6 +14063,7 @@ MonoBehaviour:
|
||||
great_judge_prefab: {fileID: 2415619590294459312, guid: 5464dbc0e44a5d94a88575c4f00d2977, type: 3}
|
||||
good_judge_prefab: {fileID: 9004018932644019605, guid: 5f0f778a23a4b7343a7cfc40a7d35fc7, type: 3}
|
||||
miss_judge_prefab: {fileID: 2647521509301950334, guid: 448c2d848f56b6f42bb169f0be36fd65, type: 3}
|
||||
trackSkill_prefab: {fileID: 4447276195903411839, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
baseScale: 0.5
|
||||
useRandomScale: 0
|
||||
minScaleMultiplier: 0.5
|
||||
@@ -14076,6 +14077,8 @@ MonoBehaviour:
|
||||
fadeInTime: 0.25
|
||||
fadeOutStartTime: 0.25
|
||||
fadeOutDuration: 0.25
|
||||
trackSkillUiScaleMultiplier: 0.01
|
||||
trackSkillSortingOrder: 9000
|
||||
--- !u!114 &312501159
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -15799,11 +15802,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 96.525
|
||||
value: 95.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 95.85
|
||||
value: 96.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -15819,7 +15822,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266895856353985475, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 87.75
|
||||
value: 88.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266895856353985475, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -15919,11 +15922,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 8.1
|
||||
value: 8
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 87.75
|
||||
value: 88.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -17548,7 +17551,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: 590/590
|
||||
m_text: 1630/1630
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 7cad3f54ac7b53d4885f00c16f1a6929, type: 2}
|
||||
m_sharedMaterial: {fileID: -346136068272202111, guid: 7cad3f54ac7b53d4885f00c16f1a6929, type: 2}
|
||||
@@ -18208,6 +18211,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!114 &397604453
|
||||
MonoBehaviour:
|
||||
@@ -18401,6 +18407,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 826006369}
|
||||
pausePanel: {fileID: 146358648}
|
||||
animations: {fileID: 1397448124}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 1
|
||||
judgeConfig: {fileID: 11400000, guid: f870aa419242e72438b57ac9659f61e9, type: 2}
|
||||
--- !u!1 &400656413
|
||||
GameObject:
|
||||
@@ -18729,6 +18738,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &411632596
|
||||
BoxCollider2D:
|
||||
@@ -19115,7 +19127,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: c8f373c5ea610a345a513f33ba768314, type: 3}
|
||||
m_Sprite: {fileID: 21300000, guid: 7df561841c29f7c4495809fa26c0fd87, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
@@ -19377,7 +19389,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 1482ec2642664674f9ec31a7cb9fec90, type: 3}
|
||||
m_Sprite: {fileID: 21300000, guid: 2658d847700da0541942ef5fdcc378b5, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
@@ -19730,7 +19742,7 @@ Transform:
|
||||
m_GameObject: {fileID: 424277304}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalPosition: {x: -150, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
@@ -20285,7 +20297,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: 1500/1500
|
||||
m_text: 590/590
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 7cad3f54ac7b53d4885f00c16f1a6929, type: 2}
|
||||
m_sharedMaterial: {fileID: -346136068272202111, guid: 7cad3f54ac7b53d4885f00c16f1a6929, type: 2}
|
||||
@@ -21012,7 +21024,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &480679343
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -21140,7 +21152,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: "\u6E29\u59AE"
|
||||
m_text: "\u8DC3\u6843"
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 47babadf44fc728438ae9a924c42227e, type: 2}
|
||||
m_sharedMaterial: {fileID: 6312708493749940865, guid: 47babadf44fc728438ae9a924c42227e, type: 2}
|
||||
@@ -23762,7 +23774,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &614786766
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -32337,11 +32349,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 96.525
|
||||
value: 95.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 95.85
|
||||
value: 96.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -32357,7 +32369,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266895856353985475, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 87.75
|
||||
value: 88.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266895856353985475, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -32457,11 +32469,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 8.1
|
||||
value: 8
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 87.75
|
||||
value: 88.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -33430,7 +33442,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &766553121
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -37044,7 +37056,7 @@ Transform:
|
||||
m_GameObject: {fileID: 825327586}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalPosition: {x: -150, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
@@ -38142,7 +38154,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &856873761
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -38884,7 +38896,7 @@ Transform:
|
||||
m_GameObject: {fileID: 882707961}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalPosition: {x: -150, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
@@ -39698,7 +39710,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &919628105
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -41190,7 +41202,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 7df561841c29f7c4495809fa26c0fd87, type: 3}
|
||||
m_Sprite: {fileID: 21300000, guid: 1482ec2642664674f9ec31a7cb9fec90, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
@@ -41370,8 +41382,8 @@ MonoBehaviour:
|
||||
m_TargetGraphic: {fileID: 1501988772}
|
||||
m_HandleRect: {fileID: 1501988771}
|
||||
m_Direction: 0
|
||||
m_Value: -0.11386139
|
||||
m_Size: 0.9999692
|
||||
m_Value: -0.109836064
|
||||
m_Size: 0.99996895
|
||||
m_NumberOfSteps: 0
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
@@ -41571,7 +41583,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &997867646
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -43469,6 +43481,11 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1044607013}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!224 &1052701310 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
m_PrefabInstance: {fileID: 4536750610831610918}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &1054652914 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 3022102558446658867, guid: cd3d579a2d65b3241a1812f639db37ec, type: 3}
|
||||
@@ -44815,6 +44832,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &1090508772
|
||||
BoxCollider2D:
|
||||
@@ -47153,6 +47173,7 @@ RectTransform:
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 865113943}
|
||||
- {fileID: 1052701310}
|
||||
m_Father: {fileID: 1989375500}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
@@ -47280,7 +47301,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: 620/620
|
||||
m_text: 570/570
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 7cad3f54ac7b53d4885f00c16f1a6929, type: 2}
|
||||
m_sharedMaterial: {fileID: -346136068272202111, guid: 7cad3f54ac7b53d4885f00c16f1a6929, type: 2}
|
||||
@@ -47377,11 +47398,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 96.525
|
||||
value: 95.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 95.85
|
||||
value: 96.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -47397,7 +47418,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266895856353985475, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 87.75
|
||||
value: 88.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266895856353985475, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -47497,11 +47518,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 8.1
|
||||
value: 8
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 87.75
|
||||
value: 88.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -48824,7 +48845,7 @@ Transform:
|
||||
m_GameObject: {fileID: 1204483344}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalPosition: {x: -150, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
@@ -50438,7 +50459,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &1269063488
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -52482,14 +52503,14 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
slotIndex: 3
|
||||
maxHP: 1500
|
||||
currentHP: 1500
|
||||
maxMana: 300
|
||||
maxHP: 590
|
||||
currentHP: 590
|
||||
maxMana: 400
|
||||
currentMana: 0
|
||||
damageResistance: 0
|
||||
scoreEfficiency: 0.09
|
||||
scoreEfficiency: 0.01
|
||||
bmm: {fileID: 1261342020}
|
||||
attack: 38
|
||||
attack: 9
|
||||
baseTrackScore: 1000
|
||||
perfectRatio: 1
|
||||
greatRatio: 0.75
|
||||
@@ -53788,6 +53809,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &1370459979
|
||||
BoxCollider2D:
|
||||
@@ -57078,6 +57102,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &1486355885
|
||||
BoxCollider2D:
|
||||
@@ -57409,14 +57436,14 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
slotIndex: 2
|
||||
maxHP: 620
|
||||
currentHP: 620
|
||||
maxHP: 570
|
||||
currentHP: 570
|
||||
maxMana: 200
|
||||
currentMana: 0
|
||||
damageResistance: 0
|
||||
scoreEfficiency: 0.04
|
||||
scoreEfficiency: 0.02
|
||||
bmm: {fileID: 1261342020}
|
||||
attack: 7
|
||||
attack: 8
|
||||
baseTrackScore: 1000
|
||||
perfectRatio: 1
|
||||
greatRatio: 0.75
|
||||
@@ -59395,11 +59422,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 96.525
|
||||
value: 95.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 95.85
|
||||
value: 96.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 682261594771255014, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -59415,7 +59442,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266895856353985475, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 87.75
|
||||
value: 88.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266895856353985475, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -59515,11 +59542,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 8.1
|
||||
value: 8
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 87.75
|
||||
value: 88.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6759631360171910247, guid: 7e80d4de8ab2ff04a9bf1c54e5a064b1, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
@@ -59646,6 +59673,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &1572820069
|
||||
BoxCollider2D:
|
||||
@@ -60620,14 +60650,14 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
slotIndex: 1
|
||||
maxHP: 1630
|
||||
currentHP: 1630
|
||||
maxMana: 300
|
||||
maxHP: 620
|
||||
currentHP: 620
|
||||
maxMana: 200
|
||||
currentMana: 0
|
||||
damageResistance: 0
|
||||
scoreEfficiency: 0.09
|
||||
scoreEfficiency: 0.04
|
||||
bmm: {fileID: 1261342020}
|
||||
attack: 38
|
||||
attack: 7
|
||||
baseTrackScore: 1000
|
||||
perfectRatio: 1
|
||||
greatRatio: 0.75
|
||||
@@ -62780,7 +62810,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: "\u9ECE\u5999\u5999"
|
||||
m_text: "\u6E29\u59AE"
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 47babadf44fc728438ae9a924c42227e, type: 2}
|
||||
m_sharedMaterial: {fileID: 6312708493749940865, guid: 47babadf44fc728438ae9a924c42227e, type: 2}
|
||||
@@ -65367,7 +65397,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &1814450260
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -66739,7 +66769,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: 1630/1630
|
||||
m_text: 620/620
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 7cad3f54ac7b53d4885f00c16f1a6929, type: 2}
|
||||
m_sharedMaterial: {fileID: -346136068272202111, guid: 7cad3f54ac7b53d4885f00c16f1a6929, type: 2}
|
||||
@@ -67386,14 +67416,14 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
slotIndex: 4
|
||||
maxHP: 590
|
||||
currentHP: 590
|
||||
maxMana: 400
|
||||
maxHP: 1630
|
||||
currentHP: 1630
|
||||
maxMana: 300
|
||||
currentMana: 0
|
||||
damageResistance: 0
|
||||
scoreEfficiency: 0.01
|
||||
scoreEfficiency: 0.09
|
||||
bmm: {fileID: 1261342020}
|
||||
attack: 9
|
||||
attack: 38
|
||||
baseTrackScore: 1000
|
||||
perfectRatio: 1
|
||||
greatRatio: 0.75
|
||||
@@ -67791,7 +67821,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &1899720854
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -69106,6 +69136,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &1938112567
|
||||
BoxCollider2D:
|
||||
@@ -70307,7 +70340,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: "\u8DC3\u6843"
|
||||
m_text: "\u58A8\u5F69\u79BB"
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 47babadf44fc728438ae9a924c42227e, type: 2}
|
||||
m_sharedMaterial: {fileID: 6312708493749940865, guid: 47babadf44fc728438ae9a924c42227e, type: 2}
|
||||
@@ -71051,8 +71084,9 @@ MonoBehaviour:
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
cameraDashDuration: 0.075
|
||||
cameraWaveEnterDuration: 0.05
|
||||
cameraWaveRecoverDuration: 0.05
|
||||
cameraWaveIntensity: 1
|
||||
cameraWaveEnterDuration: 0.075
|
||||
cameraWaveRecoverDuration: 0.075
|
||||
cameraGlitchEnterDuration: 0.05
|
||||
cameraGlitchRecoverDuration: 0.05
|
||||
cameraMonochromeEnterDuration: 0.1
|
||||
@@ -72696,6 +72730,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &2072610047
|
||||
BoxCollider2D:
|
||||
@@ -74077,6 +74114,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &2112693515
|
||||
BoxCollider2D:
|
||||
@@ -75537,6 +75577,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!114 &176508373887267660
|
||||
MonoBehaviour:
|
||||
@@ -76535,6 +76578,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!61 &2411783544221426195
|
||||
BoxCollider2D:
|
||||
@@ -77344,6 +77390,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!114 &3721014538685850233
|
||||
MonoBehaviour:
|
||||
@@ -77979,6 +78028,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!114 &4363530835118640843
|
||||
MonoBehaviour:
|
||||
@@ -78247,6 +78299,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!114 &4387330747821330999
|
||||
MonoBehaviour:
|
||||
@@ -78335,6 +78390,107 @@ RectTransform:
|
||||
m_AnchoredPosition: {x: -154.3, y: 0}
|
||||
m_SizeDelta: {x: 29, y: 29}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!1001 &4536750610831610918
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 1159013136}
|
||||
m_Modifications:
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 100
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 100
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2454380874622603429, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4447276195903411839, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: trackSkillPrefab
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4447276195903411839, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 023b66fc78c17124d9840cf8d728b0c7, type: 3}
|
||||
--- !u!1001 &4666275742349675508
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -80587,6 +80743,9 @@ MonoBehaviour:
|
||||
immediateSettlementButton: {fileID: 0}
|
||||
pausePanel: {fileID: 0}
|
||||
animations: {fileID: 0}
|
||||
useBpmForSpeed: 0
|
||||
referenceBpm: 120
|
||||
useMultiSegmentFill: 0
|
||||
judgeConfig: {fileID: 0}
|
||||
--- !u!114 &9221553951272782130
|
||||
MonoBehaviour:
|
||||
|
||||
+27970
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b72cf0d289c3f72498b163dbcbe86dd5
|
||||
guid: 8895a1a9a0f42cb449dae9edc7ba338e
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
+29143
File diff suppressed because it is too large
Load Diff
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b669f41f328e6b43a6375f90b02cfed
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ab483e7c47c97b41b2fa048fa1d27f9
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1eff0b560e1e7b14f95669d4bc24a553
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class trackSkillPrefab : MonoBehaviour
|
||||
{
|
||||
public Text track_skillName;
|
||||
public Image track_skillIcon;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e84c9d75ff70c954a9e1828a00a18498
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &2052636663938950983
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 328570594430980671}
|
||||
- component: {fileID: 4030790630745708841}
|
||||
- component: {fileID: 307577278921986757}
|
||||
m_Layer: 0
|
||||
m_Name: GameObject
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &328570594430980671
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2052636663938950983}
|
||||
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: 9011044283025364914}
|
||||
- {fileID: 1307227743765364543}
|
||||
m_Father: {fileID: 2454380874622603429}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &4030790630745708841
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2052636663938950983}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Padding:
|
||||
m_Left: 0
|
||||
m_Right: 0
|
||||
m_Top: 0
|
||||
m_Bottom: 0
|
||||
m_ChildAlignment: 4
|
||||
m_Spacing: 0
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 1
|
||||
m_ChildControlWidth: 0
|
||||
m_ChildControlHeight: 0
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!114 &307577278921986757
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2052636663938950983}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_HorizontalFit: 2
|
||||
m_VerticalFit: 0
|
||||
--- !u!1 &4447276195903411839
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2454380874622603429}
|
||||
- component: {fileID: 5611750753715808825}
|
||||
m_Layer: 0
|
||||
m_Name: trackSkillPrefab
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2454380874622603429
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4447276195903411839}
|
||||
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: 328570594430980671}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &5611750753715808825
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4447276195903411839}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e84c9d75ff70c954a9e1828a00a18498, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
track_skillName: {fileID: 727773236840584476}
|
||||
track_skillIcon: {fileID: 4645656031428478769}
|
||||
--- !u!1 &6674845584449045048
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 9011044283025364914}
|
||||
- component: {fileID: 4286554360502884719}
|
||||
- component: {fileID: 4645656031428478769}
|
||||
m_Layer: 0
|
||||
m_Name: skill_icon
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &9011044283025364914
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6674845584449045048}
|
||||
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: 328570594430980671}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 80, y: 80}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4286554360502884719
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6674845584449045048}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &4645656031428478769
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6674845584449045048}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &7202114771598519337
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1307227743765364543}
|
||||
- component: {fileID: 20899202308215794}
|
||||
- component: {fileID: 727773236840584476}
|
||||
- component: {fileID: 3909682056884181069}
|
||||
m_Layer: 0
|
||||
m_Name: skillname
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1307227743765364543
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7202114771598519337}
|
||||
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: 328570594430980671}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 231.5, y: -50}
|
||||
m_SizeDelta: {x: 303, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &20899202308215794
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7202114771598519337}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &727773236840584476
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7202114771598519337}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: d254d20d93651ae448ff13d10dff30ab, type: 3}
|
||||
m_FontSize: 70
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 0
|
||||
m_MaxSize: 70
|
||||
m_Alignment: 4
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: New Text
|
||||
--- !u!114 &3909682056884181069
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7202114771598519337}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_HorizontalFit: 2
|
||||
m_VerticalFit: 2
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 023b66fc78c17124d9840cf8d728b0c7
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -146,7 +146,7 @@ Transform:
|
||||
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: 3.5, z: 1}
|
||||
m_LocalScale: {x: 1, y: 3.75, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 806226407933355351}
|
||||
|
||||
@@ -27,7 +27,7 @@ Transform:
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: -8.76, z: 0}
|
||||
m_LocalScale: {x: 1, y: 3.5, z: 1}
|
||||
m_LocalScale: {x: 1, y: 3.75, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 8162722342572194734}
|
||||
@@ -115,7 +115,7 @@ Transform:
|
||||
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: 1
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 6792274648119312710}
|
||||
m_Father: {fileID: 0}
|
||||
|
||||
@@ -27,7 +27,7 @@ Transform:
|
||||
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: 3.5, z: 1}
|
||||
m_LocalScale: {x: 1, y: 3.75, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 8805100122384193708}
|
||||
|
||||
@@ -27,7 +27,7 @@ Transform:
|
||||
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: 3.5, z: 1}
|
||||
m_LocalScale: {x: 1, y: 3.75, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 6650826276162497469}
|
||||
|
||||
@@ -146,7 +146,7 @@ Transform:
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 255.5, y: 52.7, z: 0}
|
||||
m_LocalScale: {x: 1, y: 3.5, z: 1}
|
||||
m_LocalScale: {x: 1, y: 3.75, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 777335502243705040}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 158aceb99959cf24aaadd70fe5fe16a9
|
||||
AudioImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 8
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
conversionMode: 0
|
||||
preloadAudioData: 1
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -669,7 +669,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: -37.00002, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &7230413747790136415
|
||||
|
||||
@@ -562,6 +562,11 @@ public class UI_Idols : MonoBehaviour
|
||||
SetText(idolBackNameText, hero.ally_heroName);
|
||||
SetText(idol_chenghaoText, hero.ally_heroDesignation);
|
||||
SetText(idol_nameText, hero.ally_heroName);
|
||||
// 名字文本挂 ContentSizeFitter(H+V=Preferred):改文字后布局要到帧末才 rebuild,
|
||||
// 期间会以旧尺寸显示一帧、下一帧才修正,表现为"名字偏移后恢复"。这里立即强制重建,消除该帧。
|
||||
ForceRebuildText(idol_nameText);
|
||||
ForceRebuildText(idolBackNameText);
|
||||
ForceRebuildText(idol_chenghaoText);
|
||||
SetIdolDocumentText(hero.ally_heroDescription);
|
||||
bool animateExpBar = pendingGrowthAnimationHeroId > 0 && hero.ally_heroID == pendingGrowthAnimationHeroId;
|
||||
SetSliderVisual(idol_expBar, snapshot.sliderValue, snapshot.sliderColor, animateExpBar);
|
||||
@@ -579,7 +584,8 @@ public class UI_Idols : MonoBehaviour
|
||||
ApplyBehaviourRadar(hero);
|
||||
|
||||
PopulateSpecialSkills(hero);
|
||||
UI_OrderedEntryAnimator.PlayRefresh(rightPanel, 0.16f, -8f, 0.99f, true);
|
||||
// 改用只淡入的动画,避免位移导致名字跳动
|
||||
UI_OrderedEntryAnimator.PlayFadeOnly(rightPanel, 0, 0.2f, 0f, true);
|
||||
}
|
||||
|
||||
private void SetIdolDocumentText(string value)
|
||||
@@ -889,6 +895,19 @@ public class UI_Idols : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// 改完文字后立即重建其(及自身)布局,让 ContentSizeFitter 当帧算好尺寸,
|
||||
// 避免"旧尺寸显示一帧→下一帧修正"的偏移跳动。仅对挂了 ContentSizeFitter 的文本有意义,
|
||||
// 对普通文本也无害(RebuildLayoutImmediate 开销很小、每次切换偶像才调用几次)。
|
||||
private static void ForceRebuildText(Text target)
|
||||
{
|
||||
if (target == null) return;
|
||||
RectTransform rt = target.rectTransform;
|
||||
if (rt != null)
|
||||
{
|
||||
UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(rt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetImageSprite(Image target, Sprite sprite)
|
||||
{
|
||||
if (target == null)
|
||||
|
||||
@@ -2053,6 +2053,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
else
|
||||
{
|
||||
SkillBuilder.Instance?.TriggerOnAdjacentAllySkillCast(slotIndex);
|
||||
GameplayLevelRuleEventBus.NotifyAllySkillCast(slotIndex, GameplayClock.NowSongTime);
|
||||
}
|
||||
return anyTriggered;
|
||||
}
|
||||
|
||||
@@ -90,19 +90,46 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
SkillBuilder.Instance.TriggerOnEnemyRevive(this);
|
||||
}
|
||||
|
||||
// Trigger primary skill on revive if defined and trigger condition matches
|
||||
TryTriggerPrimarySkill(SkillDefinition.SkillTrigger.OnEnemyRevive);
|
||||
// Trigger configured enemy skills on revive if defined and trigger condition matches.
|
||||
TryTriggerConfiguredSkills(SkillDefinition.SkillTrigger.OnEnemyRevive);
|
||||
}
|
||||
|
||||
// Documentation text normalized.
|
||||
private void TryTriggerPrimarySkill(SkillDefinition.SkillTrigger when)
|
||||
private void TryTriggerConfiguredSkills(SkillDefinition.SkillTrigger when)
|
||||
{
|
||||
if (sourceData == null) return;
|
||||
var def = sourceData.GetPrimarySkill();
|
||||
if (def == null) return;
|
||||
|
||||
HashSet<SkillDefinition> executed = new HashSet<SkillDefinition>();
|
||||
SkillDefinition primary = sourceData.GetPrimarySkill();
|
||||
ExecuteEnemySkillIfMatched(primary, when, executed);
|
||||
|
||||
int[] groupIds = sourceData.GetEffectiveEquippedSkillGroupIDs();
|
||||
if (groupIds == null || groupIds.Length == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < groupIds.Length; i++)
|
||||
{
|
||||
int groupId = groupIds[i];
|
||||
if (groupId == 0) continue;
|
||||
|
||||
SkillGroup group = sourceData.GetSkillGroupByID(groupId);
|
||||
if (group == null || group.skills == null)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < group.skills.Length; j++)
|
||||
{
|
||||
ExecuteEnemySkillIfMatched(group.skills[j], when, executed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteEnemySkillIfMatched(SkillDefinition def, SkillDefinition.SkillTrigger when, HashSet<SkillDefinition> executed)
|
||||
{
|
||||
if (def == null || executed == null) return;
|
||||
if (executed.Contains(def)) return;
|
||||
if (def.triggerCondition != when) return;
|
||||
|
||||
// Evaluate formula into amount using enemy fields as variables
|
||||
executed.Add(def);
|
||||
|
||||
float perTick = 0f;
|
||||
var vars = new Dictionary<string, float>();
|
||||
vars["maxHP"] = maxHP;
|
||||
@@ -135,7 +162,6 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
totalAmount = perTick * ticks;
|
||||
}
|
||||
|
||||
// Use SkillBuilder to execute effect, using this Enemy GameObject as caster
|
||||
if (SkillBuilder.Instance != null)
|
||||
{
|
||||
SkillBuilder.Instance.ExecuteSkill(def.displayName ?? def.skillId, def.effectType, totalAmount, def.defaultSelector, this.gameObject, null, def.defaultDuration, def.defaultTickInterval);
|
||||
@@ -190,6 +216,11 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
}
|
||||
}
|
||||
|
||||
if (actual > 0)
|
||||
{
|
||||
GameplayLevelRuleEventBus.NotifyEnemyDamaged(this, actual, source);
|
||||
}
|
||||
|
||||
// grant mana based on sourceData setting
|
||||
if (sourceData != null && sourceData.manaGainOnDamagePercent > 0f && maxMana > 0)
|
||||
{
|
||||
@@ -352,7 +383,16 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
var type = actual > 0 ? iNumberPrefabController.InstantNumberType.Heal : iNumberPrefabController.InstantNumberType.Damage;
|
||||
iNumberPrefabController.SpawnForEnemyStatic(type, actual);
|
||||
}
|
||||
if (currentHP <= 0) Die();
|
||||
if (currentHP <= 0)
|
||||
{
|
||||
if (GameplayLevelRuleEventBus.ShouldPreventEnemyDeath(this))
|
||||
{
|
||||
currentHP = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
public void ModifyMana(int delta, bool animate = true)
|
||||
@@ -371,18 +411,18 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
}
|
||||
}
|
||||
|
||||
// If mana reached full, trigger primary skill and reset mana
|
||||
// If mana reached full, trigger configured skills and reset mana
|
||||
if (currentMana >= maxMana && maxMana > 0 && Application.isPlaying)
|
||||
{
|
||||
Debug.Log($"[EnemyCombatant] Mana full for {gameObject.name} (slot) - triggering OnManaFull");
|
||||
try
|
||||
{
|
||||
// Trigger primary skill configured for OnManaFull
|
||||
TryTriggerPrimarySkill(SkillDefinition.SkillTrigger.OnManaFull);
|
||||
TryTriggerConfiguredSkills(SkillDefinition.SkillTrigger.OnManaFull);
|
||||
GameplayLevelRuleEventBus.NotifyEnemyManaFull(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[EnemyCombatant] TryTriggerPrimarySkill threw: {ex}");
|
||||
Debug.LogError($"[EnemyCombatant] OnManaFull handling threw: {ex}");
|
||||
}
|
||||
|
||||
// reset mana after casting
|
||||
@@ -457,8 +497,8 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger primary skill on death if configured (enemy's own skill)
|
||||
TryTriggerPrimarySkill(SkillDefinition.SkillTrigger.OnEnemyDead);
|
||||
// Trigger configured enemy skills on death if configured.
|
||||
TryTriggerConfiguredSkills(SkillDefinition.SkillTrigger.OnEnemyDead);
|
||||
|
||||
// notify listeners (UI/manager) that enemy died AFTER triggering skills so they operate on the current instance
|
||||
OnEnemyDied?.Invoke(this);
|
||||
|
||||
@@ -1369,6 +1369,10 @@ ResolvedGroup:
|
||||
}
|
||||
catch { /* never break gameplay because of optional UI */ }
|
||||
|
||||
// Spawn the track skill popup as soon as the skill is confirmed for this frame.
|
||||
// This avoids later target-resolution branches swallowing the popup on early returns.
|
||||
SpawnTrackSkillPopup(slotIndex, smallSkillName, smallSkillIcon);
|
||||
|
||||
GameObject caster = GetAllyObjectBySlot(slotIndex);
|
||||
if (caster == null)
|
||||
{
|
||||
@@ -1926,7 +1930,10 @@ ResolvedGroup:
|
||||
}
|
||||
|
||||
// For listed skills: do not stack when retriggered, only refresh duration.
|
||||
if (appliedRefreshOnly) return;
|
||||
if (appliedRefreshOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// For non-direct path, EffectSystem expects 'amount' to be total amount for sustained effects
|
||||
@@ -1986,7 +1993,6 @@ ResolvedGroup:
|
||||
GameplaySkillLogger.RecordSkillRelease(casterDisplayName, skillDisplayName, def.effectType.ToString(), effectSummary, targetSummary);
|
||||
}
|
||||
catch { }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2111,6 +2117,21 @@ ResolvedGroup:
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string[] s_trackSkillPopupColors = { "red", "green", "yellow", "purple", "blue" };
|
||||
|
||||
private void SpawnTrackSkillPopup(int slotIndex, string skillName, Sprite skillIcon)
|
||||
{
|
||||
if (slotIndex < 0 || slotIndex >= s_trackSkillPopupColors.Length)
|
||||
return;
|
||||
|
||||
var fx = Animation_GenerateJudgementSituationPrefab.Instance
|
||||
?? SceneObjectLookupCache.FindAny<Animation_GenerateJudgementSituationPrefab>();
|
||||
if (fx == null)
|
||||
return;
|
||||
|
||||
fx.SpawnTrackSkillPrefab(s_trackSkillPopupColors[slotIndex], skillIcon, skillName ?? string.Empty);
|
||||
}
|
||||
|
||||
private bool HandleEquipSkillGhoulSchool(int slotIndex)
|
||||
{
|
||||
_equipGhoulSchoolActiveSlots.Add(slotIndex);
|
||||
|
||||
@@ -123,6 +123,26 @@ public class EnemyData_SO : ScriptableObject
|
||||
return availableSkills[primarySkillIndex];
|
||||
}
|
||||
|
||||
public SkillGroup GetSkillGroupByID(int groupId)
|
||||
{
|
||||
if (skillGroups == null || skillGroups.Length == 0)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < skillGroups.Length; i++)
|
||||
{
|
||||
SkillGroup group = skillGroups[i];
|
||||
if (group != null && group.skillGroupID == groupId)
|
||||
return group;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int[] GetEffectiveEquippedSkillGroupIDs()
|
||||
{
|
||||
return equippedSkillGroupIDs ?? System.Array.Empty<int>();
|
||||
}
|
||||
|
||||
// New: groups of skills. Each group contains a numeric skillGroupID and a variable-length list of SkillDefinitions.
|
||||
[Tooltip("Skill groups defined on this enemy. Each SkillGroup has a numeric skillGroupID which can be equipped into slots to allow activation of the entire group via that ID.")]
|
||||
public SkillGroup[] skillGroups = new SkillGroup[0];
|
||||
|
||||
@@ -24,17 +24,21 @@ public sealed class UICanvasMatchFitter : MonoBehaviour
|
||||
private void OnEnable()
|
||||
{
|
||||
scaler = GetComponent<CanvasScaler>();
|
||||
Apply();
|
||||
// 切换 UI 时 Canvas 激活会走到这里。若只是设 match 而不立即同步,
|
||||
// 序列化的旧 match(=1) 会在激活当帧先生效、下一帧才被本组件改成正确值,
|
||||
// 造成偏心元素(如偶像名字 anchoredX≈-201)"先偏后正"的一帧跳动。
|
||||
// 故激活时同步应用并强制刷新 Canvas,消除这一帧时序。
|
||||
Apply(forceCanvasUpdate: true);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 分辨率/窗口变化时才重算。
|
||||
float aspect = (float)Screen.width / Mathf.Max(1, Screen.height);
|
||||
if (Mathf.Abs(aspect - lastAspect) > 0.0001f) Apply();
|
||||
if (Mathf.Abs(aspect - lastAspect) > 0.0001f) Apply(forceCanvasUpdate: false);
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
private void Apply(bool forceCanvasUpdate)
|
||||
{
|
||||
if (scaler == null) scaler = GetComponent<CanvasScaler>();
|
||||
if (scaler == null) return;
|
||||
@@ -45,6 +49,16 @@ public sealed class UICanvasMatchFitter : MonoBehaviour
|
||||
|
||||
scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
|
||||
// 更宽→锁高度(1);更窄→锁宽度(0)。等于始终匹配“更受限的一侧”,设计区域完整可见。
|
||||
scaler.matchWidthOrHeight = aspect >= DesignAspect ? 1f : 0f;
|
||||
float target = aspect >= DesignAspect ? 1f : 0f;
|
||||
bool changed = !Mathf.Approximately(scaler.matchWidthOrHeight, target);
|
||||
scaler.matchWidthOrHeight = target;
|
||||
|
||||
if (forceCanvasUpdate && changed)
|
||||
{
|
||||
// 让 CanvasScaler 立即按新 match 重算 scaleFactor、并在同一帧完成布局,
|
||||
// 避免"旧 scale 定位一帧 → 下一帧修正"的可见跳动。
|
||||
// ForceUpdateCanvases 会驱动 CanvasScaler 重算并刷新布局。
|
||||
Canvas.ForceUpdateCanvases();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public sealed class UIResolutionAutoFitter : MonoBehaviour
|
||||
_enemyFollowAttached = false;
|
||||
_trackStabilizerAttached = false;
|
||||
_cameraTunerAttached = false;
|
||||
_trackScalerAttached = false;
|
||||
StartCoroutine(FitAfterFrame());
|
||||
}
|
||||
|
||||
@@ -301,9 +302,22 @@ public sealed class UIResolutionAutoFitter : MonoBehaviour
|
||||
_cameraTunerAttached = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 轨道缩放器:挂到 effectEventController(已是单例集中点)或任意对象,运行时找 track1-5 拉长。
|
||||
if (!_trackScalerAttached)
|
||||
{
|
||||
var eff = effectEventController.Instance;
|
||||
if (eff != null)
|
||||
{
|
||||
if (eff.GetComponent<TrackScaler>() == null)
|
||||
eff.gameObject.AddComponent<TrackScaler>();
|
||||
_trackScalerAttached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _trackStabilizerAttached;
|
||||
private bool _cameraTunerAttached;
|
||||
private bool _trackScalerAttached;
|
||||
|
||||
// gameplay 敌人容器(普通 Transform,非 RectTransform):让其跟随屏幕左缘。
|
||||
// 敌人显示对象是 enemyObject_toWiggle,其定位父级是名为 empty 的普通 Transform,
|
||||
|
||||
@@ -425,6 +425,7 @@ public static class FirstRunFactoryResetService
|
||||
string[] keysToDelete =
|
||||
{
|
||||
"noteSpeedMultiplier",
|
||||
"noteSpeedMultiplierDefaultVersion",
|
||||
"UserGlobalDelaySeconds",
|
||||
"screenMode",
|
||||
"resolutionIndex",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
{
|
||||
@@ -23,16 +25,21 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
public GameObject good_judge_prefab;
|
||||
public GameObject miss_judge_prefab;
|
||||
|
||||
[Header("Inspector")]
|
||||
public GameObject trackSkill_prefab;
|
||||
|
||||
[Header("Inspector")]
|
||||
public float baseScale = 1.0f;
|
||||
public bool useRandomScale = true;
|
||||
public float minScaleMultiplier = 0.8f;
|
||||
public float maxScaleMultiplier = 1.2f;
|
||||
|
||||
[Header("Inspector")]
|
||||
public float jumpForce = 5.0f; // Documentation text normalized.
|
||||
public float gravity = -16f; // Documentation text normalized.
|
||||
[Tooltip("Horizontal drift distance range for judge popup text. Adds a real arc instead of a straight pop.")]
|
||||
[System.Serializable]
|
||||
public class PopupMotionSettings
|
||||
{
|
||||
public float jumpForce = 5.0f;
|
||||
public float gravity = -16f;
|
||||
[Tooltip("Horizontal drift distance range for popup text. Adds a real arc instead of a straight pop.")]
|
||||
public Vector2 horizontalDriftRange = new Vector2(0.25f, 0.65f);
|
||||
[Tooltip("Random Z rotation range applied over the popup lifetime.")]
|
||||
public float rotationRange = 8f;
|
||||
@@ -40,34 +47,60 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
public float scalePunch = 0.18f;
|
||||
[Tooltip("How long the initial scale punch takes before settling back.")]
|
||||
public float scalePunchDuration = 0.18f;
|
||||
public float fadeInTime = 0.1f;
|
||||
public float fadeOutStartTime = 0.6f;
|
||||
public float fadeOutDuration = 0.35f;
|
||||
}
|
||||
|
||||
[Header("Inspector")]
|
||||
public float fadeInTime = 0.1f; // Documentation text normalized.
|
||||
public float fadeOutStartTime = 0.6f; // Documentation text normalized.
|
||||
public float fadeOutDuration = 0.35f; // Documentation text normalized.
|
||||
[Header("Judge Popup")]
|
||||
public PopupMotionSettings judgePopup = new PopupMotionSettings()
|
||||
{
|
||||
jumpForce = 7.2f,
|
||||
gravity = -16f,
|
||||
horizontalDriftRange = new Vector2(0.25f, 0.65f),
|
||||
rotationRange = 8f,
|
||||
scalePunch = 0.18f,
|
||||
scalePunchDuration = 0.18f,
|
||||
fadeInTime = 0.1f,
|
||||
fadeOutStartTime = 0.3f,
|
||||
fadeOutDuration = 0.1f
|
||||
};
|
||||
|
||||
[Header("Track Skill Popup")]
|
||||
public PopupMotionSettings trackSkillPopup = new PopupMotionSettings()
|
||||
{
|
||||
jumpForce = 6.8f,
|
||||
gravity = -12.5f,
|
||||
horizontalDriftRange = new Vector2(0.18f, 0.48f),
|
||||
rotationRange = 6f,
|
||||
scalePunch = 0.14f,
|
||||
scalePunchDuration = 0.16f,
|
||||
fadeInTime = 0.08f,
|
||||
fadeOutStartTime = 0.36f,
|
||||
fadeOutDuration = 0.1f
|
||||
};
|
||||
|
||||
[Header("Track Skill UI")]
|
||||
[Tooltip("UI prefab spawned under world-space Effect nodes needs a small world scale. Judge SpriteRenderer prefabs still use baseScale directly.")]
|
||||
public float trackSkillUiScaleMultiplier = 0.01f;
|
||||
public int trackSkillSortingOrder = 9000;
|
||||
|
||||
// Cache spawn transforms to avoid accessing destroyed GameObject references.
|
||||
private Transform redSpawn;
|
||||
private Transform greenSpawn;
|
||||
private Transform yellowSpawn;
|
||||
private Transform purpleSpawn;
|
||||
private Transform blueSpawn;
|
||||
|
||||
// Track last scene to detect scene changes
|
||||
private int lastSceneIndex = -1;
|
||||
private Coroutine prewarmJudgeCoroutine;
|
||||
private readonly System.Collections.Generic.Dictionary<GameObject, System.Collections.Generic.Stack<GameObject>> judgePools
|
||||
= new System.Collections.Generic.Dictionary<GameObject, System.Collections.Generic.Stack<GameObject>>();
|
||||
private readonly System.Collections.Generic.HashSet<GameObject> rentedJudges
|
||||
= new System.Collections.Generic.HashSet<GameObject>();
|
||||
private readonly Dictionary<GameObject, Stack<GameObject>> judgePools = new Dictionary<GameObject, Stack<GameObject>>();
|
||||
private readonly HashSet<GameObject> rentedJudges = new HashSet<GameObject>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
// Documentation text normalized.
|
||||
// Documentation text normalized.
|
||||
}
|
||||
else if (Instance != this)
|
||||
{
|
||||
@@ -81,13 +114,11 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Ensure spawn points are cached after all scene objects are initialized
|
||||
CacheSpawnPointsIfNeeded();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Detect scene reload and refresh cache
|
||||
int currentSceneIndex = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex;
|
||||
if (currentSceneIndex != lastSceneIndex)
|
||||
{
|
||||
@@ -103,10 +134,8 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
|
||||
private void CacheSpawnPointsIfNeeded()
|
||||
{
|
||||
// Only recache if any of the references appear to be invalid (destroyed)
|
||||
if (redSpawn == null || greenSpawn == null || yellowSpawn == null || purpleSpawn == null || blueSpawn == null)
|
||||
{
|
||||
// Check if the effect GameObjects are still valid
|
||||
bool anyInvalid = (redEffect == null || redEffect.transform == null) ||
|
||||
(greenEffect == null || greenEffect.transform == null) ||
|
||||
(yellowEffect == null || yellowEffect.transform == null) ||
|
||||
@@ -122,7 +151,6 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
|
||||
private void CacheSpawnPoints()
|
||||
{
|
||||
// Important: check Unity "fake null" before accessing .transform
|
||||
redSpawn = redEffect != null ? redEffect.transform : null;
|
||||
greenSpawn = greenEffect != null ? greenEffect.transform : null;
|
||||
yellowSpawn = yellowEffect != null ? yellowEffect.transform : null;
|
||||
@@ -131,7 +159,7 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
|
||||
if (JudgeManager.IsDebugEnabled)
|
||||
{
|
||||
Debug.Log($"[Animation_Generate] Cached spawn points: red={redSpawn!=null}, green={greenSpawn!=null}, yellow={yellowSpawn!=null}, purple={purpleSpawn!=null}, blue={blueSpawn!=null}");
|
||||
Debug.Log($"[Animation_Generate] Cached spawn points: red={redSpawn != null}, green={greenSpawn != null}, yellow={yellowSpawn != null}, purple={purpleSpawn != null}, blue={blueSpawn != null}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,13 +200,37 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
instance.transform.localPosition = Vector3.zero;
|
||||
instance.transform.localRotation = Quaternion.identity;
|
||||
|
||||
// Documentation text normalized.
|
||||
float finalScale = baseScale;
|
||||
if (useRandomScale) finalScale *= Random.Range(minScaleMultiplier, maxScaleMultiplier);
|
||||
instance.transform.localScale = new Vector3(finalScale, finalScale, 1f);
|
||||
|
||||
// Documentation text normalized.
|
||||
StartCoroutine(AnimateSprite(instance));
|
||||
StartCoroutine(AnimatePopup(instance, judgePopup, false));
|
||||
}
|
||||
|
||||
public void SpawnTrackSkillPrefab(string color, Sprite skillIcon, string skillName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(color) || trackSkill_prefab == null)
|
||||
return;
|
||||
|
||||
Transform spawnPoint = GetSpawnPoint(color);
|
||||
if (spawnPoint == null)
|
||||
return;
|
||||
|
||||
GameObject instance = Instantiate(trackSkill_prefab, spawnPoint, false);
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
EnsureRenderableTrackSkillInstance(instance);
|
||||
BindTrackSkillContent(instance, skillIcon, skillName);
|
||||
|
||||
instance.transform.localPosition = Vector3.zero;
|
||||
instance.transform.localRotation = Quaternion.identity;
|
||||
float finalScale = baseScale;
|
||||
if (useRandomScale) finalScale *= Random.Range(minScaleMultiplier, maxScaleMultiplier);
|
||||
ApplyTrackSkillScale(instance, finalScale);
|
||||
instance.SetActive(true);
|
||||
|
||||
StartCoroutine(AnimatePopup(instance, trackSkillPopup, true));
|
||||
}
|
||||
|
||||
public void PrewarmJudgePrefabs(int perPrefab = 1)
|
||||
@@ -231,78 +283,98 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
prewarmJudgeCoroutine = null;
|
||||
}
|
||||
|
||||
private IEnumerator AnimateSprite(GameObject obj)
|
||||
private IEnumerator AnimatePopup(GameObject obj, PopupMotionSettings settings, bool destroyAtEnd = false)
|
||||
{
|
||||
if (obj == null) yield break;
|
||||
if (settings == null) settings = judgePopup;
|
||||
|
||||
SpriteRenderer[] renderers = obj.GetComponentsInChildren<SpriteRenderer>(true);
|
||||
Graphic[] graphics = obj.GetComponentsInChildren<Graphic>(true);
|
||||
CanvasGroup canvasGroup = obj.GetComponent<CanvasGroup>();
|
||||
|
||||
float elapsed = 0f;
|
||||
float vVelocity = jumpForce;
|
||||
float totalLifeTime = fadeOutStartTime + fadeOutDuration;
|
||||
float vVelocity = settings.jumpForce;
|
||||
float fadeDuration = Mathf.Max(0.001f, settings.fadeOutDuration);
|
||||
float estimatedApexTime = settings.gravity < -0.001f ? Mathf.Max(0f, settings.jumpForce / -settings.gravity) : settings.fadeOutStartTime;
|
||||
float totalLifeTime = Mathf.Max(settings.fadeInTime + fadeDuration, estimatedApexTime + fadeDuration);
|
||||
float driftSign = Random.value < 0.5f ? -1f : 1f;
|
||||
float driftDistance = Random.Range(horizontalDriftRange.x, horizontalDriftRange.y) * driftSign;
|
||||
float driftDistance = Random.Range(settings.horizontalDriftRange.x, settings.horizontalDriftRange.y) * driftSign;
|
||||
float horizontalVelocity = totalLifeTime > 0.001f ? driftDistance / totalLifeTime : 0f;
|
||||
float targetRotation = Random.Range(-rotationRange, rotationRange);
|
||||
float targetRotation = Random.Range(-settings.rotationRange, settings.rotationRange);
|
||||
Vector3 currentLocalPos = Vector3.zero;
|
||||
Vector3 baseScaleValue = obj.transform.localScale;
|
||||
bool fadeOutStarted = false;
|
||||
float fadeOutElapsed = 0f;
|
||||
float fadeOutStartAlpha = 1f;
|
||||
float currentAlpha = 0f;
|
||||
|
||||
SetRenderersAlpha(renderers, 0f);
|
||||
SetGraphicsAlpha(graphics, canvasGroup, 0f);
|
||||
|
||||
// Documentation text normalized.
|
||||
while (elapsed < totalLifeTime)
|
||||
while (!fadeOutStarted || fadeOutElapsed < fadeDuration)
|
||||
{
|
||||
if (obj == null) yield break;
|
||||
|
||||
float dt = Time.deltaTime;
|
||||
elapsed += dt;
|
||||
|
||||
// Documentation text normalized.
|
||||
vVelocity += gravity * dt;
|
||||
float previousVelocity = vVelocity;
|
||||
vVelocity += settings.gravity * dt;
|
||||
currentLocalPos.x += horizontalVelocity * dt;
|
||||
currentLocalPos.y += vVelocity * dt;
|
||||
obj.transform.localPosition = currentLocalPos;
|
||||
obj.transform.localRotation = Quaternion.Euler(0f, 0f, Mathf.Lerp(0f, targetRotation, Mathf.Clamp01(elapsed / totalLifeTime)));
|
||||
|
||||
float scaleT = scalePunchDuration > 0.001f ? Mathf.Clamp01(elapsed / scalePunchDuration) : 1f;
|
||||
float punch = Mathf.Sin(scaleT * Mathf.PI) * scalePunch;
|
||||
float scaleT = settings.scalePunchDuration > 0.001f ? Mathf.Clamp01(elapsed / settings.scalePunchDuration) : 1f;
|
||||
float punch = Mathf.Sin(scaleT * Mathf.PI) * settings.scalePunch;
|
||||
obj.transform.localScale = baseScaleValue * (1f + punch);
|
||||
|
||||
// Documentation text normalized.
|
||||
float alpha;
|
||||
bool reachedApex = previousVelocity > 0f && vVelocity <= 0f;
|
||||
if (!fadeOutStarted && (reachedApex || elapsed >= totalLifeTime - fadeDuration))
|
||||
{
|
||||
fadeOutStarted = true;
|
||||
fadeOutElapsed = 0f;
|
||||
fadeOutStartAlpha = currentAlpha;
|
||||
}
|
||||
|
||||
// Documentation text normalized.
|
||||
if (elapsed < fadeInTime)
|
||||
float alpha;
|
||||
if (fadeOutStarted)
|
||||
{
|
||||
alpha = Mathf.InverseLerp(0f, fadeInTime, elapsed);
|
||||
fadeOutElapsed += dt;
|
||||
alpha = Mathf.Lerp(fadeOutStartAlpha, 0f, Mathf.Clamp01(fadeOutElapsed / fadeDuration));
|
||||
}
|
||||
// Documentation text normalized.
|
||||
else if (elapsed > fadeOutStartTime)
|
||||
else if (elapsed < settings.fadeInTime)
|
||||
{
|
||||
// Documentation text normalized.
|
||||
alpha = Mathf.InverseLerp(totalLifeTime, fadeOutStartTime, elapsed);
|
||||
alpha = Mathf.InverseLerp(0f, settings.fadeInTime, elapsed);
|
||||
}
|
||||
// Documentation text normalized.
|
||||
else
|
||||
{
|
||||
alpha = 1f;
|
||||
}
|
||||
|
||||
SetRenderersAlpha(renderers, Mathf.Clamp01(alpha));
|
||||
alpha = Mathf.Clamp01(alpha);
|
||||
currentAlpha = alpha;
|
||||
SetRenderersAlpha(renderers, alpha);
|
||||
SetGraphicsAlpha(graphics, canvasGroup, alpha);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Documentation text normalized.
|
||||
if (obj != null)
|
||||
{
|
||||
obj.transform.localScale = baseScaleValue;
|
||||
obj.transform.localRotation = Quaternion.identity;
|
||||
SetRenderersAlpha(renderers, 0f);
|
||||
SetGraphicsAlpha(graphics, canvasGroup, 0f);
|
||||
}
|
||||
|
||||
// Documentation text normalized.
|
||||
|
||||
if (obj != null) ReturnJudgeToPool(obj);
|
||||
if (obj != null)
|
||||
{
|
||||
if (destroyAtEnd)
|
||||
Destroy(obj);
|
||||
else
|
||||
ReturnJudgeToPool(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetRenderersAlpha(SpriteRenderer[] renderers, float alpha)
|
||||
@@ -322,6 +394,104 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetGraphicsAlpha(Graphic[] graphics, CanvasGroup canvasGroup, float alpha)
|
||||
{
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
if (graphics == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < graphics.Length; i++)
|
||||
{
|
||||
Graphic graphic = graphics[i];
|
||||
if (graphic == null)
|
||||
continue;
|
||||
|
||||
Color c = graphic.color;
|
||||
c.a = alpha;
|
||||
graphic.color = c;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureRenderableTrackSkillInstance(GameObject instance)
|
||||
{
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
if (instance.GetComponentInChildren<Graphic>(true) == null)
|
||||
return;
|
||||
|
||||
Canvas canvas = instance.GetComponent<Canvas>();
|
||||
if (canvas == null)
|
||||
canvas = instance.AddComponent<Canvas>();
|
||||
|
||||
canvas.renderMode = RenderMode.WorldSpace;
|
||||
canvas.overrideSorting = true;
|
||||
canvas.sortingOrder = trackSkillSortingOrder;
|
||||
canvas.pixelPerfect = false;
|
||||
|
||||
CanvasGroup canvasGroup = instance.GetComponent<CanvasGroup>();
|
||||
if (canvasGroup == null)
|
||||
canvasGroup = instance.AddComponent<CanvasGroup>();
|
||||
canvasGroup.alpha = 1f;
|
||||
}
|
||||
|
||||
private void ApplyTrackSkillScale(GameObject instance, float finalScale)
|
||||
{
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
bool isUiPrefab = instance.GetComponentInChildren<Graphic>(true) != null;
|
||||
float appliedScale = isUiPrefab ? finalScale * trackSkillUiScaleMultiplier : finalScale;
|
||||
instance.transform.localScale = new Vector3(appliedScale, appliedScale, 1f);
|
||||
}
|
||||
|
||||
private void BindTrackSkillContent(GameObject instance, Sprite skillIcon, string skillName)
|
||||
{
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
Image targetImage = null;
|
||||
Text targetText = null;
|
||||
|
||||
TrackSkillPrefab modern = instance.GetComponent<TrackSkillPrefab>();
|
||||
if (modern != null)
|
||||
{
|
||||
modern.Bind(skillIcon, skillName);
|
||||
targetImage = modern.skillImage;
|
||||
targetText = modern.skillText;
|
||||
}
|
||||
else
|
||||
{
|
||||
trackSkillPrefab legacy = instance.GetComponent<trackSkillPrefab>();
|
||||
if (legacy != null)
|
||||
{
|
||||
targetImage = legacy.track_skillIcon;
|
||||
targetText = legacy.track_skillName;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetImage == null)
|
||||
targetImage = instance.GetComponentInChildren<Image>(true);
|
||||
if (targetText == null)
|
||||
targetText = instance.GetComponentInChildren<Text>(true);
|
||||
|
||||
if (targetImage != null)
|
||||
{
|
||||
targetImage.sprite = skillIcon;
|
||||
targetImage.enabled = skillIcon != null;
|
||||
}
|
||||
|
||||
if (targetText != null)
|
||||
{
|
||||
targetText.text = string.IsNullOrWhiteSpace(skillName) ? string.Empty : skillName;
|
||||
}
|
||||
}
|
||||
|
||||
private GameObject GetPooledJudge(GameObject prefab, Transform parent)
|
||||
{
|
||||
if (prefab == null || parent == null)
|
||||
@@ -329,7 +499,7 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
|
||||
if (!judgePools.TryGetValue(prefab, out var pool))
|
||||
{
|
||||
pool = new System.Collections.Generic.Stack<GameObject>();
|
||||
pool = new Stack<GameObject>();
|
||||
judgePools[prefab] = pool;
|
||||
}
|
||||
|
||||
@@ -384,7 +554,7 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
|
||||
if (!judgePools.TryGetValue(tag.prefabSource, out var pool))
|
||||
{
|
||||
pool = new System.Collections.Generic.Stack<GameObject>();
|
||||
pool = new Stack<GameObject>();
|
||||
judgePools[tag.prefabSource] = pool;
|
||||
}
|
||||
|
||||
@@ -399,8 +569,6 @@ public class Animation_GenerateJudgementSituationPrefab : MonoBehaviour
|
||||
{
|
||||
if (string.IsNullOrEmpty(color)) return null;
|
||||
|
||||
// Documentation text normalized.
|
||||
|
||||
Transform point = null;
|
||||
switch (color.ToLower())
|
||||
{
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 根据屏幕宽高比,对 gameplay 相机做"适当微调":叠加一点 X 旋转(俯角)与 Z 距离增量。
|
||||
/// 目的:不同分辨率下让轨道透视观感更协调(如超宽屏略微拉远/调俯角),与 contain 式 FOV 适配互补。
|
||||
/// 根据屏幕宽高比,对 gameplay 相机做分辨率适配,让宽屏下轨道观感更接近设计(16:9)。
|
||||
///
|
||||
/// 安全性(核心):**绝不直接写相机 transform**。所有微调通过 effectEventController.SetResolutionCameraAdjust
|
||||
/// 施加到"漂移 pivot"(顶层基准节点),并由该方法同步漂移基准 z——因此与相机漂移、SmoothShake 抖动、
|
||||
/// 技能偏移、冲刺特效等所有"写相机位姿"的业务逻辑完全不冲突(它们叠加在 pivot 之下或相机自身)。
|
||||
/// 需求(用户指定):越宽的屏幕(21:9/32:9)轨道越缩在中间、显细,左右与 UI 空出大黑缝。
|
||||
/// 解决方案:宽屏时把相机从设计基准(z=-8, X=-40°)逐步插值到"z 更高 + X 更朝水平"的目标位姿,
|
||||
/// 让轨道透视放大、铺满视野,同时保持判定线距离/视距/业务逻辑不变(音符/判定用世界坐标,不受相机缩放影响)。
|
||||
///
|
||||
/// 用一个 bool(enableTuning)开关。关闭时把增量清零(恢复设计基准),行为与未加此脚本一致。
|
||||
/// 增量相对"设计基准"计算,不累积;16:9 时增量为 0(与原设计逐像素一致)。
|
||||
/// 操作对象:**直接改相机本体(Main Camera 子物体)自身的 local z 高度与 local X 旋转**,
|
||||
/// 不动其父/祖父 pivot(drift/skill pivot)。通过 effectEventController.SetResolutionCameraSelf 施加,
|
||||
/// 由该方法处理与 SmoothShake(写相机 local)、冲刺(写相机 local z)的共享——空闲时写入持久生效,
|
||||
/// 抖动/冲刺触发时会以当前 local 重新捕获基准。不碰任何 UI:UI 贴边逻辑独立,本脚本只改相机位姿。
|
||||
///
|
||||
/// 用一个 bool(enableTuning)开关。关闭时恢复设计基准(与未加此脚本逐像素一致)。
|
||||
/// 16:9 及更窄(平板 1.6、竖屏)保持设计基准不动;宽屏线性插值;21:9 及更宽夹在最大偏移(用户指定的上限)。
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class CameraResolutionTuner : MonoBehaviour
|
||||
{
|
||||
[Tooltip("是否启用按分辨率微调相机(X旋转/Z)。关闭则恢复设计基准。")]
|
||||
[Tooltip("是否启用按分辨率微调相机。关闭则恢复设计基准。")]
|
||||
public bool enableTuning = true;
|
||||
|
||||
[Tooltip("设计基准宽高比。16:9 = 1.7778。此比例下增量为 0。")]
|
||||
[Tooltip("设计基准宽高比。16:9 = 1.7778。此比例及更窄时无微调。")]
|
||||
public float designAspect = 16f / 9f;
|
||||
|
||||
[Header("宽屏(aspect > 设计)微调")]
|
||||
[Tooltip("每偏离设计宽高比 1.0,额外叠加的 X 旋转角度(正=更俯视)。建议很小。")]
|
||||
public float widePerAspectRotX = 3f;
|
||||
[Tooltip("每偏离设计宽高比 1.0,额外叠加的 Z 距离(负=相机后退拉远)。")]
|
||||
public float widePerAspectZ = -1.0f;
|
||||
[Tooltip("宽屏微调的绝对上限,避免极端超宽屏过度。")]
|
||||
public float wideMaxRotX = 6f;
|
||||
public float wideMaxZ = -2.0f;
|
||||
[Tooltip("最大偏移的宽高比上限。21:9 = 2.3333。达到或超过此比例时夹在最大偏移。")]
|
||||
public float maxOffsetAspect = 21f / 9f;
|
||||
|
||||
[Header("窄屏(aspect < 设计)微调")]
|
||||
[Tooltip("每偏离设计宽高比 1.0,额外叠加的 X 旋转角度。")]
|
||||
public float narrowPerAspectRotX = 0f;
|
||||
[Tooltip("每偏离设计宽高比 1.0,额外叠加的 Z 距离。")]
|
||||
public float narrowPerAspectZ = 0f;
|
||||
public float narrowMaxRotX = 4f;
|
||||
public float narrowMaxZ = 2.0f;
|
||||
[Header("21:9 时相机本体(Main Camera)的目标 local 位姿(最大偏移)")]
|
||||
[Tooltip("21:9 时相机本体的目标 local Z 高度。用户指定 2.5(相机自身 z=2.5)。")]
|
||||
public float targetCameraZ = 2.5f;
|
||||
|
||||
[Tooltip("21:9 时相机本体的目标 local X 旋转角度。用户指定 5(相机自身 X=5°)。")]
|
||||
public float targetCameraRotX = 5f;
|
||||
|
||||
private float _lastAspect = -1f;
|
||||
private bool _lastEnabled;
|
||||
private bool _applied; // 上次 Apply 是否成功写入相机(相机/单例未就绪时为 false,下一帧重试)
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_lastAspect = -1f;
|
||||
_applied = false;
|
||||
Apply();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
float aspect = (float)Screen.width / Mathf.Max(1, Screen.height);
|
||||
if (Mathf.Abs(aspect - _lastAspect) > 0.0001f || enableTuning != _lastEnabled)
|
||||
// 宽高比/开关变化,或此前尚未成功写入(相机/单例未就绪),则重试。
|
||||
if (!_applied || Mathf.Abs(aspect - _lastAspect) > 0.0001f || enableTuning != _lastEnabled)
|
||||
{
|
||||
Apply();
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public sealed class CameraResolutionTuner : MonoBehaviour
|
||||
private void Apply()
|
||||
{
|
||||
var eff = effectEventController.Instance;
|
||||
if (eff == null) return;
|
||||
if (eff == null) { _applied = false; return; }
|
||||
|
||||
float aspect = (float)Screen.width / Mathf.Max(1, Screen.height);
|
||||
_lastAspect = aspect;
|
||||
@@ -66,26 +66,30 @@ public sealed class CameraResolutionTuner : MonoBehaviour
|
||||
|
||||
if (!enableTuning)
|
||||
{
|
||||
eff.SetResolutionCameraAdjust(0f, 0f); // 恢复设计基准
|
||||
// 关闭:t=0 → 相机本体回到 local 设计基准(运行时为 0/identity)。
|
||||
_applied = eff.SetResolutionCameraSelf(0f, targetCameraRotX, targetCameraZ);
|
||||
return;
|
||||
}
|
||||
|
||||
float rotX = 0f;
|
||||
float z = 0f;
|
||||
// 16:9 及更窄:t=0(无微调,与设计逐像素一致)。
|
||||
// 16:9 ~ 21:9:线性插值 t ∈ [0,1]。
|
||||
// 21:9 及更宽:t=1(夹在最大偏移上限)。
|
||||
float t = 0f;
|
||||
if (aspect > designAspect)
|
||||
{
|
||||
float dev = aspect - designAspect; // 宽屏偏离量
|
||||
rotX = Mathf.Clamp(widePerAspectRotX * dev, Mathf.Min(0f, wideMaxRotX), Mathf.Max(0f, wideMaxRotX));
|
||||
z = Mathf.Clamp(widePerAspectZ * dev, Mathf.Min(0f, wideMaxZ), Mathf.Max(0f, wideMaxZ));
|
||||
}
|
||||
else if (aspect < designAspect)
|
||||
if (aspect >= maxOffsetAspect)
|
||||
{
|
||||
float dev = designAspect - aspect; // 窄屏偏离量
|
||||
rotX = Mathf.Clamp(narrowPerAspectRotX * dev, Mathf.Min(0f, narrowMaxRotX), Mathf.Max(0f, narrowMaxRotX));
|
||||
z = Mathf.Clamp(narrowPerAspectZ * dev, Mathf.Min(0f, narrowMaxZ), Mathf.Max(0f, narrowMaxZ));
|
||||
t = 1f; // 超宽屏:夹在最大偏移
|
||||
}
|
||||
// 16:9:rotX=z=0,无微调。
|
||||
else
|
||||
{
|
||||
// 插值区间:designAspect(16:9≈1.778) → maxOffsetAspect(21:9≈2.333)
|
||||
t = (aspect - designAspect) / Mathf.Max(0.0001f, maxOffsetAspect - designAspect);
|
||||
t = Mathf.Clamp01(t);
|
||||
}
|
||||
}
|
||||
// aspect <= designAspect:t=0(无微调)。
|
||||
|
||||
eff.SetResolutionCameraAdjust(rotX, z);
|
||||
_applied = eff.SetResolutionCameraSelf(t, targetCameraRotX, targetCameraZ);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,23 @@ public class HoldNote : BaseNote
|
||||
private string noteID = string.Empty;
|
||||
private NoteSegment segment = NoteSegment.None;
|
||||
|
||||
// 获取用于生成判定 prefab 的颜色。如果 noteColor 为空(初始化异常),从 trackIndex 推导兜底。
|
||||
private string GetColorForPrefab()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(noteColor)) return noteColor;
|
||||
|
||||
if (trackIndex >= 0 && trackIndex < 5)
|
||||
{
|
||||
string[] trackColors = { "red", "green", "yellow", "purple", "blue" };
|
||||
string fallback = trackColors[trackIndex];
|
||||
if (JudgeManager.IsDebugEnabled) Debug.LogWarning($"[HoldNote] noteColor was null, using trackIndex {trackIndex} -> {fallback}");
|
||||
return fallback;
|
||||
}
|
||||
|
||||
Debug.LogError($"[HoldNote] Cannot derive color: noteColor is null and trackIndex {trackIndex} is invalid");
|
||||
return null;
|
||||
}
|
||||
|
||||
private float releaseTime = 0f;
|
||||
private bool hasReleased = true;
|
||||
private bool hasEnteredLine = false;
|
||||
@@ -810,19 +827,20 @@ public class HoldNote : BaseNote
|
||||
return;
|
||||
}
|
||||
|
||||
// If the key is still being held down when end segment enters judge zone, immediately judge.
|
||||
// Autoplay must ignore physical input to guarantee Perfect at scheduledEndTime.
|
||||
if (!GameConfig.autoPlayEnabled && IsHeld() && JudgeManager.Instance.IsStartJudged(noteID) && !isJudged)
|
||||
// END 段到达判定区时,如果头部已判定且玩家还没松手,立即自动结束(不等松手)。
|
||||
// 让玩家"长按到尾部"即视为完成,无需精确松手时机——更符合直觉且降低难度。
|
||||
// Autoplay 必须忽略物理输入,保证在 scheduledEndTime 精确判定 Perfect。
|
||||
if (JudgeManager.Instance.IsStartJudged(noteID) && !JudgeManager.Instance.HasNoteReleased(noteID) && !isJudged)
|
||||
{
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] End entered while key still held: {noteColor}, forcing Perfect");
|
||||
// Record release time as current time (player is still holding)
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] End entered while note not yet released: {noteColor}, auto-finishing hold as Perfect");
|
||||
// 记录松手时间为当前时刻(玩家按到尾部视为完美松手)
|
||||
releaseTime = GameplayClock.NowSongTime;
|
||||
hasReleased = true;
|
||||
JudgeManager.Instance?.RegisterNoteReleased(noteID, true);
|
||||
// Evaluate and judge the hold end - force Perfect since we reached the end while holding
|
||||
// 判定并生成 prefab——force Perfect(按到尾部=完美完成)
|
||||
EvaluateHoldEnd(releaseTime, false, true);
|
||||
isJudged = true;
|
||||
// Terminate input - mark key as released
|
||||
// 终止输入:标记键已松开,停止持续特效
|
||||
isHoldActive = false;
|
||||
StopHoldFxLoop();
|
||||
return;
|
||||
@@ -1056,6 +1074,7 @@ public class HoldNote : BaseNote
|
||||
if (result != "Miss")
|
||||
{
|
||||
TryRewriteNonMissJudgeToPerfect(trackIndex, ref result);
|
||||
GameplayLevelRuleEventBus.NotifyHoldStarted(trackIndex, result, pressTime, noteData);
|
||||
}
|
||||
|
||||
// show judge result and update combo/UI like short notes
|
||||
@@ -1079,10 +1098,30 @@ public class HoldNote : BaseNote
|
||||
TrackJudgeHitEffectController.PlayTrackHitFx(trackIndex);
|
||||
}
|
||||
|
||||
// Do not spawn the judgement animation prefab for Start (head) presses to avoid duplicated prefabs
|
||||
if (segment != NoteSegment.Start)
|
||||
// Spawn judgement prefab:
|
||||
// - START 段:只有 Miss 时才生成(点中的 Perfect/Good 不生成,避免头尾都弹;但 Miss 必须立即反馈)
|
||||
// - END/BODY 段:总是生成(包括 Miss)
|
||||
if (segment != NoteSegment.Start || result == "Miss")
|
||||
{
|
||||
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, result);
|
||||
string colorForPrefab = GetColorForPrefab();
|
||||
if (JudgeManager.IsDebugEnabled)
|
||||
{
|
||||
Debug.Log($"[HoldNote] Attempting to spawn prefab: segment={segment}, result={result}, noteColor={noteColor}, trackIndex={trackIndex}, derivedColor={colorForPrefab}, Instance={(Animation_GenerateJudgementSituationPrefab.Instance != null ? "EXISTS" : "NULL")}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(colorForPrefab))
|
||||
{
|
||||
Debug.LogError($"[HoldNote] Cannot spawn prefab: colorForPrefab is null! noteColor={noteColor}, trackIndex={trackIndex}, segment={segment}, result={result}");
|
||||
}
|
||||
else if (Animation_GenerateJudgementSituationPrefab.Instance == null)
|
||||
{
|
||||
Debug.LogError($"[HoldNote] Cannot spawn prefab: Animation_GenerateJudgementSituationPrefab.Instance is NULL!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Animation_GenerateJudgementSituationPrefab.Instance.SpawnJudgePrefab(colorForPrefab, result);
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] SpawnJudgePrefab called successfully for {colorForPrefab}, {result}");
|
||||
}
|
||||
}
|
||||
|
||||
if (segment != NoteSegment.Start)
|
||||
@@ -1239,6 +1278,15 @@ public class HoldNote : BaseNote
|
||||
AdjustCountsAfterRewriteToPerfect(trackIndex, originalEndJudge);
|
||||
}
|
||||
|
||||
if (result == "Miss")
|
||||
{
|
||||
GameplayLevelRuleEventBus.NotifyHoldBroken(trackIndex, result, actualReleaseTime, noteData);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameplayLevelRuleEventBus.NotifyHoldCompleted(trackIndex, result, actualReleaseTime, noteData);
|
||||
}
|
||||
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] END result for {noteID}: {result} (color={noteColor})");
|
||||
|
||||
// show UI/audio
|
||||
@@ -1257,7 +1305,25 @@ public class HoldNote : BaseNote
|
||||
}
|
||||
|
||||
// Ensure END always spawns judgement prefab (including Miss)
|
||||
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, result);
|
||||
string colorForPrefab = GetColorForPrefab();
|
||||
if (JudgeManager.IsDebugEnabled)
|
||||
{
|
||||
Debug.Log($"[HoldNote.EvaluateHoldEnd] Attempting to spawn END prefab: result={result}, noteColor={noteColor}, trackIndex={trackIndex}, derivedColor={colorForPrefab}, Instance={(Animation_GenerateJudgementSituationPrefab.Instance != null ? "EXISTS" : "NULL")}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(colorForPrefab))
|
||||
{
|
||||
Debug.LogError($"[HoldNote.EvaluateHoldEnd] Cannot spawn END prefab: colorForPrefab is null! noteColor={noteColor}, trackIndex={trackIndex}, result={result}");
|
||||
}
|
||||
else if (Animation_GenerateJudgementSituationPrefab.Instance == null)
|
||||
{
|
||||
Debug.LogError($"[HoldNote.EvaluateHoldEnd] Cannot spawn END prefab: Animation_GenerateJudgementSituationPrefab.Instance is NULL!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Animation_GenerateJudgementSituationPrefab.Instance.SpawnJudgePrefab(colorForPrefab, result);
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote.EvaluateHoldEnd] SpawnJudgePrefab called successfully for {colorForPrefab}, {result}");
|
||||
}
|
||||
|
||||
// --- Add scoring for End like short notes ---
|
||||
try
|
||||
|
||||
@@ -12,7 +12,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 088ed1ed6b6731f43ad3166781e3e931, type: 3}
|
||||
m_Name: NoteJudgeConfig
|
||||
m_EditorClassIdentifier:
|
||||
perfectRange: 0.15
|
||||
greatRange: 0.2
|
||||
goodRange: 0.25
|
||||
missRange: 0.3
|
||||
perfectRange: 0.1
|
||||
greatRange: 0.15
|
||||
goodRange: 0.2
|
||||
missRange: 0.25
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c5b2c74f439e604e8ffdbc589075131
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
public static class GameplayLevelRuleEventBus
|
||||
{
|
||||
public static event Action<int, string, float, NoteData> TapJudged;
|
||||
public static event Action<int, string, float, NoteData> HoldStarted;
|
||||
public static event Action<int, string, float, NoteData> HoldCompleted;
|
||||
public static event Action<int, string, float, NoteData> HoldBroken;
|
||||
public static event Action<int, float> AllySkillCast;
|
||||
public static event Action<EnemyCombatant, int, EnemyData_SO> EnemySpawned;
|
||||
public static event Action<EnemyCombatant, int, EnemyData_SO> EnemyDied;
|
||||
public static event Action<EnemyCombatant, float, GameObject> EnemyDamaged;
|
||||
public static event Action<EnemyCombatant> EnemyManaFull;
|
||||
|
||||
public static void NotifyTapJudged(int trackIndex, string result, float songTime, NoteData noteData)
|
||||
{
|
||||
TapJudged?.Invoke(trackIndex, result, songTime, noteData);
|
||||
}
|
||||
|
||||
public static void NotifyHoldStarted(int trackIndex, string result, float songTime, NoteData noteData)
|
||||
{
|
||||
HoldStarted?.Invoke(trackIndex, result, songTime, noteData);
|
||||
}
|
||||
|
||||
public static void NotifyHoldCompleted(int trackIndex, string result, float songTime, NoteData noteData)
|
||||
{
|
||||
HoldCompleted?.Invoke(trackIndex, result, songTime, noteData);
|
||||
}
|
||||
|
||||
public static void NotifyHoldBroken(int trackIndex, string result, float songTime, NoteData noteData)
|
||||
{
|
||||
HoldBroken?.Invoke(trackIndex, result, songTime, noteData);
|
||||
}
|
||||
|
||||
public static void NotifyAllySkillCast(int slotIndex, float songTime)
|
||||
{
|
||||
AllySkillCast?.Invoke(slotIndex, songTime);
|
||||
}
|
||||
|
||||
public static void NotifyEnemySpawned(EnemyCombatant enemy, int stageIndex, EnemyData_SO data)
|
||||
{
|
||||
EnemySpawned?.Invoke(enemy, stageIndex, data);
|
||||
}
|
||||
|
||||
public static void NotifyEnemyDied(EnemyCombatant enemy, int stageIndex, EnemyData_SO data)
|
||||
{
|
||||
EnemyDied?.Invoke(enemy, stageIndex, data);
|
||||
}
|
||||
|
||||
public static void NotifyEnemyDamaged(EnemyCombatant enemy, float actualDamage, GameObject source)
|
||||
{
|
||||
EnemyDamaged?.Invoke(enemy, actualDamage, source);
|
||||
}
|
||||
|
||||
public static void NotifyEnemyManaFull(EnemyCombatant enemy)
|
||||
{
|
||||
EnemyManaFull?.Invoke(enemy);
|
||||
}
|
||||
|
||||
public static bool ShouldPreventEnemyDeath(EnemyCombatant enemy)
|
||||
{
|
||||
return LevelRuleController.Instance != null && LevelRuleController.Instance.ShouldPreventEnemyDeath(enemy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eaf68fc7a5ecfce41bb49bc77dfc89cf
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(fileName = "NewLevelRuleConfig", menuName = "Bansonic/Gameplay/Level Rule Config")]
|
||||
public class LevelRuleConfig_SO : ScriptableObject
|
||||
{
|
||||
[Header("Match")]
|
||||
public int songID = 0;
|
||||
public string songName = string.Empty;
|
||||
[Tooltip("-1 means any difficulty.")]
|
||||
public int difficulty = -1;
|
||||
|
||||
[Header("Support Track")]
|
||||
public bool useLeastNoteTrackAsSupportTrack = false;
|
||||
[Tooltip("Used when useLeastNoteTrackAsSupportTrack is false. 0-4. -1 disables support-track logic.")]
|
||||
public int supportTrackIndex = -1;
|
||||
public int maxSupportCharges = 3;
|
||||
public int adjacentSkillCastsPerSupportCharge = 3;
|
||||
public float adjacentSkillCastWindow = 5f;
|
||||
|
||||
[Header("Echo")]
|
||||
public bool enableAdjacentSkillEcho = false;
|
||||
public float echoWindow = 1.5f;
|
||||
public int echoThreshold = 3;
|
||||
public int maxEchoStacks = 4;
|
||||
public float echoResistancePerStack = 0.06f;
|
||||
public float echoResistanceDuration = 4f;
|
||||
public int echoManaPenalty = 0;
|
||||
|
||||
[Header("Conditional Rewards")]
|
||||
public int allAlliesAliveScoreOnEnemyDeath = 0;
|
||||
public bool awardAllAlliesAliveScorePerEnemy = false;
|
||||
[Tooltip("At settlement: average living HP percent * this value is added as team score.")]
|
||||
public int finalAverageHpPercentScore = 0;
|
||||
|
||||
[Header("Stages")]
|
||||
public LevelRuleStage[] stages = Array.Empty<LevelRuleStage>();
|
||||
|
||||
public bool Matches(SongData song, int currentDifficulty)
|
||||
{
|
||||
if (song == null)
|
||||
return false;
|
||||
|
||||
bool idMatches = songID != 0 && song.songID == songID;
|
||||
bool nameMatches = !string.IsNullOrWhiteSpace(songName) && string.Equals(song.songName, songName, StringComparison.OrdinalIgnoreCase);
|
||||
if (!idMatches && !nameMatches)
|
||||
return false;
|
||||
|
||||
return difficulty < 0 || difficulty == currentDifficulty;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LevelRuleStage
|
||||
{
|
||||
[Tooltip("-1 means match by current stage index. If enemyID > 0, enemy ID also has to match.")]
|
||||
public int stageIndex = -1;
|
||||
public int enemyID = 0;
|
||||
[Tooltip("If > 0, this enemy is held at 1 HP before this song time.")]
|
||||
public float minimumDeathSongTime = -1f;
|
||||
public bool clearEchoOnSpawn = true;
|
||||
public bool clearSupportChargesOnSpawn = false;
|
||||
|
||||
public LevelRuleAction[] onSpawn = Array.Empty<LevelRuleAction>();
|
||||
public LevelRuleAction[] onManaFull = Array.Empty<LevelRuleAction>();
|
||||
public LevelRuleAction[] onDeath = Array.Empty<LevelRuleAction>();
|
||||
public LevelRuleAction[] onDamaged = Array.Empty<LevelRuleAction>();
|
||||
|
||||
public LevelRuleHoldAction[] onHoldStart = Array.Empty<LevelRuleHoldAction>();
|
||||
public LevelRuleHoldAction[] onHoldComplete = Array.Empty<LevelRuleHoldAction>();
|
||||
public LevelRuleHoldAction[] onHoldBreak = Array.Empty<LevelRuleHoldAction>();
|
||||
|
||||
public LevelRuleHpPhase[] hpPhases = Array.Empty<LevelRuleHpPhase>();
|
||||
|
||||
public bool Matches(int currentStageIndex, EnemyData_SO data)
|
||||
{
|
||||
if (stageIndex >= 0 && stageIndex != currentStageIndex)
|
||||
return false;
|
||||
|
||||
if (enemyID > 0 && (data == null || data.enemyID != enemyID))
|
||||
return false;
|
||||
|
||||
return stageIndex >= 0 || enemyID > 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LevelRuleHpPhase
|
||||
{
|
||||
[Range(0f, 1f)]
|
||||
public float hpPercentThreshold = 0.5f;
|
||||
public bool triggerOnce = true;
|
||||
public LevelRuleAction[] actions = Array.Empty<LevelRuleAction>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LevelRuleHoldAction
|
||||
{
|
||||
public LevelRuleAction[] actions = Array.Empty<LevelRuleAction>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LevelRuleAction
|
||||
{
|
||||
public string actionId = string.Empty;
|
||||
public LevelRuleActionType actionType = LevelRuleActionType.None;
|
||||
public LevelRuleTarget target = LevelRuleTarget.CurrentEnemy;
|
||||
public float amount = 0f;
|
||||
public bool amountScalesByDifficultyLevel = false;
|
||||
public bool amountIsPercentOfMaxHp = false;
|
||||
public float duration = 0f;
|
||||
public int maxTriggersPerEnemy = 0;
|
||||
public bool onlyIfAllAlliesAlive = false;
|
||||
public bool requireSupportCharge = false;
|
||||
public int scoreReward = 0;
|
||||
}
|
||||
|
||||
public enum LevelRuleActionType
|
||||
{
|
||||
None,
|
||||
Damage,
|
||||
Heal,
|
||||
ManaDelta,
|
||||
AttackDelta,
|
||||
ResistanceDeltaTimed,
|
||||
HealReceivedMultiplierTimed,
|
||||
AddTeamScore,
|
||||
DamageCurrentEnemyByOwnAttack,
|
||||
SpendSupportChargeForEnemyVulnerability,
|
||||
ClearEnemyMana
|
||||
}
|
||||
|
||||
public enum LevelRuleTarget
|
||||
{
|
||||
CurrentEnemy,
|
||||
AllLivingAllies,
|
||||
TriggerTrackAlly,
|
||||
HighestCurrentManaAlly,
|
||||
HighestAttackAlly,
|
||||
HighestRecentHitTrackAlly,
|
||||
HighestRecentSkillCastAlly
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0795ac37be9814449ab3d2b48aa3fdad
|
||||
@@ -0,0 +1,921 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class LevelRuleController : MonoBehaviour
|
||||
{
|
||||
public static LevelRuleController Instance { get; private set; }
|
||||
|
||||
[Header("Rule Configs")]
|
||||
public string resourcesPath = "so/levelRules";
|
||||
public LevelRuleConfig_SO[] configOverrides = Array.Empty<LevelRuleConfig_SO>();
|
||||
|
||||
[Header("Runtime")]
|
||||
[SerializeField] private bool verboseLogs = false;
|
||||
|
||||
private readonly Queue<float>[] recentHitTimes = new Queue<float>[5];
|
||||
private readonly Queue<float>[] recentSkillCastTimes = new Queue<float>[5];
|
||||
private readonly Dictionary<string, Coroutine> timedRoutines = new Dictionary<string, Coroutine>();
|
||||
private readonly Dictionary<string, int> actionTriggerCounts = new Dictionary<string, int>();
|
||||
private readonly HashSet<string> firedHpPhases = new HashSet<string>();
|
||||
|
||||
private LevelRuleConfig_SO activeConfig;
|
||||
private LevelRuleStage currentStageRule;
|
||||
private EnemyCombatant currentEnemy;
|
||||
private EnemyData_SO currentEnemyData;
|
||||
private SongData currentSong;
|
||||
private int currentDifficulty = -1;
|
||||
private int currentStageIndex = -1;
|
||||
private int supportTrackIndex = -1;
|
||||
private int supportCharges;
|
||||
private int adjacentSupportCastCount;
|
||||
private int lastSupportCastSlot = -1;
|
||||
private float lastSupportCastTime = -999f;
|
||||
private int echoStacks;
|
||||
private int lastEchoCastSlot = -1;
|
||||
private float lastEchoCastTime = -999f;
|
||||
private float nextConfigResolveTime;
|
||||
private int pendingSettlementScoreBonus;
|
||||
private bool handlingEnemyDamagedActions;
|
||||
|
||||
public int PendingSettlementScoreBonus => Mathf.Max(0, pendingSettlementScoreBonus);
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void Bootstrap()
|
||||
{
|
||||
EnsureInstance();
|
||||
}
|
||||
|
||||
public static LevelRuleController EnsureInstance()
|
||||
{
|
||||
if (Instance != null)
|
||||
return Instance;
|
||||
|
||||
LevelRuleController existing = SceneObjectLookupCache.FindAny<LevelRuleController>();
|
||||
if (existing != null)
|
||||
{
|
||||
Instance = existing;
|
||||
return existing;
|
||||
}
|
||||
|
||||
GameObject runtimeObject = new GameObject("__level_rule_controller_runtime");
|
||||
DontDestroyOnLoad(runtimeObject);
|
||||
Instance = runtimeObject.AddComponent<LevelRuleController>();
|
||||
return Instance;
|
||||
}
|
||||
|
||||
public static int ConsumePendingSettlementScoreBonus()
|
||||
{
|
||||
if (Instance == null)
|
||||
return 0;
|
||||
|
||||
if (GameConfig.autoPlayEnabled)
|
||||
{
|
||||
Instance.pendingSettlementScoreBonus = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Instance.AddFinalAverageHpScoreIfConfigured();
|
||||
int value = Mathf.Max(0, Instance.pendingSettlementScoreBonus);
|
||||
Instance.pendingSettlementScoreBonus = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
else if (Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < recentHitTimes.Length; i++)
|
||||
{
|
||||
recentHitTimes[i] = new Queue<float>();
|
||||
recentSkillCastTimes[i] = new Queue<float>();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded += HandleSceneLoaded;
|
||||
GameplayLevelRuleEventBus.TapJudged += HandleTapJudged;
|
||||
GameplayLevelRuleEventBus.HoldStarted += HandleHoldStarted;
|
||||
GameplayLevelRuleEventBus.HoldCompleted += HandleHoldCompleted;
|
||||
GameplayLevelRuleEventBus.HoldBroken += HandleHoldBroken;
|
||||
GameplayLevelRuleEventBus.AllySkillCast += HandleAllySkillCast;
|
||||
GameplayLevelRuleEventBus.EnemySpawned += HandleEnemySpawned;
|
||||
GameplayLevelRuleEventBus.EnemyDied += HandleEnemyDied;
|
||||
GameplayLevelRuleEventBus.EnemyDamaged += HandleEnemyDamaged;
|
||||
GameplayLevelRuleEventBus.EnemyManaFull += HandleEnemyManaFull;
|
||||
TryResolveActiveConfig(true);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneManager.sceneLoaded -= HandleSceneLoaded;
|
||||
GameplayLevelRuleEventBus.TapJudged -= HandleTapJudged;
|
||||
GameplayLevelRuleEventBus.HoldStarted -= HandleHoldStarted;
|
||||
GameplayLevelRuleEventBus.HoldCompleted -= HandleHoldCompleted;
|
||||
GameplayLevelRuleEventBus.HoldBroken -= HandleHoldBroken;
|
||||
GameplayLevelRuleEventBus.AllySkillCast -= HandleAllySkillCast;
|
||||
GameplayLevelRuleEventBus.EnemySpawned -= HandleEnemySpawned;
|
||||
GameplayLevelRuleEventBus.EnemyDied -= HandleEnemyDied;
|
||||
GameplayLevelRuleEventBus.EnemyDamaged -= HandleEnemyDamaged;
|
||||
GameplayLevelRuleEventBus.EnemyManaFull -= HandleEnemyManaFull;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Time.unscaledTime < nextConfigResolveTime)
|
||||
return;
|
||||
|
||||
nextConfigResolveTime = Time.unscaledTime + 0.5f;
|
||||
TryResolveActiveConfig(false);
|
||||
}
|
||||
|
||||
private void HandleSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
ResetRuntimeState();
|
||||
nextConfigResolveTime = 0f;
|
||||
TryResolveActiveConfig(true);
|
||||
}
|
||||
|
||||
private void TryResolveActiveConfig(bool force)
|
||||
{
|
||||
BeatmapManager beatmapManager = BeatmapManager.Instance;
|
||||
if (beatmapManager == null || beatmapManager.assignedSongData == null)
|
||||
{
|
||||
if (force)
|
||||
SetActiveConfig(null, null, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
SongData song = beatmapManager.assignedSongData;
|
||||
int difficulty = beatmapManager.assignedDifficulty;
|
||||
if (!force && activeConfig != null && currentSong == song && currentDifficulty == difficulty)
|
||||
return;
|
||||
|
||||
LevelRuleConfig_SO matched = FindConfig(song, difficulty);
|
||||
SetActiveConfig(matched, song, difficulty);
|
||||
}
|
||||
|
||||
private LevelRuleConfig_SO FindConfig(SongData song, int difficulty)
|
||||
{
|
||||
if (configOverrides != null)
|
||||
{
|
||||
for (int i = 0; i < configOverrides.Length; i++)
|
||||
{
|
||||
LevelRuleConfig_SO config = configOverrides[i];
|
||||
if (config != null && config.Matches(song, difficulty))
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
LevelRuleConfig_SO[] resources = Resources.LoadAll<LevelRuleConfig_SO>(resourcesPath);
|
||||
if (resources != null)
|
||||
{
|
||||
for (int i = 0; i < resources.Length; i++)
|
||||
{
|
||||
LevelRuleConfig_SO config = resources[i];
|
||||
if (config != null && config.Matches(song, difficulty))
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetActiveConfig(LevelRuleConfig_SO config, SongData song, int difficulty)
|
||||
{
|
||||
if (activeConfig == config && currentSong == song && currentDifficulty == difficulty)
|
||||
return;
|
||||
|
||||
activeConfig = config;
|
||||
currentSong = song;
|
||||
currentDifficulty = difficulty;
|
||||
ResetRuntimeState();
|
||||
|
||||
if (activeConfig != null)
|
||||
{
|
||||
supportTrackIndex = ResolveSupportTrackIndex();
|
||||
if (verboseLogs)
|
||||
Debug.Log($"[LevelRuleController] Active rule: {activeConfig.name}, supportTrack={supportTrackIndex}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetRuntimeState()
|
||||
{
|
||||
currentStageRule = null;
|
||||
currentEnemy = null;
|
||||
currentEnemyData = null;
|
||||
currentStageIndex = -1;
|
||||
supportTrackIndex = -1;
|
||||
supportCharges = 0;
|
||||
adjacentSupportCastCount = 0;
|
||||
lastSupportCastSlot = -1;
|
||||
lastSupportCastTime = -999f;
|
||||
echoStacks = 0;
|
||||
lastEchoCastSlot = -1;
|
||||
lastEchoCastTime = -999f;
|
||||
pendingSettlementScoreBonus = 0;
|
||||
actionTriggerCounts.Clear();
|
||||
firedHpPhases.Clear();
|
||||
|
||||
for (int i = 0; i < recentHitTimes.Length; i++)
|
||||
{
|
||||
recentHitTimes[i].Clear();
|
||||
recentSkillCastTimes[i].Clear();
|
||||
}
|
||||
|
||||
foreach (Coroutine routine in timedRoutines.Values)
|
||||
{
|
||||
if (routine != null)
|
||||
StopCoroutine(routine);
|
||||
}
|
||||
timedRoutines.Clear();
|
||||
}
|
||||
|
||||
private int ResolveSupportTrackIndex()
|
||||
{
|
||||
if (activeConfig == null)
|
||||
return -1;
|
||||
|
||||
if (!activeConfig.useLeastNoteTrackAsSupportTrack)
|
||||
return activeConfig.supportTrackIndex >= 0 && activeConfig.supportTrackIndex < 5 ? activeConfig.supportTrackIndex : -1;
|
||||
|
||||
Beatmap beatmap = BeatmapManager.Instance != null ? BeatmapManager.Instance.beatmap : null;
|
||||
if (beatmap == null || beatmap.notes == null || beatmap.notes.Length == 0)
|
||||
return activeConfig.supportTrackIndex >= 0 && activeConfig.supportTrackIndex < 5 ? activeConfig.supportTrackIndex : -1;
|
||||
|
||||
int[] counts = new int[5];
|
||||
for (int i = 0; i < beatmap.notes.Length; i++)
|
||||
{
|
||||
int track = beatmap.notes[i] != null ? beatmap.notes[i].trackIndex : -1;
|
||||
if (track >= 0 && track < counts.Length)
|
||||
counts[track]++;
|
||||
}
|
||||
|
||||
int bestTrack = 0;
|
||||
int bestCount = int.MaxValue;
|
||||
for (int i = 0; i < counts.Length; i++)
|
||||
{
|
||||
if (counts[i] < bestCount)
|
||||
{
|
||||
bestCount = counts[i];
|
||||
bestTrack = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestTrack;
|
||||
}
|
||||
|
||||
private void HandleTapJudged(int trackIndex, string result, float songTime, NoteData noteData)
|
||||
{
|
||||
if (activeConfig == null)
|
||||
return;
|
||||
|
||||
if (IsHit(result))
|
||||
RecordRecent(recentHitTimes, trackIndex, songTime, 10f);
|
||||
}
|
||||
|
||||
private void HandleHoldStarted(int trackIndex, string result, float songTime, NoteData noteData)
|
||||
{
|
||||
if (activeConfig == null || currentStageRule == null)
|
||||
return;
|
||||
|
||||
if (IsHit(result))
|
||||
RecordRecent(recentHitTimes, trackIndex, songTime, 10f);
|
||||
|
||||
ExecuteHoldActions(currentStageRule.onHoldStart, trackIndex);
|
||||
}
|
||||
|
||||
private void HandleHoldCompleted(int trackIndex, string result, float songTime, NoteData noteData)
|
||||
{
|
||||
if (activeConfig == null || currentStageRule == null)
|
||||
return;
|
||||
|
||||
if (IsHit(result))
|
||||
RecordRecent(recentHitTimes, trackIndex, songTime, 10f);
|
||||
|
||||
ExecuteHoldActions(currentStageRule.onHoldComplete, trackIndex);
|
||||
}
|
||||
|
||||
private void HandleHoldBroken(int trackIndex, string result, float songTime, NoteData noteData)
|
||||
{
|
||||
if (activeConfig == null || currentStageRule == null)
|
||||
return;
|
||||
|
||||
ExecuteHoldActions(currentStageRule.onHoldBreak, trackIndex);
|
||||
}
|
||||
|
||||
private void HandleAllySkillCast(int slotIndex, float songTime)
|
||||
{
|
||||
if (activeConfig == null)
|
||||
return;
|
||||
|
||||
RecordRecent(recentSkillCastTimes, slotIndex, songTime, 10f);
|
||||
UpdateSupportCharge(slotIndex, songTime);
|
||||
UpdateEcho(slotIndex, songTime);
|
||||
}
|
||||
|
||||
private void HandleEnemySpawned(EnemyCombatant enemy, int stageIndex, EnemyData_SO data)
|
||||
{
|
||||
if (activeConfig == null)
|
||||
return;
|
||||
|
||||
currentEnemy = enemy;
|
||||
currentEnemyData = data;
|
||||
currentStageIndex = stageIndex;
|
||||
currentStageRule = ResolveStageRule(stageIndex, data);
|
||||
firedHpPhases.Clear();
|
||||
actionTriggerCounts.Clear();
|
||||
|
||||
if (currentStageRule == null)
|
||||
return;
|
||||
|
||||
if (currentStageRule.clearEchoOnSpawn)
|
||||
ClearEcho();
|
||||
|
||||
if (currentStageRule.clearSupportChargesOnSpawn)
|
||||
supportCharges = 0;
|
||||
|
||||
ExecuteActions(currentStageRule.onSpawn, -1, enemy);
|
||||
}
|
||||
|
||||
private void HandleEnemyDied(EnemyCombatant enemy, int stageIndex, EnemyData_SO data)
|
||||
{
|
||||
if (activeConfig == null)
|
||||
return;
|
||||
|
||||
LevelRuleStage stage = ResolveStageRule(stageIndex, data);
|
||||
if (stage != null)
|
||||
ExecuteActions(stage.onDeath, -1, enemy);
|
||||
|
||||
if (activeConfig.awardAllAlliesAliveScorePerEnemy && activeConfig.allAlliesAliveScoreOnEnemyDeath > 0 && AreAllConfiguredAlliesAlive())
|
||||
AddPendingScore(activeConfig.allAlliesAliveScoreOnEnemyDeath);
|
||||
}
|
||||
|
||||
private void HandleEnemyDamaged(EnemyCombatant enemy, float actualDamage, GameObject source)
|
||||
{
|
||||
if (activeConfig == null || currentStageRule == null || enemy == null || enemy != currentEnemy)
|
||||
return;
|
||||
|
||||
if (!handlingEnemyDamagedActions)
|
||||
{
|
||||
handlingEnemyDamagedActions = true;
|
||||
try
|
||||
{
|
||||
ExecuteActions(currentStageRule.onDamaged, -1, enemy);
|
||||
}
|
||||
finally
|
||||
{
|
||||
handlingEnemyDamagedActions = false;
|
||||
}
|
||||
}
|
||||
|
||||
EvaluateHpPhases(enemy);
|
||||
}
|
||||
|
||||
private void HandleEnemyManaFull(EnemyCombatant enemy)
|
||||
{
|
||||
if (activeConfig == null || currentStageRule == null || enemy == null || enemy != currentEnemy)
|
||||
return;
|
||||
|
||||
ExecuteActions(currentStageRule.onManaFull, -1, enemy);
|
||||
}
|
||||
|
||||
public bool ShouldPreventEnemyDeath(EnemyCombatant enemy)
|
||||
{
|
||||
if (activeConfig == null || currentStageRule == null || enemy == null || enemy != currentEnemy)
|
||||
return false;
|
||||
|
||||
if (currentStageRule.minimumDeathSongTime <= 0f)
|
||||
return false;
|
||||
|
||||
return GameplayClock.NowSongTime < currentStageRule.minimumDeathSongTime;
|
||||
}
|
||||
|
||||
private LevelRuleStage ResolveStageRule(int stageIndex, EnemyData_SO data)
|
||||
{
|
||||
if (activeConfig == null || activeConfig.stages == null)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < activeConfig.stages.Length; i++)
|
||||
{
|
||||
LevelRuleStage stage = activeConfig.stages[i];
|
||||
if (stage != null && stage.Matches(stageIndex, data))
|
||||
return stage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ExecuteHoldActions(LevelRuleHoldAction[] groups, int triggerTrack)
|
||||
{
|
||||
if (groups == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < groups.Length; i++)
|
||||
{
|
||||
LevelRuleHoldAction group = groups[i];
|
||||
if (group == null)
|
||||
continue;
|
||||
|
||||
ExecuteActions(group.actions, triggerTrack, currentEnemy);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteActions(LevelRuleAction[] actions, int triggerTrack, EnemyCombatant eventEnemy)
|
||||
{
|
||||
if (actions == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < actions.Length; i++)
|
||||
ExecuteAction(actions[i], triggerTrack, eventEnemy);
|
||||
}
|
||||
|
||||
private void ExecuteAction(LevelRuleAction action, int triggerTrack, EnemyCombatant eventEnemy)
|
||||
{
|
||||
if (action == null || action.actionType == LevelRuleActionType.None)
|
||||
return;
|
||||
|
||||
if (action.onlyIfAllAlliesAlive && !AreAllConfiguredAlliesAlive())
|
||||
return;
|
||||
|
||||
string key = BuildActionKey(action, eventEnemy);
|
||||
if (action.maxTriggersPerEnemy > 0)
|
||||
{
|
||||
actionTriggerCounts.TryGetValue(key, out int count);
|
||||
if (count >= action.maxTriggersPerEnemy)
|
||||
return;
|
||||
|
||||
actionTriggerCounts[key] = count + 1;
|
||||
}
|
||||
|
||||
if (action.requireSupportCharge && supportCharges <= 0)
|
||||
return;
|
||||
|
||||
if (action.actionType == LevelRuleActionType.AddTeamScore)
|
||||
{
|
||||
AddPendingScore(Mathf.RoundToInt(ResolveRawAmount(action)));
|
||||
if (action.scoreReward > 0)
|
||||
AddPendingScore(action.scoreReward);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.actionType == LevelRuleActionType.SpendSupportChargeForEnemyVulnerability)
|
||||
{
|
||||
if (supportCharges <= 0 || currentEnemy == null)
|
||||
return;
|
||||
|
||||
supportCharges--;
|
||||
ApplyTimedResistance(currentEnemy, -Mathf.Abs(ResolveRawAmount(action)), Mathf.Max(0.01f, action.duration), key);
|
||||
if (action.scoreReward > 0)
|
||||
AddPendingScore(action.scoreReward);
|
||||
return;
|
||||
}
|
||||
|
||||
List<GameObject> targets = ResolveTargets(action.target, triggerTrack);
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
GameObject target = targets[i];
|
||||
if (target == null)
|
||||
continue;
|
||||
|
||||
ApplyActionToTarget(action, target, key);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyActionToTarget(LevelRuleAction action, GameObject target, string key)
|
||||
{
|
||||
AllyCombatant ally = target.GetComponent<AllyCombatant>();
|
||||
EnemyCombatant enemy = target.GetComponent<EnemyCombatant>();
|
||||
float amount = ResolveAmountForTarget(action, ally, enemy);
|
||||
|
||||
switch (action.actionType)
|
||||
{
|
||||
case LevelRuleActionType.Damage:
|
||||
if (ally != null) ally.ReceiveDamage(amount, currentEnemy != null ? currentEnemy.gameObject : null);
|
||||
else if (enemy != null) enemy.ReceiveDamage(amount, currentEnemy != null ? currentEnemy.gameObject : null);
|
||||
break;
|
||||
case LevelRuleActionType.Heal:
|
||||
if (ally != null) ally.ReceiveHeal(amount, currentEnemy != null ? currentEnemy.gameObject : null);
|
||||
else if (enemy != null) enemy.ReceiveHeal(amount, currentEnemy != null ? currentEnemy.gameObject : null);
|
||||
break;
|
||||
case LevelRuleActionType.ManaDelta:
|
||||
if (ally != null) ally.ModifyMana(Mathf.RoundToInt(amount), true, true);
|
||||
else if (enemy != null) enemy.ModifyMana(Mathf.RoundToInt(amount), true);
|
||||
break;
|
||||
case LevelRuleActionType.AttackDelta:
|
||||
if (ally != null) ally.ModifyAttack(Mathf.RoundToInt(amount));
|
||||
else if (enemy != null) enemy.ModifyAttack(Mathf.RoundToInt(amount));
|
||||
break;
|
||||
case LevelRuleActionType.ResistanceDeltaTimed:
|
||||
if (enemy != null) ApplyTimedResistance(enemy, amount, Mathf.Max(0.01f, action.duration), key);
|
||||
break;
|
||||
case LevelRuleActionType.HealReceivedMultiplierTimed:
|
||||
ApplyTimedHealMultiplier(target, amount, Mathf.Max(0.01f, action.duration), key);
|
||||
break;
|
||||
case LevelRuleActionType.DamageCurrentEnemyByOwnAttack:
|
||||
if (currentEnemy != null) currentEnemy.ReceiveDamage(Mathf.Max(0, currentEnemy.attack), currentEnemy.gameObject);
|
||||
break;
|
||||
case LevelRuleActionType.ClearEnemyMana:
|
||||
if (enemy != null) enemy.ModifyMana(-enemy.currentMana, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private float ResolveRawAmount(LevelRuleAction action)
|
||||
{
|
||||
if (action == null)
|
||||
return 0f;
|
||||
|
||||
float amount = action.amount;
|
||||
if (action.amountScalesByDifficultyLevel)
|
||||
amount *= GetCurrentDifficultyLevel();
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
private float ResolveAmountForTarget(LevelRuleAction action, AllyCombatant ally, EnemyCombatant enemy)
|
||||
{
|
||||
float amount = ResolveRawAmount(action);
|
||||
if (!action.amountIsPercentOfMaxHp)
|
||||
return amount;
|
||||
|
||||
if (ally != null)
|
||||
return ally.maxHP * amount;
|
||||
|
||||
if (enemy != null)
|
||||
return enemy.maxHP * amount;
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
private List<GameObject> ResolveTargets(LevelRuleTarget target, int triggerTrack)
|
||||
{
|
||||
List<GameObject> targets = new List<GameObject>();
|
||||
teamUIController ui = teamUIController.Instance;
|
||||
|
||||
switch (target)
|
||||
{
|
||||
case LevelRuleTarget.CurrentEnemy:
|
||||
if (currentEnemy != null) targets.Add(currentEnemy.gameObject);
|
||||
break;
|
||||
case LevelRuleTarget.AllLivingAllies:
|
||||
AddAllLivingAllies(targets, ui);
|
||||
break;
|
||||
case LevelRuleTarget.TriggerTrackAlly:
|
||||
AddAllyBySlot(targets, ui, triggerTrack);
|
||||
break;
|
||||
case LevelRuleTarget.HighestCurrentManaAlly:
|
||||
AddAllyBySlot(targets, ui, FindBestAllySlotByMana(ui));
|
||||
break;
|
||||
case LevelRuleTarget.HighestAttackAlly:
|
||||
AddAllyBySlot(targets, ui, FindBestAllySlotByAttack(ui));
|
||||
break;
|
||||
case LevelRuleTarget.HighestRecentHitTrackAlly:
|
||||
AddAllyBySlot(targets, ui, FindMostRecentCountTrack(recentHitTimes, 8f));
|
||||
break;
|
||||
case LevelRuleTarget.HighestRecentSkillCastAlly:
|
||||
AddAllyBySlot(targets, ui, FindMostRecentCountTrack(recentSkillCastTimes, 8f));
|
||||
break;
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
private void AddAllLivingAllies(List<GameObject> targets, teamUIController ui)
|
||||
{
|
||||
if (ui == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
GameObject go = ui.GetAllyObjectBySlot(i);
|
||||
AllyCombatant ally = go != null ? go.GetComponent<AllyCombatant>() : null;
|
||||
if (ally != null && !ally.IsDead && ally.maxHP > 0)
|
||||
targets.Add(go);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllyBySlot(List<GameObject> targets, teamUIController ui, int slot)
|
||||
{
|
||||
if (ui == null || slot < 0 || slot >= 5)
|
||||
return;
|
||||
|
||||
GameObject go = ui.GetAllyObjectBySlot(slot);
|
||||
if (go != null)
|
||||
targets.Add(go);
|
||||
}
|
||||
|
||||
private int FindBestAllySlotByMana(teamUIController ui)
|
||||
{
|
||||
int bestSlot = -1;
|
||||
int bestMana = int.MinValue;
|
||||
if (ui == null)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
AllyCombatant ally = ui.GetAllyObjectBySlot(i)?.GetComponent<AllyCombatant>();
|
||||
if (ally == null || ally.IsDead || ally.maxHP <= 0)
|
||||
continue;
|
||||
|
||||
if (ally.currentMana > bestMana)
|
||||
{
|
||||
bestMana = ally.currentMana;
|
||||
bestSlot = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestSlot;
|
||||
}
|
||||
|
||||
private int FindBestAllySlotByAttack(teamUIController ui)
|
||||
{
|
||||
int bestSlot = -1;
|
||||
int bestAttack = int.MinValue;
|
||||
if (ui == null)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
AllyCombatant ally = ui.GetAllyObjectBySlot(i)?.GetComponent<AllyCombatant>();
|
||||
if (ally == null || ally.IsDead || ally.maxHP <= 0)
|
||||
continue;
|
||||
|
||||
if (ally.attack > bestAttack)
|
||||
{
|
||||
bestAttack = ally.attack;
|
||||
bestSlot = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestSlot;
|
||||
}
|
||||
|
||||
private void UpdateSupportCharge(int slotIndex, float songTime)
|
||||
{
|
||||
if (activeConfig == null || supportTrackIndex < 0 || activeConfig.adjacentSkillCastsPerSupportCharge <= 0)
|
||||
return;
|
||||
|
||||
if (lastSupportCastSlot >= 0 && Mathf.Abs(slotIndex - lastSupportCastSlot) == 1 && songTime - lastSupportCastTime <= activeConfig.adjacentSkillCastWindow)
|
||||
adjacentSupportCastCount++;
|
||||
else
|
||||
adjacentSupportCastCount = 1;
|
||||
|
||||
lastSupportCastSlot = slotIndex;
|
||||
lastSupportCastTime = songTime;
|
||||
|
||||
if (adjacentSupportCastCount >= activeConfig.adjacentSkillCastsPerSupportCharge)
|
||||
{
|
||||
supportCharges = Mathf.Clamp(supportCharges + 1, 0, Mathf.Max(0, activeConfig.maxSupportCharges));
|
||||
adjacentSupportCastCount = 0;
|
||||
if (verboseLogs) Debug.Log($"[LevelRuleController] Support charge +1 => {supportCharges}");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEcho(int slotIndex, float songTime)
|
||||
{
|
||||
if (activeConfig == null || !activeConfig.enableAdjacentSkillEcho)
|
||||
return;
|
||||
|
||||
if (lastEchoCastSlot >= 0 && Mathf.Abs(slotIndex - lastEchoCastSlot) == 1 && songTime - lastEchoCastTime <= activeConfig.echoWindow)
|
||||
echoStacks = Mathf.Clamp(echoStacks + 1, 0, Mathf.Max(1, activeConfig.maxEchoStacks));
|
||||
else
|
||||
echoStacks = 1;
|
||||
|
||||
lastEchoCastSlot = slotIndex;
|
||||
lastEchoCastTime = songTime;
|
||||
|
||||
if (echoStacks >= activeConfig.echoThreshold && currentEnemy != null)
|
||||
{
|
||||
float delta = Mathf.Clamp(activeConfig.echoResistancePerStack * echoStacks, 0f, 0.6f);
|
||||
ApplyTimedResistance(currentEnemy, delta, Mathf.Max(0.01f, activeConfig.echoResistanceDuration), "echo_resistance");
|
||||
if (activeConfig.echoManaPenalty > 0)
|
||||
{
|
||||
GameObject allyGo = teamUIController.Instance != null ? teamUIController.Instance.GetAllyObjectBySlot(slotIndex) : null;
|
||||
AllyCombatant ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
|
||||
ally?.ModifyMana(-activeConfig.echoManaPenalty, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearEcho()
|
||||
{
|
||||
echoStacks = 0;
|
||||
lastEchoCastSlot = -1;
|
||||
lastEchoCastTime = -999f;
|
||||
}
|
||||
|
||||
private void EvaluateHpPhases(EnemyCombatant enemy)
|
||||
{
|
||||
if (currentStageRule.hpPhases == null || enemy.maxHP <= 0)
|
||||
return;
|
||||
|
||||
float hpPercent = Mathf.Clamp01((float)enemy.currentHP / enemy.maxHP);
|
||||
for (int i = 0; i < currentStageRule.hpPhases.Length; i++)
|
||||
{
|
||||
LevelRuleHpPhase phase = currentStageRule.hpPhases[i];
|
||||
if (phase == null)
|
||||
continue;
|
||||
|
||||
string key = $"{currentStageIndex}:{enemy.GetInstanceID()}:phase:{i}";
|
||||
if (phase.triggerOnce && firedHpPhases.Contains(key))
|
||||
continue;
|
||||
|
||||
if (hpPercent <= phase.hpPercentThreshold)
|
||||
{
|
||||
firedHpPhases.Add(key);
|
||||
ExecuteActions(phase.actions, -1, enemy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyTimedResistance(EnemyCombatant enemy, float delta, float duration, string key)
|
||||
{
|
||||
if (enemy == null)
|
||||
return;
|
||||
|
||||
string routineKey = $"res:{enemy.GetInstanceID()}:{key}";
|
||||
StopTimedRoutine(routineKey);
|
||||
timedRoutines[routineKey] = StartCoroutine(TimedResistanceRoutine(enemy, delta, duration, routineKey));
|
||||
}
|
||||
|
||||
private IEnumerator TimedResistanceRoutine(EnemyCombatant enemy, float delta, float duration, string routineKey)
|
||||
{
|
||||
if (enemy == null)
|
||||
yield break;
|
||||
|
||||
enemy.damageResistance = Mathf.Clamp(enemy.damageResistance + delta, -0.95f, 0.6f);
|
||||
yield return GameplayClock.WaitForSeconds(duration);
|
||||
if (enemy != null)
|
||||
enemy.damageResistance = Mathf.Clamp(enemy.damageResistance - delta, -0.95f, 0.6f);
|
||||
timedRoutines.Remove(routineKey);
|
||||
}
|
||||
|
||||
private void ApplyTimedHealMultiplier(GameObject target, float multiplier, float duration, string key)
|
||||
{
|
||||
ICombatant combatant = target != null ? target.GetComponent<ICombatant>() : null;
|
||||
if (combatant == null)
|
||||
return;
|
||||
|
||||
string routineKey = $"heal:{target.GetInstanceID()}:{key}";
|
||||
StopTimedRoutine(routineKey);
|
||||
timedRoutines[routineKey] = StartCoroutine(TimedHealMultiplierRoutine(target, combatant, multiplier, duration, routineKey));
|
||||
}
|
||||
|
||||
private IEnumerator TimedHealMultiplierRoutine(GameObject target, ICombatant combatant, float multiplier, float duration, string routineKey)
|
||||
{
|
||||
if (target == null || combatant == null)
|
||||
yield break;
|
||||
|
||||
Buff buff = new Buff(routineKey)
|
||||
{
|
||||
description = "Level rule heal received multiplier",
|
||||
duration = duration,
|
||||
healReceivedMultiplier = Mathf.Max(0f, multiplier)
|
||||
};
|
||||
combatant.ApplyBuff(buff, currentEnemy != null ? currentEnemy.gameObject : null);
|
||||
yield return GameplayClock.WaitForSeconds(duration);
|
||||
if (target != null)
|
||||
combatant.RemoveBuff(routineKey);
|
||||
timedRoutines.Remove(routineKey);
|
||||
}
|
||||
|
||||
private void StopTimedRoutine(string key)
|
||||
{
|
||||
if (timedRoutines.TryGetValue(key, out Coroutine routine) && routine != null)
|
||||
StopCoroutine(routine);
|
||||
timedRoutines.Remove(key);
|
||||
}
|
||||
|
||||
private void RecordRecent(Queue<float>[] buffers, int slot, float songTime, float keepWindow)
|
||||
{
|
||||
if (slot < 0 || slot >= buffers.Length)
|
||||
return;
|
||||
|
||||
Queue<float> queue = buffers[slot];
|
||||
queue.Enqueue(songTime);
|
||||
while (queue.Count > 0 && songTime - queue.Peek() > keepWindow)
|
||||
queue.Dequeue();
|
||||
}
|
||||
|
||||
private int FindMostRecentCountTrack(Queue<float>[] buffers, float window)
|
||||
{
|
||||
float now = GameplayClock.NowSongTime;
|
||||
int bestTrack = -1;
|
||||
int bestCount = -1;
|
||||
|
||||
for (int i = 0; i < buffers.Length; i++)
|
||||
{
|
||||
Queue<float> queue = buffers[i];
|
||||
while (queue.Count > 0 && now - queue.Peek() > window)
|
||||
queue.Dequeue();
|
||||
|
||||
if (queue.Count > bestCount)
|
||||
{
|
||||
bestCount = queue.Count;
|
||||
bestTrack = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestTrack;
|
||||
}
|
||||
|
||||
private bool AreAllConfiguredAlliesAlive()
|
||||
{
|
||||
teamUIController ui = teamUIController.Instance;
|
||||
if (ui == null)
|
||||
return false;
|
||||
|
||||
bool hasAny = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
AllyCombatant ally = ui.GetAllyObjectBySlot(i)?.GetComponent<AllyCombatant>();
|
||||
if (ally == null || ally.maxHP <= 0)
|
||||
continue;
|
||||
|
||||
hasAny = true;
|
||||
if (ally.IsDead)
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasAny;
|
||||
}
|
||||
|
||||
private int GetCurrentDifficultyLevel()
|
||||
{
|
||||
BeatmapManager beatmapManager = BeatmapManager.Instance;
|
||||
SongData song = beatmapManager != null ? beatmapManager.assignedSongData : currentSong;
|
||||
int difficulty = beatmapManager != null ? beatmapManager.assignedDifficulty : currentDifficulty;
|
||||
if (song != null && song.chartFiles != null)
|
||||
{
|
||||
for (int i = 0; i < song.chartFiles.Count; i++)
|
||||
{
|
||||
ChartFileEntry entry = song.chartFiles[i];
|
||||
if (entry != null && entry.difficulty == difficulty)
|
||||
return Mathf.Max(1, Mathf.RoundToInt(entry.difficultyLEVEL));
|
||||
}
|
||||
}
|
||||
|
||||
return Mathf.Max(1, difficulty);
|
||||
}
|
||||
|
||||
private void AddPendingScore(int amount)
|
||||
{
|
||||
if (amount <= 0 || GameConfig.autoPlayEnabled)
|
||||
return;
|
||||
|
||||
pendingSettlementScoreBonus += amount;
|
||||
if (verboseLogs) Debug.Log($"[LevelRuleController] Pending score +{amount}, total={pendingSettlementScoreBonus}");
|
||||
}
|
||||
|
||||
private void AddFinalAverageHpScoreIfConfigured()
|
||||
{
|
||||
if (activeConfig == null || activeConfig.finalAverageHpPercentScore <= 0)
|
||||
return;
|
||||
|
||||
teamUIController ui = teamUIController.Instance;
|
||||
if (ui == null)
|
||||
return;
|
||||
|
||||
float sumPercent = 0f;
|
||||
int count = 0;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
AllyCombatant ally = ui.GetAllyObjectBySlot(i)?.GetComponent<AllyCombatant>();
|
||||
if (ally == null || ally.maxHP <= 0)
|
||||
continue;
|
||||
|
||||
sumPercent += Mathf.Clamp01((float)Mathf.Max(0, ally.currentHP) / ally.maxHP);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
AddPendingScore(Mathf.RoundToInt((sumPercent / count) * activeConfig.finalAverageHpPercentScore));
|
||||
}
|
||||
|
||||
private static bool IsHit(string result)
|
||||
{
|
||||
return !string.Equals(result, "Miss", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private string BuildActionKey(LevelRuleAction action, EnemyCombatant enemy)
|
||||
{
|
||||
string id = !string.IsNullOrWhiteSpace(action.actionId) ? action.actionId : action.actionType.ToString();
|
||||
int enemyId = enemy != null ? enemy.GetInstanceID() : 0;
|
||||
return $"{currentStageIndex}:{enemyId}:{id}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a0b6ecf635b99f40b1e485ca8180e6e
|
||||
@@ -5,6 +5,23 @@ public class Note : BaseNote
|
||||
{
|
||||
private KeyCode keyToPress;
|
||||
private string noteColor;
|
||||
|
||||
// 获取用于生成判定 prefab 的颜色。如果 noteColor 为空(初始化异常),从 TrackIndex 推导兜底。
|
||||
private string GetColorForPrefab()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(noteColor)) return noteColor;
|
||||
|
||||
if (TrackIndex >= 0 && TrackIndex < 5)
|
||||
{
|
||||
string[] trackColors = { "red", "green", "yellow", "purple", "blue" };
|
||||
string fallback = trackColors[TrackIndex];
|
||||
if (JudgeManager.IsDebugEnabled) Debug.LogWarning($"[Note] noteColor was null, using trackIndex {TrackIndex} -> {fallback}");
|
||||
return fallback;
|
||||
}
|
||||
|
||||
Debug.LogError($"[Note] Cannot derive color: noteColor is null and TrackIndex {TrackIndex} is invalid");
|
||||
return null;
|
||||
}
|
||||
private AnimationController anim;
|
||||
private NoteController controller;
|
||||
private bool isJudged = false;
|
||||
@@ -233,12 +250,15 @@ public class Note : BaseNote
|
||||
}
|
||||
|
||||
// Force miss if we've passed the deadline without being judged
|
||||
if (!isJudged && GameplayClock.NowSongTime > missDeadlineTime && gameObject.activeSelf)
|
||||
if (!isJudged && gameObject.activeSelf)
|
||||
{
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[Note] {noteColor} on track {TrackIndex} exceeded miss deadline at time {GameplayClock.NowSongTime:F3}, forcing Miss");
|
||||
if (GameplayClock.NowSongTime >= missDeadlineTime)
|
||||
{
|
||||
Debug.Log($"[Note.Update] Forcing miss for {noteColor} on track {TrackIndex}: now={GameplayClock.NowSongTime:F3}, deadline={missDeadlineTime:F3}");
|
||||
JudgeMiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryAutoJudgePerfect()
|
||||
{
|
||||
@@ -284,8 +304,12 @@ public class Note : BaseNote
|
||||
JudgeSoundManager.Instance?.PlayJudgeSound(judgeResult);
|
||||
teamUIController.Instance?.OnJudgeResult(judgeResult);
|
||||
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[Note.AutoPlay] Spawning judge prefab: color={noteColor}, result={judgeResult}");
|
||||
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, judgeResult);
|
||||
string colorForPrefab = GetColorForPrefab();
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[Note.AutoPlay] Spawning judge prefab: color={colorForPrefab}, result={judgeResult}");
|
||||
if (!string.IsNullOrEmpty(colorForPrefab))
|
||||
{
|
||||
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(colorForPrefab, judgeResult);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -303,6 +327,7 @@ public class Note : BaseNote
|
||||
|
||||
try { SkillBuilder.Instance?.NotifyNoteHit(TrackIndex, judgeResult, SkillDefinition.NoteTypeTrigger.Tap); } catch { }
|
||||
LogTapJudge(judgeResult, 0f, false, true, pressTime);
|
||||
GameplayLevelRuleEventBus.NotifyTapJudged(TrackIndex, judgeResult, pressTime, noteData);
|
||||
|
||||
if (isSyncNote)
|
||||
{
|
||||
@@ -454,9 +479,13 @@ public class Note : BaseNote
|
||||
JudgeSoundManager.Instance?.PlayJudgeSound(judgeResult);
|
||||
teamUIController.Instance?.OnJudgeResult(judgeResult);
|
||||
|
||||
// Spawn judgment prefab - ensure noteColor is passed correctly
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[Note] Spawning judge prefab: color={noteColor}, result={judgeResult}");
|
||||
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, judgeResult);
|
||||
// Spawn judgment prefab
|
||||
string colorForPrefab = GetColorForPrefab();
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[Note] Spawning judge prefab: color={colorForPrefab}, result={judgeResult}");
|
||||
if (!string.IsNullOrEmpty(colorForPrefab))
|
||||
{
|
||||
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(colorForPrefab, judgeResult);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -474,6 +503,7 @@ public class Note : BaseNote
|
||||
|
||||
try { SkillBuilder.Instance?.NotifyNoteHit(TrackIndex, judgeResult, SkillDefinition.NoteTypeTrigger.Tap); } catch { }
|
||||
LogTapJudge(judgeResult, rawOffsetMs, rewrittenToPerfect, false, pressTime);
|
||||
GameplayLevelRuleEventBus.NotifyTapJudged(TrackIndex, judgeResult, pressTime, noteData);
|
||||
|
||||
if (isSyncNote && judgeResult != "Miss")
|
||||
{
|
||||
@@ -533,8 +563,14 @@ public class Note : BaseNote
|
||||
|
||||
public void JudgeMiss()
|
||||
{
|
||||
Debug.Log($"[Note.JudgeMiss] Called for {noteColor} on track {TrackIndex}, isJudged={isJudged}");
|
||||
|
||||
// idempotent: this can be called from multiple paths
|
||||
if (isJudged) return;
|
||||
if (isJudged)
|
||||
{
|
||||
Debug.Log($"[Note.JudgeMiss] Already judged, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
isJudged = true;
|
||||
ScoreManager.Instance.countMiss += 1;
|
||||
@@ -550,9 +586,23 @@ public class Note : BaseNote
|
||||
JudgeSoundManager.Instance?.PlayJudgeSound("Miss");
|
||||
teamUIController.Instance?.OnJudgeResult("Miss");
|
||||
|
||||
// Spawn judgment prefab for Miss - ensure noteColor is passed correctly
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[Note.JudgeMiss] Spawning miss judge prefab: color={noteColor}");
|
||||
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, "Miss");
|
||||
// 生成 Miss 判定 prefab(调试日志无条件输出,便于排查问题)
|
||||
string colorForPrefab = GetColorForPrefab();
|
||||
Debug.Log($"[Note.JudgeMiss] Attempting to spawn miss prefab: noteColor={noteColor}, TrackIndex={TrackIndex}, derivedColor={colorForPrefab}, Instance={(Animation_GenerateJudgementSituationPrefab.Instance != null ? "EXISTS" : "NULL")}");
|
||||
|
||||
if (string.IsNullOrEmpty(colorForPrefab))
|
||||
{
|
||||
Debug.LogError($"[Note.JudgeMiss] Cannot spawn miss prefab: colorForPrefab is null! noteColor={noteColor}, TrackIndex={TrackIndex}");
|
||||
}
|
||||
else if (Animation_GenerateJudgementSituationPrefab.Instance == null)
|
||||
{
|
||||
Debug.LogError($"[Note.JudgeMiss] Cannot spawn miss prefab: Animation_GenerateJudgementSituationPrefab.Instance is NULL!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Animation_GenerateJudgementSituationPrefab.Instance.SpawnJudgePrefab(colorForPrefab, "Miss");
|
||||
Debug.Log($"[Note.JudgeMiss] SpawnJudgePrefab called successfully for {colorForPrefab}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -570,6 +620,7 @@ public class Note : BaseNote
|
||||
|
||||
try { SkillBuilder.Instance?.NotifyNoteHit(TrackIndex, "Miss", SkillDefinition.NoteTypeTrigger.Tap); } catch { }
|
||||
LogTapJudge("Miss", 0f, false, false, GameplayClock.NowSongTime);
|
||||
GameplayLevelRuleEventBus.NotifyTapJudged(TrackIndex, "Miss", GameplayClock.NowSongTime, noteData);
|
||||
|
||||
// notify global judge manager that this note has been finally judged (miss)
|
||||
JudgeManager.Instance?.NotifyNoteJudged();
|
||||
@@ -623,6 +674,8 @@ public class Note : BaseNote
|
||||
/// </summary>
|
||||
public void SetJudgeZone(bool inZone)
|
||||
{
|
||||
Debug.Log($"[Note.SetJudgeZone] {noteColor} on track {TrackIndex}: inZone={inZone}, isJudged={isJudged}");
|
||||
|
||||
// leaving judge zone
|
||||
if (!inZone)
|
||||
{
|
||||
@@ -634,7 +687,7 @@ public class Note : BaseNote
|
||||
// Calling ReturnToPool (which deactivates the object) inside a physics callback
|
||||
// causes "GameObject is already being activated or deactivated" errors.
|
||||
// Instead, mark for miss and let Update handle it next frame.
|
||||
if (JudgeManager.IsDebugEnabled) Debug.Log($"[Note.SetJudgeZone] Note {noteColor} left judge zone on track {TrackIndex}, will force Miss next frame");
|
||||
Debug.Log($"[Note.SetJudgeZone] Note {noteColor} left judge zone on track {TrackIndex}, setting deadline to now");
|
||||
missDeadlineTime = GameplayClock.NowSongTime; // Force deadline to now so Update will handle it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
|
||||
// Documentation text normalized.
|
||||
[Tooltip("Multiplier applied to visual fall speed. Changing this will automatically adjust spawn timing so notes still arrive at their original beat times.")]
|
||||
[Range(0.5f, 2f)]
|
||||
public float speedMultiplier = 1f;
|
||||
[Range(1f, 3f)]
|
||||
public float speedMultiplier = 2f;
|
||||
|
||||
[Header("Calibration")]
|
||||
[Tooltip("Tolerance (world units) for snapping middle segments to expected position after spawn.")]
|
||||
@@ -84,8 +84,9 @@ public class NoteSpawner : MonoBehaviour
|
||||
public GameObject animations;
|
||||
|
||||
// optional: constants for runtime clamping (kept for internal use)
|
||||
private const float SpeedMultiplierMin = 0.5f;
|
||||
private const float SpeedMultiplierMax = 2f;
|
||||
// Player-facing speed is clamped to 1-3; FlowSpeedGlobalMultiplier makes the real range 2-6.
|
||||
private const float SpeedMultiplierMin = 1f;
|
||||
private const float SpeedMultiplierMax = 3f;
|
||||
// 全局流速系数:与用户可调流速(speedMultiplier)相乘的独立系数,加快音符下落。
|
||||
// 为什么用它而非 bpm 倍率:noteSpeed = 10.75 * sm * bpm / 240,若靠 ×bpm 提速,
|
||||
// 长条音符的视觉长度由 ApplyVisualScale(sm) 决定(只随 sm 不随 bpm)、且分段数 segmentInterval=(60/bpm)/4
|
||||
@@ -100,6 +101,28 @@ public class NoteSpawner : MonoBehaviour
|
||||
get { return Mathf.Clamp(speedMultiplier, SpeedMultiplierMin, SpeedMultiplierMax) * FlowSpeedGlobalMultiplier; }
|
||||
}
|
||||
|
||||
[Header("Note Fall Speed Source")]
|
||||
[Tooltip("true=沿用谱面 BPM 决定下落速度(旧逻辑,高 BPM 歌音符更快);false=用固定参考 BPM," +
|
||||
"下落速度不再随歌曲 BPM 变化,仅由流速 speedMultiplier 控制(全曲统一手感)。默认 false。")]
|
||||
public bool useBpmForSpeed = false;
|
||||
|
||||
[Tooltip("useBpmForSpeed=false 时用作速度基准的固定参考 BPM。等价于“所有歌曲都按此 BPM 的手感下落”。" +
|
||||
"建议 ≥60:地面粒子速度对 BPM 有 60 下限,低于 60 会使音符与粒子略微不同步。")]
|
||||
public float referenceBpm = 120f;
|
||||
|
||||
// 速度/长条分段公式统一使用的“有效 BPM”。开=真实谱面 bpm;关=固定参考 bpm。
|
||||
// 二者都同时喂给 noteSpeed 与 segmentInterval,故长条段间距(∝sm,bpm 相消)与地面粒子同步天然保持不变。
|
||||
public float EffectiveBpm
|
||||
{
|
||||
get { return useBpmForSpeed ? bpm : Mathf.Max(1f, referenceBpm); }
|
||||
}
|
||||
|
||||
[Header("Hold Note Body Fill")]
|
||||
[Tooltip("true=新逻辑(默认):长条中段不拉伸(保持 prefab 原始大小),改为按流速生成更多中段密铺填满(流速越快段越多)。" +
|
||||
"false=旧逻辑:中段数量固定,用 ApplyVisualScale(流速) 拉伸每段填满(流速越大越拉长)。" +
|
||||
"两种方案下长条视觉长度一致、判定完全不变(中段纯视觉、end 段到达时间不变)。")]
|
||||
public bool useMultiSegmentFill = true;
|
||||
|
||||
public NoteJudgeConfig judgeConfig; // Documentation text normalized.
|
||||
private Beatmap beatmap;
|
||||
private float startTime; // Absolute chart time anchor.
|
||||
@@ -113,7 +136,9 @@ public class NoteSpawner : MonoBehaviour
|
||||
private Dictionary<int, int> noteIndexToHoldId = new Dictionary<int, int>();
|
||||
|
||||
private const string NoteSpeedPrefKey = "noteSpeedMultiplier";
|
||||
private const float NoteSpeedDefault = 1f;
|
||||
private const string NoteSpeedDefaultVersionKey = "noteSpeedMultiplierDefaultVersion";
|
||||
private const int NoteSpeedDefaultVersion = 2;
|
||||
private const float NoteSpeedDefault = 2f;
|
||||
private const float BaseTravelDistance = 10.75f;
|
||||
|
||||
private Coroutine spawnCoroutine;
|
||||
@@ -146,10 +171,22 @@ public class NoteSpawner : MonoBehaviour
|
||||
{
|
||||
if (PlayerPrefs.HasKey(NoteSpeedPrefKey))
|
||||
{
|
||||
int version = PlayerPrefs.GetInt(NoteSpeedDefaultVersionKey, 0);
|
||||
float saved = PlayerPrefs.GetFloat(NoteSpeedPrefKey, NoteSpeedDefault);
|
||||
if (version < NoteSpeedDefaultVersion && Mathf.Approximately(saved, 3f))
|
||||
{
|
||||
saved = NoteSpeedDefault;
|
||||
}
|
||||
|
||||
saved = Mathf.Clamp(saved, SpeedMultiplierMin, SpeedMultiplierMax);
|
||||
PlayerPrefs.SetFloat(NoteSpeedPrefKey, saved);
|
||||
PlayerPrefs.SetInt(NoteSpeedDefaultVersionKey, NoteSpeedDefaultVersion);
|
||||
PlayerPrefs.Save();
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerPrefs.SetFloat(NoteSpeedPrefKey, NoteSpeedDefault);
|
||||
PlayerPrefs.SetInt(NoteSpeedDefaultVersionKey, NoteSpeedDefaultVersion);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
@@ -280,10 +317,13 @@ public class NoteSpawner : MonoBehaviour
|
||||
|
||||
// Cache parameters that don't change within the loop
|
||||
float sm = EffectiveSpeedMultiplier;
|
||||
float baseTravelTime = (60f / bpm) * 4f;
|
||||
// 下落速度基准:useBpmForSpeed=true 用谱面 bpm(旧逻辑),false 用固定参考 bpm(速度不随歌曲变)。
|
||||
// 判定时间不受影响:到达时刻只由 note.time 决定,speed 仅改视觉下落快慢与出现时机。
|
||||
float speedBpm = EffectiveBpm;
|
||||
float baseTravelTime = (60f / speedBpm) * 4f;
|
||||
float baseNoteTravelTime = baseTravelTime / Mathf.Max(0.0001f, sm);
|
||||
float noteSpeed = CalculateSpeed(baseNoteTravelTime);
|
||||
float segmentInterval = (60f / bpm) / 4f;
|
||||
float segmentInterval = (60f / speedBpm) / 4f;
|
||||
|
||||
EnsureJudgmentLine();
|
||||
float[] laneTravelTimes = BuildLaneTravelTimes(noteSpeed);
|
||||
@@ -453,9 +493,24 @@ public class NoteSpawner : MonoBehaviour
|
||||
return -1;
|
||||
}
|
||||
|
||||
int segmentCount = Mathf.CeilToInt(noteData.length / segmentInterval);
|
||||
// 段间隔决定"每隔多少时间放一段"。段间距(世界坐标)= noteSpeed × 段间隔,而 noteSpeed ∝ 流速 sm。
|
||||
// 旧逻辑(useMultiSegmentFill=false):段间隔不随 sm 变 → 段间距 ∝ sm,段数固定,
|
||||
// 靠 ApplyVisualScale(sm) 把每段拉伸 sm 倍去填满变大的段间距(流速越大越拉长)。
|
||||
// 新逻辑(useMultiSegmentFill=true):段间隔 ÷ sm → 段数 ∝ sm,而段间距 = noteSpeed × (段间隔/sm) = 恒定,
|
||||
// 恰好等于不拉伸(scale=1)时 prefab 的原始段高,于是用等大的原始段密铺填满(流速越大段越多)。
|
||||
// 两种方案下:end 段到达时间 = baseHit + length 完全不变(段数变但 actualSegmentInterval=length/count 抵消),
|
||||
// 长条整体视觉长度一致;中段纯视觉、不参与计分/连击,故判定业务逻辑完全不受影响。
|
||||
float effectiveSegmentInterval = segmentInterval;
|
||||
if (useMultiSegmentFill)
|
||||
{
|
||||
effectiveSegmentInterval = segmentInterval / Mathf.Max(0.01f, sm);
|
||||
}
|
||||
int segmentCount = Mathf.CeilToInt(noteData.length / effectiveSegmentInterval);
|
||||
if (segmentCount < 2) segmentCount = 2; // Force at least one middle segment between start and end.
|
||||
float actualSegmentInterval = segmentCount > 0 ? noteData.length / segmentCount : segmentInterval;
|
||||
float actualSegmentInterval = segmentCount > 0 ? noteData.length / segmentCount : effectiveSegmentInterval;
|
||||
|
||||
// 新逻辑下不拉伸(每段保持 prefab 原始大小);旧逻辑下按流速 sm 拉伸每段。
|
||||
float holdVisualScale = useMultiSegmentFill ? 1f : sm;
|
||||
|
||||
Transform spawnPoint = spawnPoints[noteData.trackIndex];
|
||||
// Documentation text normalized.
|
||||
@@ -501,8 +556,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
// pass realtime hit time (startTime + note.time) and delay 0, include globalHitDelay
|
||||
holdNote.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, 0f, false, scheduledEndTime, noteData.color, key, "start", travelTime, judgeConfig, noteData, isSync);
|
||||
|
||||
// 使用流速倍率 sm 直接进行缩放,确保长条音符在 0.5-2.0 范围内依然能完美衔接
|
||||
float visualScale = sm;
|
||||
// 旧逻辑=按流速 sm 拉伸;新逻辑(多段填充)=不拉伸(holdVisualScale=1)。
|
||||
float visualScale = holdVisualScale;
|
||||
holdNote.ApplyVisualScale(visualScale);
|
||||
// inform hold note of visual speed so it can adapt judgement windows if needed
|
||||
holdNote.visualSpeedMultiplier = sm;
|
||||
@@ -533,8 +588,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
// Documentation text normalized.
|
||||
holdSeg.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, segmentDelay, false, scheduledEndTime, noteData.color, key, "middle", travelTime, judgeConfig, noteData, false);
|
||||
|
||||
// 应用与开始段一致的流速缩放
|
||||
float visualScaleMid = sm;
|
||||
// 与开始段一致:旧逻辑拉伸、新逻辑不拉伸(靠更多段密铺填满)。
|
||||
float visualScaleMid = holdVisualScale;
|
||||
holdSeg.ApplyVisualScale(visualScaleMid);
|
||||
holdSeg.visualSpeedMultiplier = sm;
|
||||
|
||||
@@ -565,8 +620,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
{
|
||||
holdEnd.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, endDelay, true, scheduledEndTime, noteData.color, key, "end", travelTime, judgeConfig, noteData, false);
|
||||
|
||||
// 同样应用流速缩放
|
||||
float visualScaleEnd = sm;
|
||||
// 与开始段一致:旧逻辑拉伸、新逻辑不拉伸。
|
||||
float visualScaleEnd = holdVisualScale;
|
||||
holdEnd.ApplyVisualScale(visualScaleEnd);
|
||||
holdEnd.visualSpeedMultiplier = sm;
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 按屏幕宽高比动态拉长轨道视觉物体(track1-5)的 Y 缩放,让宽屏下轨道更饱满。
|
||||
///
|
||||
/// 原理:宽屏时水平视野大、轨道相对缩在中间显细。降相机高度+抬头能让轨道透视放大,
|
||||
/// 但仍不够饱满。再叠加"拉长轨道 Y 缩放"填满纵向视野,视觉更满。
|
||||
///
|
||||
/// 关键:轨道只是背景视觉,判定是几何驱动(音符世界 Y vs 判定线世界 Y)。
|
||||
/// 缩放轨道 localScale 不影响音符生成/下落/判定——它们全用世界坐标,与轨道视觉无关。
|
||||
///
|
||||
/// 用法:挂到场景任意物体(会自动找 track1-5 或指定 trackParent)。运行时自动扫描并缩放。
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class TrackScaler : MonoBehaviour
|
||||
{
|
||||
[Tooltip("是否启用轨道缩放。关闭则恢复基准值 2.5。")]
|
||||
public bool enableScaling = true;
|
||||
|
||||
[Tooltip("设计基准宽高比。16:9 = 1.7778。此比例下缩放为基准值。")]
|
||||
public float designAspect = 16f / 9f;
|
||||
|
||||
[Tooltip("轨道 Y 缩放的基准值(场景序列化的初始值)。从这个值开始按宽高比放大。")]
|
||||
public float baseScaleY = 2.5f;
|
||||
|
||||
[Header("宽屏(aspect > 设计)缩放")]
|
||||
[Tooltip("每偏离设计宽高比 1.0,额外叠加的 Y 缩放倍数。宽屏拉长轨道让视觉更满。")]
|
||||
public float widePerAspectScaleY = 0.8f;
|
||||
[Tooltip("Y 缩放的绝对上限。避免极端超宽屏拉得过长。")]
|
||||
public float maxScaleY = 4.5f;
|
||||
|
||||
[Tooltip("(可选)轨道的父节点。留空则自动全场景查找 track1-5。")]
|
||||
public Transform trackParent;
|
||||
|
||||
private Transform[] _tracks;
|
||||
private float _lastAspect = -1f;
|
||||
private bool _lastEnabled;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
FindTracks();
|
||||
_lastAspect = -1f;
|
||||
Apply();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
float aspect = (float)Screen.width / Mathf.Max(1, Screen.height);
|
||||
if (Mathf.Abs(aspect - _lastAspect) > 0.0001f || enableScaling != _lastEnabled)
|
||||
{
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
private void FindTracks()
|
||||
{
|
||||
if (trackParent != null)
|
||||
{
|
||||
// 从指定父节点下找
|
||||
_tracks = new Transform[5];
|
||||
for (int i = 0; i < trackParent.childCount; i++)
|
||||
{
|
||||
Transform child = trackParent.GetChild(i);
|
||||
if (child.name == "track1") _tracks[0] = child;
|
||||
else if (child.name == "track2") _tracks[1] = child;
|
||||
else if (child.name == "track3") _tracks[2] = child;
|
||||
else if (child.name == "track4") _tracks[3] = child;
|
||||
else if (child.name == "track5") _tracks[4] = child;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 全场景查找(慢,但兜底)
|
||||
_tracks = new Transform[5];
|
||||
GameObject go1 = GameObject.Find("track1");
|
||||
GameObject go2 = GameObject.Find("track2");
|
||||
GameObject go3 = GameObject.Find("track3");
|
||||
GameObject go4 = GameObject.Find("track4");
|
||||
GameObject go5 = GameObject.Find("track5");
|
||||
if (go1 != null) _tracks[0] = go1.transform;
|
||||
if (go2 != null) _tracks[1] = go2.transform;
|
||||
if (go3 != null) _tracks[2] = go3.transform;
|
||||
if (go4 != null) _tracks[3] = go4.transform;
|
||||
if (go5 != null) _tracks[4] = go5.transform;
|
||||
}
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
if (_tracks == null || _tracks.Length == 0)
|
||||
{
|
||||
FindTracks(); // 重试
|
||||
if (_tracks == null || _tracks.Length == 0) return;
|
||||
}
|
||||
|
||||
float aspect = (float)Screen.width / Mathf.Max(1, Screen.height);
|
||||
_lastAspect = aspect;
|
||||
_lastEnabled = enableScaling;
|
||||
|
||||
float targetScaleY = baseScaleY;
|
||||
|
||||
if (enableScaling && aspect > designAspect)
|
||||
{
|
||||
// 宽屏:额外拉长 Y
|
||||
float dev = aspect - designAspect;
|
||||
float extra = widePerAspectScaleY * dev;
|
||||
targetScaleY = Mathf.Min(baseScaleY + extra, maxScaleY);
|
||||
}
|
||||
// aspect <= designAspect 或关闭:保持基准值
|
||||
|
||||
// 应用到所有轨道
|
||||
for (int i = 0; i < _tracks.Length; i++)
|
||||
{
|
||||
if (_tracks[i] == null) continue;
|
||||
Vector3 scale = _tracks[i].localScale;
|
||||
scale.y = targetScaleY;
|
||||
_tracks[i].localScale = scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29e9752cc9128104bad251941d81ffa2
|
||||
@@ -26,7 +26,9 @@ using UnityEngine;
|
||||
public sealed class TrackScreenRatioStabilizer : MonoBehaviour
|
||||
{
|
||||
[Tooltip("是否启用轨道屏占比稳定(按宽高比补偿相机垂直FOV)。关闭则恢复设计FOV。")]
|
||||
public bool enableStabilization = true;
|
||||
// 关闭:contain 式补偿会在窄屏增大垂直FOV(镜头拉远)=轨道变细+视距变长,二者都不符合需求。
|
||||
// 改由 CameraResolutionTuner(降Z+增X俯角)在保持垂直FOV恒定(视距恒定)的前提下让轨道铺开。
|
||||
public bool enableStabilization = false;
|
||||
|
||||
[Tooltip("设计基准宽高比(轨道占比在此比例下为设计值)。16:9 = 1.7778。")]
|
||||
public float designAspect = 16f / 9f;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class TrackSkillPrefab : MonoBehaviour
|
||||
{
|
||||
[Header("UI")]
|
||||
public Text skillText;
|
||||
public Image skillImage;
|
||||
|
||||
private CanvasGroup _canvasGroup;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (_canvasGroup == null) _canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||||
_canvasGroup.alpha = 0f;
|
||||
}
|
||||
|
||||
public void Bind(Sprite icon, string skillName)
|
||||
{
|
||||
ResetForReuse();
|
||||
|
||||
if (skillImage != null)
|
||||
{
|
||||
skillImage.sprite = icon;
|
||||
skillImage.enabled = icon != null;
|
||||
}
|
||||
|
||||
if (skillText != null)
|
||||
{
|
||||
skillText.text = string.IsNullOrWhiteSpace(skillName) ? string.Empty : skillName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ResetForReuse()
|
||||
{
|
||||
if (_canvasGroup != null) _canvasGroup.alpha = 0f;
|
||||
transform.localRotation = Quaternion.identity;
|
||||
transform.localScale = Vector3.one;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a51f5b8ae798a1e42a55f1654ed4237e
|
||||
@@ -38,10 +38,11 @@ public class effectEventController : MonoBehaviour
|
||||
|
||||
// 分辨率相机微调:改的是"漂移 pivot"(顶层节点,承载 -40°/-8 基准),不碰相机自身,
|
||||
// 因此不与 SmoothShake(写相机 local)、技能偏移(写技能 pivot)、冲刺(写相机 z)冲突。
|
||||
// 改 pivot 的 X 旋转无任何系统覆盖;改 pivot 的 Z 必须同步 cameraDriftBaseLocalPosition.z,
|
||||
// 否则 UpdateCameraDrift/ResetCameraDriftIfNeeded 会用旧基准把 z 拉回。
|
||||
// 改 pivot 的 X 旋转无任何系统覆盖;改 pivot 的 Y/Z 必须同步 cameraDriftBaseLocalPosition,
|
||||
// 否则 UpdateCameraDrift/ResetCameraDriftIfNeeded 会用旧基准把 Y/Z 拉回。
|
||||
private bool _cameraBaseCaptured;
|
||||
private float _designPivotRotX; // pivot 初始 X 旋转(如 -40)
|
||||
private float _designPivotY; // pivot 初始 Y(如 -6,相机高度)
|
||||
private float _designPivotZ; // pivot 初始 Z(如 -8)
|
||||
|
||||
private void Awake()
|
||||
@@ -117,16 +118,17 @@ public class effectEventController : MonoBehaviour
|
||||
if (cachedCameraDriftPivot == null) return;
|
||||
}
|
||||
_designPivotRotX = cachedCameraDriftPivot.localEulerAngles.x;
|
||||
_designPivotY = cachedCameraDriftPivot.localPosition.y;
|
||||
_designPivotZ = cachedCameraDriftPivot.localPosition.z;
|
||||
_cameraBaseCaptured = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按分辨率微调相机:在设计基准上叠加 X 旋转增量与 Z 位置增量。
|
||||
/// 改的是漂移 pivot(不动相机本身),且同步 cameraDriftBaseLocalPosition.z,
|
||||
/// 按分辨率微调相机:在设计基准上叠加 X 旋转增量、Y 高度增量、Z 位置增量。
|
||||
/// 改的是漂移 pivot(不动相机本身),且同步 cameraDriftBaseLocalPosition 的 y/z,
|
||||
/// 与漂移/抖动/技能偏移/冲刺全部不冲突。可重复调用(总是相对设计基准,不累积)。
|
||||
/// </summary>
|
||||
public void SetResolutionCameraAdjust(float extraRotX, float extraZ)
|
||||
public void SetResolutionCameraAdjust(float extraRotX, float extraZ, float extraY = 0f)
|
||||
{
|
||||
CaptureCameraBaseIfNeeded();
|
||||
if (cachedCameraDriftPivot == null) return;
|
||||
@@ -136,12 +138,91 @@ public class effectEventController : MonoBehaviour
|
||||
euler.x = _designPivotRotX + extraRotX;
|
||||
cachedCameraDriftPivot.localEulerAngles = euler;
|
||||
|
||||
// Z:设计值 + 增量,并同步漂移基准 z(漂移只改 x,y,z 恒取基准)。
|
||||
// Y(高度)与 Z:设计值 + 增量。漂移每帧写 base + (perlinX, perlinY, 0),
|
||||
// 故必须同步 cameraDriftBaseLocalPosition 的 y/z,否则下一帧漂移会用旧基准拉回。
|
||||
float targetY = _designPivotY + extraY;
|
||||
float targetZ = _designPivotZ + extraZ;
|
||||
Vector3 lp = cachedCameraDriftPivot.localPosition;
|
||||
lp.y = targetY;
|
||||
lp.z = targetZ;
|
||||
cachedCameraDriftPivot.localPosition = lp;
|
||||
cameraDriftBaseLocalPosition.z = targetZ; // 关键:否则会被 UpdateCameraDrift/Reset 拉回
|
||||
cameraDriftBaseLocalPosition.y = targetY; // 关键:否则会被 UpdateCameraDrift/Reset 拉回
|
||||
cameraDriftBaseLocalPosition.z = targetZ;
|
||||
}
|
||||
|
||||
// 相机本体(Main Camera)分辨率适配的 local 基准。运行时相机被重挂到 skill_pivot 之下、
|
||||
// local 位姿被清零(见 EnsureCameraDriftPivot/EnsureCameraSkillPivot),故基准 z≈0、X≈0。
|
||||
// 首次调用 SetResolutionCameraSelf 时惰性捕获,供插值 t=0 回到该基准。
|
||||
private bool _cameraSelfBaseCaptured;
|
||||
private float _cameraSelfBaseLocalZ;
|
||||
private float _cameraSelfBaseLocalRotX;
|
||||
|
||||
/// <summary>
|
||||
/// 按分辨率**直接调整相机本体(Main Camera 子物体)**的 local z 高度与 local X 旋转,
|
||||
/// 不动其父/祖父 pivot。t=0 → 回到相机 local 设计基准(运行时为 0/identity,与未调整一致);
|
||||
/// t=1 → 相机 local 到达 (targetLocalRotX, targetLocalZ)。始终相对基准插值,不累积。
|
||||
///
|
||||
/// 冲突处理:相机本体的 local 由 SmoothShake(抖动时每帧写)与冲刺(动画 local z)共享。
|
||||
/// - 空闲时二者不写相机(SmoothShake 无活动协程即不 ApplySum),本次写入持久生效;
|
||||
/// - 抖动/冲刺触发时会以"当前 local"为基准重新捕获(SaveDefaultValues / CacheCameraTransform),
|
||||
/// 故结束后回到本方法设定的值。为兼容"抖动进行中改分辨率"的罕见情形,
|
||||
/// 若相机上已有 SmoothShake 且正在抖动,同步其 startPosition/startRotation 基准。
|
||||
/// </summary>
|
||||
public bool SetResolutionCameraSelf(float t, float targetLocalRotX, float targetLocalZ)
|
||||
{
|
||||
if (cachedShakeTarget == null)
|
||||
{
|
||||
CacheShakeTarget();
|
||||
if (cachedShakeTarget == null) return false; // 相机(Camera.main)尚未就绪,稍后重试
|
||||
}
|
||||
t = Mathf.Clamp01(t);
|
||||
|
||||
if (!_cameraSelfBaseCaptured)
|
||||
{
|
||||
_cameraSelfBaseLocalZ = cachedShakeTarget.localPosition.z;
|
||||
_cameraSelfBaseLocalRotX = NormalizeSignedAngle(cachedShakeTarget.localEulerAngles.x);
|
||||
_cameraSelfBaseCaptured = true;
|
||||
}
|
||||
|
||||
float z = Mathf.Lerp(_cameraSelfBaseLocalZ, targetLocalZ, t);
|
||||
float rotX = Mathf.Lerp(_cameraSelfBaseLocalRotX, targetLocalRotX, t);
|
||||
|
||||
Vector3 lp = cachedShakeTarget.localPosition;
|
||||
lp.z = z;
|
||||
cachedShakeTarget.localPosition = lp;
|
||||
|
||||
Vector3 euler = cachedShakeTarget.localEulerAngles;
|
||||
euler.x = rotX;
|
||||
cachedShakeTarget.localEulerAngles = euler;
|
||||
|
||||
SyncShakeBaseIfActive(lp, euler);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 若相机本体上的 SmoothShake 正在抖动,同步其 start 基准,避免抖动进行中改分辨率被回弹覆盖。
|
||||
private void SyncShakeBaseIfActive(Vector3 localPos, Vector3 localEuler)
|
||||
{
|
||||
if (cachedShakeTarget == null) return;
|
||||
var shake = cachedShakeTarget.GetComponent<SmoothShakeFree.SmoothShake>();
|
||||
if (shake == null) return;
|
||||
if (shake.activeShakeRoutines == null || shake.activeShakeRoutines.Count == 0) return;
|
||||
|
||||
var type = typeof(SmoothShakeFree.SmoothShake);
|
||||
const System.Reflection.BindingFlags flags =
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
|
||||
var startPosField = type.GetField("startPosition", flags);
|
||||
var startRotField = type.GetField("startRotation", flags);
|
||||
if (startPosField != null) startPosField.SetValue(shake, localPos);
|
||||
if (startRotField != null) startRotField.SetValue(shake, localEuler);
|
||||
}
|
||||
|
||||
// Unity eulerAngles.x 存 [0,360),-40 存成 320;归一到 [-180,180) 便于插值(负=朝上)。
|
||||
private static float NormalizeSignedAngle(float angle)
|
||||
{
|
||||
angle %= 360f;
|
||||
if (angle > 180f) angle -= 360f;
|
||||
else if (angle < -180f) angle += 360f;
|
||||
return angle;
|
||||
}
|
||||
|
||||
private void PrewarmShakeComponent()
|
||||
|
||||
@@ -20,6 +20,8 @@ public class globalNoteEffect : MonoBehaviour
|
||||
public float cameraDashDuration = 0.12f;
|
||||
|
||||
[Header("Camera Wave FX")]
|
||||
[Range(0f, 1f)]
|
||||
public float cameraWaveIntensity = 1f;
|
||||
public float cameraWaveEnterDuration = 0.12f;
|
||||
public float cameraWaveRecoverDuration = 0.12f;
|
||||
|
||||
@@ -319,6 +321,7 @@ public class globalNoteEffect : MonoBehaviour
|
||||
private void TriggerCameraWaveFx()
|
||||
{
|
||||
GlobalDistortionRuntimeController.TriggerWaveEffect(
|
||||
Mathf.Clamp01(cameraWaveIntensity),
|
||||
Mathf.Max(0.0001f, cameraWaveEnterDuration),
|
||||
Mathf.Max(0.0001f, cameraWaveRecoverDuration)
|
||||
);
|
||||
|
||||
@@ -357,9 +357,11 @@ public class groundParticularController : MonoBehaviour
|
||||
|
||||
float currentBPM = GetCurrentBPM();
|
||||
float noteSpawnerSpeedMultiplier = 1.0f;
|
||||
// 粒子速度须与音符下落同速:跟随 NoteSpawner 的“有效 BPM”(开关决定用谱面 bpm 还是固定参考 bpm)。
|
||||
if (beatmapManager != null && beatmapManager.noteSpawner != null)
|
||||
{
|
||||
noteSpawnerSpeedMultiplier = beatmapManager.noteSpawner.EffectiveSpeedMultiplier;
|
||||
currentBPM = beatmapManager.noteSpawner.EffectiveBpm;
|
||||
}
|
||||
|
||||
float effectiveBPM = Mathf.Max(currentBPM, 60f);
|
||||
|
||||
@@ -576,7 +576,8 @@ public class settlementController : MonoBehaviour
|
||||
}
|
||||
|
||||
targetPmScore = sm.allSum_pmScore;
|
||||
targetIdolScore = sm.allSum_idolScore;
|
||||
int levelRuleScoreBonus = LevelRuleController.ConsumePendingSettlementScoreBonus();
|
||||
targetIdolScore = sm.allSum_idolScore + levelRuleScoreBonus;
|
||||
targetTotalScore = targetPmScore + targetIdolScore;
|
||||
targetTotalPercent = (float)targetTotalScore / Mathf.Max(1, _1000000) * 100f;
|
||||
|
||||
|
||||
@@ -804,6 +804,11 @@ public class teamUIController : MonoBehaviour
|
||||
return (EnemyData_SO[])currentEnemySOs.Clone();
|
||||
}
|
||||
|
||||
public EnemyCombatant GetCurrentEnemyCombatant()
|
||||
{
|
||||
return enemyCombatantInstance;
|
||||
}
|
||||
|
||||
// Debug helper to print key fields of a TeamCharacterDataInfo
|
||||
private void DebugCharacterSO(TeamCharacterDataInfo so, int slotIndex, bool isEditor)
|
||||
{
|
||||
@@ -1161,6 +1166,12 @@ public class teamUIController : MonoBehaviour
|
||||
|
||||
PopulateAllySOsFromIds();
|
||||
|
||||
// 初始化角色头像:根据槽位 ID 刷新 teammate0X_characterImage,空槽位会被禁用
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
RefreshTeammateCharacterImageBySlotId(i);
|
||||
}
|
||||
|
||||
// initialize previous flags so Update can detect changes
|
||||
prevAllyActive[0] = isAlly01_active;
|
||||
prevAllyActive[1] = isAlly02_active;
|
||||
@@ -1518,16 +1529,31 @@ public class teamUIController : MonoBehaviour
|
||||
}
|
||||
|
||||
// helper to set teammate character image based on slot index
|
||||
// 若 sprite 为 null,则禁用对应 Image GameObject(槽位无角色);否则启用并设 sprite。
|
||||
private void SetTeammateCharacterImage(int slotIndex, Sprite sprite)
|
||||
{
|
||||
if (sprite == null) return;
|
||||
Image img = null;
|
||||
switch (slotIndex)
|
||||
{
|
||||
case 0: if (teammate01_characterImage != null) teammate01_characterImage.sprite = sprite; break;
|
||||
case 1: if (teammate02_characterImage != null) teammate02_characterImage.sprite = sprite; break;
|
||||
case 2: if (teammate03_characterImage != null) teammate03_characterImage.sprite = sprite; break;
|
||||
case 3: if (teammate04_characterImage != null) teammate04_characterImage.sprite = sprite; break;
|
||||
case 4: if (teammate05_characterImage != null) teammate05_characterImage.sprite = sprite; break;
|
||||
case 0: img = teammate01_characterImage; break;
|
||||
case 1: img = teammate02_characterImage; break;
|
||||
case 2: img = teammate03_characterImage; break;
|
||||
case 3: img = teammate04_characterImage; break;
|
||||
case 4: img = teammate05_characterImage; break;
|
||||
}
|
||||
|
||||
if (img == null) return;
|
||||
|
||||
if (sprite == null)
|
||||
{
|
||||
// 槽位无角色:禁用 Image
|
||||
img.gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 槽位有角色:启用 Image 并设置 sprite
|
||||
img.sprite = sprite;
|
||||
img.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1547,7 +1573,13 @@ public class teamUIController : MonoBehaviour
|
||||
{
|
||||
if (slotIndex < 0 || slotIndex >= allySlotIds.Count) return;
|
||||
int id = allySlotIds[slotIndex];
|
||||
if (id <= 0) return;
|
||||
|
||||
// 槽位无角色(id <= 0):禁用对应 Image
|
||||
if (id <= 0)
|
||||
{
|
||||
SetTeammateCharacterImage(slotIndex, null);
|
||||
return;
|
||||
}
|
||||
|
||||
AllyHero_SO[] all = RuntimeResourcesCache.LoadAllAllyHeroes();
|
||||
for (int i = 0; i < all.Length; i++)
|
||||
@@ -1555,12 +1587,12 @@ public class teamUIController : MonoBehaviour
|
||||
AllyHero_SO so = all[i];
|
||||
if (so == null || so.ally_heroID != id) continue;
|
||||
Sprite sprite = so.ally_heroProfile != null ? so.ally_heroProfile : so.ally_heroImage;
|
||||
if (sprite != null)
|
||||
{
|
||||
SetTeammateCharacterImage(slotIndex, sprite);
|
||||
}
|
||||
SetTeammateCharacterImage(slotIndex, sprite); // sprite 可能仍为 null,SetTeammateCharacterImage 会禁用
|
||||
return;
|
||||
}
|
||||
|
||||
// 找不到对应 SO:也禁用 Image
|
||||
SetTeammateCharacterImage(slotIndex, null);
|
||||
}
|
||||
|
||||
public void SwapAllyWithLowerAdjacent(int slotIndex)
|
||||
@@ -1984,6 +2016,7 @@ public class teamUIController : MonoBehaviour
|
||||
InitializeEnemyInstance();
|
||||
int difficultyID = BeatmapManager.Instance != null ? BeatmapManager.Instance.assignedDifficulty : 0;
|
||||
enemyCombatantInstance.InitializeFromSO(so, difficultyID);
|
||||
GameplayLevelRuleEventBus.NotifyEnemySpawned(enemyCombatantInstance, enemyCurrentCount, so);
|
||||
|
||||
// set UI visuals (name / sprites)
|
||||
string enemyName = so.enemyName ?? so.name;
|
||||
@@ -2176,6 +2209,7 @@ public class teamUIController : MonoBehaviour
|
||||
so = recognizedEnemySOs[enemyCurrentCount];
|
||||
if (so != null) eName = so.enemyName ?? so.name;
|
||||
}
|
||||
GameplayLevelRuleEventBus.NotifyEnemyDied(e, enemyCurrentCount, so);
|
||||
SkillTriggerFeedUI.PushEnemyDeath(eName);
|
||||
|
||||
// notify enemy ball UI
|
||||
|
||||
@@ -235,4 +235,9 @@ public class GlobalDistortionRuntimeController : MonoBehaviour
|
||||
{
|
||||
EnsureInstance().TriggerWave(1f, enterDurationSeconds, recoverDurationSeconds);
|
||||
}
|
||||
|
||||
public static void TriggerWaveEffect(float intensity, float enterDurationSeconds, float recoverDurationSeconds)
|
||||
{
|
||||
EnsureInstance().TriggerWave(intensity, enterDurationSeconds, recoverDurationSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,13 @@ public class controllerSettings : MonoBehaviour
|
||||
private bool previousKeyValid = false;
|
||||
|
||||
// slider range
|
||||
private const float NoteSpeedMin = 0.5f;
|
||||
private const float NoteSpeedMax = 2f;
|
||||
private const float NoteSpeedMin = 1f;
|
||||
// Player-facing range is 1-3. NoteSpawner multiplies it by 2, so the real range is 2-6.
|
||||
private const float NoteSpeedMax = 3f;
|
||||
private const string NoteSpeedPrefKey = "noteSpeedMultiplier";
|
||||
private const float NoteSpeedDefault = 1.0f;
|
||||
private const string NoteSpeedDefaultVersionKey = "noteSpeedMultiplierDefaultVersion";
|
||||
private const int NoteSpeedDefaultVersion = 2;
|
||||
private const float NoteSpeedDefault = 2.0f;
|
||||
|
||||
// continuous change
|
||||
private Coroutine continuousChangeCoroutine = null;
|
||||
@@ -505,10 +508,22 @@ public class controllerSettings : MonoBehaviour
|
||||
{
|
||||
if (PlayerPrefs.HasKey(NoteSpeedPrefKey))
|
||||
{
|
||||
int version = PlayerPrefs.GetInt(NoteSpeedDefaultVersionKey, 0);
|
||||
float saved = PlayerPrefs.GetFloat(NoteSpeedPrefKey, NoteSpeedDefault);
|
||||
if (version < NoteSpeedDefaultVersion && Mathf.Approximately(saved, 3f))
|
||||
{
|
||||
saved = NoteSpeedDefault;
|
||||
}
|
||||
|
||||
saved = Mathf.Clamp(saved, NoteSpeedMin, NoteSpeedMax);
|
||||
PlayerPrefs.SetFloat(NoteSpeedPrefKey, saved);
|
||||
PlayerPrefs.SetInt(NoteSpeedDefaultVersionKey, NoteSpeedDefaultVersion);
|
||||
PlayerPrefs.Save();
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerPrefs.SetFloat(NoteSpeedPrefKey, NoteSpeedDefault);
|
||||
PlayerPrefs.SetInt(NoteSpeedDefaultVersionKey, NoteSpeedDefaultVersion);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
|
||||
@@ -6083,7 +6083,7 @@ MonoBehaviour:
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: 2.0
|
||||
m_Text: 300%
|
||||
--- !u!1 &1283681621046949199
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -13447,7 +13447,7 @@ RectTransform:
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 3840, y: 2160}
|
||||
m_SizeDelta: {x: 19200, y: 10800}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4461991833650980158
|
||||
CanvasRenderer:
|
||||
@@ -14913,10 +14913,10 @@ MonoBehaviour:
|
||||
m_FillRect: {fileID: 2478940357038280624}
|
||||
m_HandleRect: {fileID: 4123488008762013331}
|
||||
m_Direction: 0
|
||||
m_MinValue: 0
|
||||
m_MaxValue: 1
|
||||
m_MinValue: 1
|
||||
m_MaxValue: 3
|
||||
m_WholeNumbers: 0
|
||||
m_Value: 1
|
||||
m_Value: 2
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
@@ -26841,10 +26841,10 @@ MonoBehaviour:
|
||||
m_FillRect: {fileID: 4302310117072831067}
|
||||
m_HandleRect: {fileID: 1819070857763714964}
|
||||
m_Direction: 0
|
||||
m_MinValue: 0
|
||||
m_MaxValue: 1
|
||||
m_MinValue: 1
|
||||
m_MaxValue: 3
|
||||
m_WholeNumbers: 0
|
||||
m_Value: 1
|
||||
m_Value: 2
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
@@ -32449,7 +32449,7 @@ MonoBehaviour:
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: 0.5
|
||||
m_Text: 100%
|
||||
--- !u!1 &8286336829220767699
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
Reference in New Issue
Block a user