游戏的基本操作已经完成,接下来我要求老郭教我怎么去完成简单的任务发布,这个任务发布只是简单的对话和按钮选择。
第一步:

在空间中创建一个UI画布,画布我创建得很简单,有一个任务发布的版块,两个选择是否接取任务的按钮。

请忽略上面的字。
做UI的时候如果我们在场景中去找它会显得非常巨大,大小我们也非常难把控,所以点击左上角的2D,在2D模式下,我们可以清晰地看出来这个画布在你的程序运行起来能够显示出来的状态。
1、画布的布局

还是这张图,Image是我整个任务接取面板的背景,taskText是一个Text的文本文件,用来显示任务内容,Button1和Button2分别是左边的按钮和右边的按钮,按钮上有回答的文本,所以在两个Button下面都分别带有Text文件,这个文件是系统自带的,如果没有可以自己加上去。
2、图片导入前需要做的操作

图片导入时需要更改Texture Type。改成适用于2D和UI的图片即可。
3、UI面板大小可以直接拖动
第二步:代码控制部分
1、整体代码预览
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class Question
{
public int id;
public string subject;
public string answer1, answer2;
public int answer1ToId;
public int answer2ToId;
public Question(int id,string subject,string answer1,string answer2,int answer1ToId,int answer2ToId)
{
this.id = id;
this.subject = subject;
this.answer1 = answer1;
this.answer2 = answer2;
this.answer1ToId = answer1ToId;
this.answer2ToId = answer2ToId;
}
}
public class button1OnClick : MonoBehaviour
{
private GameObject button1;
private GameObject button2;
private GameObject textObj;
public Dictionary<int, Question> Qdic;
public int nowNode;
private void Awake()
{
textObj = GameObject.Find("Canvas/Image/taskText");
button1 = GameObject.Find("Canvas/Image/Button1");
button2 = GameObject.Find("Canvas/Image/Button2");
}
// Start is called before the first frame update
void Start()
{
Question Q1 = new Question(1, "小姐姐,你好呀,听说你今天生日是吗?", "对呀,你怎么知道?", "臭嗨,才不是,辣鸡", 2, 3);
Question Q2 = new Question(2, "那我祝你……永远没有人跟你说生日快乐!", "去你妈的臭嗨!", "噢,好的", 4, 5);
Question Q3 = new Question(3, "没想到吧,根本没人记得!", "我早他妈知道了!傻逼!", "你去死吧!", 6, 7);
Qdic = new Dictionary<int, Question>();
Qdic.Add(Q1.id, Q1);
Qdic.Add(Q2.id, Q2);
Qdic.Add(Q3.id, Q3);
nowNode = 1;
Set();
button1.GetComponent<Button>().onClick.AddListener(Answer1);
button2.GetComponent<Button>().onClick.AddListener(Answer2);
}
//根据nowNode在Qdic中查找,获得当前的Question,然后将anwser1ToId赋值给nowNode,再Set一下
public void Answer1()
{
nowNode = Qdic[nowNode].answer1ToId;
Set();
}
//根据nowNode在Qdic中查找,获得当前的Question,然后将anwser2ToId赋值给nowNode,再Set一下
public void Answer2()
{
nowNode = Qdic[nowNode].answer2ToId;
Set();
}
// Update is called once per frame
void Update()
{
}
//该函数,用来将当前Question显示到UI上。
//根据nowNode在Qdic中查找,获得当前的Question,然后将这个Question中的几个string赋值给三个Text的text。
public void Set()
{
Question Q = Qdic[nowNode];
Text tt = textObj.GetComponent<Text>();
tt.text = Q.subject;
Text t1 = button1.GetComponentInChildren<Text>();
t1.text = Q.answer1;
Text t2 = button2.GetComponentInChildren<Text>();
t2.text = Q.answer2;
}
}
2、头文件增加库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
UnityEngine.EventSystems;
UnityEngine.UI;
需要增加事件系统和UI界面系统两个库
3、将需要的具体内容抽象化
首先思考一下我们要做的内容,内容有三个板块:发布内容1、回答1、回答2;选择回答1之后,界面会跳转到发布内容2、回答1、回答2;选择回答2之后,界面会跳转到发布内容3,回答1,回答2……依次下去。
可以看出来,这里一定会需要的是发布内容、两个回答,但是可以注意到,我们回答完问题1之后有一个分歧,如果选择回答1,就i会跳转到发布内容2的模块,如果选择回答2,就会跳转到发布内容3的模块,所以在这里,我们还需要两个隐藏变量,选择左回答后,我的发布内容会跳转到第几个,选择又回答后,我的发布内容又会跳转到第几个。画个图示:

