項目 divider 尚未註冊或是沒有一個 view.php 檔案.

《Carto》實作對話系統07-加入對話結束後會觸發的事件

改善原因

有時候會希望某一句話結束或對話結束時,可以觸發某個行為或某件事,像是移動鏡頭到某個物件上幾秒等等的,讓對話的過程可以更豐富。

實作

增加要觸發的事件

Dialogue

而要實現這個功能,需要引入UnityEngine.Events的名稱空間(Namespace),如此才能使用其中的UnityEvent類別。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

[System.Serializable]
public class Dialogue
{
    public string dialogueName;
    public bool isCompelete = false;
    
    public List<Condition> taskConditions;

    [Space(15)]
    public List<Sentence> sentences;

    public UnityEvent finishEvent;

    /// <summary>
    /// 啟動對話結束時的事件
    /// </summary>
    public void InvokeFinishEvent()
    {
        finishEvent.Invoke();
    }
}

當我們宣告了型別為UnityEvent的公開變數,我們可以在Inspector視窗上設定他要執行哪個物件的哪個元件的哪個方法。

FinishEvent的左側,其右邊有圓形icon的那一欄,是設定要是哪個物件

FinishEvent的右側,其右邊有三角形icon的那一欄,是設定要用物件的哪個元件的哪個方法。

新增對話結束時的事件

如果要執行的方法有參數

如果方法有一個參數,底下還會顯示一欄,讓你輸入要傳入方法的值。

設定Inovke事件時執行的方法的參數

然而,若是想要執行有更多參數的方法,需要自己創建覆寫UnityEvent的新類別,可以參考這一篇,最多可以有四個參數。

Unity – Scripting API: UnityEvent<T0,T1,T2,T3> (unity3d.com)

改善玩家體驗

DialoguesManager

希望在每個對話結束後,都能在出現可對話的提示訊息,因為角色還在可進行對話的範圍內,如果要玩家走出去再回來才會顯示,覺得體驗上不是那麼好。

