diff --git a/Assets/Scenes/languageTest.unity b/Assets/Scenes/languageTest.unity index b59dddfd8..6a0699da3 100644 --- a/Assets/Scenes/languageTest.unity +++ b/Assets/Scenes/languageTest.unity @@ -180,6 +180,299 @@ MonoBehaviour: targetSentence: "\u522B\u8FC7\u6765\u6211\u611F\u89C9\u5BB3\u6015" waveformAmplitude: 0.3 interactionRadius: 0.5 + mouseInteractionRadius: 1.5 + repulsionForce: 2 + attractionForce: 1.5 + enableAutoPropagation: 1 + autoPropagationInterval: 2 + autoPropagationCount: 1 + propagationDuration: 1 + propagationDelay: 0.08 + maxPropagationNodes: 8 + targetParticleColor: {r: 0.2, g: 0.78039217, b: 1, a: 1} + nonTargetParticleColor: {r: 1, g: 0.40870008, b: 0.38823533, a: 0.69803923} + propagationColor: {r: 0.6621573, g: 0.8664762, b: 0.9433962, a: 1} + floatingTextColor: {r: 1, g: 1, b: 1, a: 1} + targetCalmColor: {r: 0.78, g: 0.59, b: 0.59, a: 1} + targetParticleFontSize: 3.5 + nonTargetParticleFontSize: 2.5 + floatingTextFontSize: 2 + targetParticleBold: 1 + nonTargetParticleBold: 0 + targetConnectionColor: {r: 0.3882353, g: 0.78039217, b: 1, a: 0.6666667} + nonTargetConnectionColor: {r: 1, g: 0.41911593, b: 0.38823533, a: 0.7} + propagationLineColor: {r: 0.6627451, g: 0.8666667, b: 0.94509804, a: 1} + targetConnectionThickness: 0.04 + nonTargetConnectionThickness: 0.02 + propagationLineThickness: 0.03 + integrationProgressText: {fileID: 1294290436} + interferenceCountText: {fileID: 528708888} + focusSequenceButton: {fileID: 153766885} + focusZoomScale: 1.6 + focusMoveDuration: 1.2 + focusFadeDuration: 1 + probabilityDuration: 3.5 + predictionSequenceGap: 0.4 + spreadDistance: 0.35 + enableSeparation: 1 + minParticleSpacing: 0.6 + separationForce: 2.5 + lineAlphaMultiplier: 0.65 +--- !u!1 &153766884 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 153766888} + - component: {fileID: 153766887} + - component: {fileID: 153766886} + - component: {fileID: 153766885} + m_Layer: 5 + m_Name: Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &153766885 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 153766884} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 153766886} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &153766886 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 153766884} + 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: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + 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!222 &153766887 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 153766884} + m_CullTransparentMesh: 1 +--- !u!224 &153766888 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 153766884} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.012000492, y: 0.012000492, z: 0.012000492} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1137525304} + m_Father: {fileID: 1665911936} + 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: 6.26, y: 2.12} + m_SizeDelta: {x: 160, y: 80} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &528708887 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 528708890} + - component: {fileID: 528708889} + - component: {fileID: 528708888} + m_Layer: 5 + m_Name: Text (TMP) (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &528708888 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 528708887} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, 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_text: New Text + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: f242fb3dde1933640859f1c54591c9c8, type: 2} + m_sharedMaterial: {fileID: -6659093157667844055, guid: f242fb3dde1933640859f1c54591c9c8, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4284643057 + m_fontColor: {r: 0.9433962, g: 0.4632709, b: 0.38625845, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.3 + m_fontSizeBase: 0.3 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0.41540456, z: -2.6862788, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &528708889 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 528708887} + m_CullTransparentMesh: 1 +--- !u!224 &528708890 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 528708887} + 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: 1665911936} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10.839999, y: -1.5600002} + m_SizeDelta: {x: 1, y: 1} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &786157861 GameObject: m_ObjectHideFlags: 0 @@ -260,8 +553,8 @@ Camera: m_GameObject: {fileID: 786157861} m_Enabled: 1 serializedVersion: 2 - m_ClearFlags: 1 - m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_ClearFlags: 2 + m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0} m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 @@ -317,6 +610,274 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1137525303 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1137525304} + - component: {fileID: 1137525306} + - component: {fileID: 1137525305} + m_Layer: 5 + m_Name: Text (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1137525304 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1137525303} + 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: 153766888} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1137525305 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1137525303} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, 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_text: "\u8F93\u51FA" + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: f242fb3dde1933640859f1c54591c9c8, type: 2} + m_sharedMaterial: {fileID: -6659093157667844055, guid: f242fb3dde1933640859f1c54591c9c8, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4281479730 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 24 + m_fontSizeBase: 24 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &1137525306 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1137525303} + m_CullTransparentMesh: 1 +--- !u!1 &1294290433 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1294290434} + - component: {fileID: 1294290435} + - component: {fileID: 1294290436} + m_Layer: 5 + m_Name: Text (TMP) (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1294290434 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1294290433} + 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: 1665911936} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10.83, y: -0.71} + m_SizeDelta: {x: 1, y: 1} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1294290435 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1294290433} + m_CullTransparentMesh: 1 +--- !u!114 &1294290436 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1294290433} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, 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_text: New Text + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: f242fb3dde1933640859f1c54591c9c8, type: 2} + m_sharedMaterial: {fileID: -6659093157667844055, guid: f242fb3dde1933640859f1c54591c9c8, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294040693 + m_fontColor: {r: 0.45745817, g: 0.86362934, b: 0.9433962, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.3 + m_fontSizeBase: 0.3 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0.41540456, z: -2.6862788, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} --- !u!1 &1665911932 GameObject: m_ObjectHideFlags: 0 @@ -410,12 +971,15 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 1294290434} + - {fileID: 528708890} + - {fileID: 153766888} m_Father: {fileID: 0} 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_AnchoredPosition: {x: -2.54, y: 0} m_SizeDelta: {x: 10, y: 10} m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1764870496 diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/CandidateParticle.cs b/Assets/Scripts/MiniGame/HuoShan/Language/CandidateParticle.cs index 323a8022a..749fd182d 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/CandidateParticle.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/CandidateParticle.cs @@ -40,6 +40,18 @@ namespace AibisDream.MiniGame.Language public float dissolveProgress = 0f; // 消散进度(0-1) public WaveformPoint waveformPoint; // 关联的波形点 + // 颜色配置(由Manager设置) + private Color targetColor = new Color(1f, 0.39f, 0.39f, 1f); // 目标粒子颜色 + private Color nonTargetColor = new Color(0.2f, 0.78f, 1f, 1f); // 非目标粒子颜色 + private Color propagationColor = new Color(1f, 0.59f, 0.2f, 1f); // 传播颜色 + private Color calmColor = new Color(0.78f, 0.59f, 0.59f, 1f); // 平静后颜色 + + // 字体样式配置(由Manager设置) + private float targetFontSize = 3.5f; + private float nonTargetFontSize = 2.5f; + private bool targetBold = true; + private bool nonTargetBold = false; + protected override void Awake() { base.Awake(); @@ -47,13 +59,14 @@ namespace AibisDream.MiniGame.Language baseAlpha = 0.6f; // 0-1 范围的透明度 probability = Random.Range(0.1f, 1f); alpha = baseAlpha + probability * 0.4f; // 最大 1.0 - size = 3f; + size = nonTargetFontSize; // 默认使用非目标粒子大小 velocity = new Vector2(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)); changeInterval = Random.Range(1f, 2.5f); if (textMesh != null) { textMesh.fontSize = size; + UpdateFontStyle(); } } @@ -125,6 +138,9 @@ namespace AibisDream.MiniGame.Language { if (textMesh == null) return; + // 更新字体样式(大小和加粗) + UpdateFontStyle(); + // 计算显示位置 Vector3 displayOffset = (Vector3)(vibrationOffset + anxietyShakeOffset) + Vector3.up * anxietyFloatY; textMesh.transform.localPosition = displayOffset; @@ -144,19 +160,21 @@ namespace AibisDream.MiniGame.Language if (isRed) { - // 红色粒子:根据平静进度调整颜色 - float r = Mathf.Lerp(1f, 0.78f, calmProgress); - float g = Mathf.Lerp(0.39f, 0.59f, calmProgress); - float b = Mathf.Lerp(0.39f, 0.59f, calmProgress); - color = new Color(r, g, b, displayAlpha); // displayAlpha 已经是 0-1 范围 + // 目标粒子:根据平静进度从目标颜色过渡到平静颜色 + float r = Mathf.Lerp(targetColor.r, calmColor.r, calmProgress); + float g = Mathf.Lerp(targetColor.g, calmColor.g, calmProgress); + float b = Mathf.Lerp(targetColor.b, calmColor.b, calmProgress); + color = new Color(r, g, b, displayAlpha); } else if (isOrange) { - color = new Color(1f, 0.59f, 0.2f, displayAlpha); + // 传播效果颜色 + color = new Color(propagationColor.r, propagationColor.g, propagationColor.b, displayAlpha); } else { - color = new Color(0.2f, 0.78f, 1f, displayAlpha); + // 非目标粒子颜色 + color = new Color(nonTargetColor.r, nonTargetColor.g, nonTargetColor.b, displayAlpha); } textMesh.color = color; @@ -199,5 +217,53 @@ namespace AibisDream.MiniGame.Language vibrationOffset = Vector2.zero; effectTimer = 0; } + + /// + /// 设置粒子颜色配置 + /// + public void SetColors(Color target, Color nonTarget, Color propagation, Color calm) + { + targetColor = target; + nonTargetColor = nonTarget; + propagationColor = propagation; + calmColor = calm; + } + + /// + /// 设置字体样式配置 + /// + public void SetFontStyles(float targetSize, float nonTargetSize, bool targetIsBold, bool nonTargetIsBold) + { + targetFontSize = targetSize; + nonTargetFontSize = nonTargetSize; + targetBold = targetIsBold; + nonTargetBold = nonTargetIsBold; + + // 立即应用 + if (textMesh != null) + { + UpdateFontStyle(); + } + } + + /// + /// 更新字体样式(根据粒子状态) + /// + private void UpdateFontStyle() + { + if (textMesh == null) return; + + // 根据是否为目标粒子设置大小和加粗 + if (isRed) + { + textMesh.fontSize = targetFontSize; + textMesh.fontStyle = targetBold ? TMPro.FontStyles.Bold : TMPro.FontStyles.Normal; + } + else + { + textMesh.fontSize = nonTargetFontSize; + textMesh.fontStyle = nonTargetBold ? TMPro.FontStyles.Bold : TMPro.FontStyles.Normal; + } + } } } diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/ConnectionRenderer.cs b/Assets/Scripts/MiniGame/HuoShan/Language/ConnectionRenderer.cs index 48b50169d..fc0d7461d 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/ConnectionRenderer.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/ConnectionRenderer.cs @@ -6,6 +6,7 @@ namespace AibisDream.MiniGame.Language { /// /// 连接线渲染器:使用Shapes绘制粒子间的连接线 + /// 注意:sortingOrder 设置为 0,确保低于文字(文字 sortingOrder = 10) /// [ExecuteAlways] public class ConnectionRenderer : ImmediateModeShapeDrawer @@ -15,19 +16,33 @@ namespace AibisDream.MiniGame.Language [Header("连接线设置")] public float connectionDistance = 2f; - public Color blueConnectionColor = new Color(0.39f, 0.78f, 1f, 0.7f); - public Color redConnectionColor = new Color(1f, 0.39f, 0.39f, 1f); - public float blueLineThickness = 0.02f; - public float redLineThickness = 0.04f; + private Color nonTargetConnectionColor = new Color(0.39f, 0.78f, 1f, 0.7f); // 非目标粒子连线颜色 + private Color targetConnectionColor = new Color(1f, 0.39f, 0.39f, 1f); // 目标粒子连线颜色 + private float nonTargetLineThickness = 0.02f; + private float targetLineThickness = 0.04f; [Header("效果传播设置")] - public Color propagationColor = new Color(1f, 0.59f, 0.2f, 1f); - public float propagationLineThickness = 0.03f; + private Color propagationColor = new Color(1f, 0.59f, 0.2f, 1f); + private float propagationLineThickness = 0.03f; private List candidateParticles; private List effectPropagations; private CompletionPhase completionPhase; private float fadeOutAlpha; + private float nonTargetAlphaScale = 1f; + private float targetAlphaScale = 1f; + + public float NonTargetAlphaScale + { + get => nonTargetAlphaScale; + set => nonTargetAlphaScale = Mathf.Clamp01(value); + } + + public float TargetAlphaScale + { + get => targetAlphaScale; + set => targetAlphaScale = Mathf.Clamp(value, 0f, 1.5f); + } public void SetData( List candidates, @@ -50,15 +65,18 @@ namespace AibisDream.MiniGame.Language { Draw.BlendMode = ShapesBlendMode.Transparent; Draw.ZTest = UnityEngine.Rendering.CompareFunction.Always; + // 注意:连接线默认在底层,文字的 sortingOrder = 10 会显示在上层 // 绘制蓝色粒子间的连线 - if (completionPhase == CompletionPhase.None || completionPhase == CompletionPhase.Arranging) + if (completionPhase == CompletionPhase.None || completionPhase == CompletionPhase.Completed) { DrawBlueConnections(); } // 绘制红色粒子间的连线 - if (completionPhase == CompletionPhase.None || completionPhase == CompletionPhase.Arranging) + if (completionPhase == CompletionPhase.None || + completionPhase == CompletionPhase.Completed || + completionPhase == CompletionPhase.Focusing) { DrawRedConnections(); } @@ -74,7 +92,7 @@ namespace AibisDream.MiniGame.Language private void DrawBlueConnections() { Draw.LineGeometry = LineGeometry.Flat2D; - Draw.Thickness = blueLineThickness; + Draw.Thickness = nonTargetLineThickness; for (int i = 0; i < candidateParticles.Count; i++) { @@ -90,8 +108,8 @@ namespace AibisDream.MiniGame.Language if (distance < connectionDistance) { float alpha = Mathf.Lerp(0.7f, 0f, distance / connectionDistance) * fadeOutAlpha; - Color lineColor = blueConnectionColor; - lineColor.a = alpha; + Color lineColor = nonTargetConnectionColor; + lineColor.a = alpha * nonTargetAlphaScale; Draw.Line(p1.transform.position, p2.transform.position, lineColor); } @@ -102,7 +120,7 @@ namespace AibisDream.MiniGame.Language private void DrawRedConnections() { Draw.LineGeometry = LineGeometry.Flat2D; - Draw.Thickness = redLineThickness; + Draw.Thickness = targetLineThickness; var redParticles = new List(); foreach (var p in candidateParticles) @@ -123,8 +141,8 @@ namespace AibisDream.MiniGame.Language if (distance < connectionDistance) { float alpha = Mathf.Lerp(1f, 0.4f, distance / connectionDistance); - Color lineColor = redConnectionColor; - lineColor.a = alpha; + Color lineColor = targetConnectionColor; + lineColor.a = alpha * targetAlphaScale; Draw.Line(redParticles[i].transform.position, redParticles[j].transform.position, lineColor); } @@ -205,6 +223,25 @@ namespace AibisDream.MiniGame.Language Draw.Triangle(tip, left, right, color); } + + /// + /// 设置连接线样式 + /// + public void SetConnectionStyles( + Color targetColor, + Color nonTargetColor, + Color propColor, + float targetThickness, + float nonTargetThickness, + float propThickness) + { + targetConnectionColor = targetColor; + nonTargetConnectionColor = nonTargetColor; + propagationColor = propColor; + targetLineThickness = targetThickness; + nonTargetLineThickness = nonTargetThickness; + propagationLineThickness = propThickness; + } } } diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/EffectPropagation.cs b/Assets/Scripts/MiniGame/HuoShan/Language/EffectPropagation.cs index 66ff537f9..52a0c2d3e 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/EffectPropagation.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/EffectPropagation.cs @@ -23,42 +23,46 @@ namespace AibisDream.MiniGame.Language } /// - /// 效果传播系统:管理从红色粒子到蓝色粒子的效果传递 + /// 效果传播系统:管理从红色粒子到蓝色粒子的效果传递(使用BFS算法) /// public class EffectPropagation { private CandidateParticle redParticle; private List propagatedParticles = new List(); - private List propagationQueue = new List(); + private Queue propagationQueue = new Queue(); private float lifeTimer; - private float propagationDelay = 0.05f; + private float propagationDelay; private float propagationCounter = 0f; - private int maxPropagationSteps = 8; - private int currentPropagationStep = 0; + private int maxPropagationNodes; + private int currentNodeCount = 0; + private int currentStep = 0; public bool IsActive => lifeTimer > 0; public List PropagatedParticles => propagatedParticles; - public EffectPropagation(CandidateParticle redParticle, float duration = 1f) + public EffectPropagation(CandidateParticle redParticle, float duration, float delay, int maxNodes) { this.redParticle = redParticle; this.lifeTimer = duration; + this.propagationDelay = delay; + this.maxPropagationNodes = maxNodes; - // 从红色粒子开始传播 + // 从红色粒子开始传播(第0步) var connectedBlues = FindConnectedBlues(redParticle); - if (connectedBlues.Count > 0 && currentPropagationStep < maxPropagationSteps) + if (connectedBlues.Count > 0 && currentNodeCount < maxPropagationNodes) { - // 随机选择一个连接的蓝色粒子 - var targetParticle = connectedBlues[Random.Range(0, connectedBlues.Count)]; - ApplyEffectToParticle(targetParticle); + // 第一步:处理所有直接连接的蓝色粒子(BFS第一层) + foreach (var targetParticle in connectedBlues) + { + if (currentNodeCount >= maxPropagationNodes) break; - // 记录传播信息 - propagatedParticles.Add(new PropagationInfo(targetParticle, redParticle, 1)); - - // 加入传播队列 - propagationQueue.Add(targetParticle); - currentPropagationStep = 1; + ApplyEffectToParticle(targetParticle); + propagatedParticles.Add(new PropagationInfo(targetParticle, redParticle, 1)); + propagationQueue.Enqueue(targetParticle); + currentNodeCount++; + } + currentStep = 1; } } @@ -67,35 +71,33 @@ namespace AibisDream.MiniGame.Language lifeTimer -= deltaTime; propagationCounter += deltaTime; - // 定期传播 - if (propagationCounter >= propagationDelay && currentPropagationStep < maxPropagationSteps) + // BFS逐层传播 + if (propagationCounter >= propagationDelay && propagationQueue.Count > 0 && currentNodeCount < maxPropagationNodes) { propagationCounter = 0f; + currentStep++; - List nextQueue = new List(); - - foreach (var sourceParticle in propagationQueue) + // 处理当前层的所有节点 + int currentLevelSize = propagationQueue.Count; + + for (int i = 0; i < currentLevelSize; i++) { - if (currentPropagationStep >= maxPropagationSteps) break; - + if (currentNodeCount >= maxPropagationNodes) break; + + var sourceParticle = propagationQueue.Dequeue(); var connectedBlues = FindConnectedBlues(sourceParticle); - if (connectedBlues.Count > 0) + + // 将所有连接的蓝色粒子加入下一层 + foreach (var targetParticle in connectedBlues) { - var targetParticle = connectedBlues[Random.Range(0, connectedBlues.Count)]; + if (currentNodeCount >= maxPropagationNodes) break; + ApplyEffectToParticle(targetParticle); - - currentPropagationStep++; - - propagatedParticles.Add(new PropagationInfo(targetParticle, sourceParticle, currentPropagationStep)); - - if (currentPropagationStep < maxPropagationSteps) - { - nextQueue.Add(targetParticle); - } + propagatedParticles.Add(new PropagationInfo(targetParticle, sourceParticle, currentStep + 1)); + propagationQueue.Enqueue(targetParticle); + currentNodeCount++; } } - - propagationQueue = nextQueue; } // 更新所有传播粒子的脉冲动画 diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/FloatingTextParticle.cs b/Assets/Scripts/MiniGame/HuoShan/Language/FloatingTextParticle.cs index a43b3fd9b..603395358 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/FloatingTextParticle.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/FloatingTextParticle.cs @@ -7,6 +7,8 @@ namespace AibisDream.MiniGame.Language /// public class FloatingTextParticle : TextParticle { + private Color baseColor = new Color(0.7f, 0.78f, 1f, 1f); // 默认颜色 + protected override void Awake() { base.Awake(); @@ -15,13 +17,37 @@ namespace AibisDream.MiniGame.Language baseAlpha = Random.Range(0.04f, 0.1f); // 0-1 范围的透明度 alpha = baseAlpha; size = Random.Range(1.5f, 2.5f); - velocity = new Vector2(Random.Range(-0.3f, 0.3f), Random.Range(-0.3f, 0.3f)); - changeInterval = Random.Range(0.5f, 0.7f); + velocity = new Vector2(Random.Range(-1.2f, 1.2f), Random.Range(-1.2f, 1.2f)); + changeInterval = Random.Range(0.2f, 0.3f); if (textMesh != null) { textMesh.fontSize = size; - textMesh.color = new Color(0.7f, 0.78f, 1f, alpha); + textMesh.color = new Color(baseColor.r, baseColor.g, baseColor.b, alpha); + } + } + + /// + /// 设置粒子颜色 + /// + public void SetColor(Color color) + { + baseColor = color; + if (textMesh != null) + { + textMesh.color = new Color(baseColor.r, baseColor.g, baseColor.b, alpha); + } + } + + /// + /// 设置字体大小 + /// + public void SetFontSize(float fontSize) + { + size = fontSize; + if (textMesh != null) + { + textMesh.fontSize = size; } } @@ -29,13 +55,13 @@ namespace AibisDream.MiniGame.Language { if (textMesh != null) { - Color color = new Color(0.7f, 0.78f, 1f, alpha); + Color color = new Color(baseColor.r, baseColor.g, baseColor.b, alpha); textMesh.color = color; } if (glowSprite != null) { - glowSprite.color = new Color(0.7f, 0.78f, 1f, alpha * 0.3f); + glowSprite.color = new Color(baseColor.r, baseColor.g, baseColor.b, alpha * 0.3f); } } } diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs index b86560dd2..3d2b1f892 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs @@ -1,9 +1,11 @@ using UnityEngine; +using System.Collections; using System.Collections.Generic; using System.Linq; using DG.Tweening; using Shapes; using TMPro; +using UnityEngine.UI; namespace AibisDream.MiniGame.Language { @@ -13,9 +15,9 @@ namespace AibisDream.MiniGame.Language public enum CompletionPhase { None, // 未完成 - Arranging, // 排列中 - Revealing, // 揭示中 - Completed // 已完成 + Completed, // 已完成但尚未进入聚焦流程 + Focusing, // 正在聚焦放大并收敛 + Finished // 全流程完成 } /// @@ -44,7 +46,7 @@ namespace AibisDream.MiniGame.Language [SerializeField] private float textMargin = 1f; [Header("目标句子")] - [SerializeField] private string targetSentence = "别过来我感觉害怕"; + [SerializeField] private string targetSentence = "这是一个测试示例"; [Header("波形参数")] [SerializeField] private float waveformAmplitude = 0.3f; @@ -55,10 +57,61 @@ namespace AibisDream.MiniGame.Language [SerializeField] private float repulsionForce = 2f; [SerializeField] private float attractionForce = 1.5f; + [Header("自动传播效果")] + [SerializeField] private bool enableAutoPropagation = true; // 是否启用自动传播 + [SerializeField] private float autoPropagationInterval = 2f; // 自动传播间隔(秒) + [SerializeField] private int autoPropagationCount = 1; // 每次自动触发的传播数量 + + [Header("传播效果参数")] + [SerializeField] private float propagationDuration = 1f; // 传播效果持续时间 + [SerializeField] private float propagationDelay = 0.08f; // 传播延迟(每层之间的间隔) + [SerializeField] private int maxPropagationNodes = 15; // 最大传播节点数量 + + [Header("颜色配置")] + [SerializeField] private Color targetParticleColor = new Color(1f, 0.39f, 0.39f, 1f); // 目标粒子颜色(原红色) + [SerializeField] private Color nonTargetParticleColor = new Color(0.2f, 0.78f, 1f, 1f); // 非目标粒子颜色(原蓝色) + [SerializeField] private Color propagationColor = new Color(1f, 0.59f, 0.2f, 1f); // 传播效果颜色(橙色) + [SerializeField] private Color floatingTextColor = new Color(0.7f, 0.78f, 1f, 1f); // 背景浮动文字颜色 + [SerializeField] private Color targetCalmColor = new Color(0.78f, 0.59f, 0.59f, 1f); // 目标粒子平静后颜色 + + [Header("文字样式配置")] + [SerializeField] private float targetParticleFontSize = 3.5f; // 目标粒子字体大小 + [SerializeField] private float nonTargetParticleFontSize = 2.5f; // 非目标粒子字体大小 + [SerializeField] private float floatingTextFontSize = 2f; // 背景浮动文字大小 + [SerializeField] private bool targetParticleBold = true; // 目标粒子是否加粗 + [SerializeField] private bool nonTargetParticleBold = false; // 非目标粒子是否加粗 + + [Header("连接线配置")] + [SerializeField] private Color targetConnectionColor = new Color(1f, 0.39f, 0.39f, 1f); // 目标粒子连线颜色 + [SerializeField] private Color nonTargetConnectionColor = new Color(0.39f, 0.78f, 1f, 0.7f);// 非目标粒子连线颜色 + [SerializeField] private Color propagationLineColor = new Color(1f, 0.59f, 0.2f, 1f); // 传播线颜色 + [SerializeField] private float targetConnectionThickness = 0.04f; // 目标粒子连线粗细 + [SerializeField] private float nonTargetConnectionThickness = 0.02f; // 非目标粒子连线粗细 + [SerializeField] private float propagationLineThickness = 0.03f; // 传播线粗细 + + [Header("目标 UI")] + [SerializeField] private TMP_Text integrationProgressText; + [SerializeField] private TMP_Text interferenceCountText; + + [Header("完成阶段 UI")] + [SerializeField] private Button focusSequenceButton; + [SerializeField] private float focusZoomScale = 1.6f; + [SerializeField] private float focusMoveDuration = 1.2f; + [SerializeField] private float focusFadeDuration = 1f; + [SerializeField] private float probabilityDuration = 3.5f; + [SerializeField] private float predictionSequenceGap = 0.4f; + [SerializeField] private float spreadDistance = 0.35f; + + [Header("布局优化")] + [SerializeField] private bool enableSeparation = true; // 是否启用粒子分离力 + [SerializeField] private float minParticleSpacing = 0.6f; // 粒子最小间距 + [SerializeField] private float separationForce = 2.5f; // 分离力度 + [SerializeField] private float lineAlphaMultiplier = 0.65f; // 连接线透明度缩放 + // 粒子列表 private List floatingParticles = new List(); private List candidateParticles = new List(); - private List redParticles = new List(); + private List targetParticles = new List(); // 目标粒子列表(原 redParticles) // 效果系统 private List effectPropagations = new List(); @@ -72,26 +125,52 @@ namespace AibisDream.MiniGame.Language private float completionTimer = 0f; private float fadeOutAlpha = 1f; private List orderedRedParticles = new List(); - private List targetPositions = new List(); + private bool allowInput = true; + private float focusTimer = 0f; + private Dictionary focusVisuals = new Dictionary(); + private Vector3 focusCentroid; + private List finalTargetCharacters = new List(); + private List finalTargetPositions = new List(); - // 波形系统 - private List waveformPoints = new List(); - private float calmProgress = 0f; - private Vector2 lastMousePos; - private float mouseDragSpeed; + private class TargetFocusVisual + { + public CandidateParticle particle; + public GameObject ringObject; + public Disc ringDisc; + public TMP_Text probabilityLabel; + public float probability = 90f; + public float targetProbability = 100f; + public float startShakeIntensity; + public string finalChar; + public bool stabilized; + public Vector3 focusPosition; + public Vector3 finalPosition; + public int orderIndex; + } + + // 自动传播计时器 + private float autoPropagationTimer = 0f; + private bool focusTweensCompleted = false; // 边界 private Bounds movementBounds; // 连接渲染(使用Shapes) private ConnectionRenderer connectionRenderer; - private WaveformRenderer waveformRenderer; private void Start() { InitializeCanvas(); InitializeParticles(); InitializeRenderers(); + UpdateStatusUI(); + if (focusSequenceButton != null) + { + focusSequenceButton.interactable = false; + focusSequenceButton.gameObject.SetActive(false); + focusSequenceButton.onClick.RemoveListener(HandleFocusButtonClicked); + focusSequenceButton.onClick.AddListener(HandleFocusButtonClicked); + } } private void InitializeCanvas() @@ -136,6 +215,8 @@ namespace AibisDream.MiniGame.Language GameObject obj = CreateParticleObject(pos, $"FloatingParticle_{i}"); FloatingTextParticle particle = obj.AddComponent(); particle.SetFont(chineseFontAsset); + particle.SetColor(floatingTextColor); + particle.SetFontSize(floatingTextFontSize); particle.SetMovementBounds(movementBounds); floatingParticles.Add(particle); } @@ -147,6 +228,8 @@ namespace AibisDream.MiniGame.Language GameObject obj = CreateParticleObject(pos, $"CandidateParticle_{i}"); CandidateParticle particle = obj.AddComponent(); particle.SetFont(chineseFontAsset); + particle.SetColors(targetParticleColor, nonTargetParticleColor, propagationColor, targetCalmColor); + particle.SetFontStyles(targetParticleFontSize, nonTargetParticleFontSize, targetParticleBold, nonTargetParticleBold); particle.SetMovementBounds(movementBounds); candidateParticles.Add(particle); } @@ -185,27 +268,30 @@ namespace AibisDream.MiniGame.Language connectionRenderer = connObj.AddComponent(); connectionRenderer.manager = this; connectionRenderer.connectionDistance = connectionDistance; - - // 波形渲染器 - GameObject waveObj = new GameObject("WaveformRenderer"); - waveObj.transform.SetParent(transform); - waveObj.transform.localPosition = Vector3.zero; - waveformRenderer = waveObj.AddComponent(); - waveformRenderer.interactionRadius = interactionRadius; + connectionRenderer.SetConnectionStyles( + targetConnectionColor, + nonTargetConnectionColor, + propagationLineColor, + targetConnectionThickness, + nonTargetConnectionThickness, + propagationLineThickness + ); + connectionRenderer.NonTargetAlphaScale = lineAlphaMultiplier; + connectionRenderer.TargetAlphaScale = 1f; } private void SelectRedParticles() { - redParticles.Clear(); + targetParticles.Clear(); List shuffled = candidateParticles.OrderBy(x => Random.value).ToList(); float minDistance = Mathf.Min(movementBounds.size.x, movementBounds.size.y) / (redParticleCount * 0.8f); foreach (var particle in shuffled) { - if (redParticles.Count >= redParticleCount) break; + if (targetParticles.Count >= redParticleCount) break; bool tooClose = false; - foreach (var selected in redParticles) + foreach (var selected in targetParticles) { if (Vector3.Distance(particle.transform.position, selected.transform.position) < minDistance) { @@ -214,23 +300,23 @@ namespace AibisDream.MiniGame.Language } } - if (!tooClose || redParticles.Count == 0) + if (!tooClose || targetParticles.Count == 0) { particle.isRed = true; particle.originalIsRed = true; - redParticles.Add(particle); + targetParticles.Add(particle); } } // 如果不够,强制添加 - int remaining = redParticleCount - redParticles.Count; + int remaining = redParticleCount - targetParticles.Count; for (int i = 0; i < remaining && i < shuffled.Count; i++) { if (!shuffled[i].isRed) { shuffled[i].isRed = true; shuffled[i].originalIsRed = true; - redParticles.Add(shuffled[i]); + targetParticles.Add(shuffled[i]); } } } @@ -252,44 +338,42 @@ namespace AibisDream.MiniGame.Language } } - // 更新连接 - UpdateConnections(); - - // 检测连接变化(触发效果传播) - if (initializationComplete && completionPhase == CompletionPhase.None) + if (completionPhase == CompletionPhase.None) { - DetectConnectionChanges(); - } + UpdateConnections(); + UpdateStatusUI(); - // 更新效果传播 - UpdateEffectPropagations(); + if (initializationComplete) + { + DetectConnectionChanges(); + UpdateAutoPropagation(); + } - // 检测完成状态 - if (!isCompleted && completionPhase == CompletionPhase.None) - { - if (CheckRedParticlesConnected()) + UpdateEffectPropagations(); + + if (!isCompleted && CheckRedParticlesConnected()) { isCompleted = true; - StartCompletionSequence(); + OnTargetsCompleted(); } - } - // 更新完成动画 - if (isCompleted) + if (allowInput) + { + UpdateInput(); + } + + ApplySeparationForces(); + } + else { - UpdateCompletionAnimation(); + UpdateConnections(); + UpdateStatusUI(); } - // 更新波形 - if (completionPhase == CompletionPhase.Revealing || completionPhase == CompletionPhase.Completed) + if (completionPhase == CompletionPhase.Focusing) { - UpdateWaveform(); + UpdateFocusSequence(); } - - // 更新输入 - UpdateInput(); - - // 更新渲染器数据 UpdateRenderers(); } @@ -305,21 +389,6 @@ namespace AibisDream.MiniGame.Language fadeOutAlpha ); } - - // 更新波形渲染器 - if (waveformRenderer != null) - { - Vector2 mousePos = GetMouseWorldPosition(); - bool mouseDown = Input.GetMouseButton(0); - waveformRenderer.SetData( - waveformPoints, - completionPhase, - calmProgress, - mousePos, - mouseDown, - mouseDragSpeed - ); - } } private void UpdateConnections() @@ -418,7 +487,12 @@ namespace AibisDream.MiniGame.Language private void TriggerEffectPropagation(CandidateParticle redParticle, CandidateParticle blueParticle) { - effectPropagations.Add(new EffectPropagation(redParticle, 1f)); + effectPropagations.Add(new EffectPropagation( + redParticle, + propagationDuration, + propagationDelay, + maxPropagationNodes + )); } private void UpdateEffectPropagations() @@ -435,16 +509,39 @@ namespace AibisDream.MiniGame.Language } } + private void UpdateAutoPropagation() + { + if (!enableAutoPropagation) return; + if (targetParticles.Count == 0) return; + + autoPropagationTimer += Time.deltaTime; + + if (autoPropagationTimer >= autoPropagationInterval) + { + autoPropagationTimer = 0f; + + // 随机选择目标粒子触发传播效果 + int triggerCount = Mathf.Min(autoPropagationCount, targetParticles.Count); + var shuffledTargets = targetParticles.OrderBy(x => Random.value).Take(triggerCount).ToList(); + + foreach (var targetParticle in shuffledTargets) + { + // 触发传播效果(即使没有建立新连接) + TriggerEffectPropagation(targetParticle, null); + } + } + } + private bool CheckRedParticlesConnected() { - if (redParticles.Count == 0) return false; - if (redParticles.Count == 1) return true; + if (targetParticles.Count == 0) return false; + if (targetParticles.Count == 1) return true; - // 检查所有红色粒子是否连通 + // 检查所有目标粒子是否连通 HashSet visited = new HashSet(); Queue queue = new Queue(); - queue.Enqueue(redParticles[0]); - visited.Add(redParticles[0]); + queue.Enqueue(targetParticles[0]); + visited.Add(targetParticles[0]); while (queue.Count > 0) { @@ -459,11 +556,11 @@ namespace AibisDream.MiniGame.Language } } - if (visited.Count != redParticles.Count) + if (visited.Count != targetParticles.Count) return false; - // 检查红色粒子是否与蓝色粒子分离 - foreach (var red in redParticles) + // 检查目标粒子是否与非目标粒子分离 + foreach (var red in targetParticles) { foreach (var other in candidateParticles) { @@ -478,62 +575,77 @@ namespace AibisDream.MiniGame.Language return true; } - private void StartCompletionSequence() + private void OnTargetsCompleted() { - completionPhase = CompletionPhase.Arranging; + completionPhase = CompletionPhase.Completed; completionTimer = 0f; + focusTimer = 0f; fadeOutAlpha = 1f; + allowInput = false; + effectPropagations.Clear(); + autoPropagationTimer = 0f; + focusVisuals.Clear(); + finalTargetCharacters.Clear(); + finalTargetPositions.Clear(); + focusTweensCompleted = false; - // 重置波形 - calmProgress = 0f; - waveformPoints.Clear(); - - // 获取排序的红色粒子 orderedRedParticles = GetOrderedRedParticles(); int displayLength = Mathf.Min(orderedRedParticles.Count, targetSentence.Length); - // 计算目标位置 - float spacing = 0.8f; - float totalWidth = displayLength * spacing; - float startX = -totalWidth / 2f + spacing / 2f; - float centerY = 0f; - - targetPositions.Clear(); - for (int i = 0; i < displayLength; i++) - { - targetPositions.Add(new Vector3(startX + i * spacing, centerY, 0f)); - } - - if (orderedRedParticles.Count > displayLength) + if (displayLength < orderedRedParticles.Count) { orderedRedParticles = orderedRedParticles.Take(displayLength).ToList(); } - // 清理效果 - effectPropagations.Clear(); - - // 停止非红色粒子 - foreach (var p in candidateParticles) + for (int i = 0; i < displayLength; i++) { - if (!p.isRed) + finalTargetCharacters.Add(targetSentence[i].ToString()); + } + + Vector3 displayCenter = worldCanvas != null ? worldCanvas.transform.position : Vector3.zero; + if (finalTargetCharacters.Count > 0) + { + float spacing = 0.9f; + float totalWidth = spacing * Mathf.Max(0, finalTargetCharacters.Count - 1); + float startX = -totalWidth / 2f; + finalTargetPositions.Clear(); + for (int i = 0; i < finalTargetCharacters.Count; i++) { - p.velocity = Vector2.zero; - if (p.isOrange) - { - p.RemoveEffect(); - } + finalTargetPositions.Add(displayCenter + new Vector3(startX + i * spacing, 0f, 0f)); } } + + if (focusSequenceButton != null) + { + focusSequenceButton.gameObject.SetActive(true); + focusSequenceButton.interactable = true; + } + + foreach (var particle in candidateParticles) + { + if (!particle.isRed) + { + particle.isCalmed = true; + particle.hasEffect = false; + particle.isOrange = false; + particle.changeSpeedMultiplier = 1f; + } + } + + foreach (var particle in floatingParticles) + { + particle.isCalmed = true; + } } private List GetOrderedRedParticles() { - if (redParticles.Count == 0) return new List(); - if (redParticles.Count == 1) return new List(redParticles); + if (targetParticles.Count == 0) return new List(); + if (targetParticles.Count == 1) return new List(targetParticles); // 从最左边的粒子开始 var ordered = new List(); - var startParticle = redParticles.OrderBy(p => p.transform.position.x).First(); + var startParticle = targetParticles.OrderBy(p => p.transform.position.x).First(); HashSet visited = new HashSet(); Stack stack = new Stack(); @@ -558,154 +670,272 @@ namespace AibisDream.MiniGame.Language return ordered; } - private void UpdateCompletionAnimation() + private void HandleFocusButtonClicked() { - completionTimer += Time.deltaTime; + if (completionPhase != CompletionPhase.Completed) + return; - if (completionPhase == CompletionPhase.Arranging) - { - bool allArrived = true; - for (int i = 0; i < orderedRedParticles.Count; i++) - { - var particle = orderedRedParticles[i]; - var target = targetPositions[i]; - - particle.transform.position = Vector3.Lerp( - particle.transform.position, - target, - Time.deltaTime * 5f - ); - - if (Vector3.Distance(particle.transform.position, target) > 0.01f) - { - allArrived = false; - } - } - - fadeOutAlpha = Mathf.Max(0f, fadeOutAlpha - Time.deltaTime * 2f); - - if (allArrived && completionTimer > 0.5f) - { - completionPhase = CompletionPhase.Revealing; - completionTimer = 0f; - InitializeWaveform(); - - // 初始化抖动效果 - foreach (var p in orderedRedParticles) - { - p.InitializeAnxietyEffect(); - } - } - } - else if (completionPhase == CompletionPhase.Revealing) - { - float revealDelay = 0.25f; - int currentIndex = Mathf.FloorToInt(completionTimer / revealDelay); - - for (int i = 0; i < orderedRedParticles.Count; i++) - { - var p = orderedRedParticles[i]; - - if (i < currentIndex) - { - // 已显示 - if (i < targetSentence.Length) - { - p.currentChar = targetSentence[i].ToString(); - p.isStatic = false; - } - } - else if (i == currentIndex) - { - // 正在显示(闪烁) - p.changeSpeedMultiplier = 20f; - if (completionTimer % revealDelay >= revealDelay - 0.1f) - { - if (i < targetSentence.Length) - { - p.currentChar = targetSentence[i].ToString(); - } - } - } - } - - if (currentIndex >= orderedRedParticles.Count) - { - completionPhase = CompletionPhase.Completed; - for (int i = 0; i < orderedRedParticles.Count && i < targetSentence.Length; i++) - { - orderedRedParticles[i].currentChar = targetSentence[i].ToString(); - orderedRedParticles[i].isStatic = false; - orderedRedParticles[i].changeSpeedMultiplier = 1f; - } - } - } - else if (completionPhase == CompletionPhase.Completed) - { - // 检查是否所有文字都已消散 - bool allDissolved = orderedRedParticles.All(p => p.dissolveProgress >= 1f); - if (allDissolved && completionTimer > 1f) - { - ResetGame(); - } - } + BeginFocusSequence(); } - private void InitializeWaveform() + private void BeginFocusSequence() { - waveformPoints.Clear(); + if (focusSequenceButton != null) + { + focusSequenceButton.interactable = false; + } - if (orderedRedParticles.Count == 0) return; + completionPhase = CompletionPhase.Focusing; + focusTimer = 0f; + focusTweensCompleted = false; - float baseY = 0f; + if (connectionRenderer != null) + { + connectionRenderer.NonTargetAlphaScale = 0f; + connectionRenderer.TargetAlphaScale = 1.2f; + } + + foreach (var particle in floatingParticles) + { + particle.isCalmed = true; + particle.velocity = Vector2.zero; + FadeOutTextParticle(particle, focusFadeDuration); + } + + foreach (var particle in candidateParticles) + { + if (!particle.isRed) + { + particle.isCalmed = true; + particle.velocity = Vector2.zero; + FadeOutTextParticle(particle, focusFadeDuration); + } + else + { + particle.isStatic = false; + particle.isCalmed = false; + particle.InitializeAnxietyEffect(); + particle.changeSpeedMultiplier = 6f; + particle.anxietyShakeIntensity = Mathf.Max(particle.anxietyShakeIntensity, 4f); + particle.SetChangeInterval(0.05f); + particle.ResetChangeTimer(0.05f); + } + } + + focusCentroid = ComputeCentroid(orderedRedParticles); + Vector3 focusTargetCenter = worldCanvas != null ? worldCanvas.transform.position : Vector3.zero; + + focusVisuals.Clear(); + int tweenTargetCount = orderedRedParticles.Count; + int completedTweens = 0; for (int i = 0; i < orderedRedParticles.Count; i++) { var particle = orderedRedParticles[i]; - var point = new WaveformPoint( - particle.transform.position.x, - baseY, - waveformAmplitude, - i, - particle - ); - waveformPoints.Add(point); - particle.waveformPoint = point; + Vector3 offset = particle.transform.position - focusCentroid; + float indexCenter = (orderedRedParticles.Count - 1) * 0.5f; + Vector3 spreadOffset = new Vector3((i - indexCenter) * spreadDistance, Mathf.Sin(i * 1.4f) * spreadDistance * 0.6f, 0f); + Vector3 focusPosition = focusTargetCenter + offset * focusZoomScale + spreadOffset; + Vector3 finalPosition = finalTargetPositions.Count > i ? finalTargetPositions[i] : focusTargetCenter; + + particle.transform.DOKill(); + particle.transform.DOMove(focusPosition, focusMoveDuration) + .SetEase(Ease.InOutQuad) + .OnComplete(() => + { + completedTweens++; + if (completedTweens >= tweenTargetCount) + { + focusTweensCompleted = true; + focusTimer = 0f; + } + }); + particle.transform.DOScale(Vector3.one * focusZoomScale, focusMoveDuration).SetEase(Ease.InOutQuad); + + string finalChar = i < finalTargetCharacters.Count ? finalTargetCharacters[i] : particle.currentChar; + TargetFocusVisual visual = CreateFocusVisual(particle, finalChar); + visual.focusPosition = focusPosition; + visual.finalPosition = finalPosition; + visual.orderIndex = i; + focusVisuals[particle] = visual; } - lastMousePos = GetMouseWorldPosition(); + if (tweenTargetCount == 0) + { + focusTweensCompleted = true; + focusTimer = 0f; + } } - private void UpdateWaveform() + private TargetFocusVisual CreateFocusVisual(CandidateParticle particle, string finalChar) { - if (waveformPoints.Count == 0) + TargetFocusVisual visual = new TargetFocusVisual { - InitializeWaveform(); + particle = particle, + finalChar = finalChar, + startShakeIntensity = Mathf.Max(particle.anxietyShakeIntensity, 4f) + }; + + GameObject ringObj = new GameObject("FocusRing"); + ringObj.transform.SetParent(particle.transform, false); + visual.ringObject = ringObj; + + float estimatedRadius = 0.55f; + if (particle != null && particle.textMesh != null) + { + estimatedRadius = Mathf.Clamp(particle.textMesh.fontSize * 0.05f, 0.35f, 0.75f); + } + + var disc = ringObj.AddComponent(); + disc.Type = DiscType.Ring; + disc.Radius = estimatedRadius; + disc.Thickness = Mathf.Clamp(estimatedRadius * 0.25f, 0.06f, 0.15f); + disc.Color = new Color(1f, 0.78f, 0.39f, 0.8f); + visual.ringDisc = disc; + + GameObject labelObj = new GameObject("ProbabilityLabel"); + labelObj.transform.SetParent(particle.transform, false); + RectTransform rectTransform = labelObj.AddComponent(); + rectTransform.localPosition = new Vector3(0f, estimatedRadius + 0.55f, 0f); + rectTransform.localScale = Vector3.one; + rectTransform.sizeDelta = new Vector2(2f, 0.6f); + + var label = labelObj.AddComponent(); + if (chineseFontAsset != null) + { + label.font = chineseFontAsset; + } + label.fontSize = 2f; + label.alignment = TextAlignmentOptions.Center; + label.text = $"{Mathf.RoundToInt(visual.probability)}%"; + visual.probabilityLabel = label; + + return visual; + } + + private void UpdateFocusSequence() + { + if (!focusTweensCompleted) return; - } - Vector2 mousePos = GetMouseWorldPosition(); - mouseDragSpeed = Vector2.Distance(mousePos, lastMousePos) / Time.deltaTime; - lastMousePos = mousePos; + focusTimer += Time.deltaTime; + float baseInterval = Mathf.Max(0.2f, probabilityDuration); + float arrangementDuration = Mathf.Max(0.2f, probabilityDuration * 0.6f); - bool mousePressed = Input.GetMouseButton(0); - - foreach (var point in waveformPoints) + foreach (var visual in focusVisuals.Values) { - point.Update(0f, waveformAmplitude, mousePos, mousePressed, mouseDragSpeed, interactionRadius); + if (visual == null || visual.particle == null) + continue; + + float localTime = focusTimer - visual.orderIndex * predictionSequenceGap; + float probabilityProgress = Mathf.Clamp01(localTime / baseInterval); + float arrangementProgress = Mathf.Clamp01(localTime / arrangementDuration); + + float probability = Mathf.Lerp(visual.probability, visual.targetProbability, probabilityProgress); + if (visual.probabilityLabel != null) + { + visual.probabilityLabel.text = $"{Mathf.RoundToInt(probability)}%"; + } + + float interval = Mathf.Lerp(0.05f, 0.8f, probabilityProgress); + visual.particle.SetChangeInterval(interval); + visual.particle.changeSpeedMultiplier = Mathf.Lerp(6f, 1f, probabilityProgress); + visual.particle.anxietyShakeIntensity = Mathf.Lerp(visual.startShakeIntensity, 0f, probabilityProgress); + + Vector3 targetPosition = Vector3.Lerp(visual.focusPosition, visual.finalPosition, Mathf.SmoothStep(0f, 1f, arrangementProgress)); + visual.particle.transform.position = targetPosition; + + if (!visual.stabilized && probabilityProgress >= 1f) + { + visual.particle.ForceSetCharacter(visual.finalChar); + visual.particle.SetChangeInterval(1.5f); + visual.particle.ResetChangeTimer(visual.particle.changeSpeedMultiplier); + visual.particle.isStatic = true; + visual.stabilized = true; + if (visual.probabilityLabel != null) + { + visual.probabilityLabel.text = "100%"; + } + } } - // 更新整体平静进度 - if (waveformPoints.Count > 0) + bool allStable = focusVisuals.Values.All(v => v.stabilized); + if (allStable) { - calmProgress = waveformPoints.Average(p => p.calmAmount); + CompleteFocusSequence(); } } + private void CompleteFocusSequence() + { + foreach (var visual in focusVisuals.Values) + { + if (visual.ringObject != null) + { + Destroy(visual.ringObject); + } + + if (visual.probabilityLabel != null) + { + Destroy(visual.probabilityLabel.gameObject); + } + + if (visual.particle != null) + { + visual.particle.transform.DOScale(Vector3.one * focusZoomScale, 0.3f).SetEase(Ease.OutQuad); + visual.particle.isStatic = true; + visual.particle.changeSpeedMultiplier = 1f; + } + } + + focusVisuals.Clear(); + + if (connectionRenderer != null) + { + connectionRenderer.TargetAlphaScale = 0f; + } + + completionPhase = CompletionPhase.Finished; + + if (focusSequenceButton != null) + { + focusSequenceButton.gameObject.SetActive(false); + } + } + + private void FadeOutTextParticle(TextParticle particle, float duration) + { + if (particle == null) return; + + DOTween.To(() => particle.alpha, value => particle.alpha = value, 0f, duration) + .SetEase(Ease.OutQuad); + + if (particle.glowSprite != null) + { + particle.glowSprite.DOFade(0f, duration); + } + } + + private Vector3 ComputeCentroid(List particles) + { + if (particles == null || particles.Count == 0) + { + return worldCanvas != null ? worldCanvas.transform.position : Vector3.zero; + } + + Vector3 sum = Vector3.zero; + foreach (var particle in particles) + { + sum += particle.transform.position; + } + + return sum / particles.Count; + } + private void UpdateInput() { - // 只在第一阶段(未完成)时处理鼠标输入 - if (completionPhase != CompletionPhase.None) return; + // 只在允许输入时处理鼠标逻辑 + if (!allowInput || completionPhase != CompletionPhase.None) return; Vector2 mouseWorldPos = GetMouseWorldPosition(); @@ -760,15 +990,33 @@ namespace AibisDream.MiniGame.Language isCompleted = false; completionPhase = CompletionPhase.None; completionTimer = 0f; + focusTimer = 0f; fadeOutAlpha = 1f; + allowInput = true; orderedRedParticles.Clear(); - targetPositions.Clear(); + focusVisuals.Clear(); + finalTargetCharacters.Clear(); + finalTargetPositions.Clear(); effectPropagations.Clear(); previousConnections.Clear(); initializationComplete = false; initializationFrames = 0; - calmProgress = 0f; - waveformPoints.Clear(); + autoPropagationTimer = 0f; + focusTweensCompleted = false; + + if (focusSequenceButton != null) + { + focusSequenceButton.onClick.RemoveListener(HandleFocusButtonClicked); + focusSequenceButton.onClick.AddListener(HandleFocusButtonClicked); + focusSequenceButton.interactable = false; + focusSequenceButton.gameObject.SetActive(false); + } + + if (connectionRenderer != null) + { + connectionRenderer.NonTargetAlphaScale = lineAlphaMultiplier; + connectionRenderer.TargetAlphaScale = 1f; + } // 重置所有粒子 foreach (var p in candidateParticles) @@ -782,13 +1030,153 @@ namespace AibisDream.MiniGame.Language p.vibrationOffset = Vector2.zero; p.velocity = new Vector2(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)); p.transform.position = GetRandomPositionInBounds(); - p.calmProgress = 0f; - p.dissolveProgress = 0f; p.isCalmed = false; - p.waveformPoint = null; + p.alpha = p.baseAlpha; + p.transform.localScale = Vector3.one; + p.transform.DOKill(); + + if (p.glowSprite != null) + { + Color glowColor = p.glowSprite.color; + glowColor.a = 0.2f; + p.glowSprite.color = glowColor; + } + } + + foreach (var floating in floatingParticles) + { + floating.isCalmed = false; + floating.alpha = floating.baseAlpha; + floating.transform.DOKill(); } SelectRedParticles(); + UpdateStatusUI(); + } + + private void ApplySeparationForces() + { + if (!enableSeparation || candidateParticles.Count <= 1) + return; + + float minSpacingSqr = minParticleSpacing * minParticleSpacing; + + for (int i = 0; i < candidateParticles.Count - 1; i++) + { + var particleA = candidateParticles[i]; + if (particleA.isStatic) + continue; + + Vector2 posA = particleA.transform.position; + + for (int j = i + 1; j < candidateParticles.Count; j++) + { + var particleB = candidateParticles[j]; + if (particleB.isStatic) + continue; + + Vector2 posB = particleB.transform.position; + Vector2 diff = posA - posB; + float distSqr = diff.sqrMagnitude; + + if (distSqr < minSpacingSqr && distSqr > 0.0001f) + { + float distance = Mathf.Sqrt(distSqr); + float pushStrength = 1f - (distance / minParticleSpacing); + Vector2 direction = diff / distance; + Vector2 push = direction * (pushStrength * separationForce); + + particleA.ApplyForce(push * Time.deltaTime); + particleB.ApplyForce(-push * Time.deltaTime); + } + } + } + } + + private void UpdateStatusUI() + { + if (integrationProgressText != null) + { + float ratio = CalculateIntegrationRatio(); + int percent = Mathf.RoundToInt(ratio * 100f); + integrationProgressText.text = $"{percent}% 语言整合完成度"; + } + + if (interferenceCountText != null) + { + int interferenceCount = CountInterferenceNodes(); + interferenceCountText.text = $"{interferenceCount} 处异常思绪仍在干扰表达"; + } + } + + private float CalculateIntegrationRatio() + { + if (targetParticles.Count == 0) + return 0f; + + HashSet visited = new HashSet(); + int largestCluster = 0; + + foreach (var red in targetParticles) + { + if (visited.Contains(red)) + continue; + + int clusterSize = 0; + Queue queue = new Queue(); + queue.Enqueue(red); + visited.Add(red); + + while (queue.Count > 0) + { + var current = queue.Dequeue(); + clusterSize++; + + foreach (var connected in current.connections) + { + if (connected == null || !connected.isRed) + continue; + + if (!visited.Contains(connected)) + { + visited.Add(connected); + queue.Enqueue(connected); + } + } + } + + if (clusterSize > largestCluster) + { + largestCluster = clusterSize; + } + } + + largestCluster = Mathf.Clamp(largestCluster, 0, targetParticles.Count); + return largestCluster / Mathf.Max(1f, targetParticles.Count); + } + + private int CountInterferenceNodes() + { + if (targetParticles.Count == 0) + return 0; + + HashSet interferingParticles = new HashSet(); + + foreach (var red in targetParticles) + { + foreach (var connected in red.connections) + { + if (connected == null) + continue; + + if (!connected.isRed) + { + interferingParticles.Add(connected); + } + } + } + + return interferingParticles.Count; } } } diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleSystem_Summary.md b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleSystem_Summary.md index 498976d24..cbe30d54d 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleSystem_Summary.md +++ b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleSystem_Summary.md @@ -38,61 +38,6 @@ ### 6. 游戏目标与完成序列 **目标**:让所有红色粒子连接成一个整体,且与蓝色粒子分离 -**完成序列**: -1. **arranging阶段**: - - 红色粒子排列成水平一行(屏幕中央) - - 蓝色/橙色粒子停止移动 - - 非红色粒子淡出 - -2. **revealing阶段**: - - 红色粒子逐个显示目标文字:"别过来我感觉害怕" - - 显示前快速闪烁随机字符 - - 文字开始剧烈抖动(焦虑效果) - -3. **completed阶段**: - - 进入波形交互阶段 - -### 7. 波形交互系统(核心创新) -**初始状态**: -- 每个文字下方有一个对应的波形点 -- 波形使用Perlin噪声生成(多层噪声叠加) -- 波形呈现异常、混乱的抖动 -- 文字随波形剧烈抖动+上下漂浮(表现焦虑情绪) - -**交互机制**: -- 玩家按住鼠标左键并拖动,抚平附近的波形 -- 波形有"抵抗力"机制: - - 异常程度越高,抵抗力越强 - - 需要持续按住30-90帧才能完全抚平 - - 拖动速度越快,抚平效果越强 -- 抚平区域有交互半径(50像素) -- 显示进度环反馈 - -**抚平效果**: -- 波形从异常噪波逐渐变为规律的正弦波 -- 波形颜色从红色渐变为蓝色 -- 对应文字的抖动和漂浮逐渐减弱 -- 波形完全抚平后: - - 文字停止变化和抖动 - - 文字开始消散(透明度降低) - - 波形点也随之消散 - -**视觉反馈**: -- 抚平时产生蓝色粒子效果 -- 鼠标周围显示交互范围圈 -- 波形点显示抚平进度环 -- 底部显示整体平静进度条 - -**情感隐喻**: -- 异常波形 = 焦虑、不安的情绪 -- 文字抖动 = 情绪的外在表现 -- 抚平波形 = 安抚、平复情绪的过程 -- 文字消散 = 负面情绪的消解 - -### 8. 重置机制 -- 所有文字消散后,游戏自动重置 -- 粒子重新随机分布 -- 重新选择8个红色目标粒子 ## Unity实现要点 diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/NeuralPropagationConfig.asset b/Assets/Scripts/MiniGame/HuoShan/Language/NeuralPropagationConfig.asset new file mode 100644 index 000000000..fb9de5da5 --- /dev/null +++ b/Assets/Scripts/MiniGame/HuoShan/Language/NeuralPropagationConfig.asset @@ -0,0 +1,32 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: aff5f43b9eb5c4242842ca3cc33afd99, type: 3} + m_Name: NeuralPropagationConfig + m_EditorClassIdentifier: + bluePropagations: + - startChar: "\u6628" + content: "\u6628\u5929\u6211\u78B0" + - startChar: "\u6211" + content: "\u544A\u8BC9\u6211" + anxietyWords: + - "\u5BB3\u6015" + - "\u600E\u4E48\u529E" + - "\u56DE\u907F" + - "\u7D27\u5F20" + - "\u7126\u8651" + - "\u62C5\u5FC3" + - "\u6050\u60E7" + - "\u4E0D\u5B89" + hiddenInfo: "\u82F1\u91CC" + bluePropagationSpeed: 1 + redOverrideInterval: 2 + propagationDistance: 2 diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/NeuralPropagationConfig.asset.meta b/Assets/Scripts/MiniGame/HuoShan/Language/NeuralPropagationConfig.asset.meta new file mode 100644 index 000000000..2912f490c --- /dev/null +++ b/Assets/Scripts/MiniGame/HuoShan/Language/NeuralPropagationConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3896a6c1f7dac79448309febc22689de +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/TextParticle.cs b/Assets/Scripts/MiniGame/HuoShan/Language/TextParticle.cs index 2466c0246..f0a8f515a 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/TextParticle.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/TextParticle.cs @@ -161,6 +161,32 @@ namespace AibisDream.MiniGame.Language textMesh.text = currentChar; } + public void ForceSetCharacter(string character) + { + currentChar = character; + UpdateText(); + } + + public void SetStaticState(bool value) + { + isStatic = value; + } + + public void SetCalmedState(bool value) + { + isCalmed = value; + } + + public void SetChangeInterval(float interval) + { + changeInterval = Mathf.Max(0.02f, interval); + } + + public void ResetChangeTimer(float timer) + { + changeTimer = timer; + } + protected virtual void UpdateVisuals() { if (textMesh != null) diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/WaveformRenderer.cs b/Assets/Scripts/MiniGame/HuoShan/Language/WaveformRenderer.cs index 89e84035c..0fd239ad2 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/WaveformRenderer.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/WaveformRenderer.cs @@ -6,6 +6,7 @@ namespace AibisDream.MiniGame.Language { /// /// 波形渲染器:使用Shapes绘制波形和交互效果 + /// 注意:渲染在底层,确保低于文字(文字 sortingOrder = 10) /// [ExecuteAlways] public class WaveformRenderer : ImmediateModeShapeDrawer @@ -83,13 +84,14 @@ namespace AibisDream.MiniGame.Language if (waveformPoints == null || waveformPoints.Count == 0) return; - if (completionPhase != CompletionPhase.Revealing && completionPhase != CompletionPhase.Completed) + if (completionPhase != CompletionPhase.Focusing) return; using (Draw.Command(cam)) { Draw.BlendMode = ShapesBlendMode.Transparent; Draw.ZTest = UnityEngine.Rendering.CompareFunction.Always; + // 注意:波形默认在底层,文字的 sortingOrder = 10 会显示在上层 // 绘制粒子效果 DrawWaveformParticles();