diff --git a/Assets/04_Models/GlobalManagers.prefab b/Assets/04_Models/GlobalManagers.prefab index 69b3b3b5..fa99f76b 100644 --- a/Assets/04_Models/GlobalManagers.prefab +++ b/Assets/04_Models/GlobalManagers.prefab @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c6898fceca735278ed807235e7b60058f97c5b43c32e12750f55e04a41b221c -size 13864 +oid sha256:8b88ca4e3f5a7b125700d8bcf23e283a1f230fc03ad55325c027aa185ea4bfd8 +size 13883 diff --git a/Assets/07_Data/Communication/DialogGraph/fishhook.meta b/Assets/07_Data/Communication/DialogGraph/fishhook.meta new file mode 100644 index 00000000..0a4c8577 --- /dev/null +++ b/Assets/07_Data/Communication/DialogGraph/fishhook.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 63da302d6cdd1e14da968a2898e6a9a5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_Area1.wdg b/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_Area1.wdg new file mode 100644 index 00000000..aa384de4 --- /dev/null +++ b/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_Area1.wdg @@ -0,0 +1,408 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + 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: 790b4d75d92f4b0984310a268dbd952f, type: 3} + m_Name: Fairy_fishRoom_Area1 + m_EditorClassIdentifier: Unity.GraphToolkit.Editor::Unity.GraphToolkit.Editor.Implementation.GraphObjectImp + m_GraphModel: + rid: 6595524353106116630 + references: + version: 2 + RefIds: + - rid: -2 + type: {class: , ns: , asm: } + - rid: 1414123175413022855 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 897520841189697386 + m_Value1: 6607104733771082257 + m_HashGuid: + serializedVersion: 2 + Hash: 6a279f2573a2740c112a3adabf26b15b + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 7989713923298697385 + m_Value1: 15604869423937906234 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: a920365f7b2ae16e3a662c1c10a28fd8 + m_UniqueId: Out + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 7697830479301862552 + m_Value1: 13043115897654624489 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 9864f63b0930d46ae940f4b3cd7402b5 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 6595524353106116630 + type: {class: GraphModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 13819889836145151562 + m_Value1: 2645381255326452780 + m_HashGuid: + serializedVersion: 2 + Hash: 4a8e76c6951ccabf2ccc35633c48b624 + m_Name: Fairy_fishRoom_Area1 + m_GraphNodeModels: + - rid: 6595524353106116633 + - rid: 6595524353106116635 + - rid: 6595524353106116646 + m_GraphWireModels: + - rid: 6595524353106116636 + - rid: 1414123175413022855 + m_GraphStickyNoteModels: [] + m_GraphPlacematModels: [] + m_GraphVariableModels: [] + m_GraphPortalModels: [] + m_SectionModels: + - rid: 6595524353106116631 + m_LocalSubgraphs: [] + m_LastKnownBounds: + serializedVersion: 2 + x: 222 + y: 84 + width: 923 + height: 386 + m_GraphElementMetaData: + - m_Guid: + m_Value0: 14845512388065122572 + m_Value1: 17804268460506216482 + m_HashGuid: + serializedVersion: 2 + Hash: 0c5948afdcda05ce22f82972d57715f7 + m_Category: 0 + m_Index: 0 + - m_Guid: + m_Value0: 7989713923298697385 + m_Value1: 15604869423937906234 + m_HashGuid: + serializedVersion: 2 + Hash: a920365f7b2ae16e3a662c1c10a28fd8 + m_Category: 0 + m_Index: 1 + - m_Guid: + m_Value0: 5269650743910428719 + m_Value1: 257959026697812224 + m_HashGuid: + serializedVersion: 2 + Hash: 2f7027896e8f214900b9ed385e749403 + m_Category: 2 + m_Index: 0 + - m_Guid: + m_Value0: 7697830479301862552 + m_Value1: 13043115897654624489 + m_HashGuid: + serializedVersion: 2 + Hash: 9864f63b0930d46ae940f4b3cd7402b5 + m_Category: 0 + m_Index: 2 + - m_Guid: + m_Value0: 897520841189697386 + m_Value1: 6607104733771082257 + m_HashGuid: + serializedVersion: 2 + Hash: 6a279f2573a2740c112a3adabf26b15b + m_Category: 2 + m_Index: 1 + m_EntryPoint: + rid: 6595524353106116633 + m_Graph: + rid: 6595524353106116632 + - rid: 6595524353106116631 + type: {class: SectionModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 13482299192089173763 + m_Value1: 8100932157345530803 + m_HashGuid: + serializedVersion: 2 + Hash: 03df02d4aebf1abbb3831e64e04a6c70 + m_Version: 2 + m_Items: [] + m_Title: + - rid: 6595524353106116632 + type: {class: DialogGraph, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524353106116633 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 14845512388065122572 + m_Value1: 17804268460506216482 + m_HashGuid: + serializedVersion: 2 + Hash: 0c5948afdcda05ce22f82972d57715f7 + m_Version: 2 + m_Position: {x: 222.2174, y: 116.434784} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: [] + m_ValueList: [] + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524353106116634 + - rid: 6595524353106116634 + type: {class: DialogStartNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524353106116635 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 7989713923298697385 + m_Value1: 15604869423937906234 + m_HashGuid: + serializedVersion: 2 + Hash: a920365f7b2ae16e3a662c1c10a28fd8 + m_Version: 2 + m_Position: {x: 431.77588, y: 86.04323} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - __option_ChoiceCount + - Speaker + - TalkText + - Gesture + - Expression + - Voice + - LineDuration + - LookAtPlayer + - WaitForInput + - __option_EventKey + m_ValueList: + - rid: 6595524353106116637 + - rid: 6595524353106116638 + - rid: 6595524353106116639 + - rid: 6595524353106116640 + - rid: 6595524353106116641 + - rid: 6595524353106116642 + - rid: 6595524353106116643 + - rid: 6595524353106116644 + - rid: 6595524374970761386 + - rid: 6595524374970761398 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524353106116645 + - rid: 6595524353106116636 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 5269650743910428719 + m_Value1: 257959026697812224 + m_HashGuid: + serializedVersion: 2 + Hash: 2f7027896e8f214900b9ed385e749403 + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 14845512388065122572 + m_Value1: 17804268460506216482 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 0c5948afdcda05ce22f82972d57715f7 + m_UniqueId: Out + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 7989713923298697385 + m_Value1: 15604869423937906234 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: a920365f7b2ae16e3a662c1c10a28fd8 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 6595524353106116637 + type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524353106116638 + type: {class: 'Constant`1[[CharacterData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 11400000, guid: 816884903bb3c4d478520286d768c304, type: 2} + - rid: 6595524353106116639 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: "\uC774\uACF3\uC740 \uAE30\uBB18\uD55C \uB09A\uC2DC\uD130\uC57C." + - rid: 6595524353106116640 + type: {class: 'Constant`1[[GestureData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116641 + type: {class: 'Constant`1[[ExpressionData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116642 + type: {class: 'Constant`1[[VoiceClip, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116643 + type: {class: 'Constant`1[[System.Single, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 5 + - rid: 6595524353106116644 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 1 + - rid: 6595524353106116645 + type: {class: DialogLineNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524353106116646 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 7697830479301862552 + m_Value1: 13043115897654624489 + m_HashGuid: + serializedVersion: 2 + Hash: 9864f63b0930d46ae940f4b3cd7402b5 + m_Version: 2 + m_Position: {x: 806, y: 84} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - __option_ChoiceCount + - Speaker + - TalkText + - Gesture + - Expression + - Voice + - LineDuration + - LookAtPlayer + - WaitForInput + - __option_EventKey + m_ValueList: + - rid: 6595524353106116648 + - rid: 6595524353106116649 + - rid: 6595524353106116650 + - rid: 6595524353106116651 + - rid: 6595524353106116652 + - rid: 6595524353106116653 + - rid: 6595524353106116654 + - rid: 6595524353106116655 + - rid: 6595524374970761387 + - rid: 6595524374970761399 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524353106116656 + - rid: 6595524353106116648 + type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524353106116649 + type: {class: 'Constant`1[[CharacterData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 11400000, guid: 816884903bb3c4d478520286d768c304, type: 2} + - rid: 6595524353106116650 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: "\uAE30\uC5B5\uC758 \uC870\uAC01\uC744 \uB09A\uC544\uC57C\uD574." + - rid: 6595524353106116651 + type: {class: 'Constant`1[[GestureData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116652 + type: {class: 'Constant`1[[ExpressionData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116653 + type: {class: 'Constant`1[[VoiceClip, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116654 + type: {class: 'Constant`1[[System.Single, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 5 + - rid: 6595524353106116655 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524353106116656 + type: {class: DialogLineNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524374970761386 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524374970761387 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524374970761398 + type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + - rid: 6595524374970761399 + type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: diff --git a/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_Area1.wdg.meta b/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_Area1.wdg.meta new file mode 100644 index 00000000..d1500a41 --- /dev/null +++ b/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_Area1.wdg.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: b44d9587db51ade41a11fc53f47dd1a7 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 2ae5ca89bbed445479d9023586f0c041, type: 3} diff --git a/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_ClearArea.wdg b/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_ClearArea.wdg new file mode 100644 index 00000000..a4a7531e --- /dev/null +++ b/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_ClearArea.wdg @@ -0,0 +1,737 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + 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: 790b4d75d92f4b0984310a268dbd952f, type: 3} + m_Name: Fairy_fishRoom_ClearArea + m_EditorClassIdentifier: Unity.GraphToolkit.Editor::Unity.GraphToolkit.Editor.Implementation.GraphObjectImp + m_GraphModel: + rid: 6595524353106116630 + references: + version: 2 + RefIds: + - rid: -2 + type: {class: , ns: , asm: } + - rid: 6595524353106116630 + type: {class: GraphModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 13819889836145151562 + m_Value1: 2645381255326452780 + m_HashGuid: + serializedVersion: 2 + Hash: 4a8e76c6951ccabf2ccc35633c48b624 + m_Name: Fairy_fishRoom_ClearArea + m_GraphNodeModels: + - rid: 6595524353106116633 + - rid: 6595524353106116635 + - rid: 6595524353106116646 + - rid: 6595524374970761358 + - rid: 6595524374970761369 + m_GraphWireModels: + - rid: 6595524353106116636 + - rid: 6595524353106116647 + - rid: 6595524374970761359 + - rid: 6595524374970761370 + m_GraphStickyNoteModels: [] + m_GraphPlacematModels: [] + m_GraphVariableModels: [] + m_GraphPortalModels: [] + m_SectionModels: + - rid: 6595524353106116631 + m_LocalSubgraphs: [] + m_LastKnownBounds: + serializedVersion: 2 + x: 224 + y: -42 + width: 1430 + height: 883 + m_GraphElementMetaData: + - m_Guid: + m_Value0: 14845512388065122572 + m_Value1: 17804268460506216482 + m_HashGuid: + serializedVersion: 2 + Hash: 0c5948afdcda05ce22f82972d57715f7 + m_Category: 0 + m_Index: 0 + - m_Guid: + m_Value0: 7989713923298697385 + m_Value1: 15604869423937906234 + m_HashGuid: + serializedVersion: 2 + Hash: a920365f7b2ae16e3a662c1c10a28fd8 + m_Category: 0 + m_Index: 1 + - m_Guid: + m_Value0: 5269650743910428719 + m_Value1: 257959026697812224 + m_HashGuid: + serializedVersion: 2 + Hash: 2f7027896e8f214900b9ed385e749403 + m_Category: 2 + m_Index: 0 + - m_Guid: + m_Value0: 7697830479301862552 + m_Value1: 13043115897654624489 + m_HashGuid: + serializedVersion: 2 + Hash: 9864f63b0930d46ae940f4b3cd7402b5 + m_Category: 0 + m_Index: 2 + - m_Guid: + m_Value0: 13678802302849805841 + m_Value1: 5869810211712229956 + m_HashGuid: + serializedVersion: 2 + Hash: 116e289638ded4bd446211b849c17551 + m_Category: 2 + m_Index: 1 + - m_Guid: + m_Value0: 6309969824669224220 + m_Value1: 11825358839457157206 + m_HashGuid: + serializedVersion: 2 + Hash: 1ccdd7b61f84915756905e07361d1ca4 + m_Category: 0 + m_Index: 3 + - m_Guid: + m_Value0: 3397748348636220684 + m_Value1: 10317322138978035326 + m_HashGuid: + serializedVersion: 2 + Hash: 0ccda1fc1e3a272f7e260f67d27d2e8f + m_Category: 2 + m_Index: 2 + - m_Guid: + m_Value0: 14492247198202050630 + m_Value1: 8595945445982712313 + m_HashGuid: + serializedVersion: 2 + Hash: 46b8fb250bce1ec9f9b16f00d7ee4a77 + m_Category: 0 + m_Index: 4 + - m_Guid: + m_Value0: 4733159566535984953 + m_Value1: 16169765346515963957 + m_HashGuid: + serializedVersion: 2 + Hash: 39879e648c8faf413550815ce98b66e0 + m_Category: 2 + m_Index: 3 + m_EntryPoint: + rid: 6595524353106116633 + m_Graph: + rid: 6595524353106116632 + - rid: 6595524353106116631 + type: {class: SectionModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 13482299192089173763 + m_Value1: 8100932157345530803 + m_HashGuid: + serializedVersion: 2 + Hash: 03df02d4aebf1abbb3831e64e04a6c70 + m_Version: 2 + m_Items: [] + m_Title: + - rid: 6595524353106116632 + type: {class: DialogGraph, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524353106116633 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 14845512388065122572 + m_Value1: 17804268460506216482 + m_HashGuid: + serializedVersion: 2 + Hash: 0c5948afdcda05ce22f82972d57715f7 + m_Version: 2 + m_Position: {x: 224, y: 117} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: [] + m_ValueList: [] + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524353106116634 + - rid: 6595524353106116634 + type: {class: DialogStartNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524353106116635 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 7989713923298697385 + m_Value1: 15604869423937906234 + m_HashGuid: + serializedVersion: 2 + Hash: a920365f7b2ae16e3a662c1c10a28fd8 + m_Version: 2 + m_Position: {x: 433, y: 87} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - __option_ChoiceCount + - Speaker + - TalkText + - Gesture + - Expression + - Voice + - LineDuration + - LookAtPlayer + - WaitForInput + - __option_EventKey + m_ValueList: + - rid: 6595524353106116637 + - rid: 6595524353106116638 + - rid: 6595524353106116639 + - rid: 6595524353106116640 + - rid: 6595524353106116641 + - rid: 6595524353106116642 + - rid: 6595524353106116643 + - rid: 6595524353106116644 + - rid: 6595524374970761380 + - rid: 6595524374970761406 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524353106116645 + - rid: 6595524353106116636 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 5269650743910428719 + m_Value1: 257959026697812224 + m_HashGuid: + serializedVersion: 2 + Hash: 2f7027896e8f214900b9ed385e749403 + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 14845512388065122572 + m_Value1: 17804268460506216482 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 0c5948afdcda05ce22f82972d57715f7 + m_UniqueId: Out + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 7989713923298697385 + m_Value1: 15604869423937906234 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: a920365f7b2ae16e3a662c1c10a28fd8 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 6595524353106116637 + type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524353106116638 + type: {class: 'Constant`1[[CharacterData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 11400000, guid: 816884903bb3c4d478520286d768c304, type: 2} + - rid: 6595524353106116639 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: "\uB2E4\uC74C \uACF5\uAC04\uC73C\uB85C \uAC08 \uC218 \uC788\uC5B4." + - rid: 6595524353106116640 + type: {class: 'Constant`1[[GestureData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116641 + type: {class: 'Constant`1[[ExpressionData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116642 + type: {class: 'Constant`1[[VoiceClip, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116643 + type: {class: 'Constant`1[[System.Single, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 5 + - rid: 6595524353106116644 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 1 + - rid: 6595524353106116645 + type: {class: DialogLineNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524353106116646 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 7697830479301862552 + m_Value1: 13043115897654624489 + m_HashGuid: + serializedVersion: 2 + Hash: 9864f63b0930d46ae940f4b3cd7402b5 + m_Version: 2 + m_Position: {x: 808.30005, y: 85.15} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - __option_ChoiceCount + - Speaker + - TalkText + - Gesture + - Expression + - Voice + - LineDuration + - LookAtPlayer + - ChoiceQuestion + - Choice0Text + - Choice1Text + - WaitForInput + - __option_EventKey + - Choice0Code + - Choice1Code + m_ValueList: + - rid: 6595524353106116648 + - rid: 6595524353106116649 + - rid: 6595524353106116650 + - rid: 6595524353106116651 + - rid: 6595524353106116652 + - rid: 6595524353106116653 + - rid: 6595524353106116654 + - rid: 6595524353106116655 + - rid: 6595524374970761341 + - rid: 6595524374970761342 + - rid: 6595524374970761343 + - rid: 6595524374970761381 + - rid: 6595524374970761407 + - rid: 6595524374970761412 + - rid: 6595524374970761413 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524353106116656 + - rid: 6595524353106116647 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 13678802302849805841 + m_Value1: 5869810211712229956 + m_HashGuid: + serializedVersion: 2 + Hash: 116e289638ded4bd446211b849c17551 + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 7989713923298697385 + m_Value1: 15604869423937906234 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: a920365f7b2ae16e3a662c1c10a28fd8 + m_UniqueId: Out + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 7697830479301862552 + m_Value1: 13043115897654624489 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 9864f63b0930d46ae940f4b3cd7402b5 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 6595524353106116648 + type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 2 + - rid: 6595524353106116649 + type: {class: 'Constant`1[[CharacterData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 11400000, guid: 816884903bb3c4d478520286d768c304, type: 2} + - rid: 6595524353106116650 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: "\uC5B4\uB290 \uACF5\uAC04\uC73C\uB85C \uC774\uB3D9\uD560\uB798?" + - rid: 6595524353106116651 + type: {class: 'Constant`1[[GestureData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116652 + type: {class: 'Constant`1[[ExpressionData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116653 + type: {class: 'Constant`1[[VoiceClip, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524353106116654 + type: {class: 'Constant`1[[System.Single, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 5 + - rid: 6595524353106116655 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524353106116656 + type: {class: DialogLineNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524374970761341 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: + - rid: 6595524374970761342 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: '{SpaceSceneName1}' + - rid: 6595524374970761343 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: '{SpaceSceneName2}' + - rid: 6595524374970761358 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 6309969824669224220 + m_Value1: 11825358839457157206 + m_HashGuid: + serializedVersion: 2 + Hash: 1ccdd7b61f84915756905e07361d1ca4 + m_Version: 2 + m_Position: {x: 1312.1732, y: -41.937325} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - __option_ChoiceCount + - Speaker + - TalkText + - Gesture + - Expression + - Voice + - LineDuration + - LookAtPlayer + - WaitForInput + - __option_EventKey + m_ValueList: + - rid: 6595524374970761360 + - rid: 6595524374970761361 + - rid: 6595524374970761362 + - rid: 6595524374970761363 + - rid: 6595524374970761364 + - rid: 6595524374970761365 + - rid: 6595524374970761366 + - rid: 6595524374970761367 + - rid: 6595524374970761382 + - rid: 6595524374970761408 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524374970761368 + - rid: 6595524374970761359 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 3397748348636220684 + m_Value1: 10317322138978035326 + m_HashGuid: + serializedVersion: 2 + Hash: 0ccda1fc1e3a272f7e260f67d27d2e8f + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 7697830479301862552 + m_Value1: 13043115897654624489 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 9864f63b0930d46ae940f4b3cd7402b5 + m_UniqueId: Choice0Out + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: "Choice 1 \u2192" + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 6309969824669224220 + m_Value1: 11825358839457157206 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 1ccdd7b61f84915756905e07361d1ca4 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 6595524374970761360 + type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524374970761361 + type: {class: 'Constant`1[[CharacterData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 11400000, guid: 816884903bb3c4d478520286d768c304, type: 2} + - rid: 6595524374970761362 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: "\uC88B\uC544! {SpaceSceneName1}\uB85C \uC774\uB3D9\uD558\uC790." + - rid: 6595524374970761363 + type: {class: 'Constant`1[[GestureData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524374970761364 + type: {class: 'Constant`1[[ExpressionData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524374970761365 + type: {class: 'Constant`1[[VoiceClip, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524374970761366 + type: {class: 'Constant`1[[System.Single, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 5 + - rid: 6595524374970761367 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524374970761368 + type: {class: DialogLineNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524374970761369 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 14492247198202050630 + m_Value1: 8595945445982712313 + m_HashGuid: + serializedVersion: 2 + Hash: 46b8fb250bce1ec9f9b16f00d7ee4a77 + m_Version: 2 + m_Position: {x: 1314.8867, y: 457.07712} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - __option_ChoiceCount + - Speaker + - TalkText + - Gesture + - Expression + - Voice + - LineDuration + - LookAtPlayer + - WaitForInput + - __option_EventKey + m_ValueList: + - rid: 6595524374970761371 + - rid: 6595524374970761372 + - rid: 6595524374970761373 + - rid: 6595524374970761374 + - rid: 6595524374970761375 + - rid: 6595524374970761376 + - rid: 6595524374970761377 + - rid: 6595524374970761378 + - rid: 6595524374970761383 + - rid: 6595524374970761409 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524374970761379 + - rid: 6595524374970761370 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 4733159566535984953 + m_Value1: 16169765346515963957 + m_HashGuid: + serializedVersion: 2 + Hash: 39879e648c8faf413550815ce98b66e0 + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 7697830479301862552 + m_Value1: 13043115897654624489 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 9864f63b0930d46ae940f4b3cd7402b5 + m_UniqueId: Choice1Out + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: "Choice 2 \u2192" + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 14492247198202050630 + m_Value1: 8595945445982712313 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 46b8fb250bce1ec9f9b16f00d7ee4a77 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 6595524374970761371 + type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524374970761372 + type: {class: 'Constant`1[[CharacterData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 11400000, guid: 816884903bb3c4d478520286d768c304, type: 2} + - rid: 6595524374970761373 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: "\uC88B\uC544! {SpaceSceneName2}\uB85C \uC774\uB3D9\uD558\uC790." + - rid: 6595524374970761374 + type: {class: 'Constant`1[[GestureData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524374970761375 + type: {class: 'Constant`1[[ExpressionData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524374970761376 + type: {class: 'Constant`1[[VoiceClip, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524374970761377 + type: {class: 'Constant`1[[System.Single, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 5 + - rid: 6595524374970761378 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524374970761379 + type: {class: DialogLineNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524374970761380 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524374970761381 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524374970761382 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 1 + - rid: 6595524374970761383 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 1 + - rid: 6595524374970761406 + type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + - rid: 6595524374970761407 + type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + - rid: 6595524374970761408 + type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: OpenDoor1 + - rid: 6595524374970761409 + type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: OpenDoor2 + - rid: 6595524374970761412 + type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 1 + - rid: 6595524374970761413 + type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 2 diff --git a/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_ClearArea.wdg.meta b/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_ClearArea.wdg.meta new file mode 100644 index 00000000..978cf1ff --- /dev/null +++ b/Assets/07_Data/Communication/DialogGraph/fishhook/Fairy_fishRoom_ClearArea.wdg.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: e6f745d905312c14699dd74c40a28852 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 2ae5ca89bbed445479d9023586f0c041, type: 3} diff --git a/Assets/07_Data/Terrain/RoomOriginTerrain.asset b/Assets/07_Data/Terrain/RoomOriginTerrain.asset index 34896d06..e71d995f 100644 --- a/Assets/07_Data/Terrain/RoomOriginTerrain.asset +++ b/Assets/07_Data/Terrain/RoomOriginTerrain.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca4eb2dd1ffa75a5c7f362c27bcfbc12a792c462a8eabe542ba439c0f14976b4 -size 5581228 +oid sha256:f3e11c49539aa72f350f9932e7d1825d43a6173aad4dc64e01b9c440af40d1ed +size 5582252 diff --git a/Assets/My project/Fishing Scripts/Prefabs.meta b/Assets/My project/Fishing Scripts/Prefabs.meta new file mode 100644 index 00000000..78c63d7a --- /dev/null +++ b/Assets/My project/Fishing Scripts/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1e1f71c106e40544a96417f581abeec7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/Prefabs/14.glb b/Assets/My project/Fishing Scripts/Prefabs/14.glb new file mode 100644 index 00000000..914c1102 Binary files /dev/null and b/Assets/My project/Fishing Scripts/Prefabs/14.glb differ diff --git a/Assets/My project/Fishing Scripts/Prefabs/14.glb.meta b/Assets/My project/Fishing Scripts/Prefabs/14.glb.meta new file mode 100644 index 00000000..88485447 --- /dev/null +++ b/Assets/My project/Fishing Scripts/Prefabs/14.glb.meta @@ -0,0 +1,28 @@ +fileFormatVersion: 2 +guid: b205275e9229b8b40bcfb446b9fa4cb4 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 715df9372183c47e389bb6e19fbc3b52, type: 3} + editorImportSettings: + generateSecondaryUVSet: 0 + importSettings: + nodeNameMethod: 1 + animationMethod: 2 + generateMipMaps: 1 + texturesReadable: 0 + defaultMinFilterMode: 9729 + defaultMagFilterMode: 9729 + anisotropicFilterLevel: 1 + instantiationSettings: + mask: -1 + layer: 0 + skinUpdateWhenOffscreen: 1 + lightIntensityFactor: 1 + sceneObjectCreation: 2 + assetDependencies: [] + reportItems: [] diff --git a/Assets/My project/Fishing Scripts/Prefabs/FishingSystemPrefab.prefab b/Assets/My project/Fishing Scripts/Prefabs/FishingSystemPrefab.prefab new file mode 100644 index 00000000..7be143df --- /dev/null +++ b/Assets/My project/Fishing Scripts/Prefabs/FishingSystemPrefab.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64239652fbbd1b648441a25bbc7adae5c75781bab7b25c4133c73dd6fd9a6b28 +size 220519 diff --git a/Assets/My project/Fishing Scripts/Prefabs/FishingSystemPrefab.prefab.meta b/Assets/My project/Fishing Scripts/Prefabs/FishingSystemPrefab.prefab.meta new file mode 100644 index 00000000..67c53b66 --- /dev/null +++ b/Assets/My project/Fishing Scripts/Prefabs/FishingSystemPrefab.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e9d8394fffa0b5b4e81c71ad0000f45a +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/Prefabs/fish.fbx b/Assets/My project/Fishing Scripts/Prefabs/fish.fbx new file mode 100644 index 00000000..8853a813 --- /dev/null +++ b/Assets/My project/Fishing Scripts/Prefabs/fish.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d4c0bb2fe2203ded9ddf86d9fda8dec577c3e512b476bff641e9a8a5835b56d +size 30108 diff --git a/Assets/My project/Fishing Scripts/Prefabs/fish.fbx.meta b/Assets/My project/Fishing Scripts/Prefabs/fish.fbx.meta new file mode 100644 index 00000000..8d6e16ba --- /dev/null +++ b/Assets/My project/Fishing Scripts/Prefabs/fish.fbx.meta @@ -0,0 +1,110 @@ +fileFormatVersion: 2 +guid: 3f6af38d640059d49af824ba9ba06e73 +ModelImporter: + serializedVersion: 24200 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + removeConstantScaleCurves: 0 + motionNodeName: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importPhysicalCameras: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + nodeNameCollisionStrategy: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + optimizeBones: 1 + generateMeshLods: 0 + meshLodGenerationFlags: 0 + maximumMeshLod: -1 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + strictVertexDataChecks: 0 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + importBlendShapeDeformPercent: 1 + remapMaterialsIfMaterialImportModeIsNone: 0 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/UI/FishingGameManager.cs b/Assets/My project/Fishing Scripts/UI/FishingGameManager.cs index b111ce28..45084c9b 100644 --- a/Assets/My project/Fishing Scripts/UI/FishingGameManager.cs +++ b/Assets/My project/Fishing Scripts/UI/FishingGameManager.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using UnityEngine; using UnityEngine.InputSystem; @@ -11,12 +12,57 @@ public enum ResultType Miss } + public enum FishingControlMode + { + TimingGauge, + SimpleReel + } + + [Header("Auto Bind")] + [Tooltip("현재 만든 Prefab 구조 기준으로 비어 있는 참조를 자동 연결합니다.")] + [SerializeField] private bool autoBindMissingReferences = true; + [Header("References")] + [Tooltip("낚시 UI 전체 루트입니다. UI를 자동으로 켜고 끄고 싶을 때만 연결하세요.")] + [SerializeField] private GameObject uiRoot; + [SerializeField] private FishingGaugeUI ui; [SerializeField] private FishingRewardSystem rewardSystem; [SerializeField] private FishingHapticManager haptic; [SerializeField] private RotateUI centerIconRotate; + [Header("Simple Reel Fishing")] + [Tooltip("TimingGauge는 기존 원형 타이밍 게이지 방식, SimpleReel은 릴을 감아 줄 길이를 줄이면 성공하는 방식입니다.")] + [SerializeField] private FishingControlMode fishingControlMode = FishingControlMode.SimpleReel; + + [Tooltip("낚싯대 / 찌 / 릴 제어 스크립트입니다. SimpleReel 모드에서 줄 길이를 읽습니다.")] + [SerializeField] private FishingRodVRController rodController; + + [Tooltip("현재 줄 길이가 이 값 이하가 되면 낚기 성공으로 처리합니다.")] + [SerializeField] private float catchCompleteLineLength = 0.7f; + + [Tooltip("릴 낚시 시작 시 줄 길이가 성공 길이보다 너무 짧으면 이 거리만큼 최소 시작 길이를 보정합니다.")] + [SerializeField] private float simpleReelMinimumPullDistance = 0.35f; + + [Tooltip("릴 낚기 성공 후 찌를 낚싯대 쪽 대기 위치로 회수합니다.")] + [SerializeField] private bool returnBobberAfterSimpleReelCatch = true; + + [Tooltip("SimpleReel 모드에서 기존 타이밍 게이지 UI를 숨기고 진행도/줄 길이 중심으로 표시합니다.")] + [SerializeField] private bool hideTimingGaugeInSimpleReelMode = true; + + [Tooltip("SimpleReel 모드에서는 StartFishing이 여러 번 호출됩니다. 켜면 첫 시작 때만 연못 상태를 초기화합니다.")] + [SerializeField] private bool resetPondOnlyOnFirstSimpleReelStart = true; + + [Header("XR Controller Input")] + [Tooltip("낚시 판정을 실행할 컨트롤러 입력입니다. 예: XRI Right Interaction / Select")] + [SerializeField] private InputActionReference submitAction; + + [Tooltip("낚시를 다시 시작할 컨트롤러 입력입니다. 필요 없으면 비워둬도 됩니다.")] + [SerializeField] private InputActionReference resetAction; + + [Tooltip("씬에 Input Action Manager가 없거나 액션이 자동 활성화되지 않으면 켜두세요.")] + [SerializeField] private bool enableInputActionsManually = true; + [Header("Pointer")] [SerializeField] private float pointerAngle; [SerializeField] private float pointerSpeed = 180f; @@ -30,29 +76,110 @@ public enum ResultType [SerializeField] private float maxZoneSize = 120f; [SerializeField] private float zoneCenter; - [Header("Rules")] + [Header("Catch Rules")] + [Tooltip("아이템 1개를 낚기 위해 필요한 성공 횟수입니다.")] [SerializeField] private int requiredSuccesses = 3; + + [Tooltip("아이템 1개를 낚는 동안 허용되는 실패 횟수입니다. 초과하면 낚싯줄이 끊어진 것으로 처리합니다.")] [SerializeField] private int allowedFails = 3; + [Header("Pond Cleanup Rules")] + [Tooltip("연못을 맑게 만들기 위해 필요한 쓰레기성 아이템 수입니다. 쓰레기, 병, 봉지가 여기에 포함됩니다.")] + [SerializeField] private int requiredCleanupItems = 3; + + [Tooltip("연못이 맑아진 뒤 다음 성공 낚시에서 기억의 조각을 확정으로 낚습니다.")] + [SerializeField] private bool guaranteeMemoryPieceAfterCleaned = true; + + [Tooltip("StartFishing을 호출할 때 쓰레기 수거/연못 상태를 초기화합니다.")] + [SerializeField] private bool resetPondStateOnStart = true; + + [Tooltip("낚시 세션이 진행 중일 때 StartFishing이 중복 호출되는 것을 막습니다.")] + [SerializeField] private bool preventRestartWhileSessionRunning = true; + [Header("Round Settings")] [SerializeField] private float nextRoundDelay = 0.35f; + [SerializeField] private float nextCatchDelay = 2.0f; [SerializeField] private bool randomDirectionEachRound = true; + [SerializeField] private bool resetDifficultyEachCatch = true; [SerializeField] private bool startOnAwake = true; + [Header("Final Result Settings")] + [Tooltip("최종 결과를 잠깐 보여준 뒤 uiRoot를 자동으로 끕니다. uiRoot가 비어 있으면 동작하지 않습니다.")] + [SerializeField] private bool hideUIRootAfterFinalResult = false; + + [SerializeField] private float finalResultShowTime = 1.5f; + [Header("Debug")] [SerializeField] private bool showDebugLog = true; private int successCount; private int failCount; + private int cleanupItemCount; + private int totalCaughtItems; + + // 임시 낚시 세션 카운트입니다. 나중에 공용 인벤토리와 연결할 때는 저장용으로 쓰지 말고 UI 표시용으로만 쓰세요. + private int sessionFishCount; + private int sessionTrashCount; + private int sessionMemoryPieceCount; + private int sessionCompassCount; private bool clockwise = true; private bool activeGame; private bool inputLocked; + private bool fishingSessionRunning; + private bool pondCleaned; + private bool memoryPieceCollected; + + private bool simpleReelCatchActive; + private bool simpleReelPondInitialized; + private float simpleReelStartLineLength; + private float simpleReelProgress; + + private bool submitActionWasEnabled; + private bool resetActionWasEnabled; private Coroutine nextRoundRoutine; + private Coroutine nextCatchRoutine; + private Coroutine finalResultRoutine; + + public bool IsActiveGame => activeGame; + public bool IsFishingSessionRunning => fishingSessionRunning; + public bool IsPondCleaned => pondCleaned; + public bool IsMemoryPieceCollected => memoryPieceCollected; + public int SuccessCount => successCount; + public int FailCount => failCount; + public int CleanupItemCount => cleanupItemCount; + public int RequiredCleanupItems => requiredCleanupItems; + public int TotalCaughtItems => totalCaughtItems; + public int SessionFishCount => sessionFishCount; + public int SessionTrashCount => sessionTrashCount; + public int SessionMemoryPieceCount => sessionMemoryPieceCount; + public int SessionCompassCount => sessionCompassCount; + public FishingControlMode ControlMode => fishingControlMode; + public bool IsSimpleReelCatchActive => simpleReelCatchActive; + public float SimpleReelProgress => simpleReelProgress; + + public event Action ItemCaught; + + private void OnEnable() + { + RegisterInputAction(submitAction, OnSubmitAction, ref submitActionWasEnabled); + RegisterInputAction(resetAction, OnResetAction, ref resetActionWasEnabled); + } + + private void OnDisable() + { + UnregisterInputAction(submitAction, OnSubmitAction, submitActionWasEnabled); + UnregisterInputAction(resetAction, OnResetAction, resetActionWasEnabled); + } private void Start() { + if (autoBindMissingReferences) + AutoBindMissingReferences(); + + ValidateRuntimeSettings(); + if (startOnAwake) { StartFishing(); @@ -68,21 +195,158 @@ private void Update() if (!activeGame) return; + if (fishingControlMode == FishingControlMode.SimpleReel) + { + UpdateSimpleReelCatch(); + return; + } + if (inputLocked) return; RotatePointer(); } + private void OnValidate() + { + ValidateRuntimeSettings(); + } + + + + [ContextMenu("Auto Bind Fishing References")] + public void AutoBindMissingReferences() + { + Transform searchRoot = GetSearchRoot(); + + if (uiRoot == null) + { + Transform canvasTransform = FindTransformRecursive(searchRoot, "FishingCanvas"); + if (canvasTransform != null) + uiRoot = canvasTransform.gameObject; + } + + if (ui == null) + ui = searchRoot != null ? searchRoot.GetComponentInChildren(true) : GetComponentInChildren(true); + + if (rewardSystem == null) + rewardSystem = searchRoot != null ? searchRoot.GetComponentInChildren(true) : GetComponentInChildren(true); + + if (haptic == null) + haptic = searchRoot != null ? searchRoot.GetComponentInChildren(true) : GetComponentInChildren(true); + + if (rodController == null) + rodController = searchRoot != null ? searchRoot.GetComponentInChildren(true) : GetComponentInChildren(true); + + if (centerIconRotate == null) + { + Transform centerIconTransform = FindTransformRecursive(searchRoot, "CenterIcon"); + if (centerIconTransform != null) + centerIconRotate = centerIconTransform.GetComponent(); + + if (centerIconRotate == null && searchRoot != null) + centerIconRotate = searchRoot.GetComponentInChildren(true); + } + } + + private Transform GetSearchRoot() + { + Transform current = transform; + + while (current.parent != null) + current = current.parent; + + return current; + } + + private Transform FindTransformRecursive(Transform current, string targetName) + { + if (current == null || string.IsNullOrEmpty(targetName)) + return null; + + if (NormalizeName(current.name) == NormalizeName(targetName)) + return current; + + for (int i = 0; i < current.childCount; i++) + { + Transform found = FindTransformRecursive(current.GetChild(i), targetName); + if (found != null) + return found; + } + + return null; + } + + private string NormalizeName(string value) + { + return value.Replace(" ", string.Empty) + .Replace("_", string.Empty) + .Replace("-", string.Empty) + .ToLowerInvariant(); + } + + private void RegisterInputAction(InputActionReference actionReference, System.Action callback, ref bool wasEnabled) + { + if (actionReference == null || actionReference.action == null) + return; + + wasEnabled = actionReference.action.enabled; + actionReference.action.performed += callback; + + if (enableInputActionsManually && !wasEnabled) + actionReference.action.Enable(); + } + + private void UnregisterInputAction(InputActionReference actionReference, System.Action callback, bool wasEnabled) + { + if (actionReference == null || actionReference.action == null) + return; + + actionReference.action.performed -= callback; + + if (enableInputActionsManually && !wasEnabled) + actionReference.action.Disable(); + } + + private void ValidateRuntimeSettings() + { + requiredSuccesses = Mathf.Max(1, requiredSuccesses); + allowedFails = Mathf.Max(1, allowedFails); + requiredCleanupItems = Mathf.Max(1, requiredCleanupItems); + + minSpeed = Mathf.Max(1f, minSpeed); + maxSpeed = Mathf.Max(minSpeed, maxSpeed); + pointerSpeed = Mathf.Clamp(pointerSpeed, minSpeed, maxSpeed); + + minZoneSize = Mathf.Clamp(minZoneSize, 1f, 360f); + maxZoneSize = Mathf.Clamp(maxZoneSize, minZoneSize, 360f); + startZoneSize = Mathf.Clamp(startZoneSize, minZoneSize, maxZoneSize); + zoneSize = Mathf.Clamp(zoneSize, minZoneSize, maxZoneSize); + + nextRoundDelay = Mathf.Max(0f, nextRoundDelay); + nextCatchDelay = Mathf.Max(0f, nextCatchDelay); + finalResultShowTime = Mathf.Max(0f, finalResultShowTime); + catchCompleteLineLength = Mathf.Max(0.05f, catchCompleteLineLength); + simpleReelMinimumPullDistance = Mathf.Max(0.01f, simpleReelMinimumPullDistance); + + pointerAngle = Normalize(pointerAngle); + zoneCenter = Normalize(zoneCenter); + cleanupItemCount = Mathf.Max(0, cleanupItemCount); + totalCaughtItems = Mathf.Max(0, totalCaughtItems); + } + private void InitializeIdleUI() { if (ui != null) { - ui.HideRoundResult(); - ui.HideFinalResult(); + ui.InitializeFishingUI(); ui.UpdateCounter(0, requiredSuccesses, 0, allowedFails); + ui.UpdateFishingProgress(cleanupItemCount, requiredCleanupItems, pondCleaned, memoryPieceCollected, totalCaughtItems); + ui.UpdateInventoryUI(sessionFishCount, sessionTrashCount, sessionMemoryPieceCount, sessionCompassCount); ui.SetPointerRotation(0f); ui.SetZone(0f, startZoneSize); + ui.SetSimpleReelUIVisible(false); + ui.SetTimingGaugeVisible(fishingControlMode == FishingControlMode.TimingGauge); } if (centerIconRotate != null) @@ -91,30 +355,182 @@ private void InitializeIdleUI() public void StartFishing() { - if (nextRoundRoutine != null) + StartFishing(false); + } + + public void StartFishing(bool forceRestart) + { + ValidateRuntimeSettings(); + + if (preventRestartWhileSessionRunning && fishingSessionRunning && !forceRestart) { - StopCoroutine(nextRoundRoutine); - nextRoundRoutine = null; + if (showDebugLog) + Debug.Log("낚시 세션이 이미 진행 중이라 StartFishing 호출을 무시했습니다."); + return; + } + + StopRunningRoutines(); + + if (uiRoot != null) + uiRoot.SetActive(true); + + bool shouldResetPondState = forceRestart || resetPondStateOnStart; + + if (fishingControlMode == FishingControlMode.SimpleReel && resetPondOnlyOnFirstSimpleReelStart) + shouldResetPondState = forceRestart || (resetPondStateOnStart && !simpleReelPondInitialized); + + if (shouldResetPondState) + { + ResetPondState(); + if (fishingControlMode == FishingControlMode.SimpleReel) + simpleReelPondInitialized = true; + } + + fishingSessionRunning = true; + + if (ui != null) + ui.InitializeFishingUI(); + + if (fishingControlMode == FishingControlMode.SimpleReel) + StartSimpleReelCatch(); + else + StartNewCatchAttempt(); + + if (showDebugLog) + Debug.Log(fishingControlMode == FishingControlMode.SimpleReel ? "릴 감기 낚시 시작" : "기묘한 낚시터 시작"); + } + + public void StopFishing(bool hideUI = false) + { + StopRunningRoutines(); + + activeGame = false; + inputLocked = true; + fishingSessionRunning = false; + simpleReelCatchActive = false; + + if (centerIconRotate != null) + centerIconRotate.StopRotate(); + + if (hideUI && uiRoot != null) + uiRoot.SetActive(false); + } + + public void ResetFishing() + { + fishingSessionRunning = false; + simpleReelCatchActive = false; + simpleReelPondInitialized = false; + StartFishing(true); + } + + public void ResetPondState() + { + successCount = 0; + failCount = 0; + cleanupItemCount = 0; + totalCaughtItems = 0; + sessionFishCount = 0; + sessionTrashCount = 0; + sessionMemoryPieceCount = 0; + sessionCompassCount = 0; + pondCleaned = false; + memoryPieceCollected = false; + simpleReelCatchActive = false; + simpleReelProgress = 0f; + simpleReelStartLineLength = 0f; + } + + private void StartSimpleReelCatch() + { + if (rodController == null && autoBindMissingReferences) + AutoBindMissingReferences(); + + if (rodController == null) + { + Debug.LogWarning("[FishingGameManager] SimpleReel 모드에는 FishingRodVRController 참조가 필요합니다.", this); + activeGame = false; + inputLocked = true; + fishingSessionRunning = false; + return; } successCount = 0; failCount = 0; - - pointerAngle = 0f; - pointerSpeed = minSpeed; - zoneSize = startZoneSize; - - activeGame = true; inputLocked = false; + activeGame = true; + simpleReelCatchActive = true; - clockwise = Random.value > 0.5f; + float currentLineLength = Mathf.Max(rodController.CurrentLineLength, catchCompleteLineLength + simpleReelMinimumPullDistance); + simpleReelStartLineLength = currentLineLength; + simpleReelProgress = 0f; + + if (centerIconRotate != null) + { + centerIconRotate.ResetRotation(); + centerIconRotate.StopRotate(); + } if (ui != null) { - ui.HideRoundResult(); - ui.HideFinalResult(); + ui.SetSimpleReelUIVisible(true); + if (hideTimingGaugeInSimpleReelMode) + ui.SetTimingGaugeVisible(false); + ui.UpdateSimpleReelUI(simpleReelProgress, rodController.CurrentLineLength, rodController.IsReeling); + ui.ShowPersistentResult("릴을 감아 끌어올려라!"); } + UpdateUI(); + } + + private void UpdateSimpleReelCatch() + { + if (!simpleReelCatchActive || rodController == null) + return; + + float currentLineLength = rodController.CurrentLineLength; + float totalPullDistance = Mathf.Max(0.001f, simpleReelStartLineLength - catchCompleteLineLength); + float pulledDistance = simpleReelStartLineLength - currentLineLength; + simpleReelProgress = Mathf.Clamp01(pulledDistance / totalPullDistance); + + if (ui != null) + ui.UpdateSimpleReelUI(simpleReelProgress, currentLineLength, rodController.IsReeling); + + if (currentLineLength <= catchCompleteLineLength || simpleReelProgress >= 1f) + { + simpleReelCatchActive = false; + inputLocked = true; + + if (haptic != null) + haptic.Perfect(); + + if (ui != null) + { + ui.UpdateSimpleReelUI(1f, currentLineLength, false); + ui.ShowPersistentResult("낚았다!"); + } + + CatchItemSuccess(); + } + } + + private void StartNewCatchAttempt() + { + successCount = 0; + failCount = 0; + inputLocked = false; + activeGame = true; + + if (resetDifficultyEachCatch) + { + pointerAngle = 0f; + pointerSpeed = minSpeed; + zoneSize = startZoneSize; + } + + if (randomDirectionEachRound) + clockwise = UnityEngine.Random.value > 0.5f; + if (centerIconRotate != null) { centerIconRotate.ResetRotation(); @@ -123,9 +539,27 @@ public void StartFishing() RandomizeZone(); UpdateUI(); + } - if (showDebugLog) - Debug.Log(" "); + private void StopRunningRoutines() + { + if (nextRoundRoutine != null) + { + StopCoroutine(nextRoundRoutine); + nextRoundRoutine = null; + } + + if (nextCatchRoutine != null) + { + StopCoroutine(nextCatchRoutine); + nextCatchRoutine = null; + } + + if (finalResultRoutine != null) + { + StopCoroutine(finalResultRoutine); + finalResultRoutine = null; + } } private void RotatePointer() @@ -141,6 +575,9 @@ private void RotatePointer() public void SubmitAttempt() { + if (fishingControlMode == FishingControlMode.SimpleReel) + return; + if (!activeGame) return; @@ -149,8 +586,17 @@ public void SubmitAttempt() inputLocked = true; + if (ui != null) + ui.HideControllerGuide(); + ResultType result = EvaluateResult(); + if (ui != null) + { + ui.ShowResultForType(result); + ui.FlashFeedbackForType(result); + } + switch (result) { case ResultType.Perfect: @@ -168,7 +614,7 @@ public void SubmitAttempt() if (successCount >= requiredSuccesses) { - FishingSuccess(); + CatchItemSuccess(); return; } @@ -207,11 +653,8 @@ private void OnPerfect() if (haptic != null) haptic.Perfect(); - if (ui != null) - ui.ShowResult("Ϻ!"); - if (showDebugLog) - Debug.Log(" : Perfect"); + Debug.Log("낚시 판정: Perfect"); ClampDifficulty(); UpdateUI(); @@ -227,11 +670,8 @@ private void OnGood() if (haptic != null) haptic.Good(); - if (ui != null) - ui.ShowResult("!"); - if (showDebugLog) - Debug.Log(" : Good"); + Debug.Log("낚시 판정: Good"); ClampDifficulty(); UpdateUI(); @@ -247,11 +687,8 @@ private void OnMiss() if (haptic != null) haptic.Miss(); - if (ui != null) - ui.ShowResult("!"); - if (showDebugLog) - Debug.Log(" : Miss"); + Debug.Log("낚시 판정: Miss"); ClampDifficulty(); UpdateUI(); @@ -262,10 +699,13 @@ private IEnumerator NextRoundRoutine() yield return new WaitForSeconds(nextRoundDelay); if (!activeGame) + { + nextRoundRoutine = null; yield break; + } if (randomDirectionEachRound) - clockwise = Random.value > 0.5f; + clockwise = UnityEngine.Random.value > 0.5f; RandomizeZone(); UpdateUI(); @@ -274,6 +714,133 @@ private IEnumerator NextRoundRoutine() nextRoundRoutine = null; } + private void CatchItemSuccess() + { + activeGame = false; + inputLocked = true; + + if (centerIconRotate != null) + centerIconRotate.StopRotate(); + + bool forceMemoryPiece = pondCleaned && guaranteeMemoryPieceAfterCleaned && !memoryPieceCollected; + + FishingRewardSystem.CatchResult catchResult; + + if (rewardSystem != null) + catchResult = rewardSystem.RollCatch(pondCleaned, forceMemoryPiece); + else + catchResult = new FishingRewardSystem.CatchResult(FishingItemType.Fish, "생선", false, false); + + ApplyCatchResult(catchResult); + } + + private void ApplyCatchResult(FishingRewardSystem.CatchResult catchResult) + { + totalCaughtItems++; + + AddToSessionCounts(catchResult.ItemType); + ItemCaught?.Invoke(catchResult.ItemType, 1); + + bool pondJustCleaned = false; + string extraHint = string.Empty; + + if (catchResult.CountsAsCleanupItem) + { + cleanupItemCount = Mathf.Min(cleanupItemCount + 1, requiredCleanupItems); + extraHint = $"연못 정화 {cleanupItemCount}/{requiredCleanupItems}"; + + if (!pondCleaned && cleanupItemCount >= requiredCleanupItems) + { + pondCleaned = true; + pondJustCleaned = true; + extraHint = "연못이 맑아졌다. 기억의 조각이 모습을 드러낸다."; + } + } + + if (catchResult.IsMemoryPiece) + { + memoryPieceCollected = true; + extraHint = "잃어버린 기억의 일부다."; + } + + UpdateUI(); + + if (ui != null) + { + ui.HighlightSlotForItem(catchResult.ItemType); + + if (!catchResult.IsMemoryPiece) + ui.ShowCaughtItem(catchResult.ItemType, catchResult.DisplayName, catchResult.CountsAsCleanupItem, extraHint); + + if (pondJustCleaned) + ui.ShowNotice("연못이 맑아졌다!\n기억의 조각이 모습을 드러냈다!"); + + // 기억의 조각 획득은 FinalResultPanel에서 크게 보여줍니다. + // NoticePanel까지 동시에 띄우면 ShowFinalResult에서 바로 숨겨져 연출이 겹칠 수 있습니다. + } + + if (showDebugLog) + Debug.Log($"{catchResult.DisplayName}을(를) 낚았다!"); + + if (catchResult.IsMemoryPiece) + { + FishingSuccess(); + return; + } + + if (fishingControlMode == FishingControlMode.SimpleReel) + { + fishingSessionRunning = false; + simpleReelCatchActive = false; + + if (returnBobberAfterSimpleReelCatch && rodController != null) + rodController.ReturnBobberToRest(); + + return; + } + + nextCatchRoutine = StartCoroutine(NextCatchRoutine()); + } + + private IEnumerator NextCatchRoutine() + { + yield return new WaitForSeconds(nextCatchDelay); + + if (memoryPieceCollected) + { + nextCatchRoutine = null; + yield break; + } + + StartNewCatchAttempt(); + nextCatchRoutine = null; + } + + + private void AddToSessionCounts(FishingItemType itemType) + { + switch (itemType) + { + case FishingItemType.Fish: + sessionFishCount++; + break; + + case FishingItemType.Trash: + case FishingItemType.Bottle: + case FishingItemType.PlasticBag: + sessionTrashCount++; + break; + + case FishingItemType.MemoryPiece: + sessionMemoryPieceCount++; + break; + + case FishingItemType.OldCompass: + sessionCompassCount++; + break; + } + } + private void ClampDifficulty() { zoneSize = Mathf.Clamp(zoneSize, minZoneSize, maxZoneSize); @@ -282,7 +849,7 @@ private void ClampDifficulty() private void RandomizeZone() { - zoneCenter = Random.Range(0f, 360f); + zoneCenter = UnityEngine.Random.Range(0f, 360f); } private void UpdateUI() @@ -290,46 +857,88 @@ private void UpdateUI() if (ui == null) return; + if (fishingControlMode == FishingControlMode.SimpleReel) + { + ui.SetSimpleReelUIVisible(activeGame || simpleReelCatchActive); + if (hideTimingGaugeInSimpleReelMode) + ui.SetTimingGaugeVisible(false); + + ui.UpdateFishingProgress(cleanupItemCount, requiredCleanupItems, pondCleaned, memoryPieceCollected, totalCaughtItems); + ui.UpdateInventoryUI(sessionFishCount, sessionTrashCount, sessionMemoryPieceCount, sessionCompassCount); + + float currentLineLength = rodController != null ? rodController.CurrentLineLength : 0f; + bool isReeling = rodController != null && rodController.IsReeling; + ui.UpdateSimpleReelUI(simpleReelProgress, currentLineLength, isReeling); + return; + } + + ui.SetSimpleReelUIVisible(false); + ui.SetTimingGaugeVisible(true); ui.SetZone(zoneCenter, zoneSize); ui.UpdateCounter(successCount, requiredSuccesses, failCount, allowedFails); + ui.UpdateFishingProgress(cleanupItemCount, requiredCleanupItems, pondCleaned, memoryPieceCollected, totalCaughtItems); + ui.UpdateInventoryUI(sessionFishCount, sessionTrashCount, sessionMemoryPieceCount, sessionCompassCount); } private void FishingSuccess() { activeGame = false; inputLocked = true; + fishingSessionRunning = false; + simpleReelCatchActive = false; if (centerIconRotate != null) centerIconRotate.StopRotate(); - if (ui != null) - ui.ShowFinalResult(" !"); + UpdateUI(); - if (rewardSystem != null) - rewardSystem.GiveReward(); + if (ui != null) + ui.ShowFinalResult("기억의 조각 획득!\n기묘한 낚시터 클리어!"); if (showDebugLog) - Debug.Log(" "); + Debug.Log("기묘한 낚시터 클리어"); + + BeginFinalResultRoutineIfNeeded(); } private void FishingFail() { activeGame = false; inputLocked = true; + fishingSessionRunning = false; + simpleReelCatchActive = false; if (centerIconRotate != null) centerIconRotate.StopRotate(); if (ui != null) - ui.ShowFinalResult(" !"); + ui.ShowFinalResult("낚싯줄이 끊어졌다!\n다시 천천히 낚아보자."); if (showDebugLog) - Debug.Log(" "); + Debug.Log("낚시 실패: 낚싯줄 끊어짐"); + + BeginFinalResultRoutineIfNeeded(); } - public void ResetFishing() + private void BeginFinalResultRoutineIfNeeded() { - StartFishing(); + if (!hideUIRootAfterFinalResult || uiRoot == null) + return; + + if (finalResultRoutine != null) + StopCoroutine(finalResultRoutine); + + finalResultRoutine = StartCoroutine(HideUIRootAfterFinalResultRoutine()); + } + + private IEnumerator HideUIRootAfterFinalResultRoutine() + { + yield return new WaitForSeconds(finalResultShowTime); + + if (uiRoot != null) + uiRoot.SetActive(false); + + finalResultRoutine = null; } private float Normalize(float angle) @@ -342,17 +951,13 @@ private float Normalize(float angle) return angle; } - // XR Input Action Select / Trigger ư - public void OnSubmit(InputValue value) + private void OnSubmitAction(InputAction.CallbackContext context) { - if (value.isPressed) - SubmitAttempt(); + SubmitAttempt(); } - // Reset ư - public void OnReset(InputValue value) + private void OnResetAction(InputAction.CallbackContext context) { - if (value.isPressed) - ResetFishing(); + ResetFishing(); } -} \ No newline at end of file +} diff --git a/Assets/My project/Fishing Scripts/UI/FishingGaugeUI.cs b/Assets/My project/Fishing Scripts/UI/FishingGaugeUI.cs index c0a7969d..ed67dfa5 100644 --- a/Assets/My project/Fishing Scripts/UI/FishingGaugeUI.cs +++ b/Assets/My project/Fishing Scripts/UI/FishingGaugeUI.cs @@ -1,39 +1,369 @@ +using System; using System.Collections; using TMPro; using UnityEngine; using UnityEngine.UI; +[Serializable] +public class FishingItemIconData +{ + public FishingItemType itemType = FishingItemType.Fish; + public Sprite icon; + [TextArea] public string hintText; +} + public class FishingGaugeUI : MonoBehaviour { - [Header("Panel UI")] + [Header("Auto Bind")] + [SerializeField] private bool autoBindMissingReferences = true; + + [Header("Root / Main Panels")] + [SerializeField] private GameObject backgroundPanel; [SerializeField] private GameObject counterPanel; + [SerializeField] private GameObject itemSlotPanel; + [SerializeField] private GameObject caughtItemPanel; + [SerializeField] private GameObject controllerGuidePanel; + [SerializeField] private GameObject memoryPieceNoticePanel; [SerializeField] private GameObject finalResultPanel; + [SerializeField] private GameObject catchButton; - [Header("Gauge UI")] - [SerializeField] private RectTransform pointerPivot; - [SerializeField] private Image successZone; + [Header("Top Texts")] + [SerializeField] private TMP_Text titleText; + [SerializeField] private TMP_Text pondStateText; + [SerializeField] private TMP_Text objectiveText; - [Header("Text UI")] + [Header("Counter Panel Texts")] + [SerializeField] private TMP_Text catchCountText; [SerializeField] private TMP_Text successText; [SerializeField] private TMP_Text failText; + [SerializeField] private TMP_Text cleanupProgressText; + + [Header("Cleanup Gauge")] + [Tooltip("CleanupGauge/CleanupGaugeFill. Image Type = Filled, Fill Method = Horizontal, Fill Origin = Left")] + [SerializeField] private Image cleanupGaugeFill; + [SerializeField] private TMP_Text cleanupPercentText; + + [Header("Gauge UI")] + [SerializeField] private RectTransform gaugeGroupRect; + [SerializeField] private RectTransform pointerPivot; + [Tooltip("SuccessZone. Image Type = Filled, Fill Method = Radial 360")] + [SerializeField] private Image successZone; + + [Header("Simple Reel UI")] + [Tooltip("릴 감기 진행도 게이지 루트입니다. 없으면 텍스트만 업데이트됩니다.")] + [SerializeField] private GameObject reelProgressGaugeRoot; + + [Tooltip("릴 감기 진행도 Fill Image입니다. Image Type = Filled / Horizontal / Left 추천.")] + [SerializeField] private Image reelProgressGaugeFill; + + [Tooltip("현재 줄 길이를 보여줄 텍스트입니다. CounterPanel 안에 LineLengthText를 추가해서 연결하세요.")] + [SerializeField] private TMP_Text lineLengthText; + + [SerializeField] private string simpleReelObjectiveText = "릴을 감아 끌어올려라"; + [SerializeField] private string simpleReelIdleText = "릴 손잡이를 잡고 돌려라"; + [SerializeField] private string simpleReelReelingText = "좋다, 계속 감아라!"; + + [Header("Round Result UI")] [SerializeField] private TMP_Text resultText; + + [Header("Caught Item UI")] + [SerializeField] private Image itemIcon; + [SerializeField] private TMP_Text caughtItemText; + [SerializeField] private TMP_Text itemHintText; + + [Header("Item Slot UI - Fish")] + [SerializeField] private GameObject fishSlot; + [SerializeField] private Image fishSlotBackground; + [SerializeField] private Image fishIcon; + [SerializeField] private TMP_Text fishCountText; + + [Header("Item Slot UI - Trash")] + [SerializeField] private GameObject trashSlot; + [SerializeField] private Image trashSlotBackground; + [SerializeField] private Image trashIcon; + [SerializeField] private TMP_Text trashCountText; + + [Header("Item Slot UI - Memory")] + [SerializeField] private GameObject memorySlot; + [SerializeField] private Image memorySlotBackground; + [SerializeField] private Image memoryIcon; + [SerializeField] private TMP_Text memoryPieceCountText; + + [Header("Item Slot UI - Optional Compass")] + [SerializeField] private GameObject compassSlot; + [SerializeField] private Image compassSlotBackground; + [SerializeField] private Image compassIcon; + [SerializeField] private TMP_Text compassCountText; + + [Header("Panel Canvas Groups")] + [SerializeField] private CanvasGroup caughtItemCanvasGroup; + [SerializeField] private CanvasGroup controllerGuideCanvasGroup; + [SerializeField] private CanvasGroup memoryPieceNoticeCanvasGroup; + [SerializeField] private CanvasGroup finalResultCanvasGroup; + + [Header("Effects")] + [SerializeField] private FishingUIEffects effects; + [SerializeField] private bool autoCreateEffectsComponent = true; + [SerializeField] private bool animateCleanupGauge = true; + [SerializeField] private float cleanupFillTime = 0.45f; + [SerializeField] private float resultShowTime = 0.85f; + [SerializeField] private float resultPopScale = 1.18f; + [SerializeField] private float resultPopTime = 0.18f; + [SerializeField] private float caughtItemShowTime = 1.6f; + [SerializeField] private float panelFadeTime = 0.16f; + [SerializeField] private float panelPopScale = 1.06f; + [SerializeField] private float panelPopTime = 0.18f; + [SerializeField] private float noticeShowTime = 2.2f; + [SerializeField] private float finalPanelPopScale = 1.08f; + [SerializeField] private float finalPanelPopTime = 0.22f; + [SerializeField] private float gaugePopScale = 1.035f; + [SerializeField] private float gaugePopTime = 0.16f; + [SerializeField] private float missShakeStrength = 12f; + [SerializeField] private float missShakeTime = 0.18f; + + [Header("Slot Visual Settings")] + [Range(0f, 1f)] [SerializeField] private float emptyIconAlpha = 0.35f; + [Range(0f, 1f)] [SerializeField] private float filledIconAlpha = 1f; + [Range(0f, 1f)] [SerializeField] private float emptySlotAlpha = 0.55f; + [Range(0f, 1f)] [SerializeField] private float filledSlotAlpha = 1f; + [SerializeField] private float slotHighlightTime = 0.32f; + [SerializeField] private float slotHighlightScale = 1.08f; + [SerializeField] private Color slotHighlightColor = new Color(0.35f, 1f, 0.9f, 1f); + + [Header("Feedback UI")] + [SerializeField] private Image feedbackFlash; + [Range(0f, 1f)] [SerializeField] private float flashMaxAlpha = 0.25f; + [SerializeField] private float flashTime = 0.16f; + + [Header("Notice UI")] + [SerializeField] private TMP_Text noticeText; + + [Header("Final Result UI")] [SerializeField] private TMP_Text finalResultText; + [SerializeField] private bool hideCounterOnFinalResult = true; + [SerializeField] private bool hideCaughtItemOnFinalResult = true; + [SerializeField] private bool hideGuideOnFinalResult = true; + + [Header("Final Result Only Mode")] + [Tooltip("게임 클리어/실패 최종 결과가 뜰 때 진행 UI를 전부 숨기고 FinalResultPanel만 보이게 합니다.")] + [SerializeField] private bool showOnlyFinalResult = true; + + [Tooltip("FinalResultPanel을 숨길 때 진행 UI를 다시 켤지 결정합니다. 클리어 후 UI를 완전히 닫는 구조면 Off로 두세요.")] + [SerializeField] private bool restoreGameplayUIAfterFinalResult = true; + + [Tooltip("BackgroundPanel. 최종 결과창만 보이고 싶으면 자동 연결하거나 직접 연결하세요.")] + [SerializeField] private GameObject gameplayBackgroundRoot; + + [Tooltip("CleanupGauge 루트 오브젝트입니다.")] + [SerializeField] private GameObject cleanupGaugeRoot; + + [Tooltip("GaugeGroup 루트 오브젝트입니다.")] + [SerializeField] private GameObject gaugeGroupRoot; + + [Header("Item Icons")] + [SerializeField] private FishingItemIconData[] itemIcons; [Header("Zone Visual Correction")] - [Tooltip(" ̹ ġ ߳ ϼ.")] [SerializeField] private float zoneVisualOffset = 0f; - [Header("Result Settings")] - [SerializeField] private float resultShowTime = 1f; - [SerializeField] private bool hideCounterOnFinalResult = true; + [Header("Guide / Debug UI")] + [SerializeField] private bool showControllerGuideOnInitialize = true; + [SerializeField] private bool showCatchButtonOnInitialize = true; + + [Header("Text Settings")] + [SerializeField] private string titleDefaultText = "기묘한 낚시터"; + [SerializeField] private string objectiveDirtyText = "쓰레기를 건져 연못을 맑게 하자"; + [SerializeField] private string objectiveCleanText = "기억의 조각을 찾아보자"; + [SerializeField] private string objectiveClearText = "기억의 조각을 획득했다"; + + [Header("Result Colors")] + [SerializeField] private Color perfectColor = new Color(1f, 0.85f, 0.25f, 1f); + [SerializeField] private Color goodColor = new Color(0.25f, 1f, 0.8f, 1f); + [SerializeField] private Color missColor = new Color(1f, 0.3f, 0.2f, 1f); + [SerializeField] private Color defaultResultColor = Color.white; private Coroutine resultRoutine; + private Coroutine caughtItemRoutine; + private Coroutine noticeRoutine; + private bool initialized; + private float lastCleanupFill = -1f; private void Awake() { - HideRoundResult(); - HideFinalResult(); + if (autoBindMissingReferences) + AutoBindMissingReferences(); + + EnsureEffectReferences(); + InitializeFishingUI(); + } + + private void OnDisable() + { + StopRunningRoutines(); + } + + private void OnValidate() + { + cleanupFillTime = Mathf.Max(0f, cleanupFillTime); + resultShowTime = Mathf.Max(0f, resultShowTime); + resultPopScale = Mathf.Max(1f, resultPopScale); + resultPopTime = Mathf.Max(0.01f, resultPopTime); + caughtItemShowTime = Mathf.Max(0f, caughtItemShowTime); + panelFadeTime = Mathf.Max(0f, panelFadeTime); + panelPopScale = Mathf.Max(1f, panelPopScale); + panelPopTime = Mathf.Max(0.01f, panelPopTime); + noticeShowTime = Mathf.Max(0f, noticeShowTime); + finalPanelPopScale = Mathf.Max(1f, finalPanelPopScale); + finalPanelPopTime = Mathf.Max(0.01f, finalPanelPopTime); + gaugePopScale = Mathf.Max(1f, gaugePopScale); + gaugePopTime = Mathf.Max(0.01f, gaugePopTime); + missShakeStrength = Mathf.Max(0f, missShakeStrength); + missShakeTime = Mathf.Max(0.01f, missShakeTime); + slotHighlightTime = Mathf.Max(0.01f, slotHighlightTime); + slotHighlightScale = Mathf.Max(1f, slotHighlightScale); + flashMaxAlpha = Mathf.Clamp01(flashMaxAlpha); + flashTime = Mathf.Max(0.01f, flashTime); + emptyIconAlpha = Mathf.Clamp01(emptyIconAlpha); + filledIconAlpha = Mathf.Clamp01(filledIconAlpha); + emptySlotAlpha = Mathf.Clamp01(emptySlotAlpha); + filledSlotAlpha = Mathf.Clamp01(filledSlotAlpha); + } + + private void EnsureEffectReferences() + { + if (effects == null) + effects = GetComponent(); + + if (effects == null && autoCreateEffectsComponent) + effects = gameObject.AddComponent(); + + if (effects == null) + return; + + if (caughtItemCanvasGroup == null && caughtItemPanel != null) + caughtItemCanvasGroup = effects.EnsureCanvasGroup(caughtItemPanel); + + if (controllerGuideCanvasGroup == null && controllerGuidePanel != null) + controllerGuideCanvasGroup = effects.EnsureCanvasGroup(controllerGuidePanel); + + if (memoryPieceNoticeCanvasGroup == null && memoryPieceNoticePanel != null) + memoryPieceNoticeCanvasGroup = effects.EnsureCanvasGroup(memoryPieceNoticePanel); + + if (finalResultCanvasGroup == null && finalResultPanel != null) + finalResultCanvasGroup = effects.EnsureCanvasGroup(finalResultPanel); + } + + [ContextMenu("Auto Bind UI References")] + public void AutoBindMissingReferences() + { + if (backgroundPanel == null) backgroundPanel = FindGameObject("BackgroundPanel"); + if (gameplayBackgroundRoot == null) gameplayBackgroundRoot = backgroundPanel; + if (cleanupGaugeRoot == null) cleanupGaugeRoot = FindGameObject("CleanupGauge"); + if (gaugeGroupRoot == null) gaugeGroupRoot = FindGameObject("GaugeGroup"); + + if (counterPanel == null) counterPanel = FindGameObject("CounterPanel"); + if (itemSlotPanel == null) itemSlotPanel = FindGameObject("ItemSlotPanel"); + if (caughtItemPanel == null) caughtItemPanel = FindGameObject("CaughtItemPanel"); + if (controllerGuidePanel == null) controllerGuidePanel = FindGameObject("ControllerGuidePanel"); + if (memoryPieceNoticePanel == null) memoryPieceNoticePanel = FindGameObject("MemoryPieceNoticePanel"); + if (finalResultPanel == null) finalResultPanel = FindGameObject("FinalResultPanel"); + if (catchButton == null) catchButton = FindGameObject("CatchButton", "DebugCatchButton"); + + if (titleText == null) titleText = FindComponentByName("TitleText"); + if (pondStateText == null) pondStateText = FindComponentByName("PondStateText"); + if (objectiveText == null) objectiveText = FindComponentByName("ObjectiveText"); + + if (catchCountText == null) catchCountText = FindComponentByName("CatchCountText", "SuccessText"); + if (successText == null) successText = FindComponentByName("SuccessText"); + if (failText == null) failText = FindComponentByName("FailText"); + if (cleanupProgressText == null) cleanupProgressText = FindComponentByName("CleanupProgressText"); + + if (cleanupGaugeFill == null) cleanupGaugeFill = FindComponentByName("CleanupGaugeFill", "CleanupGaugeFillImage"); + if (cleanupPercentText == null) cleanupPercentText = FindComponentByName("CleanupPercentText", "CleanupGaugePercentText"); + + if (gaugeGroupRect == null) gaugeGroupRect = FindComponentByName("GaugeGroup"); + if (pointerPivot == null) pointerPivot = FindComponentByName("PointerPivot"); + if (successZone == null) successZone = FindComponentByName("SuccessZone"); + if (reelProgressGaugeRoot == null) reelProgressGaugeRoot = FindGameObject("ReelProgressGauge", "ReelProgressGroup", "ReelFightProgressGauge"); + if (reelProgressGaugeFill == null) reelProgressGaugeFill = FindComponentByName("ReelProgressGaugeFill", "ReelProgressFill", "ReelFightProgressFill"); + if (lineLengthText == null) lineLengthText = FindComponentByName("LineLengthText", "Line Length Text", "LineText"); + if (resultText == null) resultText = FindComponentByName("ResultText"); + + if (itemIcon == null) itemIcon = FindComponentByName("ItemIcon"); + if (caughtItemText == null) caughtItemText = FindComponentByName("CaughtItemText"); + if (itemHintText == null) itemHintText = FindComponentByName("ItemHintText"); + + if (fishSlot == null) fishSlot = FindGameObject("FishSlot"); + if (fishSlotBackground == null && fishSlot != null) fishSlotBackground = fishSlot.GetComponent(); + if (fishIcon == null) fishIcon = FindComponentByName("FishIcon"); + if (fishCountText == null) fishCountText = FindComponentByName("FishCountText"); + + if (trashSlot == null) trashSlot = FindGameObject("trash Slot", "TrashSlot"); + if (trashSlotBackground == null && trashSlot != null) trashSlotBackground = trashSlot.GetComponent(); + if (trashIcon == null) trashIcon = FindComponentByName("trashIcon", "TrashIcon"); + if (trashCountText == null) trashCountText = FindComponentByName("trashCountText", "TrashCountText", "CleanupCountText"); + + if (memorySlot == null) memorySlot = FindGameObject("Memory Slot", "MemorySlot", "MemoryPieceSlot"); + if (memorySlotBackground == null && memorySlot != null) memorySlotBackground = memorySlot.GetComponent(); + if (memoryIcon == null) memoryIcon = FindComponentByName("Memory Icon", "MemoryIcon", "MemoryPieceIcon"); + if (memoryPieceCountText == null) memoryPieceCountText = FindComponentByName("Memory CountText", "MemoryCountText", "MemoryPieceCountText"); + + if (compassSlot == null) compassSlot = FindGameObject("CompassSlot", "Compass Slot"); + if (compassSlotBackground == null && compassSlot != null) compassSlotBackground = compassSlot.GetComponent(); + if (compassIcon == null) compassIcon = FindComponentByName("CompassIcon", "Compass Icon"); + if (compassCountText == null) compassCountText = FindComponentByName("CompassCountText", "Compass CountText"); + + if (feedbackFlash == null) feedbackFlash = FindComponentByName("FeedbackFlash"); + if (finalResultText == null) finalResultText = FindComponentByName("FinalResultText"); + if (noticeText == null) noticeText = FindComponentByName("NoticeText"); + + if (caughtItemCanvasGroup == null && caughtItemPanel != null) caughtItemCanvasGroup = caughtItemPanel.GetComponent(); + if (controllerGuideCanvasGroup == null && controllerGuidePanel != null) controllerGuideCanvasGroup = controllerGuidePanel.GetComponent(); + if (memoryPieceNoticeCanvasGroup == null && memoryPieceNoticePanel != null) memoryPieceNoticeCanvasGroup = memoryPieceNoticePanel.GetComponent(); + if (finalResultCanvasGroup == null && finalResultPanel != null) finalResultCanvasGroup = finalResultPanel.GetComponent(); + } + + public void InitializeFishingUI() + { + StopRunningRoutines(); + EnsureEffectReferences(); + + SetGameplayUIVisible(true); + + initialized = false; + + if (titleText != null && !string.IsNullOrWhiteSpace(titleDefaultText)) + titleText.text = titleDefaultText; + ShowCounter(); + SetItemSlotPanelVisible(true); + SetControllerGuideVisible(showControllerGuideOnInitialize, true); + SetCatchButtonVisible(showCatchButtonOnInitialize); + HideRoundResult(); + HideCaughtItem(true); + HideNotice(true); + HideFinalResult(true, true); + SetFeedbackFlashVisible(false, Color.clear); + UpdateInventoryUI(0, 0, 0, 0); + lastCleanupFill = -1f; + SetCleanupFill(0f, true); + SetSimpleReelUIVisible(false); + + initialized = true; + } + + private void StopRunningRoutines() + { + if (resultRoutine != null) StopCoroutine(resultRoutine); + if (caughtItemRoutine != null) StopCoroutine(caughtItemRoutine); + if (noticeRoutine != null) StopCoroutine(noticeRoutine); + + resultRoutine = null; + caughtItemRoutine = null; + noticeRoutine = null; + + if (effects != null) + effects.StopAllEffects(); } public void SetPointerRotation(float angle) @@ -64,19 +394,262 @@ public void SetZoneRotation(float centerAngle, float size) return; float startAngle = centerAngle - size * 0.5f + zoneVisualOffset; - successZone.rectTransform.localEulerAngles = - new Vector3(0f, 0f, -startAngle); + successZone.rectTransform.localEulerAngles = new Vector3(0f, 0f, -startAngle); } public void UpdateCounter(int success, int successTarget, int fail, int failTarget) { ShowCounter(); - if (successText != null) - successText.text = $" {success}/{successTarget}"; + if (catchCountText != null) + catchCountText.text = $"낚기 {success}/{successTarget}"; + + if (successText != null && successText != catchCountText) + successText.text = $"낚기 {success}/{successTarget}"; if (failText != null) - failText.text = $" {fail}/{failTarget}"; + failText.text = $"실패 {fail}/{failTarget}"; + } + + public void SetTimingGaugeVisible(bool visible) + { + if (gaugeGroupRoot != null) + gaugeGroupRoot.SetActive(visible); + else if (gaugeGroupRect != null) + gaugeGroupRect.gameObject.SetActive(visible); + } + + public void SetSimpleReelUIVisible(bool visible) + { + if (reelProgressGaugeRoot != null) + reelProgressGaugeRoot.SetActive(visible); + } + + public void UpdateSimpleReelUI(float progress, float lineLength, bool isReeling) + { + ShowCounter(); + + progress = Mathf.Clamp01(progress); + lineLength = Mathf.Max(0f, lineLength); + int progressPercent = Mathf.RoundToInt(progress * 100f); + + if (catchCountText != null) + catchCountText.text = $"끌어올리기 {progressPercent}%"; + + if (successText != null && successText != catchCountText) + successText.text = $"끌어올리기 {progressPercent}%"; + + if (failText != null) + failText.text = $"줄 길이 {lineLength:0.0}m"; + + if (lineLengthText != null) + lineLengthText.text = $"줄 길이 {lineLength:0.0}m"; + + if (reelProgressGaugeFill != null) + reelProgressGaugeFill.fillAmount = progress; + + if (objectiveText != null && !string.IsNullOrWhiteSpace(simpleReelObjectiveText)) + objectiveText.text = simpleReelObjectiveText; + + if (resultText != null && resultText.gameObject.activeSelf) + { + if (isReeling && progress < 1f) + resultText.text = simpleReelReelingText; + else if (progress < 1f && !string.IsNullOrWhiteSpace(simpleReelIdleText)) + resultText.text = simpleReelIdleText; + } + } + + public void UpdateFishingProgress(int cleanupItemCount, int cleanupTarget, bool pondCleaned, bool memoryPieceCollected, int totalCaughtItems) + { + cleanupTarget = Mathf.Max(1, cleanupTarget); + int clampedCleanupCount = Mathf.Clamp(cleanupItemCount, 0, cleanupTarget); + float progress = Mathf.Clamp01((float)clampedCleanupCount / cleanupTarget); + + if (cleanupProgressText != null) + { + cleanupProgressText.text = memoryPieceCollected + ? $"정화 완료 {cleanupTarget}/{cleanupTarget}" + : $"쓰레기 수거 {clampedCleanupCount}/{cleanupTarget}"; + } + + SetCleanupFill(progress, !initialized || !animateCleanupGauge); + + if (cleanupPercentText != null) + cleanupPercentText.text = $"{Mathf.RoundToInt(progress * 100f)}%"; + + if (pondStateText != null) + { + if (memoryPieceCollected) + pondStateText.text = "기억의 조각 획득"; + else if (pondCleaned) + pondStateText.text = "연못이 맑아졌다!"; + else if (progress >= 0.66f) + pondStateText.text = "연못 상태: 거의 맑아짐"; + else if (progress >= 0.33f) + pondStateText.text = "연못 상태: 조금 맑아짐"; + else + pondStateText.text = "연못 상태: 탁함"; + } + + if (objectiveText != null) + { + if (memoryPieceCollected) + objectiveText.text = objectiveClearText; + else if (pondCleaned) + objectiveText.text = objectiveCleanText; + else + objectiveText.text = objectiveDirtyText; + } + } + + private void SetCleanupFill(float progress, bool instant) + { + if (cleanupGaugeFill == null) + return; + + progress = Mathf.Clamp01(progress); + + if (!instant && Mathf.Abs(lastCleanupFill - progress) < 0.001f) + return; + + lastCleanupFill = progress; + + if (instant || effects == null) + cleanupGaugeFill.fillAmount = progress; + else + effects.FillImage(cleanupGaugeFill, progress, cleanupFillTime); + } + + public void UpdateInventoryUI(int fishCount, int trashCount, int memoryPieceCount, int compassCount = 0) + { + fishCount = Mathf.Max(0, fishCount); + trashCount = Mathf.Max(0, trashCount); + memoryPieceCount = Mathf.Max(0, memoryPieceCount); + compassCount = Mathf.Max(0, compassCount); + + if (fishCountText != null) + fishCountText.text = $"생선\nx{fishCount}"; + + if (trashCountText != null) + trashCountText.text = $"쓰레기\nx{trashCount}"; + + if (memoryPieceCountText != null) + memoryPieceCountText.text = $"기억의 조각\nx{memoryPieceCount}"; + + if (compassCountText != null) + compassCountText.text = $"나침반\nx{compassCount}"; + + ApplySlotState(fishSlot, fishSlotBackground, fishIcon, fishCount > 0); + ApplySlotState(trashSlot, trashSlotBackground, trashIcon, trashCount > 0); + ApplySlotState(memorySlot, memorySlotBackground, memoryIcon, memoryPieceCount > 0); + ApplySlotState(compassSlot, compassSlotBackground, compassIcon, compassCount > 0); + } + + public void HighlightSlotForItem(FishingItemType itemType) + { + GameObject targetSlot = GetSlotForItem(itemType); + Image targetBackground = GetSlotBackgroundForItem(itemType); + Image targetIcon = GetSlotIconForItem(itemType); + + if (targetSlot == null && targetBackground == null && targetIcon == null) + return; + + if (effects != null) + effects.HighlightSlot(targetBackground, targetIcon, targetSlot != null ? targetSlot.transform : null, slotHighlightColor, slotHighlightTime, slotHighlightScale); + else + StartCoroutine(SimpleSlotHighlightRoutine(targetBackground, targetIcon)); + } + + private IEnumerator SimpleSlotHighlightRoutine(Image background, Image icon) + { + Color? originalBackground = null; + Color? originalIcon = null; + + if (background != null) + { + originalBackground = background.color; + Color c = background.color; + c.a = 1f; + background.color = c; + } + + if (icon != null) + { + originalIcon = icon.color; + Color c = icon.color; + c.a = 1f; + icon.color = c; + } + + yield return new WaitForSeconds(slotHighlightTime); + + if (background != null && originalBackground.HasValue) + background.color = originalBackground.Value; + + if (icon != null && originalIcon.HasValue) + icon.color = originalIcon.Value; + } + + private void ApplySlotState(GameObject slotRoot, Image slotBackground, Image icon, bool hasItem) + { + if (slotRoot != null) + slotRoot.SetActive(true); + + SetImageAlpha(slotBackground, hasItem ? filledSlotAlpha : emptySlotAlpha); + SetImageAlpha(icon, hasItem ? filledIconAlpha : emptyIconAlpha); + } + + private void SetImageAlpha(Image image, float alpha) + { + if (image == null) + return; + + Color color = image.color; + color.a = Mathf.Clamp01(alpha); + image.color = color; + } + + private GameObject GetSlotForItem(FishingItemType itemType) + { + switch (itemType) + { + case FishingItemType.Fish: return fishSlot; + case FishingItemType.Trash: + case FishingItemType.Bottle: + case FishingItemType.PlasticBag: return trashSlot; + case FishingItemType.MemoryPiece: return memorySlot; + case FishingItemType.OldCompass: return compassSlot; + default: return null; + } + } + + private Image GetSlotBackgroundForItem(FishingItemType itemType) + { + switch (itemType) + { + case FishingItemType.Fish: return fishSlotBackground; + case FishingItemType.Trash: + case FishingItemType.Bottle: + case FishingItemType.PlasticBag: return trashSlotBackground; + case FishingItemType.MemoryPiece: return memorySlotBackground; + case FishingItemType.OldCompass: return compassSlotBackground; + default: return null; + } + } + + private Image GetSlotIconForItem(FishingItemType itemType) + { + switch (itemType) + { + case FishingItemType.Fish: return fishIcon; + case FishingItemType.Trash: + case FishingItemType.Bottle: + case FishingItemType.PlasticBag: return trashIcon; + case FishingItemType.MemoryPiece: return memoryIcon; + case FishingItemType.OldCompass: return compassIcon; + default: return null; + } } public void ShowCounter() @@ -91,7 +664,24 @@ public void HideCounter() counterPanel.SetActive(false); } + public void SetCounterVisible(bool visible) + { + if (visible) ShowCounter(); + else HideCounter(); + } + + public void SetItemSlotPanelVisible(bool visible) + { + if (itemSlotPanel != null) + itemSlotPanel.SetActive(visible); + } + public void ShowResult(string text) + { + ShowResult(text, defaultResultColor); + } + + public void ShowResult(string text, Color color) { if (resultText == null) return; @@ -99,20 +689,425 @@ public void ShowResult(string text) if (resultRoutine != null) StopCoroutine(resultRoutine); - resultRoutine = StartCoroutine(ResultRoutine(text)); + resultRoutine = StartCoroutine(ResultRoutine(text, color)); } - private IEnumerator ResultRoutine(string text) + public void ShowPersistentResult(string text) { + ShowPersistentResult(text, defaultResultColor); + } + + public void ShowPersistentResult(string text, Color color) + { + if (resultText == null) + return; + + if (resultRoutine != null) + { + StopCoroutine(resultRoutine); + resultRoutine = null; + } + resultText.gameObject.SetActive(true); + resultText.color = color; resultText.text = text; - yield return new WaitForSeconds(resultShowTime); + if (effects != null) + effects.Pop(resultText.transform, resultPopScale, resultPopTime); + } + + public void ShowResultForType(FishingGameManager.ResultType resultType) + { + switch (resultType) + { + case FishingGameManager.ResultType.Perfect: + ShowResult("완벽!", perfectColor); + if (effects != null && gaugeGroupRect != null) + effects.Pop(gaugeGroupRect, gaugePopScale, gaugePopTime); + break; + + case FishingGameManager.ResultType.Good: + ShowResult("성공!", goodColor); + if (effects != null && gaugeGroupRect != null) + effects.Pop(gaugeGroupRect, gaugePopScale, gaugePopTime); + break; + + case FishingGameManager.ResultType.Miss: + ShowResult("실패!", missColor); + if (effects != null && gaugeGroupRect != null) + effects.Shake(gaugeGroupRect, missShakeStrength, missShakeTime); + break; + } + } + + private IEnumerator ResultRoutine(string text, Color color) + { + resultText.gameObject.SetActive(true); + resultText.color = color; + resultText.text = text; + + if (effects != null) + effects.Pop(resultText.transform, resultPopScale, resultPopTime); + + if (resultShowTime > 0f) + yield return new WaitForSeconds(resultShowTime); + else + yield return null; resultText.gameObject.SetActive(false); resultRoutine = null; } + public void ShowCaughtItem(string text) + { + ShowCaughtItem(FishingItemType.None, text, null); + } + + public void ShowCaughtItem(FishingItemType itemType, string displayName, bool countsAsCleanupItem, string extraMessage = null) + { + string caughtMessage = GetCaughtMessage(itemType, displayName); + string hintMessage = string.IsNullOrWhiteSpace(extraMessage) ? GetDefaultHint(itemType, countsAsCleanupItem) : extraMessage; + ShowCaughtItem(itemType, caughtMessage, hintMessage); + } + + private void ShowCaughtItem(FishingItemType itemType, string caughtMessage, string hintMessage) + { + if (caughtItemText == null && resultText != null) + { + ShowResult(caughtMessage); + return; + } + + if (caughtItemRoutine != null) + StopCoroutine(caughtItemRoutine); + + caughtItemRoutine = StartCoroutine(CaughtItemRoutine(itemType, caughtMessage, hintMessage)); + } + + private IEnumerator CaughtItemRoutine(FishingItemType itemType, string caughtMessage, string hintMessage) + { + if (caughtItemPanel != null) + caughtItemPanel.SetActive(true); + + if (caughtItemCanvasGroup != null) + { + caughtItemCanvasGroup.alpha = 0f; + caughtItemCanvasGroup.interactable = false; + caughtItemCanvasGroup.blocksRaycasts = false; + } + + if (caughtItemText != null) + { + caughtItemText.gameObject.SetActive(true); + caughtItemText.text = caughtMessage; + } + + ApplyItemIcon(itemType); + + if (itemHintText != null) + { + itemHintText.gameObject.SetActive(!string.IsNullOrWhiteSpace(hintMessage)); + itemHintText.text = hintMessage ?? string.Empty; + } + + if (effects != null) + { + effects.FadeCanvasGroup(caughtItemPanel, caughtItemCanvasGroup, true, panelFadeTime); + if (caughtItemPanel != null) + effects.Pop(caughtItemPanel.transform, panelPopScale, panelPopTime); + } + else if (caughtItemCanvasGroup != null) + { + caughtItemCanvasGroup.alpha = 1f; + } + + if (caughtItemShowTime > 0f) + yield return new WaitForSeconds(caughtItemShowTime); + else + yield return null; + + if (effects != null) + effects.FadeCanvasGroup(caughtItemPanel, caughtItemCanvasGroup, false, panelFadeTime); + else if (caughtItemPanel != null) + caughtItemPanel.SetActive(false); + + if (caughtItemText != null) + caughtItemText.gameObject.SetActive(false); + + if (itemHintText != null) + itemHintText.gameObject.SetActive(false); + + caughtItemRoutine = null; + } + + public void HideCaughtItem() + { + HideCaughtItem(false); + } + + private void HideCaughtItem(bool instant) + { + if (caughtItemRoutine != null) + { + StopCoroutine(caughtItemRoutine); + caughtItemRoutine = null; + } + + if (instant || effects == null) + { + if (caughtItemPanel != null) + caughtItemPanel.SetActive(false); + + if (caughtItemCanvasGroup != null) + { + caughtItemCanvasGroup.alpha = 0f; + caughtItemCanvasGroup.interactable = false; + caughtItemCanvasGroup.blocksRaycasts = false; + } + } + else + { + effects.FadeCanvasGroup(caughtItemPanel, caughtItemCanvasGroup, false, panelFadeTime); + } + + if (caughtItemText != null) + caughtItemText.gameObject.SetActive(false); + + if (itemHintText != null) + itemHintText.gameObject.SetActive(false); + } + + private void ApplyItemIcon(FishingItemType itemType) + { + if (itemIcon == null) + return; + + FishingItemIconData iconData = GetIconData(itemType); + itemIcon.sprite = iconData != null ? iconData.icon : null; + itemIcon.enabled = itemIcon.sprite != null; + + if (itemIcon.enabled && effects != null) + effects.Pop(itemIcon.transform, 1.12f, 0.18f); + } + + private FishingItemIconData GetIconData(FishingItemType itemType) + { + if (itemIcons == null) + return null; + + for (int i = 0; i < itemIcons.Length; i++) + { + if (itemIcons[i] != null && itemIcons[i].itemType == itemType) + return itemIcons[i]; + } + + return null; + } + + private string GetCaughtMessage(FishingItemType itemType, string displayName) + { + if (!string.IsNullOrWhiteSpace(displayName) && itemType != FishingItemType.None) + return $"{displayName}을(를) 낚았다!"; + + switch (itemType) + { + case FishingItemType.Fish: return "생선을 낚았다!"; + case FishingItemType.RottenFish: return "상한 생선을 낚았다..."; + case FishingItemType.Trash: return "쓰레기를 건져냈다!"; + case FishingItemType.Bottle: return "병을 건져냈다!"; + case FishingItemType.PlasticBag: return "비닐봉지를 건져냈다!"; + case FishingItemType.JellyfishPowder: return "해파리 가루를 얻었다!"; + case FishingItemType.OldCompass: return "낡은 나침반을 얻었다!"; + case FishingItemType.MemoryPiece: return "기억의 조각을 찾았다!"; + default: return string.IsNullOrWhiteSpace(displayName) ? "무언가를 낚았다!" : displayName; + } + } + + private string GetDefaultHint(FishingItemType itemType, bool countsAsCleanupItem) + { + FishingItemIconData iconData = GetIconData(itemType); + + if (iconData != null && !string.IsNullOrWhiteSpace(iconData.hintText)) + return iconData.hintText; + + if (countsAsCleanupItem) + return "연못이 조금 맑아졌다."; + + switch (itemType) + { + case FishingItemType.Fish: return "생선은 고양이들이 좋아할 것 같다."; + case FishingItemType.RottenFish: return "냄새가 심하다. 쓸 수 있을까?"; + case FishingItemType.JellyfishPowder: return "이상한 빛을 내는 가루다."; + case FishingItemType.OldCompass: return "길을 찾는 데 도움이 될 것 같다."; + case FishingItemType.MemoryPiece: return "잃어버린 기억의 일부다."; + default: return string.Empty; + } + } + + public void ShowNotice(string message) + { + if (noticeRoutine != null) + StopCoroutine(noticeRoutine); + + noticeRoutine = StartCoroutine(NoticeRoutine(message)); + } + + private IEnumerator NoticeRoutine(string message) + { + if (memoryPieceNoticePanel != null) + memoryPieceNoticePanel.SetActive(true); + + if (memoryPieceNoticeCanvasGroup != null) + { + memoryPieceNoticeCanvasGroup.alpha = 0f; + memoryPieceNoticeCanvasGroup.interactable = false; + memoryPieceNoticeCanvasGroup.blocksRaycasts = false; + } + + if (noticeText != null) + { + noticeText.gameObject.SetActive(true); + noticeText.text = message; + } + + if (effects != null) + { + effects.FadeCanvasGroup(memoryPieceNoticePanel, memoryPieceNoticeCanvasGroup, true, panelFadeTime); + if (memoryPieceNoticePanel != null) + effects.Pop(memoryPieceNoticePanel.transform, panelPopScale, panelPopTime); + } + else if (memoryPieceNoticeCanvasGroup != null) + { + memoryPieceNoticeCanvasGroup.alpha = 1f; + } + + if (noticeShowTime > 0f) + yield return new WaitForSeconds(noticeShowTime); + else + yield return null; + + if (effects != null) + effects.FadeCanvasGroup(memoryPieceNoticePanel, memoryPieceNoticeCanvasGroup, false, panelFadeTime); + else if (memoryPieceNoticePanel != null) + memoryPieceNoticePanel.SetActive(false); + + if (noticeText != null) + noticeText.gameObject.SetActive(false); + + noticeRoutine = null; + } + + public void HideNotice() + { + HideNotice(false); + } + + private void HideNotice(bool instant) + { + if (noticeRoutine != null) + { + StopCoroutine(noticeRoutine); + noticeRoutine = null; + } + + if (instant || effects == null) + { + if (memoryPieceNoticePanel != null) + memoryPieceNoticePanel.SetActive(false); + + if (memoryPieceNoticeCanvasGroup != null) + { + memoryPieceNoticeCanvasGroup.alpha = 0f; + memoryPieceNoticeCanvasGroup.interactable = false; + memoryPieceNoticeCanvasGroup.blocksRaycasts = false; + } + } + else + { + effects.FadeCanvasGroup(memoryPieceNoticePanel, memoryPieceNoticeCanvasGroup, false, panelFadeTime); + } + + if (noticeText != null) + noticeText.gameObject.SetActive(false); + } + + public void HideControllerGuide() + { + SetControllerGuideVisible(false, false); + } + + public void SetControllerGuideVisible(bool visible) + { + SetControllerGuideVisible(visible, false); + } + + private void SetControllerGuideVisible(bool visible, bool instant) + { + if (effects != null && !instant) + { + effects.FadeCanvasGroup(controllerGuidePanel, controllerGuideCanvasGroup, visible, panelFadeTime); + } + else + { + if (controllerGuidePanel != null) + controllerGuidePanel.SetActive(visible); + + if (controllerGuideCanvasGroup != null) + { + controllerGuideCanvasGroup.alpha = visible ? 1f : 0f; + controllerGuideCanvasGroup.interactable = visible; + controllerGuideCanvasGroup.blocksRaycasts = visible; + } + } + } + + public void SetCatchButtonVisible(bool visible) + { + if (catchButton != null) + catchButton.SetActive(visible); + } + + public void FlashFeedback(Color color) + { + if (feedbackFlash == null) + return; + + if (effects != null) + effects.Flash(feedbackFlash, color, flashMaxAlpha, flashTime); + else + { + Color clear = color; + clear.a = 0f; + feedbackFlash.color = clear; + feedbackFlash.gameObject.SetActive(false); + } + } + + public void FlashFeedbackForType(FishingGameManager.ResultType resultType) + { + switch (resultType) + { + case FishingGameManager.ResultType.Perfect: + FlashFeedback(perfectColor); + break; + case FishingGameManager.ResultType.Good: + FlashFeedback(goodColor); + break; + case FishingGameManager.ResultType.Miss: + FlashFeedback(missColor); + break; + } + } + + private void SetFeedbackFlashVisible(bool visible, Color color) + { + if (feedbackFlash == null) + return; + + feedbackFlash.gameObject.SetActive(visible); + feedbackFlash.color = color; + } + public void HideRoundResult() { if (resultRoutine != null) @@ -125,31 +1120,206 @@ public void HideRoundResult() resultText.gameObject.SetActive(false); } + + private void SetGameplayUIVisible(bool visible) + { + SetGameObjectVisible(gameplayBackgroundRoot, visible); + + if (titleText != null) + titleText.gameObject.SetActive(visible); + + if (pondStateText != null) + pondStateText.gameObject.SetActive(visible); + + if (objectiveText != null) + objectiveText.gameObject.SetActive(visible); + + SetGameObjectVisible(counterPanel, visible); + SetGameObjectVisible(itemSlotPanel, visible); + SetGameObjectVisible(cleanupGaugeRoot, visible); + + if (!visible) + SetSimpleReelUIVisible(false); + + if (gaugeGroupRoot != null) + gaugeGroupRoot.SetActive(visible); + else if (gaugeGroupRect != null) + gaugeGroupRect.gameObject.SetActive(visible); + + SetControllerGuideVisible(visible && showControllerGuideOnInitialize, true); + SetCatchButtonVisible(visible && showCatchButtonOnInitialize); + + if (!visible) + { + HideRoundResult(); + HideCaughtItem(true); + HideNotice(true); + SetFeedbackFlashVisible(false, Color.clear); + } + } + + private void SetGameObjectVisible(GameObject target, bool visible) + { + if (target != null) + target.SetActive(visible); + } + public void ShowFinalResult(string text) { HideRoundResult(); - if (hideCounterOnFinalResult) - HideCounter(); + if (hideCaughtItemOnFinalResult) + HideCaughtItem(true); + + HideNotice(true); + + if (showOnlyFinalResult) + { + SetGameplayUIVisible(false); + } + else + { + if (hideCounterOnFinalResult) + HideCounter(); + + if (hideGuideOnFinalResult) + SetControllerGuideVisible(false, true); + } if (finalResultPanel != null) finalResultPanel.SetActive(true); + if (finalResultCanvasGroup != null) + { + finalResultCanvasGroup.alpha = 0f; + finalResultCanvasGroup.interactable = true; + finalResultCanvasGroup.blocksRaycasts = true; + } + if (finalResultText != null) { finalResultText.gameObject.SetActive(true); finalResultText.text = text; } + + if (effects != null) + { + effects.FadeCanvasGroup(finalResultPanel, finalResultCanvasGroup, true, panelFadeTime); + if (finalResultPanel != null) + effects.Pop(finalResultPanel.transform, finalPanelPopScale, finalPanelPopTime); + } + else if (finalResultCanvasGroup != null) + { + finalResultCanvasGroup.alpha = 1f; + } } public void HideFinalResult() { - if (finalResultPanel != null) - finalResultPanel.SetActive(false); + HideFinalResult(true, false); + } + + public void HideFinalResult(bool showCounterAfterHide) + { + HideFinalResult(showCounterAfterHide, false); + } + + private void HideFinalResult(bool showCounterAfterHide, bool instant) + { + if (instant || effects == null) + { + if (finalResultPanel != null) + finalResultPanel.SetActive(false); + + if (finalResultCanvasGroup != null) + { + finalResultCanvasGroup.alpha = 0f; + finalResultCanvasGroup.interactable = false; + finalResultCanvasGroup.blocksRaycasts = false; + } + } + else + { + effects.FadeCanvasGroup(finalResultPanel, finalResultCanvasGroup, false, panelFadeTime); + } if (finalResultText != null) finalResultText.gameObject.SetActive(false); - ShowCounter(); + if (showCounterAfterHide) + { + if (showOnlyFinalResult) + { + if (restoreGameplayUIAfterFinalResult) + SetGameplayUIVisible(true); + } + else + { + ShowCounter(); + } + } } -} \ No newline at end of file + + private GameObject FindGameObject(params string[] names) + { + Transform found = FindTransformByName(transform, names); + return found != null ? found.gameObject : null; + } + + private T FindComponentByName(params string[] names) where T : Component + { + Transform found = FindTransformByName(transform, names); + return found != null ? found.GetComponent() : null; + } + + private Transform FindTransformByName(Transform root, params string[] names) + { + if (root == null || names == null) + return null; + + for (int i = 0; i < names.Length; i++) + { + Transform exact = FindTransformRecursive(root, names[i], false); + if (exact != null) + return exact; + } + + for (int i = 0; i < names.Length; i++) + { + Transform normalized = FindTransformRecursive(root, names[i], true); + if (normalized != null) + return normalized; + } + + return null; + } + + private Transform FindTransformRecursive(Transform current, string targetName, bool normalize) + { + if (current == null || string.IsNullOrEmpty(targetName)) + return null; + + string currentName = normalize ? NormalizeName(current.name) : current.name; + string target = normalize ? NormalizeName(targetName) : targetName; + + if (currentName == target) + return current; + + for (int i = 0; i < current.childCount; i++) + { + Transform found = FindTransformRecursive(current.GetChild(i), targetName, normalize); + if (found != null) + return found; + } + + return null; + } + + private string NormalizeName(string value) + { + return value.Replace(" ", string.Empty) + .Replace("_", string.Empty) + .Replace("-", string.Empty) + .ToLowerInvariant(); + } +} diff --git a/Assets/My project/Fishing Scripts/UI/FishingHapticManager.cs b/Assets/My project/Fishing Scripts/UI/FishingHapticManager.cs index df257072..55e5f40f 100644 --- a/Assets/My project/Fishing Scripts/UI/FishingHapticManager.cs +++ b/Assets/My project/Fishing Scripts/UI/FishingHapticManager.cs @@ -8,6 +8,7 @@ public class FishingHapticManager : MonoBehaviour [Header("Haptic Settings")] [SerializeField] private bool useHaptic = true; + [SerializeField] private bool showHapticDebugLog = false; [SerializeField] private float perfectAmplitude = 1f; [SerializeField] private float perfectDuration = 0.2f; @@ -18,6 +19,19 @@ public class FishingHapticManager : MonoBehaviour [SerializeField] private float missAmplitude = 0.2f; [SerializeField] private float missDuration = 0.05f; + public XRNode TargetHand => targetHand; + + private void OnValidate() + { + perfectAmplitude = Mathf.Clamp01(perfectAmplitude); + goodAmplitude = Mathf.Clamp01(goodAmplitude); + missAmplitude = Mathf.Clamp01(missAmplitude); + + perfectDuration = Mathf.Max(0f, perfectDuration); + goodDuration = Mathf.Max(0f, goodDuration); + missDuration = Mathf.Max(0f, missDuration); + } + private void SendHaptic(float amplitude, float duration) { if (!useHaptic) @@ -26,15 +40,46 @@ private void SendHaptic(float amplitude, float duration) InputDevice device = InputDevices.GetDeviceAtXRNode(targetHand); if (!device.isValid) - return; - - if (device.TryGetHapticCapabilities(out HapticCapabilities capabilities)) { - if (!capabilities.supportsImpulse) - return; + LogHapticWarning("Haptic device not found."); + return; } - device.SendHapticImpulse(0u, Mathf.Clamp01(amplitude), duration); + if (!device.TryGetHapticCapabilities(out HapticCapabilities capabilities)) + { + LogHapticWarning("Haptic capabilities not available."); + return; + } + + if (!capabilities.supportsImpulse) + { + LogHapticWarning("Haptic impulse is not supported on this device."); + return; + } + + if (capabilities.numChannels < 1) + { + LogHapticWarning("Haptic channel count is zero."); + return; + } + + device.SendHapticImpulse(0u, Mathf.Clamp01(amplitude), Mathf.Max(0f, duration)); + } + + private void LogHapticWarning(string message) + { + if (showHapticDebugLog) + Debug.LogWarning($"[FishingHapticManager] {message}", this); + } + + public void SetTargetHand(XRNode hand) + { + targetHand = hand; + } + + public void SetUseHaptic(bool value) + { + useHaptic = value; } public void Perfect() @@ -51,4 +96,4 @@ public void Miss() { SendHaptic(missAmplitude, missDuration); } -} \ No newline at end of file +} diff --git a/Assets/My project/Fishing Scripts/UI/FishingInventory.cs b/Assets/My project/Fishing Scripts/UI/FishingInventory.cs new file mode 100644 index 00000000..11f421b5 --- /dev/null +++ b/Assets/My project/Fishing Scripts/UI/FishingInventory.cs @@ -0,0 +1,178 @@ +using UnityEngine; + +public class FishingInventory : MonoBehaviour +{ + [Header("Item Counts")] + [SerializeField] private int fishCount; + [SerializeField] private int rottenFishCount; + [SerializeField] private int trashCount; + [SerializeField] private int bottleCount; + [SerializeField] private int plasticBagCount; + [SerializeField] private int jellyfishPowderCount; + [SerializeField] private int oldCompassCount; + [SerializeField] private int memoryPieceCount; + + public int FishCount => fishCount; + public int RottenFishCount => rottenFishCount; + public int TrashCount => trashCount; + public int BottleCount => bottleCount; + public int PlasticBagCount => plasticBagCount; + public int JellyfishPowderCount => jellyfishPowderCount; + public int OldCompassCount => oldCompassCount; + public int MemoryPieceCount => memoryPieceCount; + + private void OnValidate() + { + ClampCounts(); + } + + private void ClampCounts() + { + fishCount = Mathf.Max(0, fishCount); + rottenFishCount = Mathf.Max(0, rottenFishCount); + trashCount = Mathf.Max(0, trashCount); + bottleCount = Mathf.Max(0, bottleCount); + plasticBagCount = Mathf.Max(0, plasticBagCount); + jellyfishPowderCount = Mathf.Max(0, jellyfishPowderCount); + oldCompassCount = Mathf.Max(0, oldCompassCount); + memoryPieceCount = Mathf.Max(0, memoryPieceCount); + } + + public void AddItem(FishingItemType itemType, int amount = 1) + { + if (itemType == FishingItemType.None || amount <= 0) + return; + + switch (itemType) + { + case FishingItemType.Fish: + fishCount += amount; + break; + + case FishingItemType.RottenFish: + rottenFishCount += amount; + break; + + case FishingItemType.Trash: + trashCount += amount; + break; + + case FishingItemType.Bottle: + bottleCount += amount; + break; + + case FishingItemType.PlasticBag: + plasticBagCount += amount; + break; + + case FishingItemType.JellyfishPowder: + jellyfishPowderCount += amount; + break; + + case FishingItemType.OldCompass: + oldCompassCount += amount; + break; + + case FishingItemType.MemoryPiece: + memoryPieceCount += amount; + break; + } + } + + public bool HasItem(FishingItemType itemType, int amount = 1) + { + if (itemType == FishingItemType.None || amount <= 0) + return false; + + return GetItemCount(itemType) >= amount; + } + + public bool ConsumeItem(FishingItemType itemType, int amount = 1) + { + if (!HasItem(itemType, amount)) + return false; + + switch (itemType) + { + case FishingItemType.Fish: + fishCount -= amount; + break; + + case FishingItemType.RottenFish: + rottenFishCount -= amount; + break; + + case FishingItemType.Trash: + trashCount -= amount; + break; + + case FishingItemType.Bottle: + bottleCount -= amount; + break; + + case FishingItemType.PlasticBag: + plasticBagCount -= amount; + break; + + case FishingItemType.JellyfishPowder: + jellyfishPowderCount -= amount; + break; + + case FishingItemType.OldCompass: + oldCompassCount -= amount; + break; + + case FishingItemType.MemoryPiece: + memoryPieceCount -= amount; + break; + } + + return true; + } + + public int GetItemCount(FishingItemType itemType) + { + switch (itemType) + { + case FishingItemType.Fish: + return fishCount; + + case FishingItemType.RottenFish: + return rottenFishCount; + + case FishingItemType.Trash: + return trashCount; + + case FishingItemType.Bottle: + return bottleCount; + + case FishingItemType.PlasticBag: + return plasticBagCount; + + case FishingItemType.JellyfishPowder: + return jellyfishPowderCount; + + case FishingItemType.OldCompass: + return oldCompassCount; + + case FishingItemType.MemoryPiece: + return memoryPieceCount; + + case FishingItemType.None: + default: + return 0; + } + } + + public void ResetInventory() + { + fishCount = 0; + rottenFishCount = 0; + trashCount = 0; + bottleCount = 0; + plasticBagCount = 0; + jellyfishPowderCount = 0; + oldCompassCount = 0; + memoryPieceCount = 0; + } +} diff --git a/Assets/My project/Fishing Scripts/UI/FishingInventory.cs.meta b/Assets/My project/Fishing Scripts/UI/FishingInventory.cs.meta new file mode 100644 index 00000000..9d7b279c --- /dev/null +++ b/Assets/My project/Fishing Scripts/UI/FishingInventory.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: df5f5119d6ecc8746a4ee16c9f1b50cd \ No newline at end of file diff --git a/Assets/My project/Fishing Scripts/UI/FishingItemType.cs b/Assets/My project/Fishing Scripts/UI/FishingItemType.cs new file mode 100644 index 00000000..a95ffe54 --- /dev/null +++ b/Assets/My project/Fishing Scripts/UI/FishingItemType.cs @@ -0,0 +1,12 @@ +public enum FishingItemType +{ + None, + Fish, + RottenFish, + Trash, + Bottle, + PlasticBag, + JellyfishPowder, + OldCompass, + MemoryPiece +} diff --git a/Assets/My project/Fishing Scripts/UI/FishingItemType.cs.meta b/Assets/My project/Fishing Scripts/UI/FishingItemType.cs.meta new file mode 100644 index 00000000..c12f1b74 --- /dev/null +++ b/Assets/My project/Fishing Scripts/UI/FishingItemType.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6365b670fd7854c4bb6024c911f30c5c \ No newline at end of file diff --git a/Assets/My project/Fishing Scripts/UI/FishingRewardSystem.cs b/Assets/My project/Fishing Scripts/UI/FishingRewardSystem.cs index f199995e..f395ca15 100644 --- a/Assets/My project/Fishing Scripts/UI/FishingRewardSystem.cs +++ b/Assets/My project/Fishing Scripts/UI/FishingRewardSystem.cs @@ -1,53 +1,255 @@ +using System; +using System.Collections.Generic; using UnityEngine; public class FishingRewardSystem : MonoBehaviour { - public enum RewardType + [Serializable] + public class LootEntry { - Normal, - Rare + public FishingItemType itemType = FishingItemType.Fish; + public string displayName = "생선"; + [Min(0f)] public float weight = 1f; + + public LootEntry(FishingItemType itemType, string displayName, float weight) + { + this.itemType = itemType; + this.displayName = displayName; + this.weight = Mathf.Max(0f, weight); + } } - [Header("Reward Chance")] - [Range(0f, 100f)] - [SerializeField] private float rareChance = 10f; - - [Header("Reward Count")] - [SerializeField] private int normalMemory; - [SerializeField] private int rareMemory; - - public int NormalMemory => normalMemory; - public int RareMemory => rareMemory; - - public RewardType GiveReward() + public struct CatchResult { - if (Random.value < rareChance / 100f) - return GiveRareMemory(); + public FishingItemType ItemType; + public string DisplayName; + public bool CountsAsCleanupItem; + public bool IsMemoryPiece; - return GiveNormalMemory(); + public CatchResult(FishingItemType itemType, string displayName, bool countsAsCleanupItem, bool isMemoryPiece) + { + ItemType = itemType; + DisplayName = displayName; + CountsAsCleanupItem = countsAsCleanupItem; + IsMemoryPiece = isMemoryPiece; + } } - private RewardType GiveNormalMemory() + [Header("Loot Table - Dirty Pond")] + [Tooltip("연못이 정화되기 전 낚이는 아이템 목록입니다. 기억의 조각은 이 테이블에 있어도 자동 제외됩니다.")] + [SerializeField] private List dirtyPondLootTable = new List(); + + [Header("Loot Table - Clean Pond")] + [Tooltip("연못이 정화된 뒤 낚이는 아이템 목록입니다. 기억의 조각을 포함할 수 있습니다.")] + [SerializeField] private List cleanPondLootTable = new List(); + + [Header("Debug")] + [SerializeField] private bool showDebugLog = true; + + public IReadOnlyList DirtyPondLootTable => dirtyPondLootTable; + public IReadOnlyList CleanPondLootTable => cleanPondLootTable; + + private void Reset() { - normalMemory++; - - Debug.Log("Ϲ +1"); - - return RewardType.Normal; + SetDefaultLootTables(); } - private RewardType GiveRareMemory() + private void Awake() { - rareMemory++; - - Debug.Log(" +1"); - - return RewardType.Rare; + EnsureLootTables(); } - public void ResetRewardCount() + private void OnValidate() { - normalMemory = 0; - rareMemory = 0; + EnsureLootTables(); + ClampLootTable(dirtyPondLootTable); + ClampLootTable(cleanPondLootTable); } -} \ No newline at end of file + + private void EnsureLootTables() + { + if (dirtyPondLootTable == null) + dirtyPondLootTable = new List(); + + if (cleanPondLootTable == null) + cleanPondLootTable = new List(); + + if (dirtyPondLootTable.Count == 0) + SetDefaultDirtyLootTable(); + + if (cleanPondLootTable.Count == 0) + SetDefaultCleanLootTable(); + } + + private void SetDefaultLootTables() + { + SetDefaultDirtyLootTable(); + SetDefaultCleanLootTable(); + } + + private void SetDefaultDirtyLootTable() + { + dirtyPondLootTable = new List + { + new LootEntry(FishingItemType.Fish, "생선", 25f), + new LootEntry(FishingItemType.RottenFish, "상한 생선", 8f), + new LootEntry(FishingItemType.Trash, "쓰레기", 28f), + new LootEntry(FishingItemType.Bottle, "병", 18f), + new LootEntry(FishingItemType.PlasticBag, "봉지", 18f), + new LootEntry(FishingItemType.JellyfishPowder, "해파리 가루", 6f), + new LootEntry(FishingItemType.OldCompass, "낡은 나침반", 4f) + }; + } + + private void SetDefaultCleanLootTable() + { + cleanPondLootTable = new List + { + new LootEntry(FishingItemType.MemoryPiece, "기억의 조각", 30f), + new LootEntry(FishingItemType.Fish, "생선", 30f), + new LootEntry(FishingItemType.OldCompass, "낡은 나침반", 15f), + new LootEntry(FishingItemType.JellyfishPowder, "해파리 가루", 15f), + new LootEntry(FishingItemType.RottenFish, "상한 생선", 4f) + }; + } + + private void ClampLootTable(List table) + { + if (table == null) + return; + + for (int i = 0; i < table.Count; i++) + { + if (table[i] == null) + { + table[i] = new LootEntry(FishingItemType.Fish, "생선", 1f); + continue; + } + + table[i].weight = Mathf.Max(0f, table[i].weight); + + if (string.IsNullOrWhiteSpace(table[i].displayName)) + table[i].displayName = GetDefaultDisplayName(table[i].itemType); + } + } + + public CatchResult RollCatch(bool pondCleaned, bool forceMemoryPiece) + { + if (forceMemoryPiece) + return CreateCatchResult(FishingItemType.MemoryPiece, GetDefaultDisplayName(FishingItemType.MemoryPiece)); + + List table = pondCleaned ? cleanPondLootTable : dirtyPondLootTable; + LootEntry selectedEntry = RollFromTable(table, pondCleaned); + + if (selectedEntry == null) + return CreateCatchResult(FishingItemType.Fish, GetDefaultDisplayName(FishingItemType.Fish)); + + return CreateCatchResult(selectedEntry.itemType, selectedEntry.displayName); + } + + private LootEntry RollFromTable(List table, bool pondCleaned) + { + if (table == null || table.Count == 0) + return null; + + float totalWeight = 0f; + + for (int i = 0; i < table.Count; i++) + { + LootEntry entry = table[i]; + + if (!IsRollableEntry(entry, pondCleaned)) + continue; + + totalWeight += entry.weight; + } + + if (totalWeight <= 0f) + return null; + + float roll = UnityEngine.Random.Range(0f, totalWeight); + float current = 0f; + + for (int i = 0; i < table.Count; i++) + { + LootEntry entry = table[i]; + + if (!IsRollableEntry(entry, pondCleaned)) + continue; + + current += entry.weight; + + if (roll <= current) + return entry; + } + + return null; + } + + private bool IsRollableEntry(LootEntry entry, bool pondCleaned) + { + if (entry == null) + return false; + + if (entry.weight <= 0f) + return false; + + if (!pondCleaned && entry.itemType == FishingItemType.MemoryPiece) + return false; + + return entry.itemType != FishingItemType.None; + } + + private CatchResult CreateCatchResult(FishingItemType itemType, string displayName) + { + string finalDisplayName = string.IsNullOrWhiteSpace(displayName) ? GetDefaultDisplayName(itemType) : displayName; + bool countsAsCleanupItem = IsCleanupItem(itemType); + bool isMemoryPiece = itemType == FishingItemType.MemoryPiece; + + if (showDebugLog) + Debug.Log($"낚시 획득: {finalDisplayName}"); + + return new CatchResult(itemType, finalDisplayName, countsAsCleanupItem, isMemoryPiece); + } + + public bool IsCleanupItem(FishingItemType itemType) + { + return itemType == FishingItemType.Trash || + itemType == FishingItemType.Bottle || + itemType == FishingItemType.PlasticBag; + } + + public string GetDefaultDisplayName(FishingItemType itemType) + { + switch (itemType) + { + case FishingItemType.Fish: + return "생선"; + + case FishingItemType.RottenFish: + return "상한 생선"; + + case FishingItemType.Trash: + return "쓰레기"; + + case FishingItemType.Bottle: + return "병"; + + case FishingItemType.PlasticBag: + return "봉지"; + + case FishingItemType.JellyfishPowder: + return "해파리 가루"; + + case FishingItemType.OldCompass: + return "낡은 나침반"; + + case FishingItemType.MemoryPiece: + return "기억의 조각"; + + case FishingItemType.None: + default: + return "없음"; + } + } +} diff --git a/Assets/My project/Fishing Scripts/UI/FishingRodVRController.cs b/Assets/My project/Fishing Scripts/UI/FishingRodVRController.cs new file mode 100644 index 00000000..4c4dea39 --- /dev/null +++ b/Assets/My project/Fishing Scripts/UI/FishingRodVRController.cs @@ -0,0 +1,1537 @@ +using System; +using System.Collections; +using UnityEngine; +using UnityEngine.InputSystem; + +/// +/// Integrated VR fishing rod controller. +/// +/// Replaces the following helper scripts when you want a simpler VR fishing setup: +/// - FishingModelLineFollower +/// - FishingBobberVRController +/// - FishingBobberWaterDetector +/// - FishingRodState +/// - FishingStartTrigger +/// +/// Keep FishingGameManager, FishingGaugeUI, FishingRewardSystem, FishingHapticManager, +/// FishingUIEffects, FishingItemType, and RotateUI as separate scripts. +/// +/// Recommended hierarchy: +/// Fishing rod +/// - Rigidbody +/// - XR Grab Interactable +/// - FishingRodVRController +/// - RodLineEndPoint +/// - BobberLineStartPoint +/// - Bobber / Hook_2 +/// - FishingLineRoot (optional empty object for generated line mesh) +/// +public class FishingRodVRController : MonoBehaviour +{ + public enum BobberState + { + Idle, + Casting, + WaitingInWater, + Bite, + MiniGameActive, + Returning + } + + public enum StartMode + { + Disabled, + StartActionInArea, + OnBite, + ManualOnly + } + + [Header("Core References")] + [SerializeField] private FishingGameManager fishingGameManager; + [SerializeField] private FishingHapticManager haptic; + + [Tooltip("Line start point. Place this at the fishing rod tip / final guide ring.")] + [SerializeField] private Transform rodLineEndPoint; + + [Tooltip("Line end point. The visible bobber should usually be a child of this object.")] + [SerializeField] private Transform bobberLineStartPoint; + + [Tooltip("Visible bobber or hook model. Usually a child of BobberLineStartPoint. Used for optional visual correction/shake.")] + [SerializeField] private Transform bobberVisualRoot; + + [Header("Rod Held State")] + [Tooltip("Call MarkHeld/MarkReleased from XR Grab Interactable Select Entered/Exited events.")] + [SerializeField] private bool isHeld; + [SerializeField] private bool showHeldDebugLog = true; + + [Header("Input Actions")] + [Tooltip("Used to cast the bobber forward.")] + [SerializeField] private InputActionReference castAction; + + [Tooltip("Used to return the bobber to the rod tip.")] + [SerializeField] private InputActionReference returnAction; + + [Tooltip("Used to start fishing while player is in the start area, if Start Mode = StartActionInArea.")] + [SerializeField] private InputActionReference startAction; + + [Tooltip("If the action is not already enabled by Input Action Manager, this script enables it temporarily.")] + [SerializeField] private bool enableInputActionsManually = true; + + [Header("Start Area / Start Rules")] + [SerializeField] private StartMode startMode = StartMode.OnBite; + + [Tooltip("If On, StartActionInArea mode requires the rod to be held.")] + [SerializeField] private bool requireRodHeldToStart = true; + + [Tooltip("If On, CastForward requires the rod to be held.")] + [SerializeField] private bool requireRodHeldToCast = true; + + [Tooltip("Shown while player is inside the start area and the rules allow starting.")] + [SerializeField] private GameObject startGuideUI; + + [Tooltip("If On, an OverlapBox is used instead of a separate FishingStartTrigger component.")] + [SerializeField] private bool useStartAreaCheck = false; + + [SerializeField] private Transform startAreaCenter; + [SerializeField] private Vector3 startAreaHalfExtents = new Vector3(1.5f, 1.2f, 1.5f); + [SerializeField] private bool startAreaUsePlayerLayerMask = false; + [SerializeField] private LayerMask startAreaPlayerLayerMask = ~0; + [SerializeField] private string playerTag = "Player"; + [SerializeField] private bool hideGuideWhenFishingStarts = true; + [SerializeField] private bool allowRestartAfterClear = false; + + [Header("Procedural Fishing Line")] + [Tooltip("Optional transform that owns the generated MeshFilter/MeshRenderer. If empty, this object is used.")] + [SerializeField] private Transform lineMeshRoot; + + [SerializeField] private Material lineMaterial; + [SerializeField] private float lineRadius = 0.0012f; + [SerializeField] private int lineSegments = 12; + [SerializeField] private int lineSides = 6; + [SerializeField] private bool lineUseSag = false; + [SerializeField] private float lineSagAmount = 0.02f; + [SerializeField] private float lineDistanceSagMultiplier = 0.01f; + [SerializeField] private float lineMaxSagAmount = 0.08f; + [SerializeField] private Vector3 lineSagDirection = Vector3.down; + [SerializeField] private bool lineAddEndCaps = false; + [SerializeField] private bool hideLineWhenMissingTargets = true; + [SerializeField] private bool drawLineDebug = false; + + [Header("Bobber Idle / Attachment")] + [Tooltip("If set, ReturnBobber uses this world position. If empty, initial BobberLineStartPoint local position is used.")] + [SerializeField] private Transform bobberRestPoint; + + [Tooltip("On Start, put BobberLineStartPoint at its rest position.")] + [SerializeField] private bool returnBobberToRestOnStart = true; + + [Tooltip("If On, BobberLineStartPoint is kept at the cast world position after casting, even while the rod moves.")] + [SerializeField] private bool pinBobberWorldPositionAfterCast = true; + + [Tooltip("If the visible bobber is not a child of BobberLineStartPoint, this can force it to the line end.")] + [SerializeField] private bool forceBobberVisualToLineEnd = false; + + [SerializeField] private Vector3 bobberVisualLocalOffset = Vector3.zero; + [SerializeField] private Vector3 bobberVisualWorldOffset = Vector3.zero; + + [Header("Casting")] + [SerializeField] private Transform castDirectionSource; + [SerializeField] private float castDistance = 4f; + [SerializeField] private float castHeight = 1.1f; + [SerializeField] private float castDuration = 0.65f; + [SerializeField] private bool flattenCastDirection = true; + + [Tooltip("If On, raycast down to find water hit point for the cast target.")] + [SerializeField] private bool useWaterRaycastTarget = true; + [SerializeField] private LayerMask waterRaycastMask = 0; + [SerializeField] private float waterRaycastStartUp = 2f; + [SerializeField] private float waterRaycastDownDistance = 8f; + [SerializeField] private bool useFixedWaterHeightFallback = false; + [SerializeField] private float fixedWaterHeight = 0f; + [SerializeField] private float bobberFloatHeightOffset = 0.02f; + + [Header("Water Detection")] + [Tooltip("If On, this script checks water with OverlapSphere around BobberLineStartPoint. No BobberWaterDetector is needed.")] + [SerializeField] private bool useWaterOverlapCheck = true; + [SerializeField] private float waterCheckRadius = 0.08f; + [SerializeField] private bool useWaterLayerMask = true; + [SerializeField] private LayerMask waterLayerMask = 0; + [SerializeField] private bool useWaterTag = true; + [SerializeField] private string waterTag = "Water"; + + [Header("Bite")] + [SerializeField] private bool useBiteTimer = true; + [SerializeField] private Vector2 biteDelayRange = new Vector2(1.5f, 4.0f); + [SerializeField] private bool startMiniGameOnBite = true; + [SerializeField] private bool stopBiteShakeWhenMiniGameStarts = true; + [SerializeField] private float biteShakeAmplitude = 0.035f; + [SerializeField] private float biteShakeFrequency = 18f; + [SerializeField] private float biteShakeDuration = 1.25f; + [SerializeField] private bool keepShakingUntilMiniGameStarts = false; + + [Header("Reel Handle / Line Length Control")] + [Tooltip("If On, rotating a hand/controller around the reel center changes BobberLineStartPoint distance, so the procedural line becomes shorter/longer.")] + [SerializeField] private bool useReelControl = true; + + [Tooltip("Usually the center/pivot of the reel. The controller should move around this point.")] + [SerializeField] private Transform reelCenterPoint; + + [Tooltip("Usually the off-hand controller transform, or a reel-handle tracking transform.")] + [SerializeField] private Transform reelRotationSource; + + [Tooltip("If On, Reel Hold Action must be pressed to reel. If Off and Use Reel Grab Events is also Off, being near the reel is enough.")] + [SerializeField] private bool useReelHoldAction = true; + + [Tooltip("Button/Grip action used while reeling. Example binding: {LeftHand}/gripPressed or {LeftHand}/triggerPressed.")] + [SerializeField] private InputActionReference reelHoldAction; + + [Tooltip("If On, call MarkReelHandleGrabbed/MarkReelHandleReleased from a reel handle XR Grab Interactable or trigger volume.")] + [SerializeField] private bool useReelGrabEvents = false; + + [Tooltip("If On, the rod must be held before reeling is allowed.")] + [SerializeField] private bool requireRodHeldToReel = true; + + [Tooltip("켜면 입질 이후 또는 미니게임 중일 때만 릴 감기가 됩니다. 기본 낚시가 먼저 되게 하려면 On 추천.")] + [SerializeField] private bool reelOnlyAfterBiteOrMiniGame = true; + + [Tooltip("디버그용입니다. Hold Action과 Grab Events가 모두 꺼져 있어도 릴 근처에 있으면 감기게 합니다.")] + [SerializeField] private bool allowReelByProximityOnlyForDebug = false; + + [Tooltip("Maximum distance from Reel Center Point to Reel Rotation Source to start reeling.")] + [SerializeField] private float reelActivationRadius = 0.35f; + + [Tooltip("Reel axis in Reel Center Point local space. Try Local X first, then Y/Z depending on your reel model.")] + [SerializeField] private Vector3 reelLocalAxis = Vector3.right; + + [Tooltip("Invert if turning the handle makes the line longer instead of shorter.")] + [SerializeField] private bool invertReelDirection = false; + + [Tooltip("How many meters of line change per full 360 degree reel turn.")] + [SerializeField] private float lineLengthPerReelTurn = 0.45f; + + [SerializeField] private float minReelLineLength = 0.35f; + [SerializeField] private float maxReelLineLength = 8f; + + [Tooltip("If Off, reverse rotation will not let more line out; it only reels in.")] + [SerializeField] private bool allowReelOut = true; + + [Tooltip("Optional visual object to rotate when reeling. This is only visual.")] + [SerializeField] private Transform reelHandleVisual; + + [SerializeField] private bool rotateReelHandleVisual = true; + + [Tooltip("Reel handle visual axis in its own local space.")] + [SerializeField] private Vector3 reelHandleVisualLocalAxis = Vector3.right; + + [SerializeField] private float reelHandleVisualDegreesMultiplier = 1f; + + [Tooltip("When a cast finishes, current line length is initialized from rod tip to bobber distance.")] + [SerializeField] private bool initializeLineLengthAfterCast = true; + + [SerializeField] private bool showReelDebugLog = false; + + [Header("Return")] + [SerializeField] private float returnDuration = 0.35f; + + [Header("Debug")] + [SerializeField] private bool showDebugLog = true; + [SerializeField] private bool drawStartAreaGizmo = true; + [SerializeField] private bool drawWaterCheckGizmo = true; + + private MeshFilter lineMeshFilter; + private MeshRenderer lineMeshRenderer; + private Mesh lineMesh; + private Material generatedLineMaterial; + + private Coroutine bobberMoveRoutine; + private Coroutine biteWaitRoutine; + private Coroutine biteShakeRoutine; + + private bool castActionWasEnabled; + private bool returnActionWasEnabled; + private bool reelHoldActionWasEnabled; + private bool startActionWasEnabled; + + private BobberState state = BobberState.Idle; + private bool playerInsideStartArea; + private bool bobberInWater; + private bool bobberWorldPinned; + private Vector3 pinnedBobberWorldPosition; + private Vector3 initialBobberLocalPosition; + private Quaternion initialBobberLocalRotation; + private bool hasInitialBobberTransform; + private Vector3 bobberVisualInitialLocalPosition; + private bool hasBobberVisualInitialLocalPosition; + + private bool reelHeldByEvent; + private bool reelInteractionActive; + private bool reelAngleInitialized; + private float previousReelAngle; + private float currentLineLength; + private bool hasCurrentLineLength; + + private const float Epsilon = 0.000001f; + + public bool IsHeld => isHeld; + public bool PlayerInsideStartArea => playerInsideStartArea; + public bool IsBobberInWater => bobberInWater; + public bool IsBiting => state == BobberState.Bite; + public bool IsReeling => reelInteractionActive; + public float CurrentLineLength => hasCurrentLineLength ? currentLineLength : GetCurrentRodToBobberDistance(); + public BobberState State => state; + + private void Reset() + { + AutoBindCommonReferences(); + } + + private void Awake() + { + AutoBindCommonReferences(); + CacheInitialBobberTransform(); + CacheBobberVisualPosition(); + EnsureLineMeshComponents(true); + SetStartGuideVisible(false); + } + + private void Start() + { + if (returnBobberToRestOnStart) + ReturnBobberToRestInstant(); + } + + private void OnEnable() + { + RegisterInputAction(castAction, OnCastInput, ref castActionWasEnabled); + RegisterInputAction(returnAction, OnReturnInput, ref returnActionWasEnabled); + RegisterPassiveInputAction(reelHoldAction, ref reelHoldActionWasEnabled); + RegisterInputAction(startAction, OnStartInput, ref startActionWasEnabled); + } + + private void OnDisable() + { + UnregisterInputAction(castAction, OnCastInput, castActionWasEnabled); + UnregisterInputAction(returnAction, OnReturnInput, returnActionWasEnabled); + UnregisterPassiveInputAction(reelHoldAction, reelHoldActionWasEnabled); + UnregisterInputAction(startAction, OnStartInput, startActionWasEnabled); + StopAllRoutinesSafe(); + SetStartGuideVisible(false); + } + + private void OnValidate() + { + startAreaHalfExtents.x = Mathf.Max(0.01f, startAreaHalfExtents.x); + startAreaHalfExtents.y = Mathf.Max(0.01f, startAreaHalfExtents.y); + startAreaHalfExtents.z = Mathf.Max(0.01f, startAreaHalfExtents.z); + + lineRadius = Mathf.Max(0.00005f, lineRadius); + lineSegments = Mathf.Clamp(lineSegments, 1, 128); + lineSides = Mathf.Clamp(lineSides, 3, 24); + lineSagAmount = Mathf.Max(0f, lineSagAmount); + lineDistanceSagMultiplier = Mathf.Max(0f, lineDistanceSagMultiplier); + lineMaxSagAmount = Mathf.Max(0f, lineMaxSagAmount); + + castDistance = Mathf.Max(0.1f, castDistance); + castHeight = Mathf.Max(0f, castHeight); + castDuration = Mathf.Max(0.01f, castDuration); + waterRaycastStartUp = Mathf.Max(0f, waterRaycastStartUp); + waterRaycastDownDistance = Mathf.Max(0.1f, waterRaycastDownDistance); + bobberFloatHeightOffset = Mathf.Max(0f, bobberFloatHeightOffset); + waterCheckRadius = Mathf.Max(0.001f, waterCheckRadius); + + biteDelayRange.x = Mathf.Max(0f, biteDelayRange.x); + biteDelayRange.y = Mathf.Max(biteDelayRange.x, biteDelayRange.y); + biteShakeAmplitude = Mathf.Max(0f, biteShakeAmplitude); + biteShakeFrequency = Mathf.Max(0f, biteShakeFrequency); + biteShakeDuration = Mathf.Max(0f, biteShakeDuration); + reelActivationRadius = Mathf.Max(0.01f, reelActivationRadius); + lineLengthPerReelTurn = Mathf.Max(0.0001f, lineLengthPerReelTurn); + minReelLineLength = Mathf.Max(0.01f, minReelLineLength); + maxReelLineLength = Mathf.Max(minReelLineLength, maxReelLineLength); + if (reelLocalAxis.sqrMagnitude <= Epsilon) + reelLocalAxis = Vector3.right; + if (reelHandleVisualLocalAxis.sqrMagnitude <= Epsilon) + reelHandleVisualLocalAxis = Vector3.right; + returnDuration = Mathf.Max(0.01f, returnDuration); + } + + private void Update() + { + UpdateStartAreaState(); + UpdateWaterState(); + UpdateReelControl(); + UpdateBiteVisualPin(); + } + + private void LateUpdate() + { + if (bobberWorldPinned && bobberLineStartPoint != null) + bobberLineStartPoint.position = GetPinnedBobberPositionWithBiteOffset(); + + UpdateBobberVisualAttachment(); + UpdateProceduralLine(); + } + + [ContextMenu("Auto Bind Common References")] + public void AutoBindCommonReferences() + { + if (fishingGameManager == null) + fishingGameManager = GetComponentInParent(); + + if (haptic == null) + haptic = GetComponentInParent(); + + if (rodLineEndPoint == null) + rodLineEndPoint = FindChildRecursive(transform, "RodLineEndPoint", "RodTipPoint", "RodTip", "TipPoint"); + + if (bobberLineStartPoint == null) + bobberLineStartPoint = FindChildRecursive(transform, "BobberLineStartPoint", "BobberPoint", "BobberRoot", "HookPoint"); + + if (bobberVisualRoot == null && bobberLineStartPoint != null && bobberLineStartPoint.childCount > 0) + bobberVisualRoot = bobberLineStartPoint.GetChild(0); + + if (lineMeshRoot == null) + lineMeshRoot = FindChildRecursive(transform, "FishingLine", "FishingLineRoot", "LineRoot", "ProceduralLineRoot"); + + if (castDirectionSource == null) + castDirectionSource = rodLineEndPoint != null ? rodLineEndPoint : transform; + + if (reelCenterPoint == null) + reelCenterPoint = FindChildRecursive(transform, "ReelCenterPoint", "ReelPivot", "ReelHandlePivot", "ReelCenter"); + + if (reelHandleVisual == null) + reelHandleVisual = FindChildRecursive(transform, "ReelHandleVisual", "ReelHandle", "ReelKnob", "ReelHandleKnob"); + } + + public void MarkHeld() + { + SetHeld(true); + } + + public void MarkReleased() + { + SetHeld(false); + } + + public void MarkReelHandleGrabbed() + { + reelHeldByEvent = true; + reelAngleInitialized = false; + LogReel("릴 손잡이 잡음"); + } + + public void MarkReelHandleReleased() + { + reelHeldByEvent = false; + StopReelInteraction(); + LogReel("릴 손잡이 놓음"); + } + + public void SetHeld(bool value) + { + if (isHeld == value) + return; + + isHeld = value; + + if (showHeldDebugLog) + Debug.Log(isHeld ? "낚싯대 잡음" : "낚싯대 놓음", this); + } + + public void CastForward() + { + if (requireRodHeldToCast && !isHeld) + { + Log("낚싯대를 잡고 있지 않아 캐스팅을 무시했습니다."); + return; + } + + if (rodLineEndPoint == null || bobberLineStartPoint == null) + { + Debug.LogWarning("[FishingRodVRController] RodLineEndPoint 또는 BobberLineStartPoint가 없습니다.", this); + return; + } + + Vector3 target = CalculateCastTarget(); + CastToWorldPoint(target); + } + + public void CastToWorldPoint(Vector3 worldTarget) + { + if (bobberLineStartPoint == null) + return; + + StopBobberMoveAndBiteRoutines(); + bobberWorldPinned = false; + state = BobberState.Casting; + bobberMoveRoutine = StartCoroutine(CastRoutine(worldTarget)); + } + + public void ReturnBobberToRest() + { + if (bobberLineStartPoint == null) + return; + + StopBobberMoveAndBiteRoutines(); + bobberWorldPinned = false; + state = BobberState.Returning; + bobberMoveRoutine = StartCoroutine(MoveBobberRoutine(GetRestWorldPosition(), returnDuration, BobberState.Idle)); + } + + public void ReturnBobberToRestInstant() + { + if (bobberLineStartPoint == null) + return; + + StopBobberMoveAndBiteRoutines(); + bobberWorldPinned = false; + bobberLineStartPoint.position = GetRestWorldPosition(); + InitializeReelLineLengthFromCurrentDistance(); + ResetBobberVisualPosition(); + state = BobberState.Idle; + bobberInWater = false; + } + + public void ForceSetReelLineLength(float length) + { + SetReelLineLength(length); + } + + public void ForceMiniGameActiveState() + { + if (state == BobberState.Bite || state == BobberState.WaitingInWater || state == BobberState.Idle) + state = BobberState.MiniGameActive; + } + + public bool TryStartFishing() + { + if (fishingGameManager == null) + { + Debug.LogWarning("[FishingRodVRController] FishingGameManager가 연결되지 않았습니다.", this); + return false; + } + + if (!allowRestartAfterClear && fishingGameManager.IsMemoryPieceCollected) + { + Log("이미 기억의 조각을 획득해서 낚시 시작을 막았습니다."); + return false; + } + + if (requireRodHeldToStart && !isHeld) + { + Log("낚싯대를 잡고 있지 않아 낚시 시작을 막았습니다."); + return false; + } + + if (startMode == StartMode.StartActionInArea && useStartAreaCheck && !playerInsideStartArea) + { + Log("플레이어가 낚시 시작 영역 안에 있지 않습니다."); + return false; + } + + if (fishingGameManager.IsFishingSessionRunning) + { + Log("낚시 세션이 이미 진행 중입니다."); + return false; + } + + fishingGameManager.StartFishing(); + state = BobberState.MiniGameActive; + + if (hideGuideWhenFishingStarts) + SetStartGuideVisible(false); + + Log("낚시 미니게임 시작"); + return true; + } + + public void ForceBite() + { + TriggerBite(); + } + + public void StopFishingRodController() + { + StopAllRoutinesSafe(); + bobberWorldPinned = false; + state = BobberState.Idle; + SetStartGuideVisible(false); + } + + private void OnCastInput(InputAction.CallbackContext context) + { + CastForward(); + } + + private void OnReturnInput(InputAction.CallbackContext context) + { + ReturnBobberToRest(); + } + + private void OnStartInput(InputAction.CallbackContext context) + { + if (startMode == StartMode.StartActionInArea || startMode == StartMode.ManualOnly) + TryStartFishing(); + } + + private IEnumerator CastRoutine(Vector3 target) + { + Vector3 start = bobberLineStartPoint.position; + Vector3 control = (start + target) * 0.5f + Vector3.up * castHeight; + float timer = 0f; + + while (timer < castDuration) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / castDuration); + t = EaseOutCubic(t); + bobberLineStartPoint.position = QuadraticBezier(start, control, target, t); + yield return null; + } + + bobberLineStartPoint.position = target; + pinnedBobberWorldPosition = target; + bobberWorldPinned = pinBobberWorldPositionAfterCast; + if (initializeLineLengthAfterCast) + InitializeReelLineLengthFromCurrentDistance(); + bobberMoveRoutine = null; + state = BobberState.WaitingInWater; + + Log("찌 캐스팅 완료"); + + if (!useWaterOverlapCheck && useBiteTimer) + StartWaitingForBite(); + } + + private IEnumerator MoveBobberRoutine(Vector3 target, float duration, BobberState endState) + { + Vector3 start = bobberLineStartPoint.position; + float timer = 0f; + + while (timer < duration) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / duration); + bobberLineStartPoint.position = Vector3.Lerp(start, target, EaseOutCubic(t)); + yield return null; + } + + bobberLineStartPoint.position = target; + InitializeReelLineLengthFromCurrentDistance(); + ResetBobberVisualPosition(); + state = endState; + bobberMoveRoutine = null; + } + + private void StartWaitingForBite() + { + if (!useBiteTimer) + return; + + if (biteWaitRoutine != null || state == BobberState.Bite || state == BobberState.MiniGameActive) + return; + + state = BobberState.WaitingInWater; + biteWaitRoutine = StartCoroutine(BiteWaitRoutine()); + Log("입질 대기 시작"); + } + + private IEnumerator BiteWaitRoutine() + { + float delay = UnityEngine.Random.Range(biteDelayRange.x, biteDelayRange.y); + float timer = 0f; + + while (timer < delay) + { + timer += Time.deltaTime; + + if (useWaterOverlapCheck && !bobberInWater) + { + biteWaitRoutine = null; + yield break; + } + + yield return null; + } + + biteWaitRoutine = null; + TriggerBite(); + } + + private void TriggerBite() + { + if (state == BobberState.MiniGameActive) + return; + + state = BobberState.Bite; + Log("입질 발생"); + + if (haptic != null) + haptic.Good(); + + if (biteShakeRoutine != null) + StopCoroutine(biteShakeRoutine); + + biteShakeRoutine = StartCoroutine(BiteShakeRoutine()); + + if (startMode == StartMode.OnBite && startMiniGameOnBite) + { + if (TryStartFishing() && stopBiteShakeWhenMiniGameStarts && !keepShakingUntilMiniGameStarts) + StopBiteShakeOnly(); + } + } + + private IEnumerator BiteShakeRoutine() + { + CacheBobberVisualPosition(); + + float timer = 0f; + while (keepShakingUntilMiniGameStarts || timer < biteShakeDuration) + { + timer += Time.deltaTime; + yield return null; + } + + ResetBobberVisualPosition(); + biteShakeRoutine = null; + } + + private void UpdateBiteVisualPin() + { + // The actual vertical offset is applied through GetPinnedBobberPositionWithBiteOffset() + // so the line end and bobber point always remain identical. + } + + private Vector3 GetPinnedBobberPositionWithBiteOffset() + { + if (state != BobberState.Bite || biteShakeAmplitude <= 0f) + return pinnedBobberWorldPosition; + + float y = Mathf.Sin(Time.time * biteShakeFrequency) * biteShakeAmplitude; + return pinnedBobberWorldPosition + Vector3.up * y; + } + + private void UpdateStartAreaState() + { + if (startMode != StartMode.StartActionInArea || !useStartAreaCheck) + { + playerInsideStartArea = true; + if (startMode != StartMode.StartActionInArea) + SetStartGuideVisible(false); + return; + } + + bool inside = IsPlayerInsideStartArea(); + playerInsideStartArea = inside; + + bool canShow = inside && CanShowStartGuide(); + SetStartGuideVisible(canShow); + } + + private bool CanShowStartGuide() + { + if (fishingGameManager != null) + { + if (fishingGameManager.IsFishingSessionRunning) + return false; + + if (!allowRestartAfterClear && fishingGameManager.IsMemoryPieceCollected) + return false; + } + + if (requireRodHeldToStart && !isHeld) + return false; + + return true; + } + + private bool IsPlayerInsideStartArea() + { + Transform area = startAreaCenter != null ? startAreaCenter : transform; + LayerMask mask = startAreaUsePlayerLayerMask ? startAreaPlayerLayerMask : ~0; + Collider[] hits = Physics.OverlapBox(area.position, startAreaHalfExtents, area.rotation, mask, QueryTriggerInteraction.Collide); + + for (int i = 0; i < hits.Length; i++) + { + if (IsPlayerCollider(hits[i])) + return true; + } + + return false; + } + + private bool IsPlayerCollider(Collider other) + { + if (other == null) + return false; + + if (startAreaUsePlayerLayerMask && startAreaPlayerLayerMask.value != 0) + { + Transform currentForLayer = other.transform; + while (currentForLayer != null) + { + if (((1 << currentForLayer.gameObject.layer) & startAreaPlayerLayerMask.value) != 0) + return true; + + currentForLayer = currentForLayer.parent; + } + } + + if (string.IsNullOrWhiteSpace(playerTag)) + return !startAreaUsePlayerLayerMask; + + Transform current = other.transform; + while (current != null) + { + if (CompareTagSafe(current.gameObject, playerTag)) + return true; + + current = current.parent; + } + + return false; + } + + private void UpdateWaterState() + { + if (!useWaterOverlapCheck || bobberLineStartPoint == null) + return; + + bool inWaterNow = IsBobberOverlappingWater(); + + if (inWaterNow == bobberInWater) + return; + + bobberInWater = inWaterNow; + + if (bobberInWater) + { + Log("찌가 물에 들어감"); + if (state == BobberState.Casting || state == BobberState.WaitingInWater || state == BobberState.Idle) + StartWaitingForBite(); + } + else + { + Log("찌가 물에서 나감"); + + if (state == BobberState.WaitingInWater || state == BobberState.Bite) + { + StopBiteWaitOnly(); + StopBiteShakeOnly(); + state = BobberState.Idle; + } + } + } + + private bool IsBobberOverlappingWater() + { + LayerMask mask = useWaterLayerMask && waterLayerMask.value != 0 ? waterLayerMask : ~0; + Collider[] hits = Physics.OverlapSphere(bobberLineStartPoint.position, waterCheckRadius, mask, QueryTriggerInteraction.Collide); + + for (int i = 0; i < hits.Length; i++) + { + Collider hit = hits[i]; + if (hit == null) + continue; + + bool layerMatch = !useWaterLayerMask || waterLayerMask.value == 0 || ((1 << hit.gameObject.layer) & waterLayerMask.value) != 0; + bool tagMatch = !useWaterTag || string.IsNullOrWhiteSpace(waterTag) || CompareTagSafe(hit.gameObject, waterTag); + + if (useWaterLayerMask && useWaterTag && waterLayerMask.value != 0 && !string.IsNullOrWhiteSpace(waterTag)) + { + if (layerMatch || tagMatch) + return true; + } + else if (layerMatch && tagMatch) + { + return true; + } + } + + return false; + } + + + private void UpdateReelControl() + { + if (!useReelControl) + { + StopReelInteraction(); + return; + } + + if (rodLineEndPoint == null || bobberLineStartPoint == null || reelCenterPoint == null || reelRotationSource == null) + { + StopReelInteraction(); + return; + } + + if (requireRodHeldToReel && !isHeld) + { + StopReelInteraction(); + return; + } + + if (state == BobberState.Casting || state == BobberState.Returning) + { + StopReelInteraction(); + return; + } + + if (reelOnlyAfterBiteOrMiniGame && state != BobberState.Bite && state != BobberState.MiniGameActive) + { + StopReelInteraction(); + return; + } + + if (!ShouldReelInputBeActive()) + { + StopReelInteraction(); + return; + } + + float distanceToReel = Vector3.Distance(reelCenterPoint.position, reelRotationSource.position); + if (distanceToReel > reelActivationRadius) + { + StopReelInteraction(); + return; + } + + float currentAngle = GetReelSourceAngle(); + + if (!reelAngleInitialized) + { + previousReelAngle = currentAngle; + reelAngleInitialized = true; + reelInteractionActive = true; + InitializeReelLineLengthFromCurrentDistanceIfNeeded(); + LogReel("릴 감기 시작"); + return; + } + + float deltaAngle = Mathf.DeltaAngle(previousReelAngle, currentAngle); + previousReelAngle = currentAngle; + + if (Mathf.Abs(deltaAngle) < 0.01f) + return; + + if (invertReelDirection) + deltaAngle = -deltaAngle; + + // Positive deltaAngle is treated as reeling in, so line length decreases. + float lengthDelta = -deltaAngle / 360f * lineLengthPerReelTurn; + + if (!allowReelOut && lengthDelta > 0f) + lengthDelta = 0f; + + SetReelLineLength(CurrentLineLength + lengthDelta); + RotateReelHandleVisual(deltaAngle); + } + + private bool ShouldReelInputBeActive() + { + bool actionActive = useReelHoldAction && reelHoldAction != null && reelHoldAction.action != null && reelHoldAction.action.IsPressed(); + bool eventActive = useReelGrabEvents && reelHeldByEvent; + + if (!useReelHoldAction && !useReelGrabEvents) + return allowReelByProximityOnlyForDebug; + + return actionActive || eventActive; + } + + private void StopReelInteraction() + { + reelInteractionActive = false; + reelAngleInitialized = false; + } + + private float GetReelSourceAngle() + { + Vector3 axis = GetReelWorldAxis(); + Vector3 fromCenter = reelRotationSource.position - reelCenterPoint.position; + Vector3 projected = Vector3.ProjectOnPlane(fromCenter, axis); + + if (projected.sqrMagnitude <= Epsilon) + return previousReelAngle; + + projected.Normalize(); + + Vector3 reference = Vector3.ProjectOnPlane(reelCenterPoint.up, axis); + if (reference.sqrMagnitude <= Epsilon) + reference = Vector3.ProjectOnPlane(reelCenterPoint.forward, axis); + if (reference.sqrMagnitude <= Epsilon) + reference = Vector3.ProjectOnPlane(Vector3.up, axis); + if (reference.sqrMagnitude <= Epsilon) + reference = Vector3.right; + + reference.Normalize(); + return Vector3.SignedAngle(reference, projected, axis); + } + + private Vector3 GetReelWorldAxis() + { + if (reelCenterPoint == null) + return Vector3.right; + + Vector3 localAxis = reelLocalAxis.sqrMagnitude > Epsilon ? reelLocalAxis.normalized : Vector3.right; + Vector3 worldAxis = reelCenterPoint.TransformDirection(localAxis); + return SafeNormalize(worldAxis, reelCenterPoint.right); + } + + private void InitializeReelLineLengthFromCurrentDistanceIfNeeded() + { + if (!hasCurrentLineLength) + InitializeReelLineLengthFromCurrentDistance(); + } + + private void InitializeReelLineLengthFromCurrentDistance() + { + if (rodLineEndPoint == null || bobberLineStartPoint == null) + return; + + currentLineLength = Mathf.Clamp(GetCurrentRodToBobberDistance(), minReelLineLength, maxReelLineLength); + hasCurrentLineLength = true; + } + + private float GetCurrentRodToBobberDistance() + { + if (rodLineEndPoint == null || bobberLineStartPoint == null) + return 0f; + + Vector3 end = bobberWorldPinned ? pinnedBobberWorldPosition : bobberLineStartPoint.position; + return Vector3.Distance(rodLineEndPoint.position, end); + } + + private void SetReelLineLength(float targetLength) + { + if (rodLineEndPoint == null || bobberLineStartPoint == null) + return; + + currentLineLength = Mathf.Clamp(targetLength, minReelLineLength, maxReelLineLength); + hasCurrentLineLength = true; + + Vector3 start = rodLineEndPoint.position; + Vector3 baseEnd = bobberWorldPinned ? pinnedBobberWorldPosition : bobberLineStartPoint.position; + Vector3 direction = baseEnd - start; + + if (direction.sqrMagnitude <= Epsilon) + { + Transform source = castDirectionSource != null ? castDirectionSource : transform; + direction = source.forward; + } + + direction = SafeNormalize(direction, transform.forward); + Vector3 newEnd = start + direction * currentLineLength; + + if (bobberWorldPinned) + pinnedBobberWorldPosition = newEnd; + + bobberLineStartPoint.position = newEnd; + + if (state == BobberState.Idle && currentLineLength > minReelLineLength + 0.05f) + state = BobberState.WaitingInWater; + + LogReel($"릴 줄 길이: {currentLineLength:F2}m"); + } + + private void RotateReelHandleVisual(float deltaAngle) + { + if (!rotateReelHandleVisual || reelHandleVisual == null) + return; + + Vector3 axis = reelHandleVisualLocalAxis.sqrMagnitude > Epsilon ? reelHandleVisualLocalAxis.normalized : Vector3.right; + reelHandleVisual.Rotate(axis, deltaAngle * reelHandleVisualDegreesMultiplier, Space.Self); + } + + private Vector3 CalculateCastTarget() + { + Transform source = castDirectionSource != null ? castDirectionSource : (rodLineEndPoint != null ? rodLineEndPoint : transform); + Vector3 direction = source.forward; + + if (flattenCastDirection) + { + direction.y = 0f; + if (direction.sqrMagnitude <= Epsilon) + direction = transform.forward; + } + + direction = SafeNormalize(direction, transform.forward); + + Vector3 fallbackTarget = rodLineEndPoint.position + direction * castDistance; + + if (useFixedWaterHeightFallback) + fallbackTarget.y = fixedWaterHeight + bobberFloatHeightOffset; + + if (useWaterRaycastTarget && waterRaycastMask.value != 0) + { + Vector3 rayStart = fallbackTarget + Vector3.up * waterRaycastStartUp; + if (Physics.Raycast(rayStart, Vector3.down, out RaycastHit hit, waterRaycastDownDistance, waterRaycastMask, QueryTriggerInteraction.Collide)) + return hit.point + Vector3.up * bobberFloatHeightOffset; + } + + return fallbackTarget; + } + + private Vector3 GetRestWorldPosition() + { + if (bobberRestPoint != null) + return bobberRestPoint.position; + + if (hasInitialBobberTransform && bobberLineStartPoint.parent != null) + return bobberLineStartPoint.parent.TransformPoint(initialBobberLocalPosition); + + if (rodLineEndPoint != null) + return rodLineEndPoint.position + rodLineEndPoint.TransformDirection(new Vector3(0f, -0.2f, -0.15f)); + + return transform.position; + } + + private void UpdateBobberVisualAttachment() + { + if (!forceBobberVisualToLineEnd || bobberVisualRoot == null || bobberLineStartPoint == null) + return; + + bobberVisualRoot.position = bobberLineStartPoint.position + + bobberVisualWorldOffset + + bobberLineStartPoint.rotation * bobberVisualLocalOffset; + } + + private void CacheInitialBobberTransform() + { + if (bobberLineStartPoint == null) + return; + + initialBobberLocalPosition = bobberLineStartPoint.localPosition; + initialBobberLocalRotation = bobberLineStartPoint.localRotation; + hasInitialBobberTransform = true; + } + + private void CacheBobberVisualPosition() + { + if (bobberVisualRoot == null || hasBobberVisualInitialLocalPosition) + return; + + bobberVisualInitialLocalPosition = bobberVisualRoot.localPosition; + hasBobberVisualInitialLocalPosition = true; + } + + private void ResetBobberVisualPosition() + { + if (bobberVisualRoot == null || !hasBobberVisualInitialLocalPosition) + return; + + bobberVisualRoot.localPosition = bobberVisualInitialLocalPosition; + } + + private void UpdateProceduralLine() + { + if (rodLineEndPoint == null || bobberLineStartPoint == null) + { + SetLineVisible(!hideLineWhenMissingTargets); + return; + } + + EnsureLineMeshComponents(true); + + if (lineMeshFilter == null || lineMeshRenderer == null || lineMesh == null) + return; + + SetLineVisible(true); + + if (lineMaterial != null) + lineMeshRenderer.sharedMaterial = lineMaterial; + else + EnsureFallbackLineMaterial(); + + Vector3 start = rodLineEndPoint.position; + Vector3 end = bobberLineStartPoint.position; + Vector3 control = (start + end) * 0.5f; + + if (lineUseSag) + { + float distance = Vector3.Distance(start, end); + Vector3 sagDir = lineSagDirection.sqrMagnitude > Epsilon ? lineSagDirection.normalized : Vector3.down; + float finalSag = Mathf.Clamp(lineSagAmount + distance * lineDistanceSagMultiplier, 0f, lineMaxSagAmount); + control += sagDir * finalSag; + } + + BuildProceduralTubeMesh(start, control, end); + + if (drawLineDebug) + Debug.DrawLine(start, end, Color.yellow); + } + + private void EnsureLineMeshComponents(bool createIfMissing) + { + Transform root = lineMeshRoot != null ? lineMeshRoot : transform; + if (root == null) + return; + + if (lineMeshFilter == null) + lineMeshFilter = root.GetComponent(); + + if (lineMeshRenderer == null) + lineMeshRenderer = root.GetComponent(); + + if (!createIfMissing) + return; + + if (lineMeshFilter == null) + lineMeshFilter = root.gameObject.AddComponent(); + + if (lineMeshRenderer == null) + lineMeshRenderer = root.gameObject.AddComponent(); + + if (lineMesh == null) + { + lineMesh = new Mesh(); + lineMesh.name = "VR Fishing Procedural Line"; + lineMesh.MarkDynamic(); + } + + if (lineMeshFilter.sharedMesh != lineMesh) + lineMeshFilter.sharedMesh = lineMesh; + } + + private void EnsureFallbackLineMaterial() + { + if (lineMeshRenderer == null) + return; + + if (generatedLineMaterial == null) + { + Shader shader = Shader.Find("Universal Render Pipeline/Unlit"); + if (shader == null) + shader = Shader.Find("Unlit/Color"); + if (shader == null) + shader = Shader.Find("Standard"); + + generatedLineMaterial = new Material(shader); + generatedLineMaterial.name = "Generated_FishingLine_Material"; + generatedLineMaterial.hideFlags = HideFlags.DontSave; + + if (generatedLineMaterial.HasProperty("_Color")) + generatedLineMaterial.SetColor("_Color", new Color(0.02f, 0.02f, 0.02f, 1f)); + if (generatedLineMaterial.HasProperty("_BaseColor")) + generatedLineMaterial.SetColor("_BaseColor", new Color(0.02f, 0.02f, 0.02f, 1f)); + } + + lineMeshRenderer.sharedMaterial = generatedLineMaterial; + } + + private void SetLineVisible(bool visible) + { + if (lineMeshRenderer != null) + lineMeshRenderer.enabled = visible; + } + + private void BuildProceduralTubeMesh(Vector3 worldStart, Vector3 worldControl, Vector3 worldEnd) + { + if (lineMesh == null || lineMeshFilter == null) + return; + + Transform meshTransform = lineMeshFilter.transform; + int rings = Mathf.Max(2, lineSegments + 1); + int sides = Mathf.Max(3, lineSides); + int capVertexCount = lineAddEndCaps ? 2 : 0; + int vertexCount = rings * sides + capVertexCount; + int sideTriangleCount = (rings - 1) * sides * 6; + int capTriangleCount = lineAddEndCaps ? sides * 6 : 0; + + Vector3[] vertices = new Vector3[vertexCount]; + Vector3[] normals = new Vector3[vertexCount]; + Vector2[] uvs = new Vector2[vertexCount]; + int[] triangles = new int[sideTriangleCount + capTriangleCount]; + + Vector3 previousNormal = Vector3.zero; + + for (int r = 0; r < rings; r++) + { + float t = (float)r / (rings - 1); + Vector3 point = QuadraticBezier(worldStart, worldControl, worldEnd, t); + Vector3 tangent = SafeNormalize(QuadraticBezierTangent(worldStart, worldControl, worldEnd, t), Vector3.forward); + + Vector3 normal; + if (r == 0 || previousNormal.sqrMagnitude <= Epsilon) + { + Vector3 reference = Mathf.Abs(Vector3.Dot(tangent, Vector3.up)) > 0.94f ? Vector3.right : Vector3.up; + Vector3 binormalStart = Vector3.Cross(tangent, reference).normalized; + normal = Vector3.Cross(binormalStart, tangent).normalized; + } + else + { + normal = Vector3.ProjectOnPlane(previousNormal, tangent); + if (normal.sqrMagnitude <= Epsilon) + { + Vector3 reference = Mathf.Abs(Vector3.Dot(tangent, Vector3.up)) > 0.94f ? Vector3.right : Vector3.up; + Vector3 binormalFallback = Vector3.Cross(tangent, reference).normalized; + normal = Vector3.Cross(binormalFallback, tangent).normalized; + } + else + { + normal.Normalize(); + } + } + + Vector3 binormal = Vector3.Cross(tangent, normal).normalized; + previousNormal = normal; + + for (int s = 0; s < sides; s++) + { + float angle = Mathf.PI * 2f * s / sides; + Vector3 radial = Mathf.Cos(angle) * normal + Mathf.Sin(angle) * binormal; + Vector3 worldVertex = point + radial * lineRadius; + int index = r * sides + s; + vertices[index] = meshTransform.InverseTransformPoint(worldVertex); + normals[index] = meshTransform.InverseTransformDirection(radial).normalized; + uvs[index] = new Vector2((float)s / sides, t); + } + } + + int tri = 0; + for (int r = 0; r < rings - 1; r++) + { + for (int s = 0; s < sides; s++) + { + int a = r * sides + s; + int b = r * sides + (s + 1) % sides; + int c = (r + 1) * sides + s; + int d = (r + 1) * sides + (s + 1) % sides; + + triangles[tri++] = a; + triangles[tri++] = c; + triangles[tri++] = b; + + triangles[tri++] = b; + triangles[tri++] = c; + triangles[tri++] = d; + } + } + + if (lineAddEndCaps) + { + int startCenter = rings * sides; + int endCenter = startCenter + 1; + vertices[startCenter] = meshTransform.InverseTransformPoint(worldStart); + vertices[endCenter] = meshTransform.InverseTransformPoint(worldEnd); + normals[startCenter] = meshTransform.InverseTransformDirection(SafeNormalize(worldStart - worldControl, -Vector3.forward)); + normals[endCenter] = meshTransform.InverseTransformDirection(SafeNormalize(worldEnd - worldControl, Vector3.forward)); + uvs[startCenter] = new Vector2(0.5f, 0f); + uvs[endCenter] = new Vector2(0.5f, 1f); + + for (int s = 0; s < sides; s++) + { + int a = s; + int b = (s + 1) % sides; + triangles[tri++] = startCenter; + triangles[tri++] = b; + triangles[tri++] = a; + + int c = (rings - 1) * sides + s; + int d = (rings - 1) * sides + (s + 1) % sides; + triangles[tri++] = endCenter; + triangles[tri++] = c; + triangles[tri++] = d; + } + } + + lineMesh.Clear(); + lineMesh.vertices = vertices; + lineMesh.normals = normals; + lineMesh.uv = uvs; + lineMesh.triangles = triangles; + lineMesh.RecalculateBounds(); + } + + private void RegisterPassiveInputAction(InputActionReference actionReference, ref bool wasEnabled) + { + if (actionReference == null || actionReference.action == null) + return; + + wasEnabled = actionReference.action.enabled; + + if (enableInputActionsManually && !wasEnabled) + actionReference.action.Enable(); + } + + private void UnregisterPassiveInputAction(InputActionReference actionReference, bool wasEnabled) + { + if (actionReference == null || actionReference.action == null) + return; + + if (enableInputActionsManually && !wasEnabled) + actionReference.action.Disable(); + } + + private void RegisterInputAction(InputActionReference actionReference, Action callback, ref bool wasEnabled) + { + if (actionReference == null || actionReference.action == null) + return; + + wasEnabled = actionReference.action.enabled; + actionReference.action.performed += callback; + + if (enableInputActionsManually && !wasEnabled) + actionReference.action.Enable(); + } + + private void UnregisterInputAction(InputActionReference actionReference, Action callback, bool wasEnabled) + { + if (actionReference == null || actionReference.action == null) + return; + + actionReference.action.performed -= callback; + + if (enableInputActionsManually && !wasEnabled) + actionReference.action.Disable(); + } + + private void StopAllRoutinesSafe() + { + StopBobberMoveAndBiteRoutines(); + } + + private void StopBobberMoveAndBiteRoutines() + { + if (bobberMoveRoutine != null) + StopCoroutine(bobberMoveRoutine); + bobberMoveRoutine = null; + + StopBiteWaitOnly(); + StopBiteShakeOnly(); + } + + private void StopBiteWaitOnly() + { + if (biteWaitRoutine != null) + StopCoroutine(biteWaitRoutine); + biteWaitRoutine = null; + } + + private void StopBiteShakeOnly() + { + if (biteShakeRoutine != null) + StopCoroutine(biteShakeRoutine); + biteShakeRoutine = null; + ResetBobberVisualPosition(); + } + + private void SetStartGuideVisible(bool visible) + { + if (startGuideUI != null && startGuideUI.activeSelf != visible) + startGuideUI.SetActive(visible); + } + + private Transform FindChildRecursive(Transform root, params string[] names) + { + if (root == null || names == null) + return null; + + for (int i = 0; i < names.Length; i++) + { + if (string.Equals(NormalizeName(root.name), NormalizeName(names[i]), StringComparison.OrdinalIgnoreCase)) + return root; + } + + for (int i = 0; i < root.childCount; i++) + { + Transform found = FindChildRecursive(root.GetChild(i), names); + if (found != null) + return found; + } + + return null; + } + + private string NormalizeName(string value) + { + if (string.IsNullOrEmpty(value)) + return string.Empty; + + return value.Replace(" ", string.Empty) + .Replace("_", string.Empty) + .Replace("-", string.Empty) + .ToLowerInvariant(); + } + + private bool CompareTagSafe(GameObject obj, string tagName) + { + if (obj == null || string.IsNullOrWhiteSpace(tagName)) + return false; + + try + { + return obj.CompareTag(tagName); + } + catch + { + return false; + } + } + + private Vector3 QuadraticBezier(Vector3 a, Vector3 b, Vector3 c, float t) + { + float u = 1f - t; + return u * u * a + 2f * u * t * b + t * t * c; + } + + private Vector3 QuadraticBezierTangent(Vector3 a, Vector3 b, Vector3 c, float t) + { + return 2f * (1f - t) * (b - a) + 2f * t * (c - b); + } + + private Vector3 SafeNormalize(Vector3 value, Vector3 fallback) + { + if (value.sqrMagnitude <= Epsilon) + return fallback.sqrMagnitude > Epsilon ? fallback.normalized : Vector3.forward; + + return value.normalized; + } + + private float EaseOutCubic(float t) + { + t = Mathf.Clamp01(t); + t = 1f - t; + return 1f - t * t * t; + } + + private void LogReel(string message) + { + if (showReelDebugLog) + Debug.Log($"[FishingRodVRController/Reel] {message}", this); + } + + private void Log(string message) + { + if (showDebugLog) + Debug.Log($"[FishingRodVRController] {message}", this); + } + + private void OnDrawGizmosSelected() + { + if (drawStartAreaGizmo && startMode == StartMode.StartActionInArea && useStartAreaCheck) + { + Transform area = startAreaCenter != null ? startAreaCenter : transform; + Gizmos.color = new Color(0.2f, 0.8f, 1f, 0.25f); + Matrix4x4 oldMatrix = Gizmos.matrix; + Gizmos.matrix = Matrix4x4.TRS(area.position, area.rotation, Vector3.one); + Gizmos.DrawCube(Vector3.zero, startAreaHalfExtents * 2f); + Gizmos.color = new Color(0.2f, 0.8f, 1f, 1f); + Gizmos.DrawWireCube(Vector3.zero, startAreaHalfExtents * 2f); + Gizmos.matrix = oldMatrix; + } + + if (useReelControl && reelCenterPoint != null) + { + Gizmos.color = new Color(1f, 0.65f, 0.15f, 0.7f); + Gizmos.DrawWireSphere(reelCenterPoint.position, reelActivationRadius); + Vector3 axis = reelCenterPoint.TransformDirection(reelLocalAxis.sqrMagnitude > Epsilon ? reelLocalAxis.normalized : Vector3.right); + Gizmos.DrawLine(reelCenterPoint.position - axis * 0.15f, reelCenterPoint.position + axis * 0.15f); + } + + if (drawWaterCheckGizmo && bobberLineStartPoint != null) + { + Gizmos.color = new Color(0.2f, 0.6f, 1f, 0.6f); + Gizmos.DrawWireSphere(bobberLineStartPoint.position, waterCheckRadius); + } + } +} diff --git a/Assets/My project/Fishing Scripts/UI/FishingRodVRController.cs.meta b/Assets/My project/Fishing Scripts/UI/FishingRodVRController.cs.meta new file mode 100644 index 00000000..14351e73 --- /dev/null +++ b/Assets/My project/Fishing Scripts/UI/FishingRodVRController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f8b17139274961d4d9b190b0bb7d6e8e \ No newline at end of file diff --git a/Assets/My project/Fishing Scripts/UI/FishingUIEffects.cs b/Assets/My project/Fishing Scripts/UI/FishingUIEffects.cs new file mode 100644 index 00000000..14ef9bec --- /dev/null +++ b/Assets/My project/Fishing Scripts/UI/FishingUIEffects.cs @@ -0,0 +1,361 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +public class FishingUIEffects : MonoBehaviour +{ + private readonly Dictionary runningEffects = new Dictionary(); + private readonly Dictionary originalScales = new Dictionary(); + private readonly Dictionary originalAnchoredPositions = new Dictionary(); + + public CanvasGroup EnsureCanvasGroup(GameObject target) + { + if (target == null) + return null; + + CanvasGroup group = target.GetComponent(); + if (group == null) + group = target.AddComponent(); + + return group; + } + + public void SetCanvasGroupInstant(GameObject target, CanvasGroup group, bool visible) + { + if (target == null && group == null) + return; + + if (target != null) + target.SetActive(visible); + + if (group != null) + { + group.alpha = visible ? 1f : 0f; + group.interactable = visible; + group.blocksRaycasts = visible; + } + } + + public void FadeCanvasGroup(GameObject target, CanvasGroup group, bool visible, float duration) + { + if (target == null && group == null) + return; + + if (group == null && target != null) + group = EnsureCanvasGroup(target); + + if (group == null) + { + if (target != null) + target.SetActive(visible); + return; + } + + StopManaged(group); + runningEffects[group] = StartCoroutine(FadeCanvasGroupRoutine(target, group, visible, Mathf.Max(0f, duration))); + } + + private IEnumerator FadeCanvasGroupRoutine(GameObject target, CanvasGroup group, bool visible, float duration) + { + if (target != null && visible) + target.SetActive(true); + + group.interactable = visible; + group.blocksRaycasts = visible; + + float startAlpha = group.alpha; + float endAlpha = visible ? 1f : 0f; + + if (duration <= 0f) + { + group.alpha = endAlpha; + } + else + { + float timer = 0f; + while (timer < duration) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / duration); + t = EaseOutCubic(t); + group.alpha = Mathf.Lerp(startAlpha, endAlpha, t); + yield return null; + } + + group.alpha = endAlpha; + } + + if (target != null && !visible) + target.SetActive(false); + + runningEffects.Remove(group); + } + + public void Pop(Transform target, float peakScale = 1.12f, float duration = 0.18f) + { + if (target == null) + return; + + StopManaged(target); + runningEffects[target] = StartCoroutine(PopRoutine(target, Mathf.Max(1f, peakScale), Mathf.Max(0.01f, duration))); + } + + private IEnumerator PopRoutine(Transform target, float peakScale, float duration) + { + if (!originalScales.TryGetValue(target, out Vector3 originalScale)) + { + originalScale = target.localScale; + originalScales[target] = originalScale; + } + + float half = duration * 0.5f; + float timer = 0f; + + while (timer < half) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / half); + target.localScale = Vector3.Lerp(originalScale, originalScale * peakScale, EaseOutCubic(t)); + yield return null; + } + + timer = 0f; + Vector3 peak = originalScale * peakScale; + + while (timer < half) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / half); + target.localScale = Vector3.Lerp(peak, originalScale, EaseOutCubic(t)); + yield return null; + } + + target.localScale = originalScale; + runningEffects.Remove(target); + } + + public void Shake(RectTransform target, float strength = 12f, float duration = 0.18f) + { + if (target == null) + return; + + StopManaged(target); + runningEffects[target] = StartCoroutine(ShakeRoutine(target, Mathf.Max(0f, strength), Mathf.Max(0.01f, duration))); + } + + private IEnumerator ShakeRoutine(RectTransform target, float strength, float duration) + { + if (!originalAnchoredPositions.TryGetValue(target, out Vector2 originalPosition)) + { + originalPosition = target.anchoredPosition; + originalAnchoredPositions[target] = originalPosition; + } + + float timer = 0f; + while (timer < duration) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / duration); + float power = Mathf.Lerp(strength, 0f, t); + Vector2 offset = UnityEngine.Random.insideUnitCircle * power; + target.anchoredPosition = originalPosition + offset; + yield return null; + } + + target.anchoredPosition = originalPosition; + runningEffects.Remove(target); + } + + public void Flash(Image image, Color color, float maxAlpha = 0.28f, float duration = 0.16f) + { + if (image == null) + return; + + StopManaged(image); + runningEffects[image] = StartCoroutine(FlashRoutine(image, color, Mathf.Clamp01(maxAlpha), Mathf.Max(0.01f, duration))); + } + + private IEnumerator FlashRoutine(Image image, Color color, float maxAlpha, float duration) + { + image.gameObject.SetActive(true); + image.raycastTarget = false; + + float timer = 0f; + while (timer < duration) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / duration); + Color current = color; + current.a = Mathf.Lerp(maxAlpha, 0f, EaseOutCubic(t)); + image.color = current; + yield return null; + } + + Color clear = color; + clear.a = 0f; + image.color = clear; + image.gameObject.SetActive(false); + runningEffects.Remove(image); + } + + public void FillImage(Image image, float targetFill, float duration) + { + if (image == null) + return; + + targetFill = Mathf.Clamp01(targetFill); + + StopManaged(image); + runningEffects[image] = StartCoroutine(FillImageRoutine(image, targetFill, Mathf.Max(0f, duration))); + } + + private IEnumerator FillImageRoutine(Image image, float targetFill, float duration) + { + float startFill = image.fillAmount; + + if (duration <= 0f) + { + image.fillAmount = targetFill; + } + else + { + float timer = 0f; + while (timer < duration) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / duration); + image.fillAmount = Mathf.Lerp(startFill, targetFill, EaseOutCubic(t)); + yield return null; + } + + image.fillAmount = targetFill; + } + + runningEffects.Remove(image); + } + + public void HighlightSlot(Image background, Image icon, Transform slotRoot, Color highlightColor, float duration, float popScale) + { + Object key = background != null ? background : icon != null ? icon : slotRoot; + if (key == null) + return; + + StopManaged(key); + runningEffects[key] = StartCoroutine(HighlightSlotRoutine(background, icon, slotRoot, highlightColor, Mathf.Max(0.01f, duration), Mathf.Max(1f, popScale), key)); + } + + private IEnumerator HighlightSlotRoutine(Image background, Image icon, Transform slotRoot, Color highlightColor, float duration, float popScale, Object key) + { + Color? originalBackground = null; + Color? originalIcon = null; + Vector3 originalScale = Vector3.one; + + if (background != null) + { + originalBackground = background.color; + Color c = Color.Lerp(background.color, highlightColor, 0.65f); + c.a = 1f; + background.color = c; + } + + if (icon != null) + { + originalIcon = icon.color; + Color c = icon.color; + c.a = 1f; + icon.color = c; + } + + if (slotRoot != null) + { + if (!originalScales.TryGetValue(slotRoot, out originalScale)) + { + originalScale = slotRoot.localScale; + originalScales[slotRoot] = originalScale; + } + } + + float half = duration * 0.5f; + float timer = 0f; + while (timer < half) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / half); + if (slotRoot != null) + slotRoot.localScale = Vector3.Lerp(originalScale, originalScale * popScale, EaseOutCubic(t)); + yield return null; + } + + timer = 0f; + Vector3 peak = originalScale * popScale; + while (timer < half) + { + timer += Time.deltaTime; + float t = Mathf.Clamp01(timer / half); + + if (slotRoot != null) + slotRoot.localScale = Vector3.Lerp(peak, originalScale, EaseOutCubic(t)); + + if (background != null && originalBackground.HasValue) + background.color = Color.Lerp(background.color, originalBackground.Value, t); + + if (icon != null && originalIcon.HasValue) + icon.color = Color.Lerp(icon.color, originalIcon.Value, t); + + yield return null; + } + + if (slotRoot != null) + slotRoot.localScale = originalScale; + + if (background != null && originalBackground.HasValue) + background.color = originalBackground.Value; + + if (icon != null && originalIcon.HasValue) + icon.color = originalIcon.Value; + + runningEffects.Remove(key); + } + + public void StopAllEffects() + { + foreach (Coroutine coroutine in runningEffects.Values) + { + if (coroutine != null) + StopCoroutine(coroutine); + } + + runningEffects.Clear(); + + foreach (KeyValuePair pair in originalScales) + { + if (pair.Key != null) + pair.Key.localScale = pair.Value; + } + + foreach (KeyValuePair pair in originalAnchoredPositions) + { + if (pair.Key != null) + pair.Key.anchoredPosition = pair.Value; + } + } + + private void StopManaged(Object key) + { + if (key == null) + return; + + if (runningEffects.TryGetValue(key, out Coroutine coroutine) && coroutine != null) + StopCoroutine(coroutine); + + runningEffects.Remove(key); + } + + private float EaseOutCubic(float t) + { + t = Mathf.Clamp01(t); + t = 1f - Mathf.Pow(1f - t, 3f); + return t; + } +} diff --git a/Assets/My project/Fishing Scripts/UI/FishingUIEffects.cs.meta b/Assets/My project/Fishing Scripts/UI/FishingUIEffects.cs.meta new file mode 100644 index 00000000..aa67d19f --- /dev/null +++ b/Assets/My project/Fishing Scripts/UI/FishingUIEffects.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b9b3bfbbd08e39e42b891728dcad3a9c \ No newline at end of file diff --git a/Assets/My project/Fishing Scripts/UI/RotateUI.cs b/Assets/My project/Fishing Scripts/UI/RotateUI.cs index 15471656..bad45262 100644 --- a/Assets/My project/Fishing Scripts/UI/RotateUI.cs +++ b/Assets/My project/Fishing Scripts/UI/RotateUI.cs @@ -5,15 +5,22 @@ public class RotateUI : MonoBehaviour [Header("Rotation Settings")] [SerializeField] private float rotateSpeed = 30f; [SerializeField] private bool clockwise = true; - [SerializeField] private bool playOnStart = true; + [SerializeField] private bool playOnStart = false; private bool isRotating; + public bool IsRotating => isRotating; + private void Awake() { isRotating = playOnStart; } + private void OnValidate() + { + rotateSpeed = Mathf.Max(0f, rotateSpeed); + } + private void Update() { if (!isRotating) @@ -39,8 +46,13 @@ public void SetClockwise(bool value) clockwise = value; } + public void SetRotateSpeed(float value) + { + rotateSpeed = Mathf.Max(0f, value); + } + public void ResetRotation() { transform.localEulerAngles = Vector3.zero; } -} \ No newline at end of file +} diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (1)-Photoroom.png b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (1)-Photoroom.png new file mode 100644 index 00000000..4309447e --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (1)-Photoroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e15c07504a960d011578fbc86c82db02fb349e6d56701f0a48028246557589f1 +size 1691308 diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (1)-Photoroom.png.meta b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (1)-Photoroom.png.meta new file mode 100644 index 00000000..dfd1546a --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (1)-Photoroom.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: e36e14e60fc40cf41bfd445f80e62535 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (2)-Photoroom.png b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (2)-Photoroom.png new file mode 100644 index 00000000..4d5c0c6a --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (2)-Photoroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87c18ab0fce8dd68b0928ca89f8e796aef46fea1177b2cede65cdbac8f3c6134 +size 879161 diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (2)-Photoroom.png.meta b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (2)-Photoroom.png.meta new file mode 100644 index 00000000..80a6619b --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_24 (2)-Photoroom.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: 9b1669e795bdb7b4c88e4376a98347d9 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 0 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_25 (3)-Photoroom.png b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_25 (3)-Photoroom.png new file mode 100644 index 00000000..f41566bc --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_25 (3)-Photoroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e2f75c920c25713be3f93a34b9a6bceb961e5ea4c094ac6eb05f5a0cac2e967 +size 826794 diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_25 (3)-Photoroom.png.meta b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_25 (3)-Photoroom.png.meta new file mode 100644 index 00000000..7845ba5d --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_25 (3)-Photoroom.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: d57aa12e9412ea74d844438588adc0f5 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 0 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (5)-Photoroom.png b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (5)-Photoroom.png new file mode 100644 index 00000000..553fe337 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (5)-Photoroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3083be1f1934941ffef3ef5a89a27338cb45dc3ad4bc70d0e53e6dbe179bf435 +size 1532183 diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (5)-Photoroom.png.meta b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (5)-Photoroom.png.meta new file mode 100644 index 00000000..12476e15 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (5)-Photoroom.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: a9e1c44292c7bdb4da8926e8fc9addd2 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (6)-Photoroom.png b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (6)-Photoroom.png new file mode 100644 index 00000000..eb2c5da9 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (6)-Photoroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd017434935ca7e74ece87de9f42e69b8d2cb9384cb217ce17ee60943e6adc92 +size 1438465 diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (6)-Photoroom.png.meta b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (6)-Photoroom.png.meta new file mode 100644 index 00000000..b38f5962 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (6)-Photoroom.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: e76bfcda04825f043b3f6c1e6cef400b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (7)-Photoroom.png b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (7)-Photoroom.png new file mode 100644 index 00000000..30d04aa3 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (7)-Photoroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35fbb8f4db4cfefe5af142e396a6942c1a1ec1625b3f0e539b336a736b056c9a +size 862300 diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (7)-Photoroom.png.meta b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (7)-Photoroom.png.meta new file mode 100644 index 00000000..55d3e2e7 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_05_26 (7)-Photoroom.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: 6365c45c31605c745bf1a33e3a1e0791 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_14-Photoroom.png b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_14-Photoroom.png new file mode 100644 index 00000000..5787bc06 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_14-Photoroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04a6f9118dda44335f08fd4d1f5cea8271861bd76238c4e6c745cabd0965b836 +size 652262 diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_14-Photoroom.png.meta b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_14-Photoroom.png.meta new file mode 100644 index 00000000..4dc42f74 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_14-Photoroom.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: 5d0cced1099954d478056e93dd67e0b4 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_24-Photoroom.png b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_24-Photoroom.png new file mode 100644 index 00000000..c37c48c2 --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_24-Photoroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8c282108a674b34316923e540f55f2967a6cced5f480322ad7097e691b3ff79 +size 712530 diff --git a/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_24-Photoroom.png.meta b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_24-Photoroom.png.meta new file mode 100644 index 00000000..23a15c4c --- /dev/null +++ b/Assets/My project/Fishing Scripts/lmage/ChatGPT Image 2026년 6월 25일 오전 11_53_24-Photoroom.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: b20f3401dfb723541b090e4f3bcf5901 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/Fonts/Pretendard-Bold SDF.asset b/Assets/My project/Fonts/Pretendard-Bold SDF.asset new file mode 100644 index 00000000..0046c46c --- /dev/null +++ b/Assets/My project/Fonts/Pretendard-Bold SDF.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2b9f0d6b6688ce01923ece2c37d4d2968d6d94d446b1f9ac8beaf9ecec92ae5 +size 33562646 diff --git a/Assets/My project/Fonts/Pretendard-Black SDF.asset.meta b/Assets/My project/Fonts/Pretendard-Bold SDF.asset.meta similarity index 64% rename from Assets/My project/Fonts/Pretendard-Black SDF.asset.meta rename to Assets/My project/Fonts/Pretendard-Bold SDF.asset.meta index e1c0a876..bc6a24e4 100644 --- a/Assets/My project/Fonts/Pretendard-Black SDF.asset.meta +++ b/Assets/My project/Fonts/Pretendard-Bold SDF.asset.meta @@ -1,8 +1,8 @@ fileFormatVersion: 2 -guid: 21ea34fc6d434e440882af6aa29c81c3 +guid: c7f713164da2d264997c747f190e6653 NativeFormatImporter: externalObjects: {} - mainObjectFileID: 0 + mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: diff --git a/Assets/My project/fishhook scene.meta b/Assets/My project/fishhook scene.meta new file mode 100644 index 00000000..e604da30 --- /dev/null +++ b/Assets/My project/fishhook scene.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eb2c790b464b7de46aae2abae1090127 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/fishhook scene.unity b/Assets/My project/fishhook scene.unity new file mode 100644 index 00000000..05b78d0a --- /dev/null +++ b/Assets/My project/fishhook scene.unity @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0317ae16c7f72b3caa0f59ec58ff150f8bc3da503890b2cd906d39c9fd8140f9 +size 900430 diff --git a/Assets/My project/fishhook scene.unity.meta b/Assets/My project/fishhook scene.unity.meta new file mode 100644 index 00000000..48db8200 --- /dev/null +++ b/Assets/My project/fishhook scene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0fc7006bb40ed5b4d92e340defe712e6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/fishhook scene/NavMesh-Terrain.asset b/Assets/My project/fishhook scene/NavMesh-Terrain.asset new file mode 100644 index 00000000..daf0147c --- /dev/null +++ b/Assets/My project/fishhook scene/NavMesh-Terrain.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3cc17cb634f8471936bf042581a52e29a62177472d723c6bcc3b776471356c12 +size 7599792 diff --git a/Assets/My project/fishhook scene/NavMesh-Terrain.asset.meta b/Assets/My project/fishhook scene/NavMesh-Terrain.asset.meta new file mode 100644 index 00000000..3bcd2a13 --- /dev/null +++ b/Assets/My project/fishhook scene/NavMesh-Terrain.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 52b638411302f9445a3b1e998f291209 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 23800000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/My project/test scene.unity b/Assets/My project/test scene.unity index 0e8d1617..7cb5f30c 100644 --- a/Assets/My project/test scene.unity +++ b/Assets/My project/test scene.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c11086d508ae29f3be61bac587fc6856db8ab0f5c10f2298ca789f4c992a8b5 -size 1344162 +oid sha256:6bf40d5cf8ff76ca7b214aa21dce7377165ed7b9ca4e17015e55fd346256e287 +size 1292493 diff --git a/Assets/Stylized Water 3/Materials/StylizedWater3_Ocean.mat b/Assets/Stylized Water 3/Materials/StylizedWater3_Ocean.mat index 65dd3f09..a554f63d 100644 --- a/Assets/Stylized Water 3/Materials/StylizedWater3_Ocean.mat +++ b/Assets/Stylized Water 3/Materials/StylizedWater3_Ocean.mat @@ -40,7 +40,7 @@ Material: - _ZCLIP_ON - _ZWRITE_ON m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 + m_EnableInstancingVariants: 1 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: {} diff --git a/Assets/Stylized Water 3/Materials/StylizedWater3_Toon.mat b/Assets/Stylized Water 3/Materials/StylizedWater3_Toon.mat index 0517f2c3..1037aa8e 100644 --- a/Assets/Stylized Water 3/Materials/StylizedWater3_Toon.mat +++ b/Assets/Stylized Water 3/Materials/StylizedWater3_Toon.mat @@ -38,7 +38,7 @@ Material: - _WORLDSPACEUV_ON - _ZCLIP_ON m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 + m_EnableInstancingVariants: 1 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: {} @@ -185,6 +185,7 @@ Material: - _FoamBubblesSpread: 1 - _FoamBubblesStrength: 0.217 - _FoamClipping: 0.5 + - _FoamClippingDynamic: 0 - _FoamDistanceOn: 1 - _FoamDistortion: 0.34 - _FoamOn: 1 @@ -238,6 +239,7 @@ Material: - _PlanarReflectionsParams: 0 - _PointSpotLightReflectionDistortion: 0.5 - _PointSpotLightReflectionExp: 64 + - _PointSpotLightReflectionSharp: 0 - _PointSpotLightReflectionSize: 0 - _PointSpotLightReflectionStrength: 10 - _QueueOffset: 0 @@ -282,6 +284,7 @@ Material: - _SrcBlend: 1 - _SunReflectionDistortion: 0.49 - _SunReflectionPerturbance: 1 + - _SunReflectionSharp: 0 - _SunReflectionSize: 0.122 - _SunReflectionStrength: 0 - _Surface: 0 @@ -295,6 +298,7 @@ Material: - _TranslucencyReflectionMask: 0 - _TranslucencyStrength: 1 - _TranslucencyStrengthDirect: 0.05 + - _UnderwaterReflectionStrength: 0.5 - _UnderwaterRefractionOffset: 0.2 - _UnderwaterSurfaceSmoothness: 0.8 - _VertexColorFoam: 0 diff --git a/Assets/XR/AndroidXR/AndroidXRSettingsInitializer b/Assets/XR/AndroidXR/AndroidXRSettingsInitializer index f9a9372d..32023f45 100644 --- a/Assets/XR/AndroidXR/AndroidXRSettingsInitializer +++ b/Assets/XR/AndroidXR/AndroidXRSettingsInitializer @@ -2,7 +2,7 @@ %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: - m_ObjectHideFlags: 53 + m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} @@ -11,5 +11,5 @@ MonoBehaviour: m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d7e9a0ba2b7f40ebbb3dad8385aa807b, type: 3} m_Name: - m_EditorClassIdentifier: Unity.XR.AndroidOpenXR.Editor::UnityEditor.XR.OpenXR.Features.Android.AndroidXRSettingsInitializer + m_EditorClassIdentifier: isInitialized: 0 diff --git a/Assets/XR/Settings/OpenXRPackageSettings.asset b/Assets/XR/Settings/OpenXRPackageSettings.asset index e32a3335..e1978558 100644 --- a/Assets/XR/Settings/OpenXRPackageSettings.asset +++ b/Assets/XR/Settings/OpenXRPackageSettings.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7aef8727367b0d4ce651fc1ae72013a99d388b056e775efd854cb25d2b741dc9 -size 96308 +oid sha256:541f41beb968bf1698702e33dd837acb9bba3d27757762a4f6b25ed555774c20 +size 98721 diff --git a/Assets/Samples/XR Interaction Toolkit/3.4.1/Starter Assets/XRI Default Input Actions.inputactions b/Assets/XRI Default Input Actions.inputactions similarity index 97% rename from Assets/Samples/XR Interaction Toolkit/3.4.1/Starter Assets/XRI Default Input Actions.inputactions rename to Assets/XRI Default Input Actions.inputactions index 66a9ab11..dea57a9d 100644 --- a/Assets/Samples/XR Interaction Toolkit/3.4.1/Starter Assets/XRI Default Input Actions.inputactions +++ b/Assets/XRI Default Input Actions.inputactions @@ -1,4 +1,5 @@ { + "version": 1, "name": "XRI Default Input Actions", "maps": [ { @@ -3373,6 +3374,94 @@ "isPartOfComposite": true } ] + }, + { + "name": "Fishing", + "id": "3c6bb206-3104-4a53-bb96-176241ce078c", + "actions": [ + { + "name": "ReturnBobber", + "type": "Button", + "id": "9cdb56e8-00cd-48ec-9198-6c70a8d9fe39", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "CastBobber", + "type": "Button", + "id": "c85d2e74-550d-4a30-9a50-17d5daf11580", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "ReelHold", + "type": "Button", + "id": "796d924d-c49e-49a2-89c8-a3336b7faed2", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "SubmitFishing", + "type": "Button", + "id": "c45b8ecb-0e50-49f0-bc60-479385b0bf5f", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "", + "id": "deb6a7a2-8de5-46b1-812b-d24b20c907a2", + "path": "{RightHand}/secondaryButton", + "interactions": "", + "processors": "", + "groups": "", + "action": "ReturnBobber", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "22448257-ef2a-42bf-a6c0-66fad17ee5e8", + "path": "{RightHand}/primaryButton", + "interactions": "", + "processors": "", + "groups": "", + "action": "CastBobber", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "a6cfa3d8-bd52-4c0e-8ff2-414ab0f22663", + "path": "{LeftHand}/gripPressed", + "interactions": "", + "processors": "", + "groups": "", + "action": "ReelHold", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "f435cfcf-7a21-40e3-b066-c6bbfc42783c", + "path": "{RightHand}/triggerPressed", + "interactions": "", + "processors": "", + "groups": "", + "action": "SubmitFishing", + "isComposite": false, + "isPartOfComposite": false + } + ] } ], "controlSchemes": [] diff --git a/Assets/Samples/XR Interaction Toolkit/3.4.1/Starter Assets/XRI Default Input Actions.inputactions.meta b/Assets/XRI Default Input Actions.inputactions.meta similarity index 100% rename from Assets/Samples/XR Interaction Toolkit/3.4.1/Starter Assets/XRI Default Input Actions.inputactions.meta rename to Assets/XRI Default Input Actions.inputactions.meta diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset index 35160ab3..b7046c80 100644 --- a/ProjectSettings/EditorBuildSettings.asset +++ b/ProjectSettings/EditorBuildSettings.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff2128e80b7a7a4f51cce17f1d1ac5811177c85a2d3f2579f7bb69ef6a8cd870 -size 1736 +oid sha256:6bffe07e2b31834b876304b2d0850e107d1a3fb52445f5ddb00f12804ab14a0e +size 2196 diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset index e5096487..637ba983 100644 --- a/ProjectSettings/TagManager.asset +++ b/ProjectSettings/TagManager.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da4b5eefc927f8b91bcf9e56a186d2b852f49d9cbe91d466638d26c79d6a83ce -size 411 +oid sha256:e8d4ffbba55f43de3c4c2454e262aa0d4d01351b23ec0462dee606021029f4b3 +size 483