所以在这里,每一个问题都包含有五个部分:内容、回答1、回答2、回答1指向内容、回答2指向内容。最后还需要加上本问题自身的编号——这是第几个内容
public Question(int id,string subject,string answer1,string answer2,int answer1ToId,int answer2ToId)
{
this.id = id;
this.subject = subject;
this.answer1 = answer1;
this.answer2 = answer2;
this.answer1ToId = answer1ToId;
this.answer2ToId = answer2ToId;
}
我们做了一个Question对象来作为每一个问题的模板,由于每个问题的6个内容各不同,我们这个对象需要在实例化的时候从外部传入6个参数。
4、关联UI和代码
private GameObject button1; //对应UI的button1
private GameObject button2; //对应UI的button2
private GameObject textObj; //对应UI的taskText
public Dictionary<int, Question> Qdic; //创建一个字典Qdic,字典的键是int类型,值是Question对象。
//这里很容易读懂,我们要把每一个问题依次存入字典中。键正好和我们当前id对应
public int nowNode; //每次点击按钮后都要把更新过的当前节点保存到nowNode中
private void Awake() //这个函数比state更早调用
{
//在挂脚本的画布下找到文本和按钮。这里的"Canvas/Image/"可以不写,在电脑无法识别路径的时候用
textObj = GameObject.Find("Canvas/Image/taskText");
button1 = GameObject.Find("Canvas/Image/Button1");
button2 = GameObject.Find("Canvas/Image/Button2");
}
在前面先创建三个GameObject的对象,方便一会儿将UI上对应的taskText、以及两个按钮对象存放进去。
这里需要注意的是,由于我们这个画布并不是一直在屏幕上出现。我之前有设置过,在碰到NPC并且要按下键盘上F键才会出现这个画布,所以在NPC的state中,画布设置的是False。
一开始我们查找文本和按钮的位置一直报错,后来发现原因是NPC的state会比画布的先运行,就导致了在或许按钮的时候,按钮被隐藏,系统就找不到隐藏的按钮就会报错,所以我们把关联文本和按钮的三行代码放到Awake()函数中,Awake函数会比state函数更早运行,所以会先关联,再隐藏。这里要注意顺序。
5、初始化数据
碎碎念:
类型分为引用类型和值类型,值类型就是简单的int、float、string……,引用类型比较复杂,例如之前我们创建的Question,系统中的GameObject等,引用类型在使用的时候需要new出来。但是之前我们使用的GameObject没有new出来,这是因为我们先在场景中创建了这个Object,它已经存在,所以不用new。而这里用到的字典,则需要new出来才能使用。
void Start()
{
//三个Question都需要传参,参数对应分别是,当前id, 内容,1回答,2回答,1回答对应跳转id,2回答对应跳转id
Question Q1 = new Question(1, "小姐姐,你好呀,听说你今天生日是吗?", "对呀,你怎么知道?", "臭嗨,才不是,辣鸡", 2, 3);
Question Q2 = new Question(2, "那我祝你……永远没有人跟你说生日快乐!", "去你妈的臭嗨!", "噢,好的", 4, 5);
Question Q3 = new Question(3, "没想到吧,根本没人记得!", "我早他妈知道了!傻逼!", "你去死吧!", 6, 7);
//new一个字典类
Qdic = new Dictionary<int, Question>();
Qdic.Add(Q1.id, Q1); //把每个Q依次加入到字典中
Qdic.Add(Q2.id, Q2);
Qdic.Add(Q3.id, Q3);
nowNode = 1; //初始id为1
Set(); //这里调用一个Set函数,下面会有介绍使用
//1按钮获取一个点击监听事件,这个点击监听事件会执行Answer1函数
button1.GetComponent<Button>().onClick.AddListener(Answer1);
button2.GetComponent<Button>().onClick.AddListener(Answer2);
}
6、Set函数设置
set函数就相当于一个显示函数,所有需要显示的内容都放在这里,在这里我们需要知道,我们要表示出来的主要是三个模块,内容,回答1,回答2。获取到面板中的内容,用代码更改成对应Question模块中的内容即可。
public void Set()
{
Question Q = Qdic[nowNode]; //把每一次查询到的问题整模块赋值给Q
Text tt = textObj.GetComponent<Text>(); //用text类文件来保存任务内容
tt.text = Q.subject; //把任务内容替换成Q模块的任务内容
Text t1 = button1.GetComponentInChildren<Text>(); //获取button1.text中文本内容
t1.text = Q.answer1;//把回答1替换成Q模块的回答1
Text t2 = button2.GetComponentInChildren<Text>();//获取button2.text中文本内容
t2.text = Q.answer2;//把回答2替换成Q模块的回答2
}
这里需要注意的是,button1中的text文件属于button1的子文件,所以要获取到这个text,需要用的函数是:GetComponentInChildren<>()
7、设置按键函数
//根据nowNode在Qdic中查找,获得当前的Question,然后将anwser1ToId赋值给nowNode,再Set一下
public void Answer1()
{
nowNode = Qdic[nowNode].answer1ToId;
Set();
}
//根据nowNode在Qdic中查找,获得当前的Question,然后将anwser2ToId赋值给nowNode,再Set一下
public void Answer2()
{
nowNode = Qdic[nowNode].answer2ToId;
Set();
}
如果按钮1按下,会跳转到Answer1中,这时只需要更改当前所在节点id,然后更新get()函数的显示就ok了
标注:在做这个部分的时候心情不好,所以言语比较偏激,小朋友请不要学习~