From cc13baff7863f47f3b858b447e85e2149ecbe5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=8C=90=EB=8F=8C?= Date: Wed, 29 Jan 2025 22:06:06 +0900 Subject: [PATCH] =?UTF-8?q?WebGLInput=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/0_Scenes/Title.unity | 228 +++++---- Assets/1_Script/System/GameManager.cs | 6 + Assets/WebGLSupport/Editor/Postprocessor.cs | 59 +++ .../WebGLInput/Detail/RebuildChecker.cs | 62 +++ .../WebGLSupport/WebGLInput/Detail/Support.cs | 45 ++ .../WebGLInput/Events/KeyboardEvent.cs | 20 + .../WebGLInput/Mobile/WebGLInputMobile.cs | 87 ++++ .../WebGLInput/Mobile/WebGLInputMobile.jslib | 23 + .../UIToolKit/WebGLInputManipulator.cs | 67 +++ .../UIToolKit/WebGLUIToolkitTextField.cs | 13 + Assets/WebGLSupport/WebGLInput/WebGLInput.cs | 483 ++++++++++++++++++ .../WebGLSupport/WebGLInput/WebGLInput.jslib | 216 ++++++++ .../WebGLInput/Wrapper/IInputField.cs | 46 ++ .../WebGLInput/Wrapper/WrappedInputField.cs | 119 +++++ .../Wrapper/WrappedTMPInputField.cs | 212 ++++++++ .../WebGLInput/Wrapper/WrappedUIToolkit.cs | 162 ++++++ .../WebGLSupport/WebGLWindow/WebGLWindow.cs | 138 +++++ .../WebGLWindow/WebGLWindow.jslib | 204 ++++++++ ProjectSettings/ProjectSettings.asset | 4 +- 19 files changed, 2085 insertions(+), 109 deletions(-) create mode 100644 Assets/WebGLSupport/Editor/Postprocessor.cs create mode 100644 Assets/WebGLSupport/WebGLInput/Detail/RebuildChecker.cs create mode 100644 Assets/WebGLSupport/WebGLInput/Detail/Support.cs create mode 100644 Assets/WebGLSupport/WebGLInput/Events/KeyboardEvent.cs create mode 100644 Assets/WebGLSupport/WebGLInput/Mobile/WebGLInputMobile.cs create mode 100644 Assets/WebGLSupport/WebGLInput/Mobile/WebGLInputMobile.jslib create mode 100644 Assets/WebGLSupport/WebGLInput/UIToolKit/WebGLInputManipulator.cs create mode 100644 Assets/WebGLSupport/WebGLInput/UIToolKit/WebGLUIToolkitTextField.cs create mode 100644 Assets/WebGLSupport/WebGLInput/WebGLInput.cs create mode 100644 Assets/WebGLSupport/WebGLInput/WebGLInput.jslib create mode 100644 Assets/WebGLSupport/WebGLInput/Wrapper/IInputField.cs create mode 100644 Assets/WebGLSupport/WebGLInput/Wrapper/WrappedInputField.cs create mode 100644 Assets/WebGLSupport/WebGLInput/Wrapper/WrappedTMPInputField.cs create mode 100644 Assets/WebGLSupport/WebGLInput/Wrapper/WrappedUIToolkit.cs create mode 100644 Assets/WebGLSupport/WebGLWindow/WebGLWindow.cs create mode 100644 Assets/WebGLSupport/WebGLWindow/WebGLWindow.jslib diff --git a/Assets/0_Scenes/Title.unity b/Assets/0_Scenes/Title.unity index 7ca4bac..5ad0f62 100644 --- a/Assets/0_Scenes/Title.unity +++ b/Assets/0_Scenes/Title.unity @@ -623,9 +623,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 256.61765, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &144588837 @@ -1067,10 +1067,10 @@ RectTransform: - {fileID: 985133702} m_Father: {fileID: 289329350} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} + m_AnchorMin: {x: 0.4, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 75, y: 0} - m_SizeDelta: {x: -150, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &345516811 MonoBehaviour: @@ -1274,9 +1274,9 @@ RectTransform: m_Father: {fileID: 289329350} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -195, y: 0} - m_SizeDelta: {x: -390, y: 0} + m_AnchorMax: {x: 0.4, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &356909098 MonoBehaviour: @@ -1328,9 +1328,9 @@ MonoBehaviour: m_fontSize: 36 m_fontSizeBase: 36 m_fontWeight: 400 - m_enableAutoSizing: 0 + m_enableAutoSizing: 1 m_fontSizeMin: 18 - m_fontSizeMax: 72 + m_fontSizeMax: 36 m_fontStyle: 0 m_HorizontalAlignment: 2 m_VerticalAlignment: 512 @@ -1468,9 +1468,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 786.0293, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &382387211 @@ -1750,9 +1750,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 388.97055, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &460722987 @@ -2060,7 +2060,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &560148188 RectTransform: m_ObjectHideFlags: 0 @@ -2075,10 +2075,10 @@ RectTransform: m_Children: [] m_Father: {fileID: 1124752560} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 1080} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 960, y: -1620} + m_SizeDelta: {x: 1920, y: 1080} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &560148189 MonoBehaviour: @@ -2151,9 +2151,9 @@ RectTransform: m_Father: {fileID: 1334489342} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -195, y: 0} - m_SizeDelta: {x: -390, y: 0} + m_AnchorMax: {x: 0.4, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &586563905 MonoBehaviour: @@ -2205,9 +2205,9 @@ MonoBehaviour: m_fontSize: 36 m_fontSizeBase: 36 m_fontWeight: 400 - m_enableAutoSizing: 0 + m_enableAutoSizing: 1 m_fontSizeMin: 18 - m_fontSizeMax: 72 + m_fontSizeMax: 36 m_fontStyle: 0 m_HorizontalAlignment: 2 m_VerticalAlignment: 512 @@ -2282,15 +2282,14 @@ RectTransform: m_Children: - {fileID: 780541864} - {fileID: 1285681088} - - {fileID: 618473815} - {fileID: 503045448} - {fileID: 2010761431} m_Father: {fileID: 364529818} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} + m_AnchorMin: {x: 0.15, y: 0} + m_AnchorMax: {x: 0.85, y: 1} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: -600, y: -200} + m_SizeDelta: {x: 0, y: -200} m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &618473814 GameObject: @@ -2318,18 +2317,18 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 618473814} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 617120531} + m_Father: {fileID: 1285681088} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMin: {x: 1, y: 0.5} m_AnchorMax: {x: 1, y: 0.5} - m_AnchoredPosition: {x: 560, y: 50} - m_SizeDelta: {x: -1220, y: 100} - m_Pivot: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 110, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 1, y: 0.5} --- !u!114 &618473816 MonoBehaviour: m_ObjectHideFlags: 0 @@ -2445,9 +2444,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 521.3235, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &625790242 @@ -2933,9 +2932,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 918.3822, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &714614569 @@ -3129,7 +3128,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -7.499939, y: -0.5} + m_AnchoredPosition: {x: -7.5, y: -0.5} m_SizeDelta: {x: -35, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &736425880 @@ -3681,9 +3680,9 @@ RectTransform: m_Father: {fileID: 954257687} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -195, y: 0} - m_SizeDelta: {x: -390, y: 0} + m_AnchorMax: {x: 0.4, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &884267091 MonoBehaviour: @@ -3735,9 +3734,9 @@ MonoBehaviour: m_fontSize: 36 m_fontSizeBase: 36 m_fontWeight: 400 - m_enableAutoSizing: 0 + m_enableAutoSizing: 1 m_fontSizeMin: 18 - m_fontSizeMax: 72 + m_fontSizeMax: 36 m_fontStyle: 0 m_HorizontalAlignment: 2 m_VerticalAlignment: 512 @@ -4094,10 +4093,10 @@ RectTransform: - {fileID: 972569514} m_Father: {fileID: 364529818} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 1, y: 1} + m_AnchorMin: {x: 0.75, y: 1} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 450, y: 350} + m_SizeDelta: {x: 0, y: 350} m_Pivot: {x: 1, y: 1} --- !u!114 &922300766 MonoBehaviour: @@ -4767,9 +4766,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 984.55865, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1023580848 @@ -4887,9 +4886,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 455.147, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1027373656 @@ -5007,9 +5006,9 @@ RectTransform: m_Father: {fileID: 471906646} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -195, y: 0} - m_SizeDelta: {x: -390, y: 0} + m_AnchorMax: {x: 0.4, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1054705960 MonoBehaviour: @@ -5061,9 +5060,9 @@ MonoBehaviour: m_fontSize: 36 m_fontSizeBase: 36 m_fontWeight: 400 - m_enableAutoSizing: 0 + m_enableAutoSizing: 1 m_fontSizeMin: 18 - m_fontSizeMax: 72 + m_fontSizeMax: 36 m_fontStyle: 0 m_HorizontalAlignment: 2 m_VerticalAlignment: 512 @@ -5141,9 +5140,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 719.85284, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1068821398 @@ -5854,7 +5853,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} m_Name: m_EditorClassIdentifier: - m_UiScaleMode: 1 + m_UiScaleMode: 0 m_ReferencePixelsPerUnit: 100 m_ScaleFactor: 1 m_ReferenceResolution: {x: 1920, y: 1080} @@ -5942,7 +5941,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -7.499939, y: -0.5} + m_AnchoredPosition: {x: -7.5, y: -0.5} m_SizeDelta: {x: -35, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1174602295 @@ -6465,9 +6464,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 322.7941, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1266200995 @@ -6564,6 +6563,7 @@ GameObject: - component: {fileID: 1285681091} - component: {fileID: 1285681090} - component: {fileID: 1285681089} + - component: {fileID: 1285681092} m_Layer: 5 m_Name: InputField (TMP) m_TagString: Untagged @@ -6584,11 +6584,12 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 33284432} + - {fileID: 618473815} m_Father: {fileID: 617120531} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.5} m_AnchorMax: {x: 1, y: 0.5} - m_AnchoredPosition: {x: -54.035828, y: 50} + m_AnchoredPosition: {x: -54.03589, y: 50} m_SizeDelta: {x: -208.0717, y: 100} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1285681089 @@ -6727,6 +6728,19 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1285681087} m_CullTransparentMesh: 1 +--- !u!114 &1285681092 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1285681087} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cc33a39070010f94fb1c2dd721c1286d, type: 3} + m_Name: + m_EditorClassIdentifier: + showHtmlElement: 0 --- !u!1 &1334489341 GameObject: m_ObjectHideFlags: 0 @@ -6907,10 +6921,10 @@ RectTransform: - {fileID: 905907880} m_Father: {fileID: 471906646} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} + m_AnchorMin: {x: 0.4, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 75, y: 0} - m_SizeDelta: {x: -150, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1365708778 MonoBehaviour: @@ -7040,7 +7054,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -7.499939, y: -0.5} + m_AnchoredPosition: {x: -7.5, y: -0.5} m_SizeDelta: {x: -35, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1404080530 @@ -7173,9 +7187,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 653.6764, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1417564764 @@ -7383,9 +7397,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 190.44116, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1454269870 @@ -7730,10 +7744,10 @@ RectTransform: - {fileID: 1364138968} m_Father: {fileID: 954257687} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} + m_AnchorMin: {x: 0.4, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 75, y: 0} - m_SizeDelta: {x: -150, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1567209629 MonoBehaviour: @@ -7865,10 +7879,10 @@ RectTransform: - {fileID: 676388685} m_Father: {fileID: 1334489342} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} + m_AnchorMin: {x: 0.4, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 75, y: 0} - m_SizeDelta: {x: -150, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1628350769 MonoBehaviour: @@ -8131,9 +8145,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 1116.9116, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1673953822 @@ -8251,9 +8265,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 587.49994, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1701226251 @@ -8447,7 +8461,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -7.499939, y: -0.5} + m_AnchoredPosition: {x: -7.5, y: -0.5} m_SizeDelta: {x: -35, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1712561597 @@ -8863,9 +8877,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 45.588234, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 75, y: 60} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1938921664 @@ -9206,7 +9220,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &2010761431 RectTransform: m_ObjectHideFlags: 0 @@ -9735,9 +9749,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 852.20575, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2098824716 @@ -9855,9 +9869,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 124.2647, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2104150797 @@ -10011,9 +10025,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 167002552} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 1050.7351, y: -30} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 50, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2110967111 diff --git a/Assets/1_Script/System/GameManager.cs b/Assets/1_Script/System/GameManager.cs index 05ff53e..daf7b89 100644 --- a/Assets/1_Script/System/GameManager.cs +++ b/Assets/1_Script/System/GameManager.cs @@ -6,4 +6,10 @@ using UnityEngine.SceneManagement; public class GameManager : DontDestroy { +#if !UNITY_EDITOR + protected override void OnAwake() + { + WebGLInput.captureAllKeyboardInput = false; + } +#endif } diff --git a/Assets/WebGLSupport/Editor/Postprocessor.cs b/Assets/WebGLSupport/Editor/Postprocessor.cs new file mode 100644 index 0000000..ae670b9 --- /dev/null +++ b/Assets/WebGLSupport/Editor/Postprocessor.cs @@ -0,0 +1,59 @@ +using System.IO; +using UnityEditor; +using UnityEditor.Callbacks; +using UnityEngine; + +namespace WebGLSupport +{ + public class Postprocessor + { + const string MenuPath = "Assets/WebGLSupport/OverwriteFullscreenButton"; + +#if UNITY_2021_1_OR_NEWER + static readonly bool supportedPostprocessor = true; + static readonly string defaultFullscreenFunc = "unityInstance.SetFullscreen(1);"; + static readonly string fullscreenNode = "unity-container"; +#else + static readonly bool supportedPostprocessor = false; + static readonly string defaultFullscreenFunc = ""; + static readonly string fullscreenNode = ""; +#endif + + private static bool IsEnable => PlayerPrefs.GetInt(MenuPath, 1) == 1; + + [PostProcessBuild(1)] + public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) + { + if (target != BuildTarget.WebGL) return; + if (!supportedPostprocessor) return; + if (!IsEnable) return; + + var path = Path.Combine(pathToBuiltProject, "index.html"); + if (!File.Exists(path)) return; + + var html = File.ReadAllText(path); + + // check node is exist + if (html.Contains(fullscreenNode)) + { + html = html.Replace(defaultFullscreenFunc, $"document.makeFullscreen('{fullscreenNode}');"); + File.WriteAllText(path, html); + } + } + + [MenuItem(MenuPath)] + public static void OverwriteDefaultFullscreenButton() + { + var flag = !Menu.GetChecked(MenuPath); + Menu.SetChecked(MenuPath, flag); + PlayerPrefs.SetInt(MenuPath, flag ? 1 : 0); + } + + [MenuItem(MenuPath, validate = true)] + private static bool OverwriteDefaultFullscreenButtonValidator() + { + Menu.SetChecked(MenuPath, IsEnable); + return true; + } + } +} diff --git a/Assets/WebGLSupport/WebGLInput/Detail/RebuildChecker.cs b/Assets/WebGLSupport/WebGLInput/Detail/RebuildChecker.cs new file mode 100644 index 0000000..2558b64 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Detail/RebuildChecker.cs @@ -0,0 +1,62 @@ +using UnityEngine; + +namespace WebGLSupport.Detail +{ + public class RebuildChecker + { + IInputField input; + + string beforeString; + int beforeCaretPosition; + int beforeSelectionFocusPosition; + int beforeSelectionAnchorPosition; + //Vector2 anchoredPosition; + + public RebuildChecker(IInputField input) + { + this.input = input; + } + + public bool NeedRebuild(bool debug = false) + { + var res = false; + + // any not same + if (beforeString != input.text) + { + if(debug) Debug.Log(string.Format("beforeString : {0} != {1}", beforeString, input.text)); + beforeString = input.text; + res = true; + } + + if (beforeCaretPosition != input.caretPosition) + { + if (debug) Debug.Log(string.Format("beforeCaretPosition : {0} != {1}", beforeCaretPosition, input.caretPosition)); + beforeCaretPosition = input.caretPosition; + res = true; + } + + if (beforeSelectionFocusPosition != input.selectionFocusPosition) + { + if (debug) Debug.Log(string.Format("beforeSelectionFocusPosition : {0} != {1}", beforeSelectionFocusPosition, input.selectionFocusPosition)); + beforeSelectionFocusPosition = input.selectionFocusPosition; + res = true; + } + + if (beforeSelectionAnchorPosition != input.selectionAnchorPosition) + { + if (debug) Debug.Log(string.Format("beforeSelectionAnchorPosition : {0} != {1}", beforeSelectionAnchorPosition, input.selectionAnchorPosition)); + beforeSelectionAnchorPosition = input.selectionAnchorPosition; + res = true; + } + + //if (anchoredPosition != input.TextComponentRectTransform().anchoredPosition) + //{ + // if (debug) Debug.Log(string.Format("anchoredPosition : {0} != {1}", anchoredPosition, input.TextComponentRectTransform().anchoredPosition)); + // anchoredPosition = input.TextComponentRectTransform().anchoredPosition; + // res = true; + //} + return res; + } + } +} diff --git a/Assets/WebGLSupport/WebGLInput/Detail/Support.cs b/Assets/WebGLSupport/WebGLInput/Detail/Support.cs new file mode 100644 index 0000000..6fd0504 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Detail/Support.cs @@ -0,0 +1,45 @@ +using UnityEngine; + +namespace WebGLSupport.Detail +{ + public static class Support + { + /// + /// 画面内の描画範囲を取得する + /// + /// + /// + public static Rect GetScreenCoordinates(RectTransform uiElement) + { + var worldCorners = new Vector3[4]; + uiElement.GetWorldCorners(worldCorners); + + // try to support RenderMode:WorldSpace + var canvas = uiElement.GetComponentInParent(); + var useCamera = (canvas.renderMode != RenderMode.ScreenSpaceOverlay); + if (canvas && useCamera) + { + var camera = canvas.worldCamera; + if (!camera) camera = Camera.main; + + for (var i = 0; i < worldCorners.Length; i++) + { + worldCorners[i] = camera.WorldToScreenPoint(worldCorners[i]); + } + } + + var min = new Vector3(float.MaxValue, float.MaxValue); + var max = new Vector3(float.MinValue, float.MinValue); + for (var i = 0; i < worldCorners.Length; i++) + { + min.x = Mathf.Min(min.x, worldCorners[i].x); + min.y = Mathf.Min(min.y, worldCorners[i].y); + max.x = Mathf.Max(max.x, worldCorners[i].x); + max.y = Mathf.Max(max.y, worldCorners[i].y); + } + + return new Rect(min.x, min.y, max.x - min.x, max.y - min.y); + } + + } +} \ No newline at end of file diff --git a/Assets/WebGLSupport/WebGLInput/Events/KeyboardEvent.cs b/Assets/WebGLSupport/WebGLInput/Events/KeyboardEvent.cs new file mode 100644 index 0000000..cec5357 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Events/KeyboardEvent.cs @@ -0,0 +1,20 @@ +namespace WebGLSupport +{ + public delegate void KeyboardEventHandler(WebGLInput input, KeyboardEvent keyboardEvent); + public sealed class KeyboardEvent + { + public string Key { get; } + public int Code { get; } + public bool ShiftKey { get; } + public bool CtrlKey { get; } + public bool AltKey { get; } + public KeyboardEvent(string key, int code, bool shiftKey, bool ctrlKey, bool altKey) + { + Key = key; + Code = code; + ShiftKey = shiftKey; + CtrlKey = ctrlKey; + AltKey = altKey; + } + } +} diff --git a/Assets/WebGLSupport/WebGLInput/Mobile/WebGLInputMobile.cs b/Assets/WebGLSupport/WebGLInput/Mobile/WebGLInputMobile.cs new file mode 100644 index 0000000..6d3360a --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Mobile/WebGLInputMobile.cs @@ -0,0 +1,87 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using System.Runtime.InteropServices; // for DllImport +using AOT; +using System; + +namespace WebGLSupport +{ + class WebGLInputMobilePlugin + { +#if UNITY_WEBGL && !UNITY_EDITOR + [DllImport("__Internal")] + public static extern int WebGLInputMobileRegister(Action OnTouchEnd); + + [DllImport("__Internal")] + public static extern void WebGLInputMobileOnFocusOut(int id, Action OnFocusOut); +#else + /// + /// ID を割り振り + /// + /// + public static int WebGLInputMobileRegister(Action OnTouchEnd) { return 0; } + + public static void WebGLInputMobileOnFocusOut(int id, Action OnFocusOut) { } +#endif + } + + public class WebGLInputMobile : MonoBehaviour, IPointerDownHandler + { + static Dictionary instances = new Dictionary(); + + int id = -1; + + private void Awake() + { +#if !(UNITY_WEBGL && !UNITY_EDITOR) + // WebGL 以外、更新メソッドは動作しないようにします + enabled = false; +#endif + } + + /// + /// 押されたら、touchend イベントを登録する + /// + /// + public void OnPointerDown(PointerEventData eventData) + { + if (id != -1) return; + id = WebGLInputMobilePlugin.WebGLInputMobileRegister(OnTouchEnd); + instances[id] = this; + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnTouchEnd(int id) + { + var @this = instances[id]; + @this.GetComponent().OnSelect(); + @this.StartCoroutine(RegisterOnFocusOut(id)); + } + + static IEnumerator RegisterOnFocusOut(int id) + { + yield return null; // wait one frame. + WebGLInputMobilePlugin.WebGLInputMobileOnFocusOut(id, OnFocusOut); + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnFocusOut(int id) + { + var @this = instances[id]; + @this.StartCoroutine(ExecFocusOut(id)); + } + + static IEnumerator ExecFocusOut(int id) + { + yield return null; // wait one frame. + var @this = instances[id]; + @this.GetComponent().DeactivateInputField(); + // release + @this.id = -1; + instances.Remove(id); + } + } +} + diff --git a/Assets/WebGLSupport/WebGLInput/Mobile/WebGLInputMobile.jslib b/Assets/WebGLSupport/WebGLInput/Mobile/WebGLInputMobile.jslib new file mode 100644 index 0000000..c3fc619 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Mobile/WebGLInputMobile.jslib @@ -0,0 +1,23 @@ +var WebGLInputMobile = { + $instances: [], + + WebGLInputMobileRegister: function (touchend) { + var id = instances.push(null) - 1; + + document.body.addEventListener("touchend", function () { + document.body.removeEventListener("touchend", arguments.callee); + Runtime.dynCall("vi", touchend, [id]); + }); + + return id; + }, + WebGLInputMobileOnFocusOut: function (id, focusout) { + document.body.addEventListener("focusout", function () { + document.body.removeEventListener("focusout", arguments.callee); + Runtime.dynCall("vi", focusout, [id]); + }); + }, +} + +autoAddDeps(WebGLInputMobile, '$instances'); +mergeInto(LibraryManager.library, WebGLInputMobile); \ No newline at end of file diff --git a/Assets/WebGLSupport/WebGLInput/UIToolKit/WebGLInputManipulator.cs b/Assets/WebGLSupport/WebGLInput/UIToolKit/WebGLInputManipulator.cs new file mode 100644 index 0000000..f4b9054 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/UIToolKit/WebGLInputManipulator.cs @@ -0,0 +1,67 @@ +using UnityEngine; +using UnityEngine.UIElements; + +namespace WebGLSupport +{ + public class WebGLInputManipulator : Manipulator + { + private GameObject go; + private bool showHtmlElement; + + public WebGLInputManipulator(bool showHtmlElement = false) + { + this.showHtmlElement = showHtmlElement; + } + protected override void RegisterCallbacksOnTarget() + { + // uitoolkit is already support mobile. + if (!Application.isMobilePlatform) + { + var textInput = target.Q("unity-text-input"); + textInput.RegisterCallback(OnFocusInEvent); + textInput.RegisterCallback(OnFocusOutEvent); + } + } + protected override void UnregisterCallbacksFromTarget() + { + // uitoolkit is already support mobile. + if (!Application.isMobilePlatform) + { + var textInput = target.Q("unity-text-input"); + textInput.UnregisterCallback(OnFocusInEvent); + textInput.UnregisterCallback(OnFocusOutEvent); + } + } + + private void OnFocusInEvent(FocusInEvent evt) + { + if (go != null) + { + GameObject.Destroy(go); + go = null; + } + + go = new GameObject("WebGLInputManipulator"); + + // add WebGLUIToolkitMonoBehaviour for hold TextField! + var uitoolkit = go.AddComponent(); + uitoolkit.TextField = target as TextField; + + // add WebGLInput to handle the event! + var webglInput = go.AddComponent(); + webglInput.showHtmlElement = showHtmlElement; + + // select it!! + webglInput.OnSelect(); + } + + private void OnFocusOutEvent(FocusOutEvent evt) + { + if (go != null) + { + GameObject.Destroy(go); + go = null; + } + } + } +} diff --git a/Assets/WebGLSupport/WebGLInput/UIToolKit/WebGLUIToolkitTextField.cs b/Assets/WebGLSupport/WebGLInput/UIToolKit/WebGLUIToolkitTextField.cs new file mode 100644 index 0000000..f714a84 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/UIToolKit/WebGLUIToolkitTextField.cs @@ -0,0 +1,13 @@ +using UnityEngine; +using UnityEngine.UIElements; + +namespace WebGLSupport +{ + /// + /// UIToolkit TextField + /// + public class WebGLUIToolkitTextField : MonoBehaviour + { + public TextField TextField { get; set; } + } +} diff --git a/Assets/WebGLSupport/WebGLInput/WebGLInput.cs b/Assets/WebGLSupport/WebGLInput/WebGLInput.cs new file mode 100644 index 0000000..a763c12 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/WebGLInput.cs @@ -0,0 +1,483 @@ +#if UNITY_2018_2_OR_NEWER +#define TMP_WEBGL_SUPPORT +#endif + +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using System; +using AOT; +using System.Runtime.InteropServices; // for DllImport +using System.Collections; +using UnityEngine.EventSystems; + +namespace WebGLSupport +{ + internal class WebGLInputPlugin + { +#if UNITY_WEBGL && !UNITY_EDITOR + [DllImport("__Internal")] + public static extern void WebGLInputInit(); + [DllImport("__Internal")] + public static extern int WebGLInputCreate(string canvasId, int x, int y, int width, int height, int fontsize, string text, string placeholder, bool isMultiLine, bool isPassword, bool isHidden, bool isMobile); + + [DllImport("__Internal")] + public static extern void WebGLInputEnterSubmit(int id, bool flag); + + [DllImport("__Internal")] + public static extern void WebGLInputTab(int id, Action cb); + + [DllImport("__Internal")] + public static extern void WebGLInputFocus(int id); + + [DllImport("__Internal")] + public static extern void WebGLInputOnFocus(int id, Action cb); + + [DllImport("__Internal")] + public static extern void WebGLInputOnBlur(int id, Action cb); + + [DllImport("__Internal")] + public static extern void WebGLInputOnValueChange(int id, Action cb); + + [DllImport("__Internal")] + public static extern void WebGLInputOnEditEnd(int id, Action cb); + + [DllImport("__Internal")] + public static extern void WebGLInputOnKeyboardEvent(int id, Action cb); + + [DllImport("__Internal")] + public static extern int WebGLInputSelectionStart(int id); + + [DllImport("__Internal")] + public static extern int WebGLInputSelectionEnd(int id); + + [DllImport("__Internal")] + public static extern int WebGLInputSelectionDirection(int id); + + [DllImport("__Internal")] + public static extern void WebGLInputSetSelectionRange(int id, int start, int end); + + [DllImport("__Internal")] + public static extern void WebGLInputMaxLength(int id, int maxlength); + + [DllImport("__Internal")] + public static extern void WebGLInputText(int id, string text); + + [DllImport("__Internal")] + public static extern bool WebGLInputIsFocus(int id); + + [DllImport("__Internal")] + public static extern void WebGLInputDelete(int id); + + [DllImport("__Internal")] + public static extern void WebGLInputForceBlur(int id); + +#if WEBGLINPUT_TAB + [DllImport("__Internal")] + public static extern void WebGLInputEnableTabText(int id, bool enable); +#endif +#else + public static void WebGLInputInit() { } + public static int WebGLInputCreate(string canvasId, int x, int y, int width, int height, int fontsize, string text, string placeholder, bool isMultiLine, bool isPassword, bool isHidden, bool isMobile) { return 0; } + public static void WebGLInputEnterSubmit(int id, bool flag) { } + public static void WebGLInputTab(int id, Action cb) { } + public static void WebGLInputFocus(int id) { } + public static void WebGLInputOnFocus(int id, Action cb) { } + public static void WebGLInputOnBlur(int id, Action cb) { } + public static void WebGLInputOnValueChange(int id, Action cb) { } + public static void WebGLInputOnEditEnd(int id, Action cb) { } + public static void WebGLInputOnKeyboardEvent(int id, Action cb) { } + public static int WebGLInputSelectionStart(int id) { return 0; } + public static int WebGLInputSelectionEnd(int id) { return 0; } + public static int WebGLInputSelectionDirection(int id) { return 0; } + public static void WebGLInputSetSelectionRange(int id, int start, int end) { } + public static void WebGLInputMaxLength(int id, int maxlength) { } + public static void WebGLInputText(int id, string text) { } + public static bool WebGLInputIsFocus(int id) { return false; } + public static void WebGLInputDelete(int id) { } + public static void WebGLInputForceBlur(int id) { } + +#if WEBGLINPUT_TAB + public static void WebGLInputEnableTabText(int id, bool enable) { } +#endif + +#endif + } + + public class WebGLInput : MonoBehaviour, IComparable + { + public static event KeyboardEventHandler OnKeyboardDown; + public static event KeyboardEventHandler OnKeyboardUp; + + static Dictionary instances = new Dictionary(); + public static string CanvasId { get; set; } + +#if WEBGLINPUT_TAB + public bool enableTabText = false; +#endif + + static WebGLInput() + { + CanvasId = WebGLWindow.GetCanvasName(); + WebGLInputPlugin.WebGLInputInit(); + } + public int Id { get { return id; } } + internal int id = -1; + public IInputField input { get; private set; } + bool blurBlock = false; + + [TooltipAttribute("show input element on canvas. this will make you select text by drag.")] + public bool showHtmlElement = false; + + private IInputField Setup() + { + if (GetComponent()) return new WrappedInputField(GetComponent()); + + if (GetComponent()) return new WrappedUIToolkit(GetComponent()); +#if TMP_WEBGL_SUPPORT + if (GetComponent()) return new WrappedTMPInputField(GetComponent()); +#endif // TMP_WEBGL_SUPPORT + + throw new Exception("Can not Setup WebGLInput!!"); + } + + private void Awake() + { + input = Setup(); +#if !(UNITY_WEBGL && !UNITY_EDITOR) + // WebGL 以外、更新メソッドは動作しないようにします + enabled = false; +#endif + // for mobile platform + if (Application.isMobilePlatform) + { + if (input.EnableMobileSupport) + { + gameObject.AddComponent(); + } + else + { + // when disable mobile input. disable self! + enabled = false; + } + } + } + + /// + /// Get the element rect of input + /// + /// + RectInt GetElemetRect() + { + var rect = input.GetScreenCoordinates(); + //// モバイルの場合、強制表示する + //if (showHtmlElement || Application.isMobilePlatform) + //{ + // var x = (int)(rect.x); + // var y = (int)(Screen.height - (rect.y + rect.height)); + // return new RectInt(x, y, (int)rect.width, (int)rect.height); + //} + //else + //{ + // var x = (int)(rect.x); + // var y = (int)(Screen.height - (rect.y)); + // return new RectInt(x, y, (int)rect.width, (int)1); + //} + var x = (int)(rect.x); + var y = (int)(Screen.height - (rect.y)); + return new RectInt(x, y, (int)rect.width, (int)1); + } + /// + /// 対象が選択されたとき + /// + /// + public void OnSelect() + { + if (id != -1) throw new Exception("OnSelect : id != -1"); + + var rect = GetElemetRect(); + bool isPassword = input.contentType == ContentType.Password; + + var fontSize = Mathf.Max(14, input.fontSize); // limit font size : 14 !! + + // モバイルの場合、強制表示する + var isHidden = !(showHtmlElement || Application.isMobilePlatform); + id = WebGLInputPlugin.WebGLInputCreate(WebGLInput.CanvasId, rect.x, rect.y, rect.width, rect.height, fontSize, input.text, input.placeholder, input.lineType != LineType.SingleLine, isPassword, isHidden, Application.isMobilePlatform); + + instances[id] = this; + WebGLInputPlugin.WebGLInputEnterSubmit(id, input.lineType != LineType.MultiLineNewline); + WebGLInputPlugin.WebGLInputOnFocus(id, OnFocus); + WebGLInputPlugin.WebGLInputOnBlur(id, OnBlur); + WebGLInputPlugin.WebGLInputOnValueChange(id, OnValueChange); + WebGLInputPlugin.WebGLInputOnEditEnd(id, OnEditEnd); + WebGLInputPlugin.WebGLInputOnKeyboardEvent(id, OnKeyboardEvent); + WebGLInputPlugin.WebGLInputTab(id, OnTab); + + // default value : https://www.w3schools.com/tags/att_input_maxlength.asp + WebGLInputPlugin.WebGLInputMaxLength(id, (input.characterLimit > 0) ? input.characterLimit : 524288); + WebGLInputPlugin.WebGLInputFocus(id); +#if WEBGLINPUT_TAB + WebGLInputPlugin.WebGLInputEnableTabText(id, enableTabText); +#endif + if (input.OnFocusSelectAll) + { + WebGLInputPlugin.WebGLInputSetSelectionRange(id, 0, input.text.Length); + } + else + { + WebGLInputPlugin.WebGLInputSetSelectionRange(id, input.caretPosition, input.caretPosition); + } + + WebGLWindow.OnBlurEvent += OnWindowBlur; + } + + /// + /// sync text from inputfield + /// + /// + public void SyncText(int? cursorIndex = null) + { + if (!instances.ContainsKey(id)) return; + + var instance = instances[id]; + + WebGLInputPlugin.WebGLInputText(id, instance.input.text); + + if (cursorIndex.HasValue) + { + WebGLInputPlugin.WebGLInputSetSelectionRange(id, cursorIndex.Value, cursorIndex.Value); + } + } + + private void OnWindowBlur() + { + blurBlock = true; + } + + internal void DeactivateInputField() + { + if (!instances.ContainsKey(id)) return; + + WebGLInputPlugin.WebGLInputDelete(id); + input.DeactivateInputField(); + instances.Remove(id); + id = -1; // reset id to -1; + WebGLWindow.OnBlurEvent -= OnWindowBlur; + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnFocus(int id) + { +#if UNITY_WEBGL && !UNITY_EDITOR + Input.ResetInputAxes(); // Inputの状態リセット + UnityEngine.WebGLInput.captureAllKeyboardInput = false; +#endif + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnBlur(int id) + { +#if UNITY_WEBGL && !UNITY_EDITOR + UnityEngine.WebGLInput.captureAllKeyboardInput = true; + Input.ResetInputAxes(); // Inputの状態リセット +#endif + instances[id].StartCoroutine(Blur(id)); + } + + static IEnumerator Blur(int id) + { + yield return null; + if (!instances.ContainsKey(id)) yield break; + + var block = instances[id].blurBlock; // get blur block state + instances[id].blurBlock = false; // reset instalce block state + if (block) yield break; // if block. break it!! + instances[id].DeactivateInputField(); + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnValueChange(int id, string value) + { + if (!instances.ContainsKey(id)) return; + + var instance = instances[id]; + if (!instance.input.ReadOnly) + { + instance.input.text = value; + } + + // InputField.ContentType.Name が Name の場合、先頭文字が強制的大文字になるため小文字にして比べる + if (instance.input.contentType == ContentType.Name) + { + if (string.Compare(instance.input.text, value, true) == 0) + { + value = instance.input.text; + } + } + + // InputField の ContentType による整形したテキストを HTML の input に再設定します + if (value != instance.input.text) + { + var start = WebGLInputPlugin.WebGLInputSelectionStart(id); + var end = WebGLInputPlugin.WebGLInputSelectionEnd(id); + // take the offset.when char remove from input. + var offset = instance.input.text.Length - value.Length; + + WebGLInputPlugin.WebGLInputText(id, instance.input.text); + // reset the input element selection range!! + WebGLInputPlugin.WebGLInputSetSelectionRange(id, start + offset, end + offset); + } + } + [MonoPInvokeCallback(typeof(Action))] + static void OnEditEnd(int id, string value) + { + if (!instances[id].input.ReadOnly) + { + instances[id].input.text = value; + } + } + [MonoPInvokeCallback(typeof(Action))] + static void OnTab(int id, int value) + { + WebGLInputTabFocus.OnTab(instances[id], value); + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnKeyboardEvent(int id, int mode, string key, int code, int shiftKey, int ctrlKey, int altKey) + { + if (!instances.ContainsKey(id)) return; + var instance = instances[id]; + + // mode : keydown(1) keyup(3) + var cb = mode switch + { + 1 => OnKeyboardDown, + 2 => OnKeyboardUp, + _ => default + }; + + if (cb != null) + { + cb(instance, new KeyboardEvent(key, code, shiftKey != 0, ctrlKey != 0, altKey != 0)); + } + } + + void Update() + { + if (input == null || !input.isFocused) + { + CheckOutFocus(); + return; + } + + // 未登録の場合、選択する + if (!instances.ContainsKey(id)) + { + if (Application.isMobilePlatform) + { + return; + } + else + { + OnSelect(); + } + } + else if (!WebGLInputPlugin.WebGLInputIsFocus(id)) + { + if (Application.isMobilePlatform) + { + //input.DeactivateInputField(); + return; + } + else + { + // focus this id + WebGLInputPlugin.WebGLInputFocus(id); + } + } + + var start = WebGLInputPlugin.WebGLInputSelectionStart(id); + var end = WebGLInputPlugin.WebGLInputSelectionEnd(id); + // 選択方向によって設定します + if (WebGLInputPlugin.WebGLInputSelectionDirection(id) == -1) + { + input.selectionFocusPosition = start; + input.selectionAnchorPosition = end; + } + else + { + input.selectionFocusPosition = end; + input.selectionAnchorPosition = start; + } + + input.Rebuild(); + } + + private void OnDestroy() + { + if (!instances.ContainsKey(id)) return; + +#if UNITY_WEBGL && !UNITY_EDITOR + UnityEngine.WebGLInput.captureAllKeyboardInput = true; + Input.ResetInputAxes(); // Inputの状態リセット +#endif + DeactivateInputField(); + } + + private void OnEnable() + { + WebGLInputTabFocus.Add(this); + } + private void OnDisable() + { + WebGLInputTabFocus.Remove(this); + } + public int CompareTo(WebGLInput other) + { + var a = input.GetScreenCoordinates(); + var b = other.input.GetScreenCoordinates(); + var res = b.y.CompareTo(a.y); + if (res == 0) res = a.x.CompareTo(b.x); + return res; + } + + private void CheckOutFocus() + { + if (!Application.isMobilePlatform) return; + if (!instances.ContainsKey(id)) return; + var current = EventSystem.current.currentSelectedGameObject; + if (current != null) return; + WebGLInputPlugin.WebGLInputForceBlur(id); // Input ではないし、キーボードを閉じる + } + + /// + /// to manage tab focus + /// base on scene position + /// + static class WebGLInputTabFocus + { + static List inputs = new List(); + + public static void Add(WebGLInput input) + { + inputs.Add(input); + inputs.Sort(); + } + + public static void Remove(WebGLInput input) + { + inputs.Remove(input); + } + + public static void OnTab(WebGLInput input, int value) + { + if (inputs.Count <= 1) return; + var index = inputs.IndexOf(input); + index += value; + if (index < 0) index = inputs.Count - 1; + else if (index >= inputs.Count) index = 0; + inputs[index].input.ActivateInputField(); + } + } + } +} diff --git a/Assets/WebGLSupport/WebGLInput/WebGLInput.jslib b/Assets/WebGLSupport/WebGLInput/WebGLInput.jslib new file mode 100644 index 0000000..a17092d --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/WebGLInput.jslib @@ -0,0 +1,216 @@ +var WebGLInput = { + $instances: [], + WebGLInputInit : function() { + // use WebAssembly.Table : makeDynCall + // when enable. dynCall is undefined + if(typeof dynCall === "undefined") + { + // make Runtime.dynCall to undefined + Runtime = { dynCall : undefined } + } + else + { + // Remove the `Runtime` object from "v1.37.27: 12/24/2017" + // if Runtime not defined. create and add functon!! + if(typeof Runtime === "undefined") Runtime = { dynCall : dynCall } + } + }, + WebGLInputCreate: function (canvasId, x, y, width, height, fontsize, text, placeholder, isMultiLine, isPassword, isHidden, isMobile) { + + var container = document.getElementById(UTF8ToString(canvasId)); + var canvas = container.getElementsByTagName('canvas')[0]; + + // if container is null and have canvas + if (!container && canvas) + { + // set the container to canvas.parentNode + container = canvas.parentNode; + } + + if(canvas) + { + var scaleX = container.offsetWidth / canvas.width; + var scaleY = container.offsetHeight / canvas.height; + + if(scaleX && scaleY) + { + x *= scaleX; + width *= scaleX; + y *= scaleY; + height *= scaleY; + } + } + + var input = document.createElement(isMultiLine?"textarea":"input"); + input.style.position = "absolute"; + + if(isMobile) { + input.style.bottom = 1 + "vh"; + input.style.left = 5 + "vw"; + input.style.width = 90 + "vw"; + input.style.height = (isMultiLine? 18 : 10) + "vh"; + input.style.fontSize = 5 + "vh"; + input.style.borderWidth = 5 + "px"; + input.style.borderColor = "#000000"; + } else { + input.style.top = y + "px"; + input.style.left = x + "px"; + input.style.width = width + "px"; + input.style.height = height + "px"; + input.style.fontSize = fontsize + "px"; + } + + input.style.outlineWidth = 1 + 'px'; + input.style.opacity = isHidden?0:1; + input.style.resize = 'none'; // for textarea + input.style.padding = '0px 1px'; + input.style.cursor = "default"; + input.style.touchAction = 'none'; + + input.spellcheck = false; + input.value = UTF8ToString(text); + input.placeholder = UTF8ToString(placeholder); + input.style.outlineColor = 'black'; + + if(isPassword){ + input.type = 'password'; + } + + if(isMobile) { + document.body.appendChild(input); + } else { + container.appendChild(input); + } + return instances.push(input) - 1; + }, + WebGLInputEnterSubmit: function(id, falg){ + var input = instances[id]; + // for enter key + input.addEventListener('keydown', function(e) { + if ((e.which && e.which === 13) || (e.keyCode && e.keyCode === 13)) { + if(falg) + { + e.preventDefault(); + input.blur(); + } + } + }); + }, + WebGLInputTab:function(id, cb) { + var input = instances[id]; + // for tab key + input.addEventListener('keydown', function (e) { + if ((e.which && e.which === 9) || (e.keyCode && e.keyCode === 9)) { + e.preventDefault(); + + // if enable tab text + if(input.enableTabText){ + var val = input.value; + var start = input.selectionStart; + var end = input.selectionEnd; + input.value = val.substr(0, start) + '\t' + val.substr(end, val.length); + input.setSelectionRange(start + 1, start + 1); + input.oninput(); // call oninput to exe ValueChange function!! + } else { + (!!Runtime.dynCall) ? Runtime.dynCall("vii", cb, [id, e.shiftKey ? -1 : 1]) : {{{ makeDynCall("vii", "cb") }}}(id, e.shiftKey ? -1 : 1); + } + } + }); + }, + WebGLInputFocus: function(id){ + var input = instances[id]; + input.focus(); + }, + WebGLInputOnFocus: function (id, cb) { + var input = instances[id]; + input.onfocus = function () { + (!!Runtime.dynCall) ? Runtime.dynCall("vi", cb, [id]) : {{{ makeDynCall("vi", "cb") }}}(id); + }; + }, + WebGLInputOnBlur: function (id, cb) { + var input = instances[id]; + input.onblur = function () { + (!!Runtime.dynCall) ? Runtime.dynCall("vi", cb, [id]) : {{{ makeDynCall("vi", "cb") }}}(id); + }; + }, + WebGLInputIsFocus: function (id) { + return instances[id] === document.activeElement; + }, + WebGLInputOnValueChange:function(id, cb){ + var input = instances[id]; + input.oninput = function () { + var returnStr = input.value; + var bufferSize = lengthBytesUTF8(returnStr) + 1; + var buffer = _malloc(bufferSize); + stringToUTF8(returnStr, buffer, bufferSize); + (!!Runtime.dynCall) ? Runtime.dynCall("vii", cb, [id, buffer]) : {{{ makeDynCall("vii", "cb") }}}(id, buffer); + }; + }, + WebGLInputOnEditEnd:function(id, cb){ + var input = instances[id]; + input.onchange = function () { + var returnStr = input.value; + var bufferSize = lengthBytesUTF8(returnStr) + 1; + var buffer = _malloc(bufferSize); + stringToUTF8(returnStr, buffer, bufferSize); + (!!Runtime.dynCall) ? Runtime.dynCall("vii", cb, [id, buffer]) : {{{ makeDynCall("vii", "cb") }}}(id, buffer); + }; + }, + WebGLInputOnKeyboardEvent:function(id, cb){ + var input = instances[id]; + var func = function(mode, e) { + if (e instanceof KeyboardEvent){ + var bufferSize = lengthBytesUTF8(e.key) + 1; + var key = _malloc(bufferSize); + stringToUTF8(e.key, key, bufferSize); + var code = e.code; + var shift = e.shiftKey ? 1 : 0; + var ctrl = e.ctrlKey ? 1 : 0; + var alt = e.altKey ? 1 : 0; + (!!Runtime.dynCall) ? Runtime.dynCall("viiiiiii", cb, [id, mode, key, code, shift, ctrl, alt]) : {{{ makeDynCall("viiiiiii", "cb") }}}(id, mode, key, code, shift, ctrl, alt); + } + } + input.addEventListener('keydown', function(e) { func(1, e); }); + input.addEventListener('keyup', function(e) { func(2, e); }); + }, + WebGLInputSelectionStart:function(id){ + var input = instances[id]; + return input.selectionStart; + }, + WebGLInputSelectionEnd:function(id){ + var input = instances[id]; + return input.selectionEnd; + }, + WebGLInputSelectionDirection:function(id){ + var input = instances[id]; + return (input.selectionDirection == "backward")?-1:1; + }, + WebGLInputSetSelectionRange:function(id, start, end){ + var input = instances[id]; + input.setSelectionRange(start, end); + }, + WebGLInputMaxLength:function(id, maxlength){ + var input = instances[id]; + input.maxLength = maxlength; + }, + WebGLInputText:function(id, text){ + var input = instances[id]; + input.value = UTF8ToString(text); + }, + WebGLInputDelete:function(id){ + var input = instances[id]; + input.parentNode.removeChild(input); + instances[id] = null; + }, + WebGLInputEnableTabText:function(id, enable) { + var input = instances[id]; + input.enableTabText = enable; + }, + WebGLInputForceBlur:function(id) { + var input = instances[id]; + input.blur(); + }, +} + +autoAddDeps(WebGLInput, '$instances'); +mergeInto(LibraryManager.library, WebGLInput); diff --git a/Assets/WebGLSupport/WebGLInput/Wrapper/IInputField.cs b/Assets/WebGLSupport/WebGLInput/Wrapper/IInputField.cs new file mode 100644 index 0000000..bac8fbd --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Wrapper/IInputField.cs @@ -0,0 +1,46 @@ +using UnityEngine; + +namespace WebGLSupport +{ + public enum ContentType + { + Standard = 0, + Autocorrected = 1, + IntegerNumber = 2, + DecimalNumber = 3, + Alphanumeric = 4, + Name = 5, + EmailAddress = 6, + Password = 7, + Pin = 8, + Custom = 9 + } + public enum LineType + { + SingleLine = 0, + MultiLineSubmit = 1, + MultiLineNewline = 2 + } + public interface IInputField + { + ContentType contentType { get; } + LineType lineType { get; } + int fontSize { get; } + string text { get; set; } + string placeholder { get; } + int characterLimit { get; } + int caretPosition { get; } + bool isFocused { get; } + int selectionFocusPosition { get; set; } + int selectionAnchorPosition { get; set; } + bool ReadOnly { get; } + bool OnFocusSelectAll { get; } + bool EnableMobileSupport { get; } + + Rect GetScreenCoordinates(); + + void ActivateInputField(); + void DeactivateInputField(); + void Rebuild(); + } +} diff --git a/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedInputField.cs b/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedInputField.cs new file mode 100644 index 0000000..7a783b8 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedInputField.cs @@ -0,0 +1,119 @@ +using UnityEngine; +using UnityEngine.UI; +using WebGLSupport.Detail; + +namespace WebGLSupport +{ + /// + /// Wrapper for UnityEngine.UI.InputField + /// + class WrappedInputField : IInputField + { + InputField input; + RebuildChecker checker; + + public bool ReadOnly { get { return input.readOnly; } } + + public string text + { + get { return input.text; } + set { input.text = value; } + } + + public string placeholder + { + get + { + if (!input.placeholder) return ""; + var text = input.placeholder.GetComponent(); + return text ? text.text : ""; + } + } + + public int fontSize + { + get { return input.textComponent.fontSize; } + } + + public ContentType contentType + { + get { return (ContentType)input.contentType; } + } + + public LineType lineType + { + get { return (LineType)input.lineType; } + } + + public int characterLimit + { + get { return input.characterLimit; } + } + + public int caretPosition + { + get { return input.caretPosition; } + } + + public bool isFocused + { + get { return input.isFocused; } + } + + public int selectionFocusPosition + { + get { return input.selectionFocusPosition; } + set { input.selectionFocusPosition = value; } + } + + public int selectionAnchorPosition + { + get { return input.selectionAnchorPosition; } + set { input.selectionAnchorPosition = value; } + } + + public bool OnFocusSelectAll + { + get { return true; } + } + + public bool EnableMobileSupport + { + get + { + // return false to use unity mobile keyboard support + return false; + } + } + + public WrappedInputField(InputField input) + { + this.input = input; + checker = new RebuildChecker(this); + } + + public void ActivateInputField() + { + input.ActivateInputField(); + } + + public void DeactivateInputField() + { + input.DeactivateInputField(); + } + + public void Rebuild() + { + if (checker.NeedRebuild()) + { + input.textComponent.SetAllDirty(); + input.Rebuild(CanvasUpdate.LatePreRender); + } + } + + public Rect GetScreenCoordinates() + { + return Support.GetScreenCoordinates(input.GetComponent()); + } + } +} \ No newline at end of file diff --git a/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedTMPInputField.cs b/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedTMPInputField.cs new file mode 100644 index 0000000..181aa28 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedTMPInputField.cs @@ -0,0 +1,212 @@ +#if UNITY_2018_2_OR_NEWER +#define TMP_WEBGL_SUPPORT +#endif + +#if TMP_WEBGL_SUPPORT +using UnityEngine; +using TMPro; +using WebGLSupport.Detail; +using UnityEngine.UI; +using System; + +namespace WebGLSupport +{ + /// + /// Wrapper for TMPro.TMP_InputField + /// + class WrappedTMPInputField : IInputField + { + TMP_InputField input; + RebuildChecker checker; + Coroutine delayedGraphicRebuild; + + public bool ReadOnly { get { return input.readOnly; } } + + public string text + { + get { return input.text; } + set { input.text = FixContentTypeByInputField(value); } + } + + /// + /// workaround!! + /// when use TMP_InputField.text = "xxx"; is will set the text directly. + /// so, use InputField for match the ContentType! + /// + /// + /// + private string FixContentTypeByInputField(string inText) + { + var go = new GameObject("FixContentTypeByInputField for WebGLInput"); + go.SetActive(false); + var i = go.AddComponent(); + i.contentType = (InputField.ContentType)Enum.Parse(typeof(InputField.ContentType), input.contentType.ToString()); + i.lineType = (InputField.LineType)Enum.Parse(typeof(InputField.LineType), input.lineType.ToString()); + i.inputType = (InputField.InputType)Enum.Parse(typeof(InputField.InputType), input.inputType.ToString()); + i.keyboardType = input.keyboardType; + i.characterValidation = (InputField.CharacterValidation)Enum.Parse(typeof(InputField.CharacterValidation), input.characterValidation.ToString()); + i.characterLimit = input.characterLimit; + i.text = inText; + var res = i.text; + GameObject.Destroy(go); + return res; + } + + public string placeholder + { + get + { + if (!input.placeholder) return ""; + var text = input.placeholder.GetComponent(); + return text ? text.text : ""; + } + } + + public int fontSize + { + get { return (int)input.textComponent.fontSize; } + } + + public ContentType contentType + { + get { return (ContentType)input.contentType; } + } + + public LineType lineType + { + get { return (LineType)input.lineType; } + } + + public int characterLimit + { + get { return input.characterLimit; } + } + + public int caretPosition + { + get { return input.caretPosition; } + } + + public bool isFocused + { + get { return input.isFocused; } + } + + public int selectionFocusPosition + { + get { return input.selectionStringFocusPosition; } + set { input.selectionStringFocusPosition = value; } + } + + public int selectionAnchorPosition + { + get { return input.selectionStringAnchorPosition; } + set { input.selectionStringAnchorPosition = value; } + } + + public bool OnFocusSelectAll + { + get { return input.onFocusSelectAll; } + } + + public bool EnableMobileSupport + { + get + { + // [2023.2] Latest Development on TextMesh Pro + // https://forum.unity.com/threads/2023-2-latest-development-on-textmesh-pro.1434757/ + // As of 2023.2, the TextMesh Pro package (com.unity.textmeshpro) has been merged into the uGUI package (com.unity.ugui) and the TextMesh Pro package has been deprecated. + // In this version, TextMeshPro is default support mobile input. so disable WebGLInput mobile support +#if UNITY_2023_2_OR_NEWER + // return false to use unity mobile keyboard support + return false; +#else + return true; +#endif + } + } + + public WrappedTMPInputField(TMP_InputField input) + { + this.input = input; + checker = new RebuildChecker(this); + } + + public Rect GetScreenCoordinates() + { + // 表示範囲 + // MEMO : + // TMP では textComponent を移動させてクリッピングするため、 + // 表示範囲外になる場合があるので、自分の範囲を返す + return Support.GetScreenCoordinates(input.GetComponent()); + } + + + + public void ActivateInputField() + { + input.ActivateInputField(); + } + + public void DeactivateInputField() + { + input.DeactivateInputField(); + } + + public void Rebuild() + { +#if UNITY_2020_1_OR_NEWER + if (checker.NeedRebuild()) + { + input.textComponent.SetVerticesDirty(); + input.textComponent.SetLayoutDirty(); + input.Rebuild(CanvasUpdate.LatePreRender); + } +#else + if (input.textComponent.enabled && checker.NeedRebuild()) + { + //================================ + // fix bug for tmp + // TMPの不具合で、正しく座標を設定されてなかったため、試しに対応する + var rt = input.textComponent.GetComponent(); + var size = input.textComponent.GetPreferredValues(); + if (size.x < rt.rect.xMax) + { + // textComponent の座標を更新 + var pos = rt.anchoredPosition; + pos.x = 0; + rt.anchoredPosition = pos; + + // caret の座標更新 + var caret = input.GetComponentInChildren(); + var caretRect = caret.GetComponent(); + caretRect.anchoredPosition = rt.anchoredPosition; + } + //============================== + + // HACK : 1フレーム無効にする + // MEMO : 他にいい方法Rebuildがあれば対応する + // LayoutRebuilder.ForceRebuildLayoutImmediate(); で試してダメでした + input.textComponent.enabled = rectOverlaps(input.textComponent.rectTransform, input.textViewport); + input.textComponent.SetAllDirty(); + input.Rebuild(CanvasUpdate.LatePreRender); + //Debug.Log(input.textComponent.enabled); + } + else + { + input.textComponent.enabled = true; + } +#endif + } + + bool rectOverlaps(RectTransform rectTrans1, RectTransform rectTrans2) + { + Rect rect1 = new Rect(rectTrans1.localPosition.x, rectTrans1.localPosition.y, rectTrans1.rect.width, rectTrans1.rect.height); + Rect rect2 = new Rect(rectTrans2.localPosition.x, rectTrans2.localPosition.y, rectTrans2.rect.width, rectTrans2.rect.height); + + return rect1.Overlaps(rect2); + } + } +} + +#endif // TMP_WEBGL_SUPPORT diff --git a/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedUIToolkit.cs b/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedUIToolkit.cs new file mode 100644 index 0000000..837f378 --- /dev/null +++ b/Assets/WebGLSupport/WebGLInput/Wrapper/WrappedUIToolkit.cs @@ -0,0 +1,162 @@ +using UnityEngine; +using UnityEngine.UIElements; + +namespace WebGLSupport +{ + /// + /// Wrapper for UnityEngine.UIElements.TextField + /// + class WrappedUIToolkit : IInputField + { + TextField input; + + public bool ReadOnly { get { return input.isReadOnly; } } + + public string text + { + get { return input.value; } + set { input.value = value; } + } + + public string placeholder + { + get + { +#if UNITY_2023_1_OR_NEWER + return input.textEdition.placeholder; +#else + return ""; +#endif + } + } + + public int fontSize + { + /// MEMO : how to get the fontsize? + get { return 20; } + } + + public ContentType contentType + { + get + { + if (input.isPasswordField) + { + return ContentType.Password; + } + +#if UNITY_2022_1_OR_NEWER + var keyboardType = input.keyboardType; +#else + var keyboardType = TouchScreenKeyboardType.Default; +#endif + return keyboardType switch + { + TouchScreenKeyboardType.Default => ContentType.Standard, + TouchScreenKeyboardType.ASCIICapable => ContentType.Alphanumeric, + TouchScreenKeyboardType.NumbersAndPunctuation => ContentType.Standard, + TouchScreenKeyboardType.URL => ContentType.Standard, + TouchScreenKeyboardType.NumberPad => ContentType.IntegerNumber, + TouchScreenKeyboardType.PhonePad => ContentType.Standard, + TouchScreenKeyboardType.NamePhonePad => ContentType.Standard, + TouchScreenKeyboardType.EmailAddress => ContentType.EmailAddress, + //TouchScreenKeyboardType.NintendoNetworkAccount => throw new System.NotImplementedException(), + TouchScreenKeyboardType.Social => ContentType.Standard, + TouchScreenKeyboardType.Search => ContentType.Standard, + TouchScreenKeyboardType.DecimalPad => ContentType.DecimalNumber, + TouchScreenKeyboardType.OneTimeCode => ContentType.Standard, + _ => ContentType.Standard, + }; + } + } + + public LineType lineType + { + get + { + return input.multiline ? LineType.MultiLineNewline : LineType.SingleLine; + } + } + + public int characterLimit + { + get { return input.maxLength; } + } + + public int caretPosition + { + get { return input.cursorIndex; } + } + + public bool isFocused + { + get + { + return true; + } + } + + public int selectionFocusPosition + { + get { return input.cursorIndex; } +#if UNITY_2022_1_OR_NEWER + set { input.cursorIndex = value; } +#else + set { input.SelectRange(value, input.selectIndex); } +#endif + } + + public int selectionAnchorPosition + { + get { return input.selectIndex; } +#if UNITY_2022_1_OR_NEWER + set { input.selectIndex = value; } +#else + set { input.SelectRange(input.cursorIndex, value); } +#endif + } + + public bool OnFocusSelectAll + { +#if UNITY_2022_1_OR_NEWER + get { return input.selectAllOnFocus || input.selectAllOnMouseUp; } +#else + get { return true; } +#endif + } + + public bool EnableMobileSupport + { + get + { + // return false to use unity mobile keyboard support + return false; + } + } + + public WrappedUIToolkit(WebGLUIToolkitTextField input) + { + this.input = input.TextField; + } + + public Rect GetScreenCoordinates() + { + var textInput = input.Q("unity-text-input"); + var rect = textInput.worldBound; + return new Rect(rect.x, Screen.height - (rect.y + rect.height), rect.width, rect.height); + } + + public void ActivateInputField() + { + } + + public void DeactivateInputField() + { + input.Blur(); + } + + public void Rebuild() + { + } + } +} \ No newline at end of file diff --git a/Assets/WebGLSupport/WebGLWindow/WebGLWindow.cs b/Assets/WebGLSupport/WebGLWindow/WebGLWindow.cs new file mode 100644 index 0000000..e35849e --- /dev/null +++ b/Assets/WebGLSupport/WebGLWindow/WebGLWindow.cs @@ -0,0 +1,138 @@ +using System; +using AOT; +using System.Runtime.InteropServices; // for DllImport +using UnityEngine; + +namespace WebGLSupport +{ + static class WebGLWindowPlugin + { +#if UNITY_WEBGL && !UNITY_EDITOR + [DllImport("__Internal")] + public static extern void WebGLWindowInit(); + [DllImport("__Internal")] + public static extern void WebGLWindowUninit(); + + [DllImport("__Internal")] + public static extern void WebGLWindowOnFocus(Action cb); + + [DllImport("__Internal")] + public static extern void WebGLWindowOnBlur(Action cb); + + [DllImport("__Internal")] + public static extern void WebGLWindowOnResize(Action cb); + + [DllImport("__Internal")] + public static extern void WebGLWindowInjectFullscreen(); + + [DllImport("__Internal")] + public static extern string WebGLWindowGetCanvasName(); + + [DllImport("__Internal")] + public static extern void MakeFullscreen(string str); + + [DllImport("__Internal")] + public static extern void ExitFullscreen(); + + [DllImport("__Internal")] + public static extern bool IsFullscreen(); +#else + public static void WebGLWindowInit() { } + public static void WebGLWindowUninit() { } + public static void WebGLWindowOnFocus(Action cb) { } + public static void WebGLWindowOnBlur(Action cb) { } + public static void WebGLWindowOnResize(Action cb) { } + public static void WebGLWindowInjectFullscreen() { } + public static string WebGLWindowGetCanvasName() { return ""; } + public static void MakeFullscreen(string str) { } + public static void ExitFullscreen() { } + public static bool IsFullscreen() { return false; } +#endif + + } + + public static class WebGLWindow + { + static WebGLWindow() + { + WebGLWindowPlugin.WebGLWindowInit(); + } + public static bool Focus { get; private set; } + public static event Action OnFocusEvent = () => { }; + public static event Action OnBlurEvent = () => { }; + public static event Action OnResizeEvent = () => { }; + + static string ViewportContent; + static void Init() + { + Focus = true; + WebGLWindowPlugin.WebGLWindowOnFocus(OnWindowFocus); + WebGLWindowPlugin.WebGLWindowOnBlur(OnWindowBlur); + WebGLWindowPlugin.WebGLWindowOnResize(OnWindowResize); + WebGLWindowPlugin.WebGLWindowInjectFullscreen(); + + Application.quitting += Uninit; + } + static void Uninit() + { + WebGLWindowPlugin.WebGLWindowUninit(); + Application.quitting -= Uninit; + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnWindowFocus() + { + Focus = true; + OnFocusEvent(); + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnWindowBlur() + { + Focus = false; + OnBlurEvent(); + } + + [MonoPInvokeCallback(typeof(Action))] + static void OnWindowResize() + { + OnResizeEvent(); + } + + [RuntimeInitializeOnLoadMethod] + static void RuntimeInitializeOnLoadMethod() + { + Init(); + } + + public static string GetCanvasName() + { + return WebGLWindowPlugin.WebGLWindowGetCanvasName(); + } + + public static void MakeFullscreen(string fullscreenElementName = null) + { + WebGLWindowPlugin.MakeFullscreen(fullscreenElementName ?? GetCanvasName()); + } + + public static void ExitFullscreen() + { + WebGLWindowPlugin.ExitFullscreen(); + } + public static bool IsFullscreen() + { + return WebGLWindowPlugin.IsFullscreen(); + } + public static void SwitchFullscreen() + { + if (IsFullscreen()) + { + ExitFullscreen(); + } + else + { + MakeFullscreen(); + } + } + } +} diff --git a/Assets/WebGLSupport/WebGLWindow/WebGLWindow.jslib b/Assets/WebGLSupport/WebGLWindow/WebGLWindow.jslib new file mode 100644 index 0000000..84f103c --- /dev/null +++ b/Assets/WebGLSupport/WebGLWindow/WebGLWindow.jslib @@ -0,0 +1,204 @@ +var WebGLWindow = { + focusListener: null, + blurListener: null, + resizeListener: null, + + WebGLWindowInit : function() { + // use WebAssembly.Table : makeDynCall + // when enable. dynCall is undefined + if(typeof dynCall === "undefined") + { + // make Runtime.dynCall to undefined + Runtime = { dynCall : undefined } + } + else + { + // Remove the `Runtime` object from "v1.37.27: 12/24/2017" + // if Runtime not defined. create and add functon!! + if(typeof Runtime === "undefined") Runtime = { dynCall : dynCall } + } + }, + WebGLWindowUninit : function() { + if(focusListener) { + window.removeEventListener('focus', this.focusListener); + this.focusListener = null; + } + if(blurListener) { + window.removeEventListener('blur', this.blurListener); + this.blurListener = null; + } + if(resizeListener) { + window.removeEventListener('resize', this.resizeListener); + this.resizeListener = null; + } + }, + + WebGLWindowGetCanvasName: function() { + var elements = document.getElementsByTagName('canvas'); + var returnStr = ""; + if(elements.length >= 1) + { + returnStr = elements[0].parentNode.id; + // workaround : for WebGLTemplate:Minimal temp! body element not have id! + if(returnStr == '') + { + returnStr = elements[0].parentNode.id = 'WebGLWindow:Canvas:ParentNode'; + } + } + var bufferSize = lengthBytesUTF8(returnStr) + 1; + var buffer = _malloc(bufferSize); + stringToUTF8(returnStr, buffer, bufferSize); + return buffer; + }, + WebGLWindowOnFocus: function (cb) { + this.focusListener = function () { + (!!Runtime.dynCall) ? Runtime.dynCall("v", cb, []) : {{{ makeDynCall("v", "cb") }}}(); + }; + window.addEventListener('focus', this.focusListener); + }, + WebGLWindowOnBlur: function (cb) { + this.blurListener = function () { + (!!Runtime.dynCall) ? Runtime.dynCall("v", cb, []) : {{{ makeDynCall("v", "cb") }}}(); + }; + window.addEventListener('blur', this.blurListener); + }, + WebGLWindowOnResize: function(cb) { + this.resizeListener = function () { + (!!Runtime.dynCall) ? Runtime.dynCall("v", cb, []) : {{{ makeDynCall("v", "cb") }}}(); + }; + window.addEventListener('resize', this.resizeListener); + }, + WebGLWindowInjectFullscreen : function () { + document.makeFullscreen = function (id, keepAspectRatio) { + // get fullscreen object + var getFullScreenObject = function () { + var doc = window.document; + var objFullScreen = doc.fullscreenElement || doc.mozFullScreenElement || doc.webkitFullscreenElement || doc.msFullscreenElement; + return (objFullScreen); + } + + // handle fullscreen event + var eventFullScreen = function (callback) { + document.addEventListener("fullscreenchange", callback, false); + document.addEventListener("webkitfullscreenchange", callback, false); + document.addEventListener("mozfullscreenchange", callback, false); + document.addEventListener("MSFullscreenChange", callback, false); + } + + var removeEventFullScreen = function (callback) { + document.removeEventListener("fullscreenchange", callback, false); + document.removeEventListener("webkitfullscreenchange", callback, false); + document.removeEventListener("mozfullscreenchange", callback, false); + document.removeEventListener("MSFullscreenChange", callback, false); + } + + var div = document.createElement("div"); + document.body.appendChild(div); + + // save canvas size to originSize + var canvas = document.getElementsByTagName('canvas')[0]; + var originSize = + { + width : canvas.style.width, + height : canvas.style.height, + }; + + var fullscreenRoot = document.getElementById(id); + + // when build with minimal default template + // the fullscreenRoot is + var isBody = fullscreenRoot.tagName.toLowerCase() == "body"; + if(isBody) + { + // swip the id to div + div.id = fullscreenRoot.id; + fullscreenRoot.id = ""; + // overwrite the fullscreen root + fullscreenRoot = canvas; + } + + var beforeParent = fullscreenRoot.parentNode; + var beforeStyle = window.getComputedStyle(fullscreenRoot); + var beforeWidth = parseInt(beforeStyle.width); + var beforeHeight = parseInt(beforeStyle.height); + + // to keep element index after fullscreen + var index = Array.from(beforeParent.children).findIndex(function (v) { return v == fullscreenRoot; }); + div.appendChild(fullscreenRoot); + + // recv fullscreen function + var fullscreenFunc = function () { + if (getFullScreenObject()) { + if (keepAspectRatio) { + var ratio = Math.min(window.screen.width / beforeWidth, window.screen.height / beforeHeight); + var width = Math.floor(beforeWidth * ratio); + var height = Math.floor(beforeHeight * ratio); + + fullscreenRoot.style.width = width + 'px'; + fullscreenRoot.style.height = height + 'px'; + } else { + fullscreenRoot.style.width = window.screen.width + 'px'; + fullscreenRoot.style.height = window.screen.height + 'px'; + } + + // make canvas size 100% to fix screen size + canvas.style.width = "100%"; + canvas.style.height = "100%"; + + } else { + fullscreenRoot.style.width = beforeWidth + 'px'; + fullscreenRoot.style.height = beforeHeight + 'px'; + beforeParent.insertBefore(fullscreenRoot, Array.from(beforeParent.children)[index]); + + if(isBody) + { + beforeParent.id = div.id; + } + + div.parentNode.removeChild(div); + + // set canvas size to origin size + canvas.style.width = originSize.width; + canvas.style.height = originSize.height; + + // remove this function + removeEventFullScreen(fullscreenFunc); + } + } + + // listener fullscreen event + eventFullScreen(fullscreenFunc); + + if (div.mozRequestFullScreen) div.mozRequestFullScreen(); + else if (div.webkitRequestFullScreen) div.webkitRequestFullScreen(); + else if (div.msRequestFullscreen) div.msRequestFullscreen(); + else if (div.requestFullscreen) div.requestFullscreen(); + } + }, + MakeFullscreen : function (str) { + document.makeFullscreen(UTF8ToString(str)); + }, + ExitFullscreen : function() { + // get fullscreen object + var doc = window.document; + var objFullScreen = doc.fullscreenElement || doc.mozFullScreenElement || doc.webkitFullscreenElement || doc.msFullscreenElement; + + if (objFullScreen) + { + if (document.exitFullscreen) document.exitFullscreen(); + else if (document.msExitFullscreen) document.msExitFullscreen(); + else if (document.mozCancelFullScreen) document.mozCancelFullScreen(); + else if (document.webkitExitFullscreen) document.webkitExitFullscreen(); + } + }, + IsFullscreen : function() { + // get fullscreen object + var doc = window.document; + var objFullScreen = doc.fullscreenElement || doc.mozFullScreenElement || doc.webkitFullscreenElement || doc.msFullscreenElement; + + // check full screen elemenet is not null! + return objFullScreen != null; + }, +} + +mergeInto(LibraryManager.library, WebGLWindow); diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 6898bc0..bc9da85 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -12,8 +12,8 @@ PlayerSettings: targetDevice: 2 useOnDemandResources: 0 accelerometerFrequency: 60 - companyName: DefaultCompany - productName: Unity_Web + companyName: MrPanGame + productName: Furlib defaultCursor: {fileID: 0} cursorHotspot: {x: 0, y: 0} m_SplashScreenBackgroundColor: {r: 1, g: 1, b: 1, a: 1}