Ways to optimize?

Announcements, support questions, and discussion for the Dialogue System.
Post Reply
Japtor
Posts: 120
Joined: Thu Jun 28, 2018 1:41 pm

Ways to optimize?

Post by Japtor »

Hi,

Are there ways/tips to optimize the dialogue UI/bark? It seems to drop a little bit the fps the first time it spawns both UI inside the scene.

Thanks! :)
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Ways to optimize?

Post by Tony Li »

Hi,

On the Dialogue Manager, tick Other Settings > Preload Resources. This will preload the dialogue database and dialogue UI when the Dialogue Manager GameObject starts, rather than lazy-loading them when they're first used. Also set Other Settings > Debug Level to Warning. If it's set to Info, it will log a lot of information to the Console/output log, which slows things down a lot.

If your actors have large portrait images, they may require some time to initialize when they're first involved in a conversation. You can add a Preload Actor Portraits component to the Dialogue Manager to initialize them when the Dialogue Manager starts instead.

If you've assigned a bark UI prefab or custom subtitle prefab to a Dialogue Actor component, they should initialize as soon as the Dialogue Actor component starts (i.e., when the scene starts), not at the start of the bark/conversation, so that shouldn't be the problem.
Japtor
Posts: 120
Joined: Thu Jun 28, 2018 1:41 pm

Re: Ways to optimize?

Post by Japtor »

Hi,

Thanks! I will take a look at your proposals and tell you about any further problems/solutions.
Japtor
Posts: 120
Joined: Thu Jun 28, 2018 1:41 pm

Re: Ways to optimize?

Post by Japtor »

Hi,

I'm not used to the profiler window, and I am not sure if this help, but this is the exact moment where the player triggers the NPC and spawns the UI Bark for the very first time.
Spoiler
Image
Any idea?

Thanks! :)
Last edited by Japtor on Mon Nov 05, 2018 8:08 am, edited 1 time in total.
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Ways to optimize?

Post by Tony Li »

Hi,

Try unticking the Input Device Manager's Control Graphic Raycasters checkbox.

This checkbox makes the Input Device Manager search the entire scene for Unity UI Graphic Raycasters, enabling them if you're in mouse mode, and disabling them in joystick mode. Most games don't need it, and it can take some time to search the scene.
Japtor
Posts: 120
Joined: Thu Jun 28, 2018 1:41 pm

Re: Ways to optimize?

Post by Japtor »

Hi,

I have unchecked it and still the same.
Spoiler
First contact with the NPC/Bark UI. I don't know if this would help, but I am using the 2.0.5 version of the tool and 2D.

Image
Any other Idea?

Thanks.
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Ways to optimize?

Post by Tony Li »

Hi,

27 ms is only 0.027 seconds. Are you sure that's where the slowdown occurs?

A common culprit is Unity UI. Can you send a copy of your project, or a small example that reproduces the issue, to tony (at) pixelcrushers.com? I'll be happy to take a look.
Japtor
Posts: 120
Joined: Thu Jun 28, 2018 1:41 pm

Re: Ways to optimize?

Post by Japtor »

Hi,

I have sent you an example to your email. Hope we can find a solution! :)

Thanks for your help!
Japtor
Posts: 120
Joined: Thu Jun 28, 2018 1:41 pm

Re: Ways to optimize?

Post by Japtor »

Hi all,

Well, finally Tony helped me to find a solution.

Most parts are explained by him in the following post: Optimization Tips for the Dialogue System, which I recommend keeping an eye on it, as he would try to continue updating some other ways to optimize the tool.

