【Unity项目实践】对话系统

Overview

采用MVC方式,将数据、UI和逻辑分开。

  • 数据使用ScriptableObject进行存储和读取
  • 采用事件的方式进行触发
  • 逻辑中使用堆栈读取ScriptableObject中的数据并进行展示


系统制作方式

Step 1: 首先创建ScriptableObject

登录后复制

using System.Collections;using System.Collections.Generic;using UnityEngine;[CreateAssetMenu(fileName = "Dialogue_SO", menuName = "Dialogue/Dialogue_SO", order =0)]public class Dialogue_SO : ScriptableObject {    public List<string> dialogueList;}1.2.3.4.5.6.7.8.9.10.

然后回到编辑器中创建一个两个SO类型的数据,一个用来播放没有找到道具时候的对话,另一个用来播放找到道具之后的对话。



Step 2 :写好事件触发逻辑

事件的发布:

登录后复制

 public static event Action<string> ShowDialogueEvent;//相当于定义委托并且声明变量    public static void CallShowDialogueEvent(string dialogue)//    {        ShowDialogueEvent?.Invoke(dialogue);//触发的时候把参数传进去    }1.2.3.4.5.

事件订阅:

在UI部分进行订阅,并且写好事件触发之后的事件处理器

登录后复制

using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class DialogueUI : MonoBehaviour{    public GameObject panel;    public Text dialogueText;    private void OnEnable()    {        EventHandler.ShowDialogueEvent += ShowDialogue;//事件订阅    }    private void OnDisable()    {        EventHandler.ShowDialogueEvent -= ShowDialogue;//取消订阅    }    private void ShowDialogue(string dialogue)//事件处理器    {        if (dialogue != string.Empty)            panel.SetActive(true);        else            panel.SetActive(false);        dialogueText.text = dialogue;    }}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.

Step 3:事件调用

在对话控制器中进行事件触发

登录后复制

using System;using System.Collections;using System.Collections.Generic;using UnityEngine;public class DialogueController : MonoBehaviour{    public Dialogue_SO dialogueEmpty;//用SO数据进行赋值    public Dialogue_SO dialogueFinish;    private Stack<string> dialogueEmptyStack;    private Stack<string> dialogueFinishStack;//堆栈,先进后出    private bool isTalking;    private void Awake()    {        FillDialogueStack();    }    private void FillDialogueStack()    {        dialogueEmptyStack = new Stack<string>();        dialogueFinishStack = new Stack<string>();        for(int i=dialogueEmpty.dialogueList.Count -1; i>-1; i--)        {            dialogueEmptyStack.Push(dialogueEmpty.dialogueList[i]);//倒叙        }        for(int i=dialogueFinish.dialogueList.Count -1; i>-1; i--)        {            dialogueFinishStack.Push(dialogueFinish.dialogueList[i]);        }    }    public void ShowDialogueEmpty()    {        if (!isTalking)            StartCoroutine(DialogueRoutine(dialogueEmptyStack));    }    public void ShowDialogueFinish()    {        if (!isTalking)            StartCoroutine(DialogueRoutine(dialogueFinishStack));    }    private IEnumerator DialogueRoutine(Stack<string> data)    {        isTalking = true;        if (data.TryPop(out string result))        {            EventHandler.CallShowDialogueEvent(result);//事件触发            yield return null;            isTalking = false;            EventHandler.CallGameStateChangeEvent(GameState.Pause);        }        else        {            EventHandler.CallShowDialogueEvent(string.Empty);//事件触发            FillDialogueStack();            isTalking = false;            EventHandler.CallGameStateChangeEvent(GameState.GamePlay);        }    }}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.


主要知识点

委托 & 事件

这篇文章讲的太好啦:​ ​https://gamedevbeginner.com/events-and-delegates-in-unity/#events​

在事件处理类中声明一个事件发布器,建立一个ShowDialogueEvent的委托事件,返回值是一个Action。


概述特征
delegate指定了返回类型和参数列表,使得函数可以作为变量进行传递
Action返回值为空的委托返回值为空
Event一种特殊的多播委托事件对外界隐藏了委托实例的大部分功能,仅展示了添加/移除事件处理器的功能只能在类里面进行调用,不可以跨类,相对安全
Func泛型委托尖括号中写参数类型和返回值类型

委托的用法

  • 定义委托
  • 声明委托的变量,并将变量初始化为与委托具有相同返回类型和参数列表的函数引用
  • 函数调用

案例:《C#入门经典》中的案例

登录后复制

class Program {  //Step 1:定义委托  delegate double ProcessDelegate(double param1, double param2);    //定义要调用的方法  static double Multiply(double param1, double param2) => param1 * param2;  static double Divide(double param1, double param2) => param1/param2;    static void Main(string[] args)  {    //Step 2:声明委托的变量    ProcessDelegate process;        //Step 3:函数调用    if(input == "M")      process = new ProcessDelegate(Multiply);    else if (inpiut == "D")      process = new ProcessDelegate(Divide);    }}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.

下面展示委托的四种挂载方式

登录后复制

public Form1(){    //这里有四种事件的挂载方式    this.button3.Click += this.ButtonClicked;//这两种挂载方式都是欧克的    this.button3.Click += new EventHandler(this.ButtonClicked);//这里后面不需要加括号,加括号表示想调用这个方法,这里只是想把方法名作为变量传进来    this.button3.Click += delegate(object sender, EventArgs e)//这是一个匿名函数,已经过时了    {        this.textBox1.Text = "haha";    }    this.button3.Click += (object sender, EventArgs e) => //这种方式是lamda表达式,参数的数据类型也可以不写    {        this.textBox1.Text = "Hoho";    }}private void ButtonClicked(object sender, EventArgs e){        if(sender == this.button1)    {        this.textBox1.Text = "Hello";        }    if (sender == this.button2)    {        this.textBox1.Text = "World!";    }}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.

Event

作为一种特殊的多播委托,很适合用来做事件触发的逻辑。

event使用起来很安全,只能订阅和取消订阅自己类里面的方法,不可以注册别的类的方法,也不可以更改别的类中订阅的方法。


Action

以下这两种方式的效果是一样的。

登录后复制

// this...public static event Action OnGameOver;// is basically the same as this...public delegate void OnGameOver();//定义委托public static event OnGameOver onGameOver;//声明委托的变量1.2.3.4.5.

Action的返回值为空,但是可以有参数和参数列表。

登录后复制

public static event Action<string> OnGameOver;public static event Action<float, bool> OnPlayerHurt;1.2.

以下是一个Action的案例,在本次的项目中也是这么用的。在事件系统中,注册的事件重点在于函数的执行,所以比较适合用Action。

登录后复制

public static event Action<string> OnGameOver;public void TakeDamage(float damage){    health -= damage;    if(health < 0)    {        OnGameOver?.Invoke("The game is over");    }}1.2.3.4.5.6.7.8.9.



免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空