通常游戲中制作一段Timeline需要插入對(duì)話框,以便展示整個(gè)劇情的完整性,本篇文章就講述一下如何在Timeline中添加自定義對(duì)話框節(jié)點(diǎn)。
Unity中的Timeline允許Playable Track中添加自定義功能。
實(shí)現(xiàn)自定義功能需要分別實(shí)現(xiàn)PlayableAsset和PlayableBehaviour。
其中在PlayableBehaviour中用于實(shí)現(xiàn)自定義功能的行為;PlayableAsset用于編輯和保存節(jié)點(diǎn)。
兩個(gè)腳本分別命名為DIalogPlayableBehaviour和DialogPlayableAsset。
1、首先是DialogPlayableBehaviour腳本,該腳本需要繼承自PlayableBehaviour。
(1)在該腳本中覆寫OnPlayableCreate()接口,當(dāng)Timeline創(chuàng)建時(shí)會(huì)調(diào)用該接口并傳入對(duì)應(yīng)的Playable資源,我們可以通過Playable對(duì)象獲取Playable Director并保存,以便操控Playable的暫停和重新播放。
(2)覆寫OnBehaviourPlay()接口
當(dāng)對(duì)話剪輯播放時(shí),會(huì)調(diào)用該接口,在該接口中調(diào)用我們自己的對(duì)話管理邏輯,對(duì)話管理邏輯中包含暫停和重啟Timeline,以及調(diào)用對(duì)話框功能,這里我們編寫了一個(gè)DialogManager腳本實(shí)現(xiàn)對(duì)應(yīng)邏輯,后邊展開講述。
在這里,我們用了一個(gè)變量clipPlayed記錄該剪輯是否播放過,應(yīng)為OnBehaviourPlay()接口在我們調(diào)用PayableDirector的Resume()接口會(huì)再次被調(diào)用,為了防止對(duì)話邏輯重復(fù)調(diào)用,加了這個(gè)變量保證只調(diào)用一次對(duì)應(yīng)邏輯。同時(shí)我們判斷FrameData.weight是否大于零,保證當(dāng)前播放的是該剪輯。
(2)添加腳本DialogPlayableAsset,繼承自Unity.Playables的PlayableAsset。
在該腳本中定義兩個(gè)變量dialogGroupId和DialogManager,分別為對(duì)話的ID和對(duì)話管理腳本,對(duì)外暴露,可以在編輯器中添加DialogPlayableAsset剪輯時(shí)自定義參數(shù)。
另外覆寫CreatePlayable接口。調(diào)用ScriptPlayable<DialogPlayableBehaviour>.Create(graph),將播放資源項(xiàng)注入到給定圖中。同時(shí)初始化DialogPlayableBehaviour的參數(shù)。
實(shí)現(xiàn)代碼如下:
using UnityEngine;
using UnityEngine.Playables;
[System.Serializable]
public class DialogPlayableAsset : PlayableAsset
{
? ? public int dialogGroupId;
? ? public ExposedReference<DialogManager> dialogManager;
? ? public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
? ? {
? ? ? ? var playable = ScriptPlayable<DialogPlayableBehaviour>.Create(graph);
? ? ? ? var dialogPlayableBehaviour = playable.GetBehaviour();
? ? ? ? dialogPlayableBehaviour.dialogManager = dialogManager.Resolve(graph.GetResolver());
? ? ? ? dialogPlayableBehaviour.dialogGroupId = dialogGroupId;
? ? ? ? return playable;
? ? }
}
(3)自定義對(duì)話管理類DialogManager,繼承自MonoBhehaviour。可以將該腳本掛在Timeline預(yù)制體上。
其中的PauseTimeline()和ResumeTimeline()接口分別用來(lái)暫停和重新播放Timeline。
SetDialog()接口中會(huì)調(diào)用具體的對(duì)話框邏輯,當(dāng)然我這兒的對(duì)話框邏輯是Lua端實(shí)現(xiàn)的,所以這樣寫,大家可以根據(jù)需要調(diào)用自己的實(shí)現(xiàn)接口。同時(shí)添加對(duì)話播放結(jié)束事件的監(jiān)聽,當(dāng)對(duì)話結(jié)束時(shí)調(diào)用ResumeTimeline()接口使Timeline繼續(xù)播放。
腳本實(shí)現(xiàn)如下:
using UnityEngine;
using UnityEngine.Playables;
using WCC.Lua;
using System;
public class DialogManager : MonoBehaviour
{
? ? int playDialogGroupId = 0;
? ? PlayableDirector activeDirector;
? ? public void SetDialog(int dialogGroupId)
? ? {
? ? ? ? if (Application.isPlaying)
? ? ? ? {
? ? ? ? ? ? playDialogGroupId = dialogGroupId;
? ? ? ? ? ? GlobalEventManager.AddListener(GlobalEventType.OnDialogGroupEnd, OnDialogGroupEnd);
? ? ? ? ? ? LuaEntrance.Instance.GetLuaEnv().DoString("local TimelineDialogMgr = require\"Logic.Timeline.TimelineDialogManager\"" +
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? $"TimelineDialogMgr:DoDialog({dialogGroupId})");
? ? ? ? }
? ? }
? ? public void PauseTimeline(PlayableDirector pd)
? ? {
? ? ? ? activeDirector = pd;
? ? ? ? activeDirector.Pause();
? ? }
? ? public void ResumeTimeline()
? ? {
? ? ? ? if(activeDirector)
? ? ? ? {
? ? ? ? ? ? activeDirector.Resume();
? ? ? ? }
? ? }
? ? private void OnDialogGroupEnd(object o1, object o2, object o3, object o4)
? ? {
? ? ? ? if (playDialogGroupId == Convert.ToInt32(o1))
? ? ? ? {
? ? ? ? ? ? GlobalEventManager.RemoveAllListener(GlobalEventType.OnDialogGroupEnd);
? ? ? ? ? ? ResumeTimeline();
? ? ? ? }
? ? }
}