Files
Cielonos/Packages/dev.yarnspinner.unity/Runtime/Views/OptionItem.cs
SoulliesOfficial 8186f54e90 新场景,剧情
2026-06-02 12:55:39 -04:00

175 lines
5.4 KiB
C#

/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
using UnityEngine;
using UnityEngine.EventSystems;
using Yarn.Unity.Attributes;
#if USE_TMP
using TMPro;
#else
using TextMeshProUGUI = Yarn.Unity.TMPShim;
#endif
#nullable enable
namespace Yarn.Unity
{
[System.Serializable]
public struct InternalAppearance
{
[SerializeField] internal Sprite sprite;
[SerializeField] internal Color colour;
}
public class OptionItem : UnityEngine.UI.Selectable, ISubmitHandler, IPointerClickHandler, IPointerEnterHandler
{
[MustNotBeNull, SerializeField] protected TextMeshProUGUI? text;
[SerializeField] protected UnityEngine.UI.Image? selectionImage;
[Group("Appearance"), SerializeField] protected InternalAppearance normal;
[Group("Appearance"), SerializeField] protected InternalAppearance selected;
[Group("Appearance"), SerializeField] protected InternalAppearance disabled;
[Group("Appearance"), SerializeField] protected bool disabledStrikeThrough = true;
public YarnTaskCompletionSource<DialogueOption?>? OnOptionSelected;
public System.Threading.CancellationToken completionToken;
protected bool hasSubmittedOptionSelection = false;
protected DialogueOption? _option;
public virtual DialogueOption Option
{
get
{
if (_option == null)
{
throw new System.NullReferenceException("Option has not been set on the option item");
}
return _option;
}
set
{
_option = value;
hasSubmittedOptionSelection = false;
// When we're given an Option, use its text and update our
// interactibility.
string line = value.Line.TextWithoutCharacterName.Text;
if (disabledStrikeThrough && !value.IsAvailable)
{
line = $"<s>{value.Line.TextWithoutCharacterName.Text}</s>";
}
if (text == null)
{
Debug.LogWarning($"The {nameof(text)} is null, is it not connected in the inspector?", this);
return;
}
text.text = line;
interactable = value.IsAvailable;
// we want to apply the default styling to the option item when they are given an option
ApplyStyle(normal);
}
}
protected virtual void ApplyStyle(InternalAppearance style)
{
Color newColour = style.colour;
Sprite newSprite = style.sprite;
if (!Option.IsAvailable)
{
newColour = disabled.colour;
newSprite = disabled.sprite;
}
if (text == null)
{
Debug.LogWarning($"The {nameof(text)} is null, is it not connected in the inspector?", this);
return;
}
text.color = newColour;
if (selectionImage != null)
{
selectionImage.color = newColour;
if (newSprite != null)
{
selectionImage.sprite = newSprite;
selectionImage.gameObject.SetActive(true);
}
else
{
selectionImage.gameObject.SetActive(false);
}
}
}
public override void OnSelect(BaseEventData eventData)
{
base.OnSelect(eventData);
ApplyStyle(selected);
}
public override void OnDeselect(BaseEventData eventData)
{
base.OnDeselect(eventData);
ApplyStyle(normal);
}
new public bool IsHighlighted
{
get
{
return EventSystem.current.currentSelectedGameObject == this.gameObject;
}
}
// If we receive a submit or click event, invoke our "we just selected this option" handler.
public void OnSubmit(BaseEventData eventData)
{
InvokeOptionSelected();
}
public void InvokeOptionSelected()
{
// turns out that Selectable subclasses aren't intrinsically interactive/non-interactive
// based on their canvasgroup, you still need to check at the moment of interaction
if (!IsInteractable())
{
return;
}
// We only want to invoke this once, because it's an error to
// submit an option when the Dialogue Runner isn't expecting it. To
// prevent this, we'll only invoke this if the flag hasn't been cleared already.
if (hasSubmittedOptionSelection == false && !completionToken.IsCancellationRequested)
{
hasSubmittedOptionSelection = true;
OnOptionSelected?.TrySetResult(this.Option);
}
}
public virtual void OnPointerClick(PointerEventData eventData)
{
InvokeOptionSelected();
}
// If we mouse-over, we're telling the UI system that this element is
// the currently 'selected' (i.e. focused) element.
public override void OnPointerEnter(PointerEventData eventData)
{
base.Select();
}
}
}