// Copyright (c) Pixel Crushers. All rights reserved. using UnityEngine; using System; namespace PixelCrushers.DialogueSystem { /// /// Standard UI implementation of IBarkUI. /// [AddComponentMenu("")] // Use wrapper. public class StandardBarkUI : AbstractBarkUI { /// /// The (optional) UI canvas group. If assigned, the fade will occur on this /// control. The other controls should be children of this canvas group. /// [Tooltip("Optional canvas group, for example to play fade animations.")] public CanvasGroup canvasGroup = null; /// /// The UI text control for the bark text. /// [Tooltip("UI text control for bark text.")] public UITextField barkText = null; /// /// The (optional) UI text control for the actor's name, if includeName is true. /// If null, the name is added to the front of the subtitle text instead. /// [Tooltip("Optional UI text control for barker's name if Include Name is ticked. If unassigned and Include Name is ticked, name is prepended to Bark Text.")] public UITextField nameText = null; /// /// Set true to include the barker's name in the text. /// [Tooltip("If Name Text is unassigned, prepend barker's name to Bark Text.")] public bool includeName = false; [Tooltip("Optional to show barker's portrait image.")] public UnityEngine.UI.Image portraitImage = null; [Tooltip("Show barker's portrait image.")] public bool showPortraitImage = false; [HideInInspector] public float doneTime = 0; [Serializable] public class AnimationTransitions { public string showTrigger = "Show"; public string hideTrigger = "Hide"; } public AnimationTransitions animationTransitions = new AnimationTransitions(); /// /// The duration in seconds to show the bark text before fading it out. /// [Tooltip("The duration in seconds to show the bark text before fading it out. If zero, use the Dialogue Manager's Bark Settings.")] public float duration = 4f; [Tooltip("Keep bark canvas anchor point always in camera view.")] public bool keepInView = false; /// /// Set true to keep the bark text onscreen until the sequence ends. /// [Tooltip("Keep the bark text onscreen until the sequence ends.")] public bool waitUntilSequenceEnds = false; [Tooltip("If bark is visible and waiting for sequence to end, but new bark wants to show, cancel wait for previous sequence.")] public bool cancelWaitUntilSequenceEndsIfReplacingBark = false; /// /// Wait for an "OnContinue" message. /// [Tooltip("Wait for an OnContinue message.")] public bool waitForContinueButton = false; /// /// If visible, hide this bark UI when any conversation starts. /// [Tooltip("If visible, hide this bark UI when any conversation starts.")] public bool hideOnConversationStart = false; /// /// The text display setting. Defaults to use the same subtitle setting as the Dialogue /// Manager, but you can also set it to always show or always hide the text. /// public BarkSubtitleSetting textDisplaySetting = BarkSubtitleSetting.SameAsDialogueManager; protected Canvas canvas { get; set; } protected Animator animator { get; set; } protected AbstractTypewriterEffect typewriter { get; set; } protected Vector3 originalCanvasLocalPosition { get; set; } protected int numSequencesActive = 0; protected bool hasEverBarked = false; /// /// Indicates whether a bark is currently playing. /// /// /// true if playing; otherwise, false. /// public override bool isPlaying { get { return (canvas != null) && canvas.enabled && (DialogueTime.time < doneTime); } } protected virtual void Awake() { canvas = GetComponentInChildren(); animator = GetComponentInChildren(); typewriter = TypewriterUtility.GetTypewriter(barkText); if ((animator == null) && (canvasGroup != null)) animator = canvasGroup.GetComponentInChildren(); } protected virtual void Start() { if (canvas != null) { if (waitForContinueButton && (canvas.worldCamera == null)) canvas.worldCamera = UnityEngine.Camera.main; canvas.enabled = false; originalCanvasLocalPosition = canvas.GetComponent().localPosition; } if (nameText != null) nameText.SetActive(includeName); Tools.SetGameObjectActive(portraitImage, false); if (hideOnConversationStart && DialogueManager.instance != null) { DialogueManager.instance.conversationStarted += OnConversationStarted; } } protected virtual void OnDestroy() { if (DialogueManager.instance != null) { DialogueManager.instance.conversationStarted -= OnConversationStarted; } } private void OnConversationStarted(Transform actor) { if (hideOnConversationStart && isPlaying) Hide(); } protected virtual void Update() { if (!hasEverBarked) return; if (!waitUntilSequenceEnds && doneTime > 0 && DialogueTime.time >= doneTime) { Hide(); } else if (keepInView && isPlaying) { var mainCamera = Camera.main; if (mainCamera == null) return; var pos = mainCamera.WorldToViewportPoint(canvas.transform.position); pos.x = Mathf.Clamp01(pos.x); pos.y = Mathf.Clamp01(pos.y); canvas.transform.position = mainCamera.ViewportToWorldPoint(pos); } } /// /// Indicates whether the bark UI should show the text, based on the /// textDisplaySetting and subtitle. /// /// /// true to show text; otherwise, false. /// public virtual bool ShouldShowText(Subtitle subtitle) { bool settingsAllowShowText = (textDisplaySetting == BarkSubtitleSetting.Show) || ((textDisplaySetting == BarkSubtitleSetting.SameAsDialogueManager) && DialogueManager.displaySettings.subtitleSettings.showNPCSubtitlesDuringLine); bool subtitleHasText = (subtitle != null) && !string.IsNullOrEmpty(subtitle.formattedText.text); return settingsAllowShowText && subtitleHasText; } /// /// Barks a subtitle. Does not observe formatting codes in the subtitle's FormattedText, /// instead using the formatting settings defined on this component. /// /// /// Subtitle to bark. /// public override void Bark(Subtitle subtitle) { if (ShouldShowText(subtitle)) { hasEverBarked = true; SetUIElementsActive(false); string subtitleText = subtitle.formattedText.text; if (includeName && !string.IsNullOrEmpty(Tools.StripTextMeshProTags(subtitle.speakerInfo.Name))) { if (nameText != null) { nameText.text = subtitle.speakerInfo.Name; } else { subtitleText = string.Format("{0}: {1}", subtitle.speakerInfo.Name, subtitle.formattedText.text); } } else { if (nameText != null && nameText.gameObject != null) nameText.gameObject.SetActive(false); } if (showPortraitImage && subtitle.speakerInfo.portrait != null) { Tools.SetGameObjectActive(portraitImage, true); portraitImage.sprite = subtitle.GetSpeakerPortrait(); } else { Tools.SetGameObjectActive(portraitImage, false); } if (barkText != null) barkText.text = subtitleText; SetUIElementsActive(true); if (CanTriggerAnimations() && !string.IsNullOrEmpty(animationTransitions.showTrigger)) { if (!string.IsNullOrEmpty(animationTransitions.hideTrigger)) { animator.ResetTrigger(animationTransitions.hideTrigger); } animator.SetTrigger(animationTransitions.showTrigger); } if (typewriter != null) typewriter.StartTyping(subtitleText); var barkDuration = Mathf.Approximately(0, duration) ? DialogueManager.GetBarkDuration(subtitleText) : duration; if (waitUntilSequenceEnds) numSequencesActive++; doneTime = waitForContinueButton ? Mathf.Infinity : (DialogueTime.time + barkDuration); } } protected virtual void SetUIElementsActive(bool value) { if (nameText.gameObject != this.gameObject && includeName) nameText.SetActive(value); if (barkText.gameObject != this.gameObject) barkText.SetActive(value); if (canvas != null && canvas.gameObject != this.gameObject) canvas.gameObject.SetActive(value); if (value == true && canvas != null) canvas.enabled = true; } public virtual void OnBarkEnd(Transform actor) { if (waitUntilSequenceEnds && !waitForContinueButton && IsActorMe(actor)) { numSequencesActive--; if (numSequencesActive <= 0) Hide(); } } protected virtual bool IsActorMe(Transform actor) { var t = transform; while (t != null) { if (t == actor) return true; t = t.parent; } return false; } public virtual void OnContinue() { Hide(); } public override void Hide() { if (!hasEverBarked) { if (canvas != null) canvas.enabled = false; return; } numSequencesActive = 0; if (CanTriggerAnimations() && !string.IsNullOrEmpty(animationTransitions.hideTrigger)) { if (!string.IsNullOrEmpty(animationTransitions.showTrigger)) { animator.ResetTrigger(animationTransitions.showTrigger); } animator.SetTrigger(animationTransitions.hideTrigger); } else if (canvas != null) { canvas.enabled = false; } if (canvas != null) canvas.GetComponent().localPosition = originalCanvasLocalPosition; doneTime = 0; } protected virtual bool CanTriggerAnimations() { return (animator != null) && (animationTransitions != null); } } }