From 925484ff415c8167c1b23f56d93f5d8f0d062b12 Mon Sep 17 00:00:00 2001 From: s402520 <sven.gerlach@stud-mail.uni-wuerzburg.de> Date: Wed, 26 Apr 2023 15:00:39 +0200 Subject: [PATCH] WorldSpaceUI (Buttons are not working in World Space) --- Assets/Scenes/SampleScene.unity | 155 ++++++-- Assets/UI Toolkit/PanelTexture.renderTexture | 39 ++ .../PanelTexture.renderTexture.meta | 8 + Assets/UI/DialogueBox/DialogueBox.cs | 27 +- Assets/UI/DialogueBox/DialogueBox.uss | 3 +- Assets/UI/DialogueBox/Storyline.cs | 9 +- Assets/UI/DialogueBox/UIDocument.prefab | 87 +++++ Assets/UI/DialogueBox/UIDocument.prefab.meta | 7 + Assets/UI/DialogueBox/WorldSpaceUIDocument.cs | 336 ++++++++++++++++++ .../DialogueBox/WorldSpaceUIDocument.cs.meta | 11 + 10 files changed, 640 insertions(+), 42 deletions(-) create mode 100644 Assets/UI Toolkit/PanelTexture.renderTexture create mode 100644 Assets/UI Toolkit/PanelTexture.renderTexture.meta create mode 100644 Assets/UI/DialogueBox/UIDocument.prefab create mode 100644 Assets/UI/DialogueBox/UIDocument.prefab.meta create mode 100644 Assets/UI/DialogueBox/WorldSpaceUIDocument.cs create mode 100644 Assets/UI/DialogueBox/WorldSpaceUIDocument.cs.meta diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 406d2b5..d2e93d3 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -123,7 +123,7 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} ---- !u!1 &316208882 +--- !u!1 &383685413 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -131,10 +131,10 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 316208886} - - component: {fileID: 316208885} - - component: {fileID: 316208884} - - component: {fileID: 316208883} + - component: {fileID: 383685417} + - component: {fileID: 383685415} + - component: {fileID: 383685414} + - component: {fileID: 383685418} m_Layer: 5 m_Name: UIDocument m_TagString: Untagged @@ -142,61 +142,72 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &316208883 +--- !u!114 &383685414 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 316208882} + m_GameObject: {fileID: 383685413} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5ba11467abf042d4f8e33891cdac93bf, type: 3} m_Name: m_EditorClassIdentifier: ---- !u!114 &316208884 + worldSpaceUIDocument: {fileID: 383685418} +--- !u!114 &383685415 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 316208882} + m_GameObject: {fileID: 383685413} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: f6a8876d339d1ce4dbb83ea0158084f2, type: 3} m_Name: m_EditorClassIdentifier: ---- !u!114 &316208885 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 316208882} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} - m_Name: - m_EditorClassIdentifier: - m_PanelSettings: {fileID: 11400000, guid: fd07337020fac9f4d817f2d168694173, type: 2} - m_ParentUI: {fileID: 0} - sourceAsset: {fileID: 0} - m_SortingOrder: 0 ---- !u!4 &316208886 + visualTree: {fileID: 9197481963319205126, guid: 603e8ecda2bc66148be7bc0929e2f25b, type: 3} + styleSheet: {fileID: 7433441132597879392, guid: 78ec576a11766354b9c6c1e555acf5e4, type: 3} + worldSpaceUIDocument: {fileID: 383685418} +--- !u!4 &383685417 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 316208882} + m_GameObject: {fileID: 383685413} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalPosition: {x: 2, y: 0, z: -9} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &383685418 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 383685413} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fbbce919b93a804449b8e74c7b1eec1b, type: 3} + m_Name: + m_EditorClassIdentifier: + _panelWidth: 500 + _panelHeight: 100 + _panelScale: 1 + _pixelsPerUnit: 1000 + _visualTreeAsset: {fileID: 0} + _panelSettingsPrefab: {fileID: 11400000, guid: fd07337020fac9f4d817f2d168694173, type: 2} + _renderTexturePrefab: {fileID: 8400000, guid: 17662cfbd375e554d91b75bf67594f93, type: 2} + UseDragEventFix: 1 + onUIDocumentReady: + m_PersistentCalls: + m_Calls: [] --- !u!1 &705507993 GameObject: m_ObjectHideFlags: 0 @@ -323,6 +334,8 @@ GameObject: - component: {fileID: 963194228} - component: {fileID: 963194227} - component: {fileID: 963194226} + - component: {fileID: 963194230} + - component: {fileID: 963194229} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -396,3 +409,89 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!137 &963194229 +SkinnedMeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 3 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + serializedVersion: 2 + m_Quality: 0 + m_UpdateWhenOffscreen: 0 + m_SkinnedMotionVectors: 1 + m_Mesh: {fileID: 0} + m_Bones: [] + m_BlendShapeWeights: [] + m_RootBone: {fileID: 0} + m_AABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_DirtyAABB: 0 +--- !u!114 &963194230 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 diff --git a/Assets/UI Toolkit/PanelTexture.renderTexture b/Assets/UI Toolkit/PanelTexture.renderTexture new file mode 100644 index 0000000..50a4a80 --- /dev/null +++ b/Assets/UI Toolkit/PanelTexture.renderTexture @@ -0,0 +1,39 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!84 &8400000 +RenderTexture: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: PanelTexture + m_ImageContentsHash: + serializedVersion: 2 + Hash: 00000000000000000000000000000000 + m_ForcedFallbackFormat: 4 + m_DownscaleFallback: 0 + m_IsAlphaChannelOptional: 0 + serializedVersion: 5 + m_Width: 256 + m_Height: 256 + m_AntiAliasing: 1 + m_MipCount: -1 + m_DepthStencilFormat: 0 + m_ColorFormat: 8 + m_MipMap: 0 + m_GenerateMips: 1 + m_SRGB: 0 + m_UseDynamicScale: 0 + m_BindMS: 0 + m_EnableCompatibleFormat: 1 + m_TextureSettings: + serializedVersion: 2 + m_FilterMode: 2 + m_Aniso: 0 + m_MipBias: 0 + m_WrapU: 1 + m_WrapV: 1 + m_WrapW: 1 + m_Dimension: 2 + m_VolumeDepth: 1 + m_ShadowSamplingMode: 2 diff --git a/Assets/UI Toolkit/PanelTexture.renderTexture.meta b/Assets/UI Toolkit/PanelTexture.renderTexture.meta new file mode 100644 index 0000000..a519711 --- /dev/null +++ b/Assets/UI Toolkit/PanelTexture.renderTexture.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17662cfbd375e554d91b75bf67594f93 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UI/DialogueBox/DialogueBox.cs b/Assets/UI/DialogueBox/DialogueBox.cs index 913c592..00c44f7 100644 --- a/Assets/UI/DialogueBox/DialogueBox.cs +++ b/Assets/UI/DialogueBox/DialogueBox.cs @@ -1,34 +1,43 @@ using UnityEditor; using UnityEngine; +using UnityEngine.Events; using UnityEngine.UIElements; -[RequireComponent(typeof(UIDocument))] + public class DialogueBox : MonoBehaviour { private UIDocument uiDocument; + [SerializeField] protected VisualTreeAsset visualTree; + [SerializeField] protected StyleSheet styleSheet; + [SerializeField] Katas.Experimental.WorldSpaceUIDocument worldSpaceUIDocument; private VisualElement root; private Button[] buttons; public void Awake() { - uiDocument = GetComponent<UIDocument>(); + worldSpaceUIDocument.onUIDocumentReady.AddListener(setUIDocument); } - public void OnEnable() { + private void setUIDocument() { + uiDocument = GetComponent<UIDocument>(); root = uiDocument.rootVisualElement; - var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UI/DialogueBox/DialogueBox.uxml"); - var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/UI/DialogueBox/DialogueBox.uss"); VisualElement tree = visualTree.CloneTree(); tree.styleSheets.Add(styleSheet); root.Add(tree); buttons = root.Query<Button>().ToList().ToArray(); - SetActor("ActorName"); - SetText("DialogueText"); - - for(var i = 0; i < buttons.Length; i++) { + for (var i = 0; i < buttons.Length; i++) + { var index = i; buttons[i].RegisterCallback<MouseUpEvent>(_ => GetComponent<Storyline>().ApplyTransition(index)); } + + } + + public void OnEnable() { + + SetActor("ActorName"); + SetText("DialogueText"); + } public void SetActor(string actor) { diff --git a/Assets/UI/DialogueBox/DialogueBox.uss b/Assets/UI/DialogueBox/DialogueBox.uss index f486d4a..207567c 100644 --- a/Assets/UI/DialogueBox/DialogueBox.uss +++ b/Assets/UI/DialogueBox/DialogueBox.uss @@ -16,8 +16,9 @@ Button { font-size: 15px; -unity-font-style:bold; color: rgb(255,255,255); - width: 150px; + width: 250px; height: 50px; + margin-top: 5px; margin-left: 15px; margin-right: 15px; padding-top: 15px; diff --git a/Assets/UI/DialogueBox/Storyline.cs b/Assets/UI/DialogueBox/Storyline.cs index cd96437..ff1d3b4 100644 --- a/Assets/UI/DialogueBox/Storyline.cs +++ b/Assets/UI/DialogueBox/Storyline.cs @@ -2,8 +2,9 @@ public class Storyline : MonoBehaviour { public Node currentNode; - - + [SerializeField] Katas.Experimental.WorldSpaceUIDocument worldSpaceUIDocument; + + public void Start() { Transition[] GHI = new Transition[2]; Node testNode = new Node("Actor 3","Testing Text 3",GHI); @@ -33,8 +34,8 @@ public void Start() { GHI[1] = testTransition3; JKL[2] = testTransition3; - currentNode = testNode3; - UpdateUi(); + currentNode = testNode4; + worldSpaceUIDocument.onUIDocumentReady.AddListener(UpdateUi); } public void ApplyTransition(int count) { diff --git a/Assets/UI/DialogueBox/UIDocument.prefab b/Assets/UI/DialogueBox/UIDocument.prefab new file mode 100644 index 0000000..270ee81 --- /dev/null +++ b/Assets/UI/DialogueBox/UIDocument.prefab @@ -0,0 +1,87 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &3227880242391464282 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3227880242391464278} + - component: {fileID: 3227880242391464280} + - component: {fileID: 3227880242391464281} + - component: {fileID: 3227880242391464277} + m_Layer: 5 + m_Name: UIDocument + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3227880242391464278 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3227880242391464282} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 2, y: 0, z: -9} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &3227880242391464280 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3227880242391464282} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f6a8876d339d1ce4dbb83ea0158084f2, type: 3} + m_Name: + m_EditorClassIdentifier: + visualTree: {fileID: 9197481963319205126, guid: 603e8ecda2bc66148be7bc0929e2f25b, type: 3} + styleSheet: {fileID: 7433441132597879392, guid: 78ec576a11766354b9c6c1e555acf5e4, type: 3} + worldSpaceUIDocument: {fileID: 3227880242391464277} +--- !u!114 &3227880242391464281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3227880242391464282} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5ba11467abf042d4f8e33891cdac93bf, type: 3} + m_Name: + m_EditorClassIdentifier: + worldSpaceUIDocument: {fileID: 3227880242391464277} +--- !u!114 &3227880242391464277 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3227880242391464282} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fbbce919b93a804449b8e74c7b1eec1b, type: 3} + m_Name: + m_EditorClassIdentifier: + _panelWidth: 2560 + _panelHeight: 1440 + _panelScale: 1 + _pixelsPerUnit: 1 + _visualTreeAsset: {fileID: 0} + _panelSettingsPrefab: {fileID: 11400000, guid: fd07337020fac9f4d817f2d168694173, type: 2} + _renderTexturePrefab: {fileID: 8400000, guid: 17662cfbd375e554d91b75bf67594f93, type: 2} + UseDragEventFix: 0 + onUIDocumentReady: + m_PersistentCalls: + m_Calls: [] diff --git a/Assets/UI/DialogueBox/UIDocument.prefab.meta b/Assets/UI/DialogueBox/UIDocument.prefab.meta new file mode 100644 index 0000000..3fb4b70 --- /dev/null +++ b/Assets/UI/DialogueBox/UIDocument.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 48ae5cbc293c0f244adb2632ee65fceb +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UI/DialogueBox/WorldSpaceUIDocument.cs b/Assets/UI/DialogueBox/WorldSpaceUIDocument.cs new file mode 100644 index 0000000..95c2de9 --- /dev/null +++ b/Assets/UI/DialogueBox/WorldSpaceUIDocument.cs @@ -0,0 +1,336 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEngine.Events; +using UnityEngine.EventSystems; +using UnityEngine.Rendering; + +namespace Katas.Experimental +{ + public class WorldSpaceUIDocument : MonoBehaviour, IPointerMoveHandler, IPointerUpHandler, IPointerDownHandler, + ISubmitHandler, ICancelHandler, IMoveHandler, IScrollHandler, ISelectHandler, IDeselectHandler, IDragHandler + { + [Tooltip("Width of the panel in pixels. The RenderTexture used to render the panel will have this width.")] + [SerializeField] protected int _panelWidth = 1280; + [Tooltip("Height of the panel in pixels. The RenderTexture used to render the panel will have this height.")] + [SerializeField] protected int _panelHeight = 720; + [Tooltip("Scale of the panel. It is like the zoom in a browser.")] + [SerializeField] protected float _panelScale = 1.0f; + [Tooltip("Pixels per world units, it will the termine the real panel size in the world based on panel pixel width and height.")] + [SerializeField] protected float _pixelsPerUnit = 1280.0f; + [Tooltip("Visual tree element object of this panel.")] + [SerializeField] protected VisualTreeAsset _visualTreeAsset; + [Tooltip("PanelSettings that will be used to create a new instance for this panel.")] + [SerializeField] protected PanelSettings _panelSettingsPrefab; + [Tooltip("RenderTexture that will be used to create a new instance for this panel.")] + [SerializeField] protected RenderTexture _renderTexturePrefab; + + [Tooltip("Some input modules (like the XRUIInputModule from the XR Interaction toolkit package) doesn't send PointerMove events. If you are using such an input module, just set this to true so at least you can properly drag things around.")] + public bool UseDragEventFix = false; + + public Vector2 PanelSize + { + get => new Vector2(_panelWidth, _panelHeight); + set + { + _panelWidth = Mathf.RoundToInt(value.x); + _panelHeight = Mathf.RoundToInt(value.y); + RefreshPanelSize(); + } + } + + public float PanelScale + { + get => _panelScale; + set + { + _panelScale = value; + + if (_panelSettings != null) + _panelSettings.scale = value; + } + } + + public VisualTreeAsset VisualTreeAsset + { + get => _visualTreeAsset; + set + { + _visualTreeAsset = value; + + if (_uiDocument != null) + _uiDocument.visualTreeAsset = value; + } + } + + public int PanelWidth { get => _panelWidth; set { _panelWidth = value; RefreshPanelSize(); } } + public int PanelHeight { get => _panelHeight; set { _panelHeight = value; RefreshPanelSize(); } } + public float PixelsPerUnit { get => _pixelsPerUnit; set { _pixelsPerUnit = value; RefreshPanelSize(); } } + public PanelSettings PanelSettingsPrefab { get => _panelSettingsPrefab; set { _panelSettingsPrefab = value; RebuildPanel(); } } + public RenderTexture RenderTexturePrefab { get => _renderTexturePrefab; set { _renderTexturePrefab = value; RebuildPanel(); } } + + protected MeshRenderer _meshRenderer; + protected PanelEventHandler _panelEventHandler; + + // runtime rebuildable stuff + protected UIDocument _uiDocument; + protected PanelSettings _panelSettings; + protected RenderTexture _renderTexture; + protected Material _material; + + public UnityEvent onUIDocumentReady; + + void Awake () + { + PixelsPerUnit = _pixelsPerUnit; + + // dynamically a MeshFilter, MeshRenderer and BoxCollider + MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>(); + + _meshRenderer = gameObject.AddComponent<MeshRenderer>(); + _meshRenderer.sharedMaterial = null; + _meshRenderer.shadowCastingMode = ShadowCastingMode.Off; + _meshRenderer.receiveShadows = false; + _meshRenderer.allowOcclusionWhenDynamic = false; + _meshRenderer.lightProbeUsage = LightProbeUsage.Off; + _meshRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off; + _meshRenderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; + + BoxCollider boxCollider = gameObject.AddComponent<BoxCollider>(); + Vector3 size = boxCollider.size; + size.z = 0; + boxCollider.size = size; + + // set the primitive quad mesh to the mesh filter + GameObject quadGo = GameObject.CreatePrimitive(PrimitiveType.Quad); + meshFilter.sharedMesh = quadGo.GetComponent<MeshFilter>().sharedMesh; + Destroy(quadGo); + } + + void Start() + { + RebuildPanel(); + } + + /// <summary> + /// Use this method to initialise the panel without triggering a rebuild (i.e.: when instantiating it from scripts). Start method + /// will always trigger RebuildPanel(), but if you are calling this after the GameObject started you must call RebuildPanel() so the + /// changes take effect. + /// </summary> + public void InitPanel (int panelWidth, int panelHeight, float panelScale, float pixelsPerUnit, VisualTreeAsset visualTreeAsset, PanelSettings panelSettingsPrefab, RenderTexture renderTexturePrefab) + { + _panelWidth = panelWidth; + _panelHeight = panelHeight; + _panelScale = panelScale; + _pixelsPerUnit = pixelsPerUnit; + _visualTreeAsset = visualTreeAsset; + _panelSettingsPrefab = panelSettingsPrefab; + _renderTexture = renderTexturePrefab; + } + + /// <summary> + /// Rebuilds the panel by destroy current assets and generating new ones based on the configuration. + /// </summary> + public void RebuildPanel () + { + DestroyGeneratedAssets(); + + // generate render texture + RenderTextureDescriptor textureDescriptor = _renderTexturePrefab.descriptor; + textureDescriptor.width = _panelWidth; + textureDescriptor.height = _panelHeight; + _renderTexture = new RenderTexture(textureDescriptor); + + // generate panel settings + _panelSettings = Instantiate(_panelSettingsPrefab); + _panelSettings.targetTexture = _renderTexture; + _panelSettings.clearColor = true; // ConstantPixelSize and clearColor are mandatory configs + _panelSettings.scaleMode = PanelScaleMode.ConstantPixelSize; + _panelSettings.scale = _panelScale; + _renderTexture.name = $"{name} - RenderTexture"; + _panelSettings.name = $"{name} - PanelSettings"; + + // generate UIDocument + _uiDocument = gameObject.AddComponent<UIDocument>(); + _uiDocument.panelSettings = _panelSettings; + _uiDocument.visualTreeAsset = _visualTreeAsset; + onUIDocumentReady.Invoke(); + + // generate material + if (_panelSettings.colorClearValue.a < 1.0f) + _material = new Material(Shader.Find("Unlit/Transparent")); + else + _material = new Material(Shader.Find("Unlit/Texture")); + + _material.SetTexture("_MainTex", _renderTexture); + _meshRenderer.sharedMaterial = _material; + + RefreshPanelSize(); + + // find the automatically generated PanelEventHandler and PanelRaycaster for this panel and disable the raycaster + PanelEventHandler[] handlers = FindObjectsOfType<PanelEventHandler>(); + + foreach (PanelEventHandler handler in handlers) + { + if (handler.panel == _uiDocument.rootVisualElement.panel) + { + _panelEventHandler = handler; + PanelRaycaster panelRaycaster = _panelEventHandler.GetComponent<PanelRaycaster>(); + if (panelRaycaster != null) + panelRaycaster.enabled = false; + + break; + } + } + } + + protected void RefreshPanelSize () + { + if (_renderTexture != null && (_renderTexture.width != _panelWidth || _renderTexture.height != _panelHeight)) + { + _renderTexture.Release(); + _renderTexture.width = _panelWidth; + _renderTexture.height = _panelHeight; + _renderTexture.Create(); + + if (_uiDocument != null) + _uiDocument.rootVisualElement?.MarkDirtyRepaint(); + } + + transform.localScale = new Vector3(_panelWidth / _pixelsPerUnit, _panelHeight / _pixelsPerUnit, 1.0f); + } + + protected void DestroyGeneratedAssets () + { + if (_uiDocument) Destroy(_uiDocument); + if (_renderTexture) Destroy(_renderTexture); + if (_panelSettings) Destroy(_panelSettings); + if (_material) Destroy(_material); + } + + void OnDestroy () + { + DestroyGeneratedAssets(); + } + +#if UNITY_EDITOR + void OnValidate () + { + if (Application.isPlaying && _material != null && _uiDocument != null) + { + if (_uiDocument.visualTreeAsset != _visualTreeAsset) + VisualTreeAsset = _visualTreeAsset; + if (_panelScale != _panelSettings.scale) + _panelSettings.scale = _panelScale; + + RefreshPanelSize(); + } + } +#endif + +///////////////////////// REDIRECTION OF EVENTS TO THE PANEL + protected readonly HashSet<(BaseEventData, int)> _eventsProcessedInThisFrame = new HashSet<(BaseEventData, int)>(); + + void LateUpdate () + { + _eventsProcessedInThisFrame.Clear(); + } + + public void OnPointerMove (PointerEventData eventData) + { + TransformPointerEventForUIToolkit(eventData); + _panelEventHandler?.OnPointerMove(eventData); + } + + public void OnPointerDown (PointerEventData eventData) + { + TransformPointerEventForUIToolkit(eventData); + _panelEventHandler?.OnPointerDown(eventData); + } + + public void OnPointerUp (PointerEventData eventData) + { + TransformPointerEventForUIToolkit(eventData); + _panelEventHandler?.OnPointerUp(eventData); + } + + public void OnSubmit (BaseEventData eventData) + { + _panelEventHandler?.OnSubmit(eventData); + } + + public void OnCancel (BaseEventData eventData) + { + _panelEventHandler?.OnCancel(eventData); + } + + public void OnMove (AxisEventData eventData) + { + _panelEventHandler?.OnMove(eventData); + } + + public void OnScroll (PointerEventData eventData) + { + TransformPointerEventForUIToolkit(eventData); + _panelEventHandler?.OnScroll(eventData); + } + + public void OnSelect (BaseEventData eventData) + { + _panelEventHandler?.OnSelect(eventData); + } + + public void OnDeselect (BaseEventData eventData) + { + _panelEventHandler?.OnDeselect(eventData); + } + + public void OnDrag (PointerEventData eventData) + { + if (UseDragEventFix) + OnPointerMove(eventData); + } + + protected void TransformPointerEventForUIToolkit (PointerEventData eventData) + { + var eventKey = (eventData, eventData.pointerId); + + if (!_eventsProcessedInThisFrame.Contains(eventKey)) + { + _eventsProcessedInThisFrame.Add(eventKey); + Camera eventCamera = eventData.enterEventCamera ?? eventData.pressEventCamera; + + if (eventCamera != null) + { + // get current event position and create the ray from the event camera + Vector3 position = eventData.position; + position.z = 1.0f; + position = eventCamera.ScreenToWorldPoint(position); + Plane panelPlane = new Plane(transform.forward, transform.position); + Ray ray = new Ray(eventCamera.transform.position, position - eventCamera.transform.position); + + if (panelPlane.Raycast(ray, out float distance)) + { + // get local pointer position within the panel + position = ray.origin + distance * ray.direction.normalized; + position = transform.InverseTransformPoint(position); + // compute a fake pointer screen position so it results in the proper panel position when projected from the camera by the PanelEventHandler + position.x += 0.5f; position.y -= 0.5f; + position = Vector3.Scale(position, new Vector3(_panelWidth, _panelHeight, 1.0f)); + position.y += Screen.height; + // print(new Vector2(position.x, Screen.height - position.y)); // print actual computed position in panel UIToolkit coords + + // update the event data with the new calculated position + eventData.position = position; + RaycastResult raycastResult = eventData.pointerCurrentRaycast; + raycastResult.screenPosition = position; + eventData.pointerCurrentRaycast = raycastResult; + raycastResult = eventData.pointerPressRaycast; + raycastResult.screenPosition = position; + eventData.pointerPressRaycast = raycastResult; + } + } + } + } + } +} diff --git a/Assets/UI/DialogueBox/WorldSpaceUIDocument.cs.meta b/Assets/UI/DialogueBox/WorldSpaceUIDocument.cs.meta new file mode 100644 index 0000000..03cbf28 --- /dev/null +++ b/Assets/UI/DialogueBox/WorldSpaceUIDocument.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fbbce919b93a804449b8e74c7b1eec1b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: -- GitLab