In my case, he shared me some scripts and recommended me to do the following things:
  • Make the Dialogue System initialize everything by initializing a conversation and stopping it at the start of the scene.
    Spoiler

    Code: Select all

    [ConversationPopup]
        public string conversation;
    
        IEnumerator Start()
        {
          PixelCrushers.DialogueSystem.DialogueManager.StartConversation(conversation);
          yield return null;
          PixelCrushers.DialogueSystem.DialogueManager.StopConversation();
        }
    
  • For barking, tick the NPC's Dialogue System Trigger > Cache Bark Lines
  • Inspect Player's Proximity Selector and untick "Broadcast To Children" if you don't need to traverse the usable's entire hierarchy.
  • Inspect the NPC's Dialogue System Trigger, and untick "Skip If No Valid Entries".
  • Assign a GUI Skin to ProximitySelector instead of leaving the field unassigned.
  • A modification to the ProximitySelector script, which initializes its font when the scene starts.
    Spoiler

    Code: Select all

    using UnityEngine;
    using System.Collections.Generic;
    using PixelCrushers.DialogueSystem.UnityGUI;
    
    namespace PixelCrushers.DialogueSystem
    {
    
        /// <summary>
        /// This component implements a proximity-based selector that allows the player to move into
        /// range and use a usable object. 
        /// 
        /// To mark an object usable, add the Usable component and a collider to it. The object's
        /// layer should be in the layer mask specified on the Selector component.
        /// 
        /// The proximity selector tracks the most recent usable object whose trigger the player has
        /// entered. It displays a targeting reticle and information about the object. If the target
        /// is in range, the inRange reticle texture is displayed; otherwise the outOfRange texture is
        /// displayed.
        /// 
        /// If the player presses the use button (which defaults to spacebar and Fire2), the targeted
        /// object will receive an "OnUse" message.
        /// 
        /// You can hook into SelectedUsableObject and DeselectedUsableObject to get notifications
        /// when the current target has changed.
        /// </summary>
        [AddComponentMenu("")] // Use wrapper.
        public class ProximitySelector : MonoBehaviour
        {
    
            /// <summary>
            /// This class defines the textures and size of the targeting reticle.
            /// </summary>
            [System.Serializable]
            public class Reticle
            {
                public Texture2D inRange;
                public Texture2D outOfRange;
                public float width = 64f;
                public float height = 64f;
            }
    
            /// <summary>
            /// If <c>true</c>, uses a default OnGUI to display a selection message and
            /// targeting reticle.
            /// </summary>
            [Tooltip("Use a default OnGUI to display selection message and targeting reticle.")]
            public bool useDefaultGUI = true;
    
            /// <summary>
            /// The GUI skin to use for the target's information (name and use message).
            /// </summary>
            [Tooltip("GUI skin to use for the target's information (name and use message).")]
            public GUISkin guiSkin;
    
            /// <summary>
            /// The name of the GUI style in the skin.
            /// </summary>
            [Tooltip("Name of the GUI style in the skin.")]
            public string guiStyleName = "label";
    
            /// <summary>
            /// The text alignment.
            /// </summary>
            public TextAnchor alignment = TextAnchor.UpperCenter;
    
            /// <summary>
            /// The color of the information labels when the target is in range.
            /// </summary>
            [Tooltip("Color of the information labels when the target is in range.")]
            public Color color = Color.yellow;
    
            /// <summary>
            /// The text style for the text.
            /// </summary>
            public TextStyle textStyle = TextStyle.Shadow;
    
            /// <summary>
            /// The color of the text style's outline or shadow.
            /// </summary>
            [Tooltip("Color of the text style's outline or shadow.")]
            public Color textStyleColor = Color.black;
    
            /// <summary>
            /// The default use message. This can be overridden in the target's Usable component.
            /// </summary>
            [Tooltip("Default use message. This can be overridden in the target's Usable component.")]
            public string defaultUseMessage = "(spacebar to interact)";
    
            /// <summary>
            /// The key that sends an OnUse message.
            /// </summary>
            [Tooltip("Key that sends an OnUse message.")]
            public KeyCode useKey = KeyCode.Space;
    
            /// <summary>
            /// The button that sends an OnUse message.
            /// </summary>
            [Tooltip("Input button that sends an OnUse message.")]
            public string useButton = "Fire2";
    
            /// <summary>
            /// Tick to enable touch triggering.
            /// </summary>
            [Tooltip("Enable touch triggering.")]
            public bool enableTouch = false;
    
            /// <summary>
            /// If touch triggering is enabled and there's a touch in this area,
            /// the selector triggers.
            /// </summary>
            public ScaledRect touchArea = new ScaledRect(ScaledRect.empty);
    
            /// <summary>
            /// If ticked, the OnUse message is broadcast to the usable object's children.
            /// </summary>
            [Tooltip("Broadcast OnUse message to Usable object's children.")]
            public bool broadcastToChildren = true;
    
            /// <summary>
            /// The actor transform to send with OnUse. Defaults to this transform.
            /// </summary>
            [Tooltip("Actor transform to send with OnUse. Defaults to this transform.")]
            public Transform actorTransform = null;
    
            public UsableUnityEvent onSelectedUsable = new UsableUnityEvent();
    
            public UsableUnityEvent onDeselectedUsable = new UsableUnityEvent();
    
            /// <summary>
            /// Occurs when the selector has targeted a usable object.
            /// </summary>
            public event SelectedUsableObjectDelegate SelectedUsableObject = null;
    
            /// <summary>
            /// Occurs when the selector has untargeted a usable object.
            /// </summary>
            public event DeselectedUsableObjectDelegate DeselectedUsableObject = null;
    
            /// <summary>
            /// Gets the current usable.
            /// </summary>
            /// <value>The usable.</value>
            public Usable CurrentUsable { get { return currentUsable; } }
    
            /// <summary>
            /// Gets the GUI style.
            /// </summary>
            /// <value>The GUI style.</value>
            public GUIStyle GuiStyle { get { SetGuiStyle(); return guiStyle; } }
    
            /// <summary>
            /// Keeps track of which usable objects' triggers the selector is currently inside.
            /// </summary>
            protected List<Usable> usablesInRange = new List<Usable>();
    
            /// <summary>
            /// The current usable that will receive an OnUse message if the player hits the use button.
            /// </summary>
            protected Usable currentUsable = null;
    
            protected string currentHeading = string.Empty;
    
            protected string currentUseMessage = string.Empty;
    
            protected bool toldListenersHaveUsable = false;
    
            /// <summary>
            /// Caches the GUI style to use when displaying the selection message in OnGUI.
            /// </summary>
            protected GUIStyle guiStyle = null;
    
            protected float guiStyleLineHeight = 16f;
    
            protected const float MinTimeBetweenUseButton = 0.5f;
            protected float timeToEnableUseButton = 0;
    
            public virtual void Start()
            {
                bool found = false;
                if (GetComponent<Collider>() != null) found = true;
    #if USE_PHYSICS2D || !UNITY_2018_1_OR_NEWER
                if (!found && GetComponent<Collider2D>() != null) found = true;
    #endif
                if (!found && DialogueDebug.logWarnings) Debug.LogWarning("Dialogue System: Proximity Selector requires a collider, but it has no collider component.", this);
            }
    
            public virtual void OnConversationEnd(Transform actor)
            {
                timeToEnableUseButton = Time.time + MinTimeBetweenUseButton;
            }
    
            /// <summary>
            /// Sends an OnUse message to the current selection if the player presses the use button.
            /// </summary>
            protected virtual void Update()
            {
                // Exit if disabled or paused:
                if (!enabled || (Time.timeScale <= 0)) return;
    
                if (DialogueManager.isConversationActive) timeToEnableUseButton = Time.time + MinTimeBetweenUseButton;
    
                // If the currentUsable went missing (was destroyed or we changed scene), tell listeners:
                if (toldListenersHaveUsable && currentUsable == null)
                {
                    SetCurrentUsable(null);
                    OnDeselectedUsableObject(null);
                    toldListenersHaveUsable = false;
                }
    
                // If the player presses the use key/button, send the OnUse message:
                if (IsUseButtonDown()) UseCurrentSelection();
            }
    
            protected void OnSelectedUsableObject(Usable usable)
            {
                if (SelectedUsableObject != null) SelectedUsableObject(usable);
                onSelectedUsable.Invoke(usable);
            }
    
            protected void OnDeselectedUsableObject(Usable usable)
            {
                if (DeselectedUsableObject != null) DeselectedUsableObject(usable);
                onDeselectedUsable.Invoke(usable);
            }
    
            /// <summary>
            /// Calls OnUse on the current selection.
            /// </summary>
            public virtual void UseCurrentSelection()
            {
                if ((currentUsable != null) && (currentUsable.gameObject != null) && (Time.time >= timeToEnableUseButton))
                {
                    var fromTransform = (actorTransform != null) ? actorTransform : this.transform;
                    if (broadcastToChildren)
                    {
                        currentUsable.gameObject.BroadcastMessage("OnUse", fromTransform, SendMessageOptions.DontRequireReceiver);
                    }
                    else
                    {
                        currentUsable.gameObject.SendMessage("OnUse", fromTransform, SendMessageOptions.DontRequireReceiver);
                    }
                }
            }
    
            /// <summary>
            /// Checks whether the player has just pressed the use button.
            /// </summary>
            /// <returns>
            /// <c>true</c> if the use button/key is down; otherwise, <c>false</c>.
            /// </returns>
            protected virtual bool IsUseButtonDown()
            {
                if (DialogueManager.IsDialogueSystemInputDisabled()) return false;
                if (enableTouch && IsTouchDown()) return true;
                return ((useKey != KeyCode.None) && Input.GetKeyDown(useKey))
                    || (!string.IsNullOrEmpty(useButton) && Input.GetButtonUp(useButton));
            }
    
            protected bool IsTouchDown()
            {
                if (Input.touchCount >= 1)
                {
                    foreach (Touch touch in Input.touches)
                    {
                        Vector2 screenPosition = new Vector2(touch.position.x, Screen.height - touch.position.y);
                        if (touchArea.GetPixelRect().Contains(screenPosition)) return true;
                    }
                }
                return false;
            }
    
            /// <summary>
            /// If we entered a trigger, check if it's a usable object. If so, update the selection
            /// and raise the SelectedUsableObject event.
            /// </summary>
            /// <param name='other'>
            /// The trigger collider.
            /// </param>
            protected void OnTriggerEnter(Collider other)
            {
                CheckTriggerEnter(other.gameObject);
            }
    
            /// <summary>
            /// If we just left a trigger, check if it's the current selection. If so, clear the
            /// selection and raise the DeselectedUsableObject event. If we're still in range of
            /// any other usables, select one of them.
            /// </summary>
            /// <param name='other'>
            /// The trigger collider.
            /// </param>
            protected void OnTriggerExit(Collider other)
            {
                CheckTriggerExit(other.gameObject);
            }
    
    #if USE_PHYSICS2D || !UNITY_2018_1_OR_NEWER
    
            /// <summary>
            /// If we entered a 2D trigger, check if it's a usable object. If so, update the selection
            /// and raise the SelectedUsableObject event.
            /// </summary>
            /// <param name='other'>
            /// The 2D trigger collider.
            /// </param>
            protected void OnTriggerEnter2D(Collider2D other)
            {
                CheckTriggerEnter(other.gameObject);
            }
    
            /// <summary>
            /// If we just left a 2D trigger, check if it's the current selection. If so, clear the
            /// selection and raise the DeselectedUsableObject event. If we're still in range of
            /// any other usables, select one of them.
            /// </summary>
            /// <param name='other'>
            /// The 2D trigger collider.
            /// </param>
            protected void OnTriggerExit2D(Collider2D other)
            {
                CheckTriggerExit(other.gameObject);
            }
    
    #endif
    
            protected virtual void CheckTriggerEnter(GameObject other)
            {
                Usable usable = other.GetComponent<Usable>();
                if (usable != null && usable.enabled)
                {
                    SetCurrentUsable(usable);
                    if (!usablesInRange.Contains(usable)) usablesInRange.Add(usable);
                    OnSelectedUsableObject(usable);
                    toldListenersHaveUsable = true;
                }
            }
    
            protected virtual void CheckTriggerExit(GameObject other)
            {
                Usable usable = other.GetComponent<Usable>();
                if (usable != null && usable.enabled)
                {
                    if (usablesInRange.Contains(usable)) usablesInRange.Remove(usable);
                    if (currentUsable == usable)
                    {
                        OnDeselectedUsableObject(usable);
                        toldListenersHaveUsable = false;
                        Usable newUsable = null;
                        if (usablesInRange.Count > 0)
                        {
                            newUsable = usablesInRange[0];
                            OnSelectedUsableObject(newUsable);
                            toldListenersHaveUsable = true;
                        }
                        SetCurrentUsable(newUsable);
                    }
                }
            }
    
            protected virtual void SetCurrentUsable(Usable usable)
            {
                currentUsable = usable;
                if (usable != null)
                {
                    currentHeading = currentUsable.GetName();
                    currentUseMessage = string.IsNullOrEmpty(currentUsable.overrideUseMessage) ? defaultUseMessage : currentUsable.overrideUseMessage;
                }
                else
                {
                    currentHeading = string.Empty;
                    currentUseMessage = string.Empty;
                }
            }
    
            /// <summary>
            /// If useDefaultGUI is <c>true</c> and a usable object has been targeted, this method
            /// draws a selection message and targeting reticle.
            /// </summary>
            public virtual void OnGUI()
            {
                if (!useDefaultGUI) return;
                if (guiStyle == null && (Event.current.type == EventType.Repaint || currentUsable != null))
                {
                    SetGuiStyle();
                }
                if (currentUsable != null)
                {
                    GUI.skin = guiSkin;
                    UnityGUITools.DrawText(new Rect(0, 0, Screen.width, Screen.height), currentHeading, guiStyle, textStyle, textStyleColor);
                    UnityGUITools.DrawText(new Rect(0, guiStyleLineHeight, Screen.width, Screen.height), currentUseMessage, guiStyle, textStyle, textStyleColor);
                }
            }
    
            protected void SetGuiStyle()
            {
                guiSkin = UnityGUITools.GetValidGUISkin(guiSkin);
                GUI.skin = guiSkin;
                guiStyle = new GUIStyle(string.IsNullOrEmpty(guiStyleName) ? GUI.skin.label : (GUI.skin.FindStyle(guiStyleName) ?? GUI.skin.label));
                guiStyle.alignment = alignment;
                guiStyle.normal.textColor = color;
                guiStyleLineHeight = guiStyle.CalcSize(new GUIContent("Ay")).y;
            }
    
        }
    
    }
    
That's all. Thank you, Tony, for your help!

See you all! :)
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Ways to optimize?

Post by Tony Li »

Thanks for posting those, Javier! The updated ProximitySelector will also be in version 2.0.6.
Post Reply