首页 > 其他分享 >【Unity项目实践】对话系统

【Unity项目实践】对话系统

时间:2022-10-22 10:35:32浏览次数:82  
标签:static void 实践 Unity 对话 事件 Action using public

这篇文章总结一下简易对话系统的制作方式,教程来自M_Studio的迷失岛制作教程。

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;
}

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


Step 2 :写好事件触发逻辑

事件的发布:

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

事件订阅:

在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;
}
}

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);
}
}
}


主要知识点

委托 & 事件

这篇文章讲的太好啦:​​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);

}
}

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

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!";
}
}

Event

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

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


Action

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

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

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

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

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

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


标签:static,void,实践,Unity,对话,事件,Action,using,public
From: https://blog.51cto.com/u_15639010/5785674

相关文章