迷失岛2(六)场景中人物对话实现

教程来自M_Studio 迷失岛2游戏框架开发

整体思路

实现对话的逻辑其实非常简单,我们还是可以使用MVC的模式进行设计。其实在设计背包时,我们就已经使用了这种设计,简单应用来说,就是将数据模型、逻辑控制、UI表现分开,将需要使用和传递的数据在数据模型中单独配置,然后通过逻辑控制,将数据模型与UI表现“连通”,使得数据的更新能够显示在UI上。

  • 对于数据模型DialougeDate_SO,我们只需要简单地使用一个List将需要使用到的对话存下来即可
  • 对于逻辑控制DialogueController,主要实现两个功能:第一,从数据模型DialogueData_SO中获取具体的对话数据并暂存;第二,对外暴露展示对话内容的方法,让相应角色触发对话时能够调用相应的触发对话的方法;第三,将暂存的对话数据转发给UI进行显示。逻辑控制和UI更新的通信毫无疑问通过事件来实现,从暂存区取出对话台词,并通知UI做对应更新,角色触发互动进行对话与逻辑控制中的对话实现也可以通过事件通信,但是这里直接让角色脚本获取DialogueController脚本简化操作(因为该游戏中所有可互动角色都有对话,这样设计也合理)
  • 对于UI表现,自然是订阅一个事件,当对话台词更新时,接受来自DialoggueController的文本,并做相应显示。同时,对于视频中出现的切换场景时对话框不会消失重置的问题,可以直接让对话UI也订阅此前实现的AfterSceneLoadedEvent事件,每次新场景加载后时直接取消激活对话框,这里采用了这种简单粗暴的方法()

最后,不要忘记为角色也添加一个脚本,继承Interactive基类,实现具体的触发互动的方法

代码实现

EventHandler补全

//更新对话UI的事件
public static event Action<string> UpdateDialogueEvent;

/// <summary>
/// 呼叫更新对话UI的事件
/// </summary>
/// <param name="dialogue"></param>
public static void CallUpdateDialogueEvent(string dialogue)
{
   	UpdateDialogueEvent?.Invoke(dialogue);
}

DialogueDate_SO实现

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "DialogueData_SO", menuName = "Dialogue/DialogueData_SO")]
public class DialogueData_SO : ScriptableObject
{
    public List<string> dialogueDateList = new List<string>();
}

DialogueController实现

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

public class DialogueController : MonoBehaviour
{
    public DialogueData_SO dialogueEmpty;
    
    public DialogueData_SO dialogueFinish;

    private Stack<string> _dialogueEmptyStack;

    private Stack<string> _dialogueFinishStack;
    
    //是否正在对话
    private bool _isTalking;

    private void Awake()
    {
        FillDialogueStack();
    }

    /// <summary>
    /// 填充对话栈的内容
    /// </summary>
    private void FillDialogueStack()
    {
        _dialogueEmptyStack = new Stack<string>();
        _dialogueFinishStack = new Stack<string>();

        for (int i = dialogueEmpty.dialogueDateList.Count - 1; i >= 0; i--)
        {
            _dialogueEmptyStack.Push(dialogueEmpty.dialogueDateList[i]);
        }

        for (int i = dialogueFinish.dialogueDateList.Count - 1; i >= 0; i--)
        {
            _dialogueFinishStack.Push(dialogueFinish.dialogueDateList[i]);
        }
    }

    public void ShowDialogueEmpty()
    {
        if (!_isTalking)
        {
            StartCoroutine(DialogueCoroutine(_dialogueEmptyStack));
        }
    }

    public void ShowDialogueFinish()
    {
        if (!_isTalking)
        {
            StartCoroutine(DialogueCoroutine(_dialogueFinishStack));
        }
    }

    /// <summary>
    /// 从对话栈中取出对话内容
    /// </summary>
    /// <param name="dialogueStack"></param>
    /// <returns></returns>
    private IEnumerator DialogueCoroutine(Stack<string> dialogueStack)
    {
        //开始对话
        _isTalking = true;

        //当前对话栈不空时,取出栈顶的对话
        if (dialogueStack.TryPop(out var result))
        {
            //呼叫对话UI更新
            EventHandler.CallUpdateDialogueEvent(result);

            yield return null;
            
            //对话结束
            _isTalking = false;
        }
        else
        {
            //当对话栈为空,重新填充对话栈
            FillDialogueStack();
            
            EventHandler.CallUpdateDialogueEvent(string.Empty);
            
            //对话结束
            _isTalking = false;
        }
    }
}

DialogueUI实现

这里还设计了一个打字机的效果,实现思路也很简单,使用协程,将需要展示的对话文本逐个字符取出并一一展示在对话框中

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class DialogueUI : MonoBehaviour
{
    public GameObject dialoguePanel;
    
    public Text dialogueText;

    private readonly float typingDelay = 0.05f;
    
    private void OnEnable()
    {
        EventHandler.UpdateDialogueEvent += OnUpdateDialogueUIEvent;
        EventHandler.AfterSceneLoadedEvent += OnAfterSceneLoadedEvent;
    }

    private void OnDisable()
    {
        EventHandler.UpdateDialogueEvent -= OnUpdateDialogueUIEvent;    
        EventHandler.AfterSceneLoadedEvent -= OnAfterSceneLoadedEvent;
    }

    /// <summary>
    /// 更新对话框及对话文本
    /// </summary>
    /// <param name="text"></param>
    private void OnUpdateDialogueUIEvent(string text)
    {
        if (text == string.Empty)  //无文本展示时,关闭对话框
        {
            dialoguePanel.SetActive(false);
        }
        else  //有文本展示时,打开对话框并设置对话文本
        {
            dialoguePanel.SetActive(true);
            
            dialogueText.text = string.Empty;
            
            StartCoroutine(DoTypingEffect(text));
        }
    }
    
    /// <summary>
    /// 实现文本的打字机效果
    /// </summary>
    /// <param name="text"></param>
    /// <returns></returns>
    private IEnumerator DoTypingEffect(string text)
    {
        foreach (char letter in text)
        {
            dialogueText.text += letter;
            yield return new WaitForSeconds(typingDelay);
        }
    }
    
    private void OnAfterSceneLoadedEvent()
    {
        //只需要取消激活对话框即可,因为每次加载新场景都会重新填充对话栈(对话栈没有做持久化处理)
        dialoguePanel.SetActive(false);
    }
}

CharacterH2实现

using UnityEngine;

[RequireComponent(typeof(DialogueController))]
public class CharacterH2 : Interactive
{
    private DialogueController _dialogueController;

    private void Awake()
    {
        _dialogueController = GetComponent<DialogueController>();
    }

    protected override void OnInteractiveAction()
    {
        //进行互动,播放互动完成的对话
        _dialogueController.ShowDialogueFinish();
    }

    public override void ClickWithoutItem()
    {
        if (isDone)  //当互动已经完成,则只展示互动完成的对话
        {
            _dialogueController.ShowDialogueFinish();
        }
        else  //否则循环播放互动互动未完成的对话
        {
            _dialogueController.ShowDialogueEmpty();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值