采用MVC方式,将数据、UI和逻辑分开。
登录后复制
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类型的数据,一个用来播放没有找到道具时候的对话,另一个用来播放找到道具之后的对话。
事件的发布:
登录后复制
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.
在对话控制器中进行事件触发
登录后复制
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使用起来很安全,只能订阅和取消订阅自己类里面的方法,不可以注册别的类的方法,也不可以更改别的类中订阅的方法。
以下这两种方式的效果是一样的。
登录后复制
// 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.
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删