宣告型別為UnityEvent的變數,並在每一個對話結束後觸發事件。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class DialoguesManager : MonoBehaviour
{
    private Dialogue dialogue;
    public Dialogue Dialogue
    {
        get
        {
            return dialogue;
        }
    }

    private bool isDialogueCompelete = true;
    public bool IsDialogueCompelete
    {
        get
        {
            return isDialogueCompelete;
        }
    }

    private RoleObj speakingRoleObjs;
    private RoleObj[] roleObj;
    private Dictionary<Role, RoleObj> dic_Role_and_DialogueObj = new Dictionary<Role, RoleObj>();

    private void GetAllRoleObj()
    {
        roleObj = FindObjectsOfType<RoleObj>();
    }

    private void Set_Role_and_DialogueObj()
    {
        for (int i = 0; i < roleObj.Length; i++)
        {
            dic_Role_and_DialogueObj.Add(roleObj[i].role, roleObj[i]);
        }
    }

    private RoleObj GetRoleObjs(Role role)
    {
        if (dic_Role_and_DialogueObj.ContainsKey(role))
        {
            return dic_Role_and_DialogueObj[role];
        }
        return null;
    }





    public List<AreaDialogues> areaDialogues;

    public PlayerProgress playerProgress;

    public UnityEvent finishEventAfterAnyDialogue;//宣告所有對話結束後要發生的事鍵的變數

    private AreaDialogues GetAreaDialogues()
    {
        for (int i = 0; i < areaDialogues.Count; i++)
        {
            if (areaDialogues[i].areaNum == AreaNumKeeper.currentNum)
            {
                return areaDialogues[i];
            }
        }
        return null;
    }

    public void GetDialogue()
    {
        if (isDialogueCompelete)
        {
            AreaDialogues areaDialogues = GetAreaDialogues();

            if (areaDialogues != null)
            {
                dialogue = areaDialogues.GetDialogue(playerProgress);
            }
        }
    }

    public void KeepingTheDialogue(int dialogeIndex)
    {
        AreaDialogues areaDialogues = GetAreaDialogues();
        areaDialogues.dialogues[dialogeIndex].isCompelete = false;
    }





    private Vector3 dialogueRolesCenterPosition;
    public Vector3 DialogueRolesCenterPosition
    {
        get
        {
            return dialogueRolesCenterPosition;
        }
    }

    private void CalculateCenterPosition()
    {
        dialogueRolesCenterPosition = Vector3.zero;
        for (int l = 0; l < roleObj.Length; l++)
        {
            dialogueRolesCenterPosition += roleObj[l].transform.position;
        }
        dialogueRolesCenterPosition = dialogueRolesCenterPosition / roleObj.Length;
    }

    private void ScaleDownAllBubble()
    {
        for (int l = 0; l < roleObj.Length; l++)
        {
            roleObj[l].BubbleScaleDown();
        }
    }

    private IEnumerator DialogueTextWriter(Dialogue dialogue)
    {
        ScaleDownAllBubble();
        CalculateCenterPosition();

        bool isFinishTextSentence = false;

        int sentenceCounter = 0;

        while (sentenceCounter < dialogue.sentences.Count)
        {
            speakingRoleObjs = GetRoleObjs(dialogue.sentences[sentenceCounter].speaker);


            if (isFinishTextSentence == false)
            {
                string sentenceContent = dialogue.sentences[sentenceCounter].text;
                speakingRoleObjs.BubbleScaleUp("<color=#00000000>" + sentenceContent + "</color>");

                yield return new WaitForSeconds(0.2f);


                float time = 0, duration = 0.04f;

                for (int j = 0; j < dialogue.sentences[sentenceCounter].text.Length; j++)
                {
                    string txt = sentenceContent.Substring(0, j + 1) + "<color=#00000000>" + sentenceContent.Substring(j + 1) + "</color>";

                    speakingRoleObjs.SetText(txt);

                    while (time < duration)
                    {
                        if (Input.GetKeyDown(KeyCode.Space))
                        {
                            duration = 0.02f;
                        }
                        time += Time.deltaTime;
                        yield return new WaitForSeconds(Time.deltaTime);
                    }

                    time = 0;
                }

                isFinishTextSentence = true;
            }
            else
            {
                if (Input.GetKeyDown(KeyCode.Space))
                {
                    if (sentenceCounter < dialogue.sentences.Count - 1)
                    {
                        sentenceCounter++;
                        isFinishTextSentence = false;

                        speakingRoleObjs.BubbleScaleDown();
                        yield return new WaitForSeconds(0.2f);
                    }
                    else if (sentenceCounter >= dialogue.sentences.Count - 1)
                    {
                        sentenceCounter++;

                        speakingRoleObjs.BubbleScaleDown();
                        yield return new WaitForSeconds(0.2f);
                        speakingRoleObjs = null;


                        isDialogueCompelete = true;
                        dialogue.isCompelete = true;


                        dialogue.InvokeFinishEvent();//啟動特定對話結束時的事件
                        finishEventAfterAnyDialogue.Invoke();//啟動所有對話結束時的事件
                    }
                }
            }
            yield return null;
        }
    }

    public void StartDialogue()
    {
        if (dialogue != null)
        {
            isDialogueCompelete = false;
            StartCoroutine(DialogueTextWriter(dialogue));
        }
    }

    private void Start()
    {
        GetAllRoleObj();
        Set_Role_and_DialogueObj();
    }

    private void OnValidate()
    {
        UpdateEditorUI();
    }

    public void UpdateEditorUI()
    {
        for (int i = 0; i < areaDialogues.Count; i++)
        {
            areaDialogues[i].Update();

            for (int j = 0; j < areaDialogues[i].dialogues.Count; j++)
            {
                for (int k = 0; k < areaDialogues[i].dialogues[j].taskConditions.Count; k++)
                {
                    if (playerProgress != null)
                    {
                        areaDialogues[i].dialogues[j].taskConditions[k].Update(playerProgress.SearchTaskName(areaDialogues[i].dialogues[j].taskConditions[k].taskIndex));
                    }
                }

                for (int l = 0; l < areaDialogues[i].dialogues[j].sentences.Count; l++)
                {
                    areaDialogues[i].dialogues[j].sentences[l].Update();
                }
            }
        }
    }
}

成果如下圖所示

新增所有對話結束時的事件

目前是持續顯示提示訊息,如果玩家按了對話建以後,沒有反應的話,玩家就知道沒有對話了。

還有另一種方式是,根據DialoguesManager那裡判斷還有沒有可以進行的對話,有的話才顯示提示訊息,沒有的話則收起提示訊息。

系列文章

《Carto》實作對話系統01-前言
《Carto》實作對話系統02-建立遊戲所需物件
《Carto》實作對話系統03-建構程式
《Carto》實作對話系統04-把腳本加到對應的物件上
《Carto》實作對話系統05-當TextMeshPro沒有辦法顯示中文
《Carto》實作對話系統06-改善對話系統的操作介面
《Carto》實作對話系統08-當條件滿足時,不用按按鍵,可以直接觸發的對話

Colin TPL
Colin TPL

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *