相关文章推荐
谦虚好学的紫菜  ·  sqlalchemy all vs ...·  2 年前    · 
逆袭的大象  ·  在Canvas ...·  2 年前    · 
温暖的梨子  ·  We need your consent·  2 年前    · 

這個對話系統是單機遊戲跟 NPC 對話的那種,不是玩家跟玩家即時聊天,此篇是參考 UNITY 2D NPC DIALOGUE SYSTEM TUTORIAL 再做一些的修改,外觀可以根據自己的喜好調整。

效果跟原本的教學影片一樣,不過圖片我有更換

原本影片的畫面

我修改的畫面

遊戲物件一覽

  • Canvas: 這個是畫布,第一次新增 UI,Unity 就會自動產生一個畫布
  • DialoguePanel: 對話外框,在選單 GameObject > UI > Panel
  • CharacterImage: 大頭照,在選單 GameObject > UI > Image
  • Name: 姓名,在選單 GameObject > UI > Text - TextMeshPro
  • DialogueText: 對話內容,在選單 GameObject > UI > Text - TextMeshPro
  • ContinueButton: 下一句按鈕,在選單 GameObject > UI > Button - TextMeshPro
  • 這些遊戲物件只要新增,依個人喜好調整即可,這裡我就不再贅述。

    對話控制器

  • DialogueController: 透過對話控制器控制對話面板,這是一個空物件,裡面只有一個腳本,記得在DialogueController 加上 tag DialogueController 方便讓其他腳本取得此遊戲物件。
  • 我由淺入深一個一個介紹

    文字速度列舉,做為文字速度的選項

    WordSpeedTypes.cs

    /// <summary>
    /// 對話文字數度
    /// </summary>
    public enum WordSpeedTypes
        /// <summary>
        /// 快
        /// </summary>
        Fast,
        /// <summary>
        /// 普通
        /// </summary>
        Normal,
        /// <summary>
        /// 慢
        /// </summary>
    

    對話物件,可以把它當作每一句話該出現的資料,例如:圖片、名字、對話內容。

    Dialogue.cs

    /// <summary>
    /// 每一句對話者資料
    /// </summary>
    public class Dialogue
        /// <summary>
        /// Resources 位置
        /// </summary>
        private string _photoPath = "Images/CharacterPhotos";
        /// <summary>
        /// 姓名
        /// </summary>
        public readonly string Name;
        /// <summary>
        /// 大頭照
        /// </summary>
        public readonly string PhotoPath;
        /// <summary>
        /// 對話內容
        /// </summary>
        public readonly string Sentence;
        public Dialogue(string name, string sentence = "", string photoPath = null)
            Name = name;
            // 可以填 null,帶入預設圖片
            photoPath = photoPath ?? "Default";
            PhotoPath = $"{_photoPath}/{photoPath}";
            Sentence = sentence;
    

    _photoPath 是 Unity Resources 資料夾底下的位置,上面的範例,大頭照放在 Assets/Resources/Images/CharacterPhotos 裡面。

    接下來是比較複雜的 DialogueController,文字速度大家可以自行調整,大家可能對協程比較不了解,因為我們要打字速度像人類打字那樣,一個一個打出來的樣子,但工作不能被打字耽擱,所以我叫了另一個人(協程)幫我處理打字,這樣原本的工作就不會被耽擱了。

    DialogueController.cs

    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    using UnityEngine.UI;
    /// <summary>
    /// 對話控制器
    /// </summary>
    public class DialogueController : MonoBehaviour
        /// <summary>
        /// 打字協程
        /// </summary>
        private Coroutine _typing;
        /// <summary>
        /// 下一句按鈕
        /// </summary>
        [SerializeField]
        private GameObject _continueButton;
        /// <summary>
        /// 對話面板
        /// </summary>
        [SerializeField]
        private GameObject _dialoguePanel;
        /// <summary>
        /// 角色大頭照
        /// </summary>
        [SerializeField]
        private Image _characterImage;
        /// <summary>
        /// 對話的內容
        /// </summary>
        private Queue<Dialogue> _dialogues;
        /// <summary>
        /// 對話框文字
        /// </summary>
        [SerializeField]
        private TextMeshProUGUI _dialogueText;
        /// <summary>
        /// 說話的姓名
        /// </summary>
        [SerializeField]
        private TextMeshProUGUI _nameText;
        [SerializeField]
        private WordSpeedTypes _wordSpeedTypes;
        /// <summary>
        /// 對話速度
        /// </summary>
        private float _wordSpeed;
        // Start is called before the first frame update
        void Start()
            Dictionary<WordSpeedTypes, float> wordSpeeds =
                new Dictionary<WordSpeedTypes, float>()
                {WordSpeedTypes.Fast, 0.02f},
                {WordSpeedTypes.Normal, 0.05f},
                {WordSpeedTypes.Slow, 0.1f},
            _wordSpeed = wordSpeeds[_wordSpeedTypes];
        // Update is called once per frame
        void Update()
        /// <summary>
        /// 對話有一個字一個字出現的效果
        /// </summary>
        /// <returns></returns>
        private IEnumerator Type()
            Dialogue dialogue = _dialogues.Dequeue();
            _nameText.text = dialogue.Name;
            _characterImage.sprite = Resources.Load<Sprite>(dialogue.PhotoPath);
            _dialogueText.text = string.Empty;
            foreach (char letter in dialogue.Sentence.ToCharArray())
                _dialogueText.text += letter;
                yield return new WaitForSeconds(_wordSpeed);
            _continueButton.SetActive(true);
        /// <summary>
        /// 關閉對話面板
        /// </summary>
        public void CloseDialogue()
            StopCoroutine(_typing);
            _dialoguePanel.SetActive(false);
        /// <summary>
        /// 説下一句
        /// </summary>
        public void SpeakNextSentence()
            _continueButton.SetActive(false);
            if (_dialogues.Count > 0)
                _typing = StartCoroutine(Type());
                CloseDialogue();
        /// <summary>
        /// 對話
        /// </summary>
        /// <param name="dialogues">對話資料</param>
        public void Talk(Queue<Dialogue> dialogues)
            _dialogues = dialogues;
            _dialoguePanel.SetActive(true);
            _typing = StartCoroutine(Type());
    

    設定遊戲物件

  • 把 DialogueController.cs 腳本加在 DialogueController 遊戲物件,然後把相關的物件打拖曳到欄位中,如上面的遊戲物件圖片那樣。
  • 設定 ContinueButton OnClick(),將 DialogueController 遊戲物件拖曳到如下圖 Runtime Only 的下面,然後選擇 DialogueController.SpeakNextSentence()
  • 關閉 ContinueButton 遊戲物件
  • 關閉 DialoguePanel 遊戲物件,意思同上。
  • 如何開啟對話

    目前我還是以腳本的方式來開啟對話,雖然我有想過非程式人員不會寫腳本,但因為目前只有我自己一個人開發遊戲…我就先不處理這個問題了。

    開啟對話只要建立一個腳本,取得對話控制器,執行 Talk() 方法就會開始對話,執行 CloseDialogue() 方法就對關閉對話。

    對話範例,這是一個遊戲物件,使用 Box Collider 2D 的 Is Trigger 碰到就會觸發的對話,離開時就會關閉對話。
    使用腳本寫對話可以自行定義觸發對話的條件和關閉對話的條件。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    /// <summary>
    /// 對話範例
    /// </summary>
    public class EndSceneDialogue1 : MonoBehaviour
        private DialogueController _dialogueController;
        private void OnTriggerEnter2D(Collider2D other)
            Queue<Dialogue> dialogues = new Queue<Dialogue>();
            dialogues.Enqueue(new Dialogue("玩家1", "我是玩家1", "Player1"));
            dialogues.Enqueue(new Dialogue("玩家2", "我是玩家2", "Player2"));
            dialogues.Enqueue(new Dialogue("玩家1", "你好玩家2", "Player1"));
            dialogues.Enqueue(new Dialogue("玩家2", "你好玩家1", "Player2"));
            _dialogueController.Talk(dialogues);
        private void OnTriggerExit2D(Collider2D other)
            _dialogueController.CloseDialogue();
        // Start is called before the first frame update
        void Start()
            _dialogueController = GameObject.FindWithTag("DialogueController")
                .GetComponent<DialogueController>();
        // Update is called once per frame
        void Update()
    
  • UNITY 2D NPC DIALOGUE SYSTEM TUTORIAL
  •