diff --git a/Assets/Unheimlich_POC/Datas/DialogueAnimationPresets/NPCDialogueAnimation.asset b/Assets/Unheimlich_POC/Datas/DialogueAnimationPresets/NPCDialogueAnimation.asset index 9aea9e46..b2d0ec1b 100644 --- a/Assets/Unheimlich_POC/Datas/DialogueAnimationPresets/NPCDialogueAnimation.asset +++ b/Assets/Unheimlich_POC/Datas/DialogueAnimationPresets/NPCDialogueAnimation.asset @@ -13,6 +13,7 @@ MonoBehaviour: m_Name: NPCDialogueAnimation m_EditorClassIdentifier: SpawnMode: 0 + DefaultScaleMultiplier: 1.2 SpawnRadius: 100 SpawnHeightOffset: 0 FacePlayer: 1 @@ -48,6 +49,7 @@ MonoBehaviour: ShakeRadius: 10 ShakeSpeed: 20 ShakeSmoothness: 20 + UnfocusedShakeSpeedMultiplier: 0.2 EnableGhostTrail: 1 GhostTrailDelay: 0.08 GhostTrailAlpha: 0.3 @@ -61,6 +63,18 @@ MonoBehaviour: MaxDistance: 10 MinScaleMultiplier: 1 MaxScaleMultiplier: 2 - MoveTowardPlayerOnFocus: 1 + MoveTowardPlayerOnFocus: 0 FocusDistanceFromPlayer: 1.5 FocusRotationSpeed: 10 + EnableCameraShakeOnFocus: 0 + CameraShakeIntensity: 5 + CameraShakeSmoothness: 10 + FocusedTextScaleMultiplier: 0.85 + FocusedTextScaleDuration: 1 + UnfocusedTextScaleDuration: 0.5 + EnableSketchFadeOnFocus: 1 + SketchMaterial: {fileID: 2100000, guid: 99ea3f23aff6ad849bb065a69a016520, type: 2} + SketchDuration: 1 + FadeOutDuration: 0.4 + SketchIntensity: 0.8 + FinalFadeAlpha: 0.4 diff --git a/Assets/Unheimlich_POC/Materials/Shaders/Text.meta b/Assets/Unheimlich_POC/Materials/Shaders/Text.meta new file mode 100644 index 00000000..c2fcb089 --- /dev/null +++ b/Assets/Unheimlich_POC/Materials/Shaders/Text.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7be34619def8beb4dac18417794d4fe2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Unheimlich_POC/Materials/Shaders/Text/UI_ScribbleEffect.shader b/Assets/Unheimlich_POC/Materials/Shaders/Text/UI_ScribbleEffect.shader new file mode 100644 index 00000000..fab20dd2 --- /dev/null +++ b/Assets/Unheimlich_POC/Materials/Shaders/Text/UI_ScribbleEffect.shader @@ -0,0 +1,107 @@ +Shader "Unheimlich/UI/ScribbleEffect" +{ + Properties + { + _Color("Scribble Color", Color) = (0.2, 0.2, 0.2, 1) + _Progress("Draw Progress", Range(0, 1)) = 0 + _LineThickness("Thickness", Range(0.005, 0.25)) = 0.02 + _ZigzagFreq("Zigzag Frequency", Float) = 10 + _ZigzagAmp("Zigzag Amplitude", Range(0, 0.3)) = 0.1 + _DiagonalSlant("Diagonal Slant", Range(-1, 1)) = 0.4 + _NumPasses("Number of Passes", Range(1, 4)) = 2 + _Roundness("Roundness", Range(0, 1)) = 0.6 + } + + SubShader + { + Tags { "Queue" = "Transparent" "RenderType" = "Transparent" } + Blend SrcAlpha OneMinusSrcAlpha + ZWrite Off + Cull Off + + Pass + { + HLSLPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" + + struct Attributes + { + float4 positionOS : POSITION; + float2 uv : TEXCOORD0; + }; + + struct Varyings + { + float4 positionCS : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(UnityPerMaterial) + half4 _Color; + half _Progress; + half _LineThickness; + half _ZigzagFreq; + half _ZigzagAmp; + half _DiagonalSlant; + int _NumPasses; + half _Roundness; + CBUFFER_END + + Varyings vert(Attributes input) + { + Varyings output; + output.positionCS = TransformObjectToHClip(input.positionOS.xyz); + output.uv = input.uv; + return output; + } + + float DrawSlantedZigzag(float2 uv, float yOffset, float passProgress) + { + float diagonalProgress = uv.x + uv.y * _DiagonalSlant; + + float zigzagCenter = yOffset + sin(diagonalProgress * _ZigzagFreq * 6.28318) * _ZigzagAmp; + + float progressEnd = passProgress * 1.6; + float progressMask = smoothstep(progressEnd + 0.15, progressEnd - 0.05, diagonalProgress); + + float fadeIn = smoothstep(-0.1, 0.2, diagonalProgress); + float capMask = fadeIn * progressMask; + + float distanceToCenter = abs(uv.y - zigzagCenter); + + float edgeSoftness = _LineThickness * (1.0 - _Roundness * 0.7); + float zigzagStrength = 1.0 - smoothstep(edgeSoftness, _LineThickness, distanceToCenter); + + zigzagStrength = pow(zigzagStrength, 1.0 + _Roundness * 3.5); + + return zigzagStrength * capMask; + } + + half4 frag(Varyings input) : SV_Target + { + float totalMask = 0.0; + + totalMask += DrawSlantedZigzag(input.uv, 0.5, _Progress); + + if (_NumPasses >= 2) + totalMask += DrawSlantedZigzag(input.uv, 0.52, _Progress * 0.95); + + if (_NumPasses >= 3) + totalMask += DrawSlantedZigzag(input.uv, 0.48, _Progress * 0.9); + + if (_NumPasses >= 4) + totalMask += DrawSlantedZigzag(input.uv, 0.54, _Progress * 0.85); + + totalMask = saturate(totalMask); + + half4 result = _Color; + result.a *= totalMask; + + return result; + } + ENDHLSL + } + } +} diff --git a/Assets/Unheimlich_POC/Materials/Shaders/Text/UI_ScribbleEffect.shader.meta b/Assets/Unheimlich_POC/Materials/Shaders/Text/UI_ScribbleEffect.shader.meta new file mode 100644 index 00000000..70880632 --- /dev/null +++ b/Assets/Unheimlich_POC/Materials/Shaders/Text/UI_ScribbleEffect.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 10b6f40fb2f72a942a493008573d70d3 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Unheimlich_POC/Materials/Text.meta b/Assets/Unheimlich_POC/Materials/Text.meta new file mode 100644 index 00000000..5320f720 --- /dev/null +++ b/Assets/Unheimlich_POC/Materials/Text.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ad07d82f93823d34782937b358daafde +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Unheimlich_POC/Materials/Text/ScribbleMaterial.mat b/Assets/Unheimlich_POC/Materials/Text/ScribbleMaterial.mat new file mode 100644 index 00000000..d6c84270 --- /dev/null +++ b/Assets/Unheimlich_POC/Materials/Text/ScribbleMaterial.mat @@ -0,0 +1,167 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: ScribbleMaterial + m_Shader: {fileID: 4800000, guid: 10b6f40fb2f72a942a493008573d70d3, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _FaceTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 28684132378477856, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _ColorMask: 15 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DiagonalAngle: 0.33 + - _DiagonalSlant: -0.22 + - _DistortionStrength: 0.01 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _FaceDilate: 0 + - _FadeAlpha: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _LineThickness: 0.25 + - _Metallic: 0 + - _NoiseScale: 50 + - _NumLines: 1 + - _NumPasses: 4 + - _OcclusionStrength: 1 + - _OutlineWidth: 0 + - _Parallax: 0.005 + - _Progress: 1 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Roundness: 1 + - _SketchIntensity: 1 + - _SketchLineWidth: 0.02 + - _SketchNoiseScale: 20 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Stencil: 0 + - _StencilComp: 8 + - _StencilOp: 0 + - _StencilReadMask: 255 + - _StencilWriteMask: 255 + - _Surface: 0 + - _WeightBold: 0.5 + - _WeightNormal: 0 + - _WorkflowMode: 1 + - _XRMotionVectorsPass: 1 + - _ZWrite: 1 + - _ZigzagAmp: 0.167 + - _ZigzagFreq: 6 + m_Colors: + - _BaseColor: {r: 1, g: 1, b: 1, a: 1} + - _Color: {r: 1, g: 0.03459108, b: 0.03459108, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _FaceColor: {r: 1, g: 1, b: 1, a: 1} + - _OutlineColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &2128116555526520463 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion + version: 10 diff --git a/Assets/Unheimlich_POC/Materials/Text/ScribbleMaterial.mat.meta b/Assets/Unheimlich_POC/Materials/Text/ScribbleMaterial.mat.meta new file mode 100644 index 00000000..2d8f34d1 --- /dev/null +++ b/Assets/Unheimlich_POC/Materials/Text/ScribbleMaterial.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 99ea3f23aff6ad849bb065a69a016520 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Unheimlich_POC/Scenes/MainScene/MainScene.unity b/Assets/Unheimlich_POC/Scenes/MainScene/MainScene.unity index 05a0c666..cbfe349d 100644 --- a/Assets/Unheimlich_POC/Scenes/MainScene/MainScene.unity +++ b/Assets/Unheimlich_POC/Scenes/MainScene/MainScene.unity @@ -1963,7 +1963,7 @@ PrefabInstance: objectReference: {fileID: 1871114643} - target: {fileID: 5019363637456399335, guid: 204c7983537bb6c44a8ecafa782fca99, type: 3} propertyPath: m_VersionIndex - value: 10300 + value: 10303 objectReference: {fileID: 0} - target: {fileID: 6096226974748024133, guid: 204c7983537bb6c44a8ecafa782fca99, type: 3} propertyPath: m_Mesh @@ -2023,7 +2023,7 @@ PrefabInstance: objectReference: {fileID: 183470776} - target: {fileID: 8474092010221730167, guid: 204c7983537bb6c44a8ecafa782fca99, type: 3} propertyPath: m_VersionIndex - value: 25624 + value: 25627 objectReference: {fileID: 0} - target: {fileID: 9107982868375055835, guid: 204c7983537bb6c44a8ecafa782fca99, type: 3} propertyPath: m_Mesh diff --git a/Assets/Unheimlich_POC/Scripts/Datas/Dialogues/DialogueAnimationSettings.cs b/Assets/Unheimlich_POC/Scripts/Datas/Dialogues/DialogueAnimationSettings.cs index d78c4ca3..2a205f89 100644 --- a/Assets/Unheimlich_POC/Scripts/Datas/Dialogues/DialogueAnimationSettings.cs +++ b/Assets/Unheimlich_POC/Scripts/Datas/Dialogues/DialogueAnimationSettings.cs @@ -13,6 +13,9 @@ namespace Unheimlich.Data.Dialogues [Tooltip("How choices appear in the world")] public ChoiceSpawnMode SpawnMode = ChoiceSpawnMode.AroundAnchor; + [Tooltip("Default scale multiplier for spawned choices (1.0 = normal size)")] + public float DefaultScaleMultiplier = 1.0f; + [Header("Spawn Settings")] [Tooltip("Radius around anchor where choices spawn")] public float SpawnRadius = 2f; @@ -53,6 +56,10 @@ namespace Unheimlich.Data.Dialogues [Tooltip("Smoothness of shake transitions (higher = smoother)")] public float ShakeSmoothness = 2f; + [Tooltip("Shake speed multiplier for unfocused choices when one is focused (0.3 = 30% speed)")] + [Range(0f, 1f)] + public float UnfocusedShakeSpeedMultiplier = 0.3f; + [Header("Ghost Trail Effect")] [Tooltip("Enable ghost/shadow trail effect behind text (Heavy Rain style)")] public bool EnableGhostTrail = false; @@ -105,6 +112,46 @@ namespace Unheimlich.Data.Dialogues [Tooltip("Rotation speed when a choice is focused (degrees per second). Higher = faster rotation.")] public float FocusRotationSpeed = 60f; + + [Tooltip("Enable camera shake when focusing on a choice")] + public bool EnableCameraShakeOnFocus = true; + + [Tooltip("Camera shake intensity when focusing (higher = less shake, lower = more shake)")] + public float CameraShakeIntensity = 30f; + + [Tooltip("Camera shake smoothness (0 = instant/jarring, higher = smoother). Controls how fast the camera moves to shake position.")] + public float CameraShakeSmoothness = 10f; + + [Tooltip("Scale down multiplier for the FOCUSED choice text (0.9 = 90% size)")] + [Range(0f, 1f)] + public float FocusedTextScaleMultiplier = 0.9f; + + [Tooltip("Duration of the text scale animation when focusing (in seconds)")] + public float FocusedTextScaleDuration = 0.3f; + + [Tooltip("Duration of the text scale reset animation when unfocusing (in seconds)")] + public float UnfocusedTextScaleDuration = 0.3f; + + [Header("Sketch & Fade on Selection")] + [Tooltip("Enable sketch and fade effect on non-selected words when a choice is focused")] + public bool EnableSketchFadeOnFocus = false; + + [Tooltip("Material with sketch shader for TextMeshPro")] + public Material SketchMaterial; + + [Tooltip("Duration of the sketch effect before fade (in seconds)")] + public float SketchDuration = 0.3f; + + [Tooltip("Duration of the fade out after sketch (in seconds)")] + public float FadeOutDuration = 0.5f; + + [Tooltip("Intensity of the sketch effect (0 = no sketch, 1 = full sketch)")] + [Range(0f, 1f)] + public float SketchIntensity = 0.8f; + + [Tooltip("Final alpha value after fade (0 = invisible, 1 = opaque)")] + [Range(0f, 1f)] + public float FinalFadeAlpha = 0.2f; } public enum ChoiceSpawnMode diff --git a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerLook.cs b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerLook.cs index d87559d9..3059821b 100644 --- a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerLook.cs +++ b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerLook.cs @@ -38,6 +38,9 @@ namespace Unheimlich.Gameplay.Player [SerializeField] private float _shakeFrequency = 40f; private float _shakeInterval; private float _shakeTimer = 0f; + private Vector3 _shakeTarget = Vector3.zero; + private Vector3 _shakeOriginalPos = Vector3.zero; + private float _shakeSmoothness = 0f; private float _horizontalRotation; private float _verticalRotation; @@ -140,20 +143,31 @@ namespace Unheimlich.Gameplay.Player /// Creates a random camera shake effect for feedback (e.g., impacts, events). /// Uses a timer-based interval to control shake frequency. /// Amplitude controls both the intensity and duration of the shake. + /// Smoothness controls how quickly the camera interpolates to the shake position (0 = instant, higher = smoother). /// - public void Shake(float amplitude) + public void Shake(float amplitude, float smoothness = 0f) { + _shakeSmoothness = smoothness; _shakeTimer -= Time.deltaTime; if (_shakeTimer <= 0f) { - _shakeTimer = _shakeInterval * amplitude; + _shakeTimer = _shakeInterval * amplitude; + _shakeOriginalPos = _playerCameraPivot.position; float x = Random.Range(-1f, 1f) * amplitude; float y = Random.Range(-1f, 1f) * amplitude; - Vector3 originalPos = _playerCameraPivot.position; - _playerCameraPivot.position = originalPos + new Vector3(x, y, 0f); + _shakeTarget = _shakeOriginalPos + new Vector3(x, y, 0f); + } + + if (_shakeSmoothness > 0f) + { + _playerCameraPivot.position = Vector3.Lerp(_playerCameraPivot.position, _shakeTarget, Time.deltaTime * _shakeSmoothness); + } + else + { + _playerCameraPivot.position = _shakeTarget; } } diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/ChoiceFocusHandler.cs b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/ChoiceFocusHandler.cs index 67e49f69..2261f2f0 100644 --- a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/ChoiceFocusHandler.cs +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/ChoiceFocusHandler.cs @@ -57,18 +57,23 @@ namespace Unheimlich.Interactions.FloatingWords private int _originalSortingOrder; private Vector3 _startLocalPos; private float _originalScale; + private float _originalTextScale = 1f; + private int _textScaleTweenId = -1; private bool _positionCaptured = false; private Vector3 _shakeOffset = Vector3.zero; private Vector3 _shakeTarget = Vector3.zero; private float _shakeTimer = 0f; private bool _isShaking = false; + private float _shakeSpeedMultiplier = 1f; private bool _disableFloatingEffects = false; private Queue _positionHistory = new Queue(); private float _historyTimer = 0f; private const float HISTORY_SAMPLE_RATE = 0.016f; + + private SketchFadeEffect _sketchFadeEffect; void Awake() { @@ -169,7 +174,7 @@ namespace Unheimlich.Interactions.FloatingWords if (!GetEnableOrganicShake()) return; - _shakeTimer += Time.deltaTime * GetShakeSpeed(); + _shakeTimer += Time.deltaTime * GetShakeSpeed() * _shakeSpeedMultiplier; if (_shakeTimer >= 1f) { @@ -204,6 +209,29 @@ namespace Unheimlich.Interactions.FloatingWords _shakeOffset = Vector3.zero; } + public void SetShakeSpeedMultiplier(float multiplier) + { + _shakeSpeedMultiplier = Mathf.Max(0f, multiplier); + } + + public void SetTextScale(float targetScale, float duration = 0.3f) + { + if (_choiceTextComponent == null) + return; + + if (_textScaleTweenId >= 0) + LeanTween.cancel(_textScaleTweenId); + + _textScaleTweenId = LeanTween.scale(_choiceTextComponent.gameObject, Vector3.one * targetScale, duration) + .setEase(LeanTweenType.easeOutQuad) + .id; + } + + public void ResetTextScale(float duration = 0.3f) + { + SetTextScale(_originalTextScale, duration); + } + public void HideGhostTrail() { if (_ghostTextComponent != null) @@ -230,6 +258,11 @@ namespace Unheimlich.Interactions.FloatingWords LeanTween.cancel(gameObject); StopOrganicShake(); + + DialogueAnimationSettings settings = GetAnimationSettings(); + float textScaleMultiplier = settings != null ? settings.FocusedTextScaleMultiplier : 0.9f; + float textScaleDuration = settings != null ? settings.FocusedTextScaleDuration : 0.3f; + SetTextScale(textScaleMultiplier, textScaleDuration); } transform.localScale = Vector3.one * Mathf.Lerp(_originalScale, MAX_FOCUS_SCALE, progress); @@ -374,7 +407,10 @@ namespace Unheimlich.Interactions.FloatingWords } _startLocalPos = transform.localPosition; - _originalScale = transform.localScale.x; + _originalScale = transform.localScale.x; + + if (_choiceTextComponent != null) + _originalTextScale = _choiceTextComponent.transform.localScale.x; LeanTween.cancel(gameObject); @@ -383,6 +419,13 @@ namespace Unheimlich.Interactions.FloatingWords if (_ghostTextComponent != null) _ghostTextComponent.text = GetChoiceText(); + DialogueAnimationSettings settings = GetAnimationSettings(); + if (settings != null && settings.EnableSketchFadeOnFocus && settings.SketchMaterial != null) + { + _sketchFadeEffect = gameObject.AddComponent(); + _sketchFadeEffect.Initialize(_choiceTextComponent, _ghostTextComponent, settings.SketchMaterial); + } + StartOrganicShake(); } @@ -465,8 +508,14 @@ namespace Unheimlich.Interactions.FloatingWords /// public void ResetFeedback(float duration) { - LeanTween.cancel(gameObject); + LeanTween.cancel(gameObject); + + if (_textScaleTweenId >= 0) + LeanTween.cancel(_textScaleTweenId); + StopOrganicShake(); + + ResetSketchFade(); LeanTween.moveLocal(gameObject, _startLocalPos, duration) .setEase(LeanTweenType.easeOutQuad) @@ -479,10 +528,11 @@ namespace Unheimlich.Interactions.FloatingWords StartOrganicShake(); }); - transform.localScale = Vector3.one; - + transform.localScale = Vector3.one * _originalScale; + if (_choiceTextComponent != null) { + _choiceTextComponent.transform.localScale = Vector3.one * _originalTextScale; _choiceTextComponent.color = Color.white; if (_ghostTextComponent != null) @@ -497,5 +547,24 @@ namespace Unheimlich.Interactions.FloatingWords if (_canvas != null) _canvas.sortingOrder = _originalSortingOrder; } + + public void ApplySketchFade() + { + DialogueAnimationSettings settings = GetAnimationSettings(); + if (settings == null || !settings.EnableSketchFadeOnFocus || _sketchFadeEffect == null) + return; + + _sketchFadeEffect.PlaySketchFadeEffect( + settings.SketchDuration, + settings.FadeOutDuration, + settings.FinalFadeAlpha + ); + } + + public void ResetSketchFade() + { + if (_sketchFadeEffect != null) + _sketchFadeEffect.ResetEffect(); + } } } \ No newline at end of file diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/FloatingWordsSpawner.cs b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/FloatingWordsSpawner.cs index adbea6d6..5a400c43 100644 --- a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/FloatingWordsSpawner.cs +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/FloatingWordsSpawner.cs @@ -41,6 +41,7 @@ namespace Unheimlich.Interactions.FloatingWords public bool MoveTowardPlayerOnFocus; public float FocusDistanceFromPlayer; public bool DisableFloatingEffects; + public float DefaultScaleMultiplier; } private static SpawnConfig GetConfig(ObjectDescriptionSettings settings) @@ -68,7 +69,8 @@ namespace Unheimlich.Interactions.FloatingWords ShakeSmoothness = settings.ShakeSmoothness, MoveTowardPlayerOnFocus = false, FocusDistanceFromPlayer = 1f, - DisableFloatingEffects = true + DisableFloatingEffects = true, + DefaultScaleMultiplier = 1f }; } @@ -97,7 +99,8 @@ namespace Unheimlich.Interactions.FloatingWords ShakeSmoothness = settings.ShakeSmoothness, MoveTowardPlayerOnFocus = settings.MoveTowardPlayerOnFocus, FocusDistanceFromPlayer = settings.FocusDistanceFromPlayer, - DisableFloatingEffects = false + DisableFloatingEffects = false, + DefaultScaleMultiplier = settings.DefaultScaleMultiplier }; } @@ -145,6 +148,7 @@ namespace Unheimlich.Interactions.FloatingWords ChoiceFocusHandler word = Object.Instantiate(wordPrefab, parent); word.transform.localPosition = localOffset; word.transform.localRotation = Quaternion.identity; + word.transform.localScale = Vector3.one * config.DefaultScaleMultiplier; CanvasGroup canvasGroup = word.GetComponent(); if (canvasGroup == null) @@ -170,6 +174,7 @@ namespace Unheimlich.Interactions.FloatingWords ChoiceFocusHandler word = Object.Instantiate(prefab, parent); word.transform.localPosition = Vector3.up * i * 50f; word.transform.localRotation = Quaternion.identity; + word.transform.localScale = Vector3.one * config.DefaultScaleMultiplier; word.Setup(choices[i], config); words.Add(word); } @@ -188,6 +193,7 @@ namespace Unheimlich.Interactions.FloatingWords Vector3 offset = playerForward * 2f + Vector3.up * (i - choices.Count / 2f) * (config.SpawnRadius / choices.Count / 100f); word.transform.position = Player.Instance.transform.position + offset; word.transform.LookAt(Player.Instance.transform); + word.transform.localScale = Vector3.one * config.DefaultScaleMultiplier; word.Setup(choices[i], config); words.Add(word); } @@ -213,6 +219,7 @@ namespace Unheimlich.Interactions.FloatingWords ChoiceFocusHandler word = Object.Instantiate(prefab, parent); word.transform.localPosition = localOffset; word.transform.LookAt(Player.Instance.transform); + word.transform.localScale = Vector3.one * config.DefaultScaleMultiplier; word.Setup(choices[i], config); words.Add(word); } diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs new file mode 100644 index 00000000..6ef33419 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs @@ -0,0 +1,223 @@ +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +namespace Unheimlich.Interactions.FloatingWords +{ + public class SketchFadeEffect : MonoBehaviour + { + private TextMeshProUGUI _textComponent; + private TextMeshProUGUI _ghostTextComponent; + private Image _scribbleOverlay; + private Material _scribbleMaterialInstance; + private Material _scribbleMaterialTemplate; + + private static readonly int ProgressID = Shader.PropertyToID("_Progress"); + + private bool _isEffectActive = false; + private bool _overlayCreated = false; + private Color _originalTextColor; + private Color _originalGhostColor; + + private int _scribbleTweenId = -1; + private int _fadeTweenId = -1; + + public void Initialize(TextMeshProUGUI textComponent, TextMeshProUGUI ghostComponent, Material scribbleMaterial) + { + _textComponent = textComponent; + _ghostTextComponent = ghostComponent; + _scribbleMaterialTemplate = scribbleMaterial; + _originalTextColor = _textComponent.color; + + if (_ghostTextComponent != null) + _originalGhostColor = _ghostTextComponent.color; + } + + private void CreateScribbleOverlay() + { + if (_overlayCreated || _scribbleMaterialTemplate == null || _textComponent == null) + return; + + GameObject overlayObject = new GameObject("ScribbleOverlay"); + overlayObject.transform.SetParent(_textComponent.transform, false); + + _scribbleOverlay = overlayObject.AddComponent(); + _scribbleOverlay.raycastTarget = false; + + RectTransform overlayRect = overlayObject.GetComponent(); + RectTransform textRect = _textComponent.GetComponent(); + + _textComponent.ForceMeshUpdate(); + + Vector2 textSize = _textComponent.GetRenderedValues(false); + + float horizontalPadding = 1.2f; + float verticalPadding = 0.8f; + Vector2 paddedSize = new Vector2( + textSize.x + horizontalPadding, + textSize.y + verticalPadding + ); + + overlayRect.anchorMin = new Vector2(0.5f, 0.5f); + overlayRect.anchorMax = new Vector2(0.5f, 0.5f); + overlayRect.pivot = new Vector2(0.5f, 0.5f); + + float leftOffset = -horizontalPadding * 0.9f; + overlayRect.anchoredPosition = new Vector2(leftOffset, 0f); + overlayRect.sizeDelta = paddedSize; + + overlayRect.localScale = Vector3.one; + + _scribbleMaterialInstance = new Material(_scribbleMaterialTemplate); + _scribbleMaterialInstance.SetFloat(ProgressID, 0f); + _scribbleOverlay.material = _scribbleMaterialInstance; + + _scribbleOverlay.gameObject.SetActive(false); + + _overlayCreated = true; + } + + public void PlaySketchFadeEffect(float scribbleDuration, float fadeDuration, float finalAlpha) + { + if (_isEffectActive || _textComponent == null) + return; + + CancelActiveTweens(); + + CreateScribbleOverlay(); + + if (_scribbleOverlay == null || _scribbleMaterialInstance == null) + return; + + _scribbleOverlay.gameObject.SetActive(true); + + _isEffectActive = true; + + _scribbleTweenId = LeanTween.value(gameObject, 0f, 1f, scribbleDuration) + .setEase(LeanTweenType.easeInOutQuad) + .setOnUpdate((float val) => { + if (_scribbleMaterialInstance != null) + _scribbleMaterialInstance.SetFloat(ProgressID, val); + }) + .setOnComplete(() => { + _scribbleTweenId = -1; + _fadeTweenId = LeanTween.value(gameObject, _originalTextColor.a, finalAlpha, fadeDuration) + .setEase(LeanTweenType.easeInQuad) + .setOnUpdate((float val) => { + if (_textComponent != null) + { + Color textColor = _textComponent.color; + textColor.a = val; + _textComponent.color = textColor; + } + + if (_ghostTextComponent != null) + { + Color ghostColor = _ghostTextComponent.color; + ghostColor.a = val * (_originalGhostColor.a / _originalTextColor.a); + _ghostTextComponent.color = ghostColor; + } + + if (_scribbleOverlay != null) + { + Color scribbleColor = _scribbleOverlay.color; + scribbleColor.a = val; + _scribbleOverlay.color = scribbleColor; + } + }) + .setOnComplete(() => { + _fadeTweenId = -1; + }).id; + }).id; + } + + public void ResetEffect() + { + CancelActiveTweens(); + + if (_scribbleMaterialInstance != null) + { + float currentProgress = _scribbleMaterialInstance.GetFloat(ProgressID); + + if (currentProgress > 0f) + { + _scribbleTweenId = LeanTween.value(gameObject, currentProgress, 0f, 0.3f) + .setEase(LeanTweenType.easeOutQuad) + .setOnUpdate((float val) => { + if (_scribbleMaterialInstance != null) + _scribbleMaterialInstance.SetFloat(ProgressID, val); + }) + .setOnComplete(() => { + _scribbleTweenId = -1; + if (_scribbleOverlay != null) + _scribbleOverlay.gameObject.SetActive(false); + }).id; + } + } + + if (_textComponent != null) + { + float currentAlpha = _textComponent.color.a; + + if (currentAlpha < _originalTextColor.a) + { + _fadeTweenId = LeanTween.value(gameObject, currentAlpha, _originalTextColor.a, 0.3f) + .setEase(LeanTweenType.easeOutQuad) + .setOnUpdate((float val) => { + if (_textComponent != null) + { + Color c = _textComponent.color; + c.a = val; + _textComponent.color = c; + } + + if (_ghostTextComponent != null) + { + Color c = _ghostTextComponent.color; + c.a = val * (_originalGhostColor.a / _originalTextColor.a); + _ghostTextComponent.color = c; + } + }) + .setOnComplete(() => { + _fadeTweenId = -1; + }).id; + } + } + + if (_scribbleOverlay != null) + { + Color c = _scribbleOverlay.color; + c.a = 1f; + _scribbleOverlay.color = c; + } + + _isEffectActive = false; + } + + private void CancelActiveTweens() + { + if (_scribbleTweenId != -1) + { + LeanTween.cancel(_scribbleTweenId); + _scribbleTweenId = -1; + } + + if (_fadeTweenId != -1) + { + LeanTween.cancel(_fadeTweenId); + _fadeTweenId = -1; + } + } + + private void OnDestroy() + { + CancelActiveTweens(); + + if (_scribbleMaterialInstance != null) + Destroy(_scribbleMaterialInstance); + + if (_scribbleOverlay != null) + Destroy(_scribbleOverlay.gameObject); + } + } +} diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs.meta b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs.meta new file mode 100644 index 00000000..ff6aadee --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0ee81e39cd832e34d9219099af39f4bb \ No newline at end of file diff --git a/Assets/Unheimlich_POC/Scripts/Managers/DialogueFocusSystem.cs b/Assets/Unheimlich_POC/Scripts/Managers/DialogueFocusSystem.cs index 537931bd..2ad25339 100644 --- a/Assets/Unheimlich_POC/Scripts/Managers/DialogueFocusSystem.cs +++ b/Assets/Unheimlich_POC/Scripts/Managers/DialogueFocusSystem.cs @@ -30,7 +30,7 @@ namespace Unheimlich.Managers private readonly float _raycastInterval; private readonly float _recoveryDuration; private readonly float _focusRetentionTime; - private readonly float _shakeIntensity; + private readonly float _fallbackShakeIntensity; private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f); @@ -46,7 +46,7 @@ namespace Unheimlich.Managers float raycastInterval, float recoveryDuration, float focusRetentionTime, - float shakeIntensity) + float fallbackShakeIntensity) { _manager = manager; _mainCamera = mainCamera; @@ -57,7 +57,7 @@ namespace Unheimlich.Managers _raycastInterval = raycastInterval; _recoveryDuration = recoveryDuration; _focusRetentionTime = focusRetentionTime; - _shakeIntensity = shakeIntensity; + _fallbackShakeIntensity = fallbackShakeIntensity; } /// @@ -87,7 +87,9 @@ namespace Unheimlich.Managers if (_isFocusLocked) { if (_retentionTimer > 0f) - _retentionTimer -= Time.deltaTime; + _retentionTimer -= Time.deltaTime; + else + _isFocusLocked = false; return; } @@ -115,8 +117,8 @@ namespace Unheimlich.Managers { if (_currentFocusedHandler != null) { - _isFocusLocked = true; - _currentFocusedHandler.ResetFeedback(_recoveryDuration); + _currentFocusedHandler.ResetFeedback(_recoveryDuration); + ResetAllUnfocusEffects(); } _currentFocusedHandler = hitHandler; @@ -126,6 +128,8 @@ namespace Unheimlich.Managers if (_playerLook != null) _playerLook.LockAim(true); + ApplyUnfocusEffectToOthers(hitHandler); + DialogueAnimationSettings settings = _manager.CurrentAnimationSettings; bool shouldRotate = settings == null || settings.EnableRotation; if (shouldRotate && _manager.CurrentDialogueUI != null) @@ -149,18 +153,24 @@ namespace Unheimlich.Managers _currentFocusedHandler.ApplyFocusFeedback(progress); - if (_playerLook != null) - _playerLook.Shake(progress / _shakeIntensity); + DialogueAnimationSettings currentSettings = _manager.CurrentAnimationSettings; + bool shouldShake = currentSettings == null || currentSettings.EnableCameraShakeOnFocus; + + if (shouldShake && _playerLook != null) + { + float shakeIntensity = currentSettings != null ? currentSettings.CameraShakeIntensity : _fallbackShakeIntensity; + float shakeSmoothness = currentSettings != null ? currentSettings.CameraShakeSmoothness : 10f; + _playerLook.Shake(progress / shakeIntensity, shakeSmoothness); + } if (_focusTimer >= _focusTime) { - _isFocusLocked = true; - _currentFocusedHandler.ResetFeedback(_recoveryDuration); + ChoiceFocusHandler selectedHandler = _currentFocusedHandler; - if (_currentFocusedHandler.DialogueData != null) + if (selectedHandler.DialogueData != null) { - OnChoiceMade?.Invoke(_currentFocusedHandler.DialogueData); - DialogueNode nextNode = _currentFocusedHandler.GetNextNode(); + OnChoiceMade?.Invoke(selectedHandler.DialogueData); + DialogueNode nextNode = selectedHandler.GetNextNode(); _manager.GoToNextNode(nextNode); } @@ -173,6 +183,9 @@ namespace Unheimlich.Managers } ResetAnimationTimers(); + ResetAllUnfocusEffects(); + + selectedHandler.ResetFeedback(_recoveryDuration); if (_playerLook != null) _playerLook.LockAim(false); @@ -180,9 +193,8 @@ namespace Unheimlich.Managers } else if (_currentFocusedHandler != null && _retentionTimer <= 0f) { - _isFocusLocked = true; - - _currentFocusedHandler.ResetFeedback(_recoveryDuration); + _currentFocusedHandler.ResetFeedback(_recoveryDuration); + ResetAllUnfocusEffects(); DialogueAnimationSettings settings = _manager.CurrentAnimationSettings; bool shouldRotate = settings == null || settings.EnableRotation; @@ -198,6 +210,41 @@ namespace Unheimlich.Managers _playerLook.LockAim(false); } } + + private void ApplyUnfocusEffectToOthers(ChoiceFocusHandler focusedHandler) + { + if (_manager.CurrentDialogueUI == null || _manager.CurrentDialogueUI.ChoiceContainer == null) + return; + + DialogueAnimationSettings settings = _manager.CurrentAnimationSettings; + float shakeSlowdown = settings != null ? settings.UnfocusedShakeSpeedMultiplier : 0.3f; + + foreach (Transform child in _manager.CurrentDialogueUI.ChoiceContainer.transform) + { + ChoiceFocusHandler handler = child.GetComponent(); + if (handler != null && handler != focusedHandler) + { + handler.SetShakeSpeedMultiplier(shakeSlowdown); + handler.ApplySketchFade(); + } + } + } + + private void ResetAllUnfocusEffects() + { + if (_manager.CurrentDialogueUI == null || _manager.CurrentDialogueUI.ChoiceContainer == null) + return; + + foreach (Transform child in _manager.CurrentDialogueUI.ChoiceContainer.transform) + { + ChoiceFocusHandler handler = child.GetComponent(); + if (handler != null) + { + handler.SetShakeSpeedMultiplier(1f); + handler.ResetSketchFade(); + } + } + } private void ResetAnimationTimers() { diff --git a/Assets/Unheimlich_POC/Scripts/Managers/DialogueManager.cs b/Assets/Unheimlich_POC/Scripts/Managers/DialogueManager.cs index b3b982a1..d4a9cd07 100644 --- a/Assets/Unheimlich_POC/Scripts/Managers/DialogueManager.cs +++ b/Assets/Unheimlich_POC/Scripts/Managers/DialogueManager.cs @@ -24,7 +24,9 @@ namespace Unheimlich.Managers [Header("Focus Stabilization")] [SerializeField] private float _recoveryDuration = 0.5f; [SerializeField] private float _focusRetentionTime = 0.1f; - [SerializeField] private float _shakeIntensity = 30f; + + [Tooltip("Fallback camera shake intensity if not set in DialogueAnimationSettings")] + [SerializeField] private float _fallbackShakeIntensity = 30f; public DialogueAnimationSettings CurrentAnimationSettings => _currentAnimationSettings; public bool IsDialogueActive => IsInDialogue; @@ -74,7 +76,7 @@ namespace Unheimlich.Managers _raycastInterval, _recoveryDuration, _focusRetentionTime, - _shakeIntensity + _fallbackShakeIntensity ); _focusSystem.OnChoiceMade += (choice) => OnChoiceMade?.Invoke(choice);