制作一個(gè)簡(jiǎn)單的NPC對(duì)話系統(tǒng)
前言
? 最近在自己寫一個(gè)比較小的項(xiàng)目,雖然自己是一個(gè)策劃,但是程序方面我覺得也是很有必要學(xué)一學(xué)的。
? 經(jīng)過了接近一年的學(xué)習(xí),也終于是可以獨(dú)自寫一些小的系統(tǒng)了。
? 這次自己寫了一個(gè)比較簡(jiǎn)單的NPC對(duì)話系統(tǒng),供大家參考。
效果展示
進(jìn)入對(duì)話區(qū)域
開始對(duì)話
Inspector面板可調(diào)選項(xiàng)
準(zhǔn)備工作
? 為了完成對(duì)話系統(tǒng),首先需要一個(gè)NPC以及一個(gè)UI界面。
? 這里為了節(jié)省篇幅,就直接上圖了。
NPC
UI
其中Panel用來控制整體顯示
NPCWord為文本
右側(cè)還有放頭像以及NPC名字的位置
代碼
完整代碼
這里就先上完整代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NPC_Talk : MonoBehaviour
{
//公共參數(shù)
[Header("NPC姓名")]
public string npcName;
[Header("是否是可對(duì)話NPC")]
public bool allowTalk;
[Header("是否循環(huán)對(duì)話")]
public bool isLoop;
[Header("對(duì)話文本")]
public TextAsset[] talkTxt;
[Header("對(duì)話提示")]
public GameObject talkSign;
//內(nèi)部參數(shù)
[HideInInspector] public bool canTalk;
private int txtOrder; //文本指針
private GameObject player;
private GameObject text;
private int textRow;
private bool isTalking;
void Start()
{
canTalk = false;
textRow = 0;
player = GameObject.Find("Player");
}
void Update()
{
ShowSign();
showText();
CleanData();
}
private void ShowSign() //生成頭頂標(biāo)識(shí)
{
if (canTalk)
{
this.talkSign.SetActive(true);
}
else
{
this.talkSign.SetActive(false);
}
}
private void OnMouseDown() //點(diǎn)擊NPC顯示對(duì)話UI 并重置Txt文本讀取位置
{
if (canTalk)
{
isTalking = true;
GameObject canvas = GameObject.Find("Canvas");
Transform panel = canvas.transform.Find("NPCTalk_Panel");
panel.gameObject.SetActive(true);
textRow = 0;
}
}
private void showText() //鏈接txt文本與UI界面Text 并且逐行讀取顯示 讀取完畢隱藏UI
{
GameObject canvas = GameObject.Find("Canvas");
Transform panel = canvas.transform.Find("NPCTalk_Panel");
Text text = canvas.transform.Find("NPCTalk_Panel/NPCWord").gameObject.GetComponent<Text>();
string[] str = talkTxt[txtOrder].text.Split('\n');
if (Input.GetMouseButtonDown(0) && isTalking)
{
canvas.transform.Find("NPCTalk_Panel/NPCName").gameObject.GetComponent<Text>().text = npcName;
canvas.transform.Find("NPCTalk_Panel/Sprite").gameObject.GetComponent<Image>().sprite = this.GetComponent<SpriteRenderer>().sprite;
text.text = str[textRow];
textRow = textRow + 1;
}
if (textRow == str.Length)
{
panel.gameObject.SetActive(false);
textRow = 0;
txtOrder = txtOrder + 1; //第一個(gè)文本播完后 加載第二個(gè)文本
if(txtOrder == talkTxt.Length)
{
txtOrder = 0; //全部文本播完后 重置文本指針
if(!isLoop) //如果為不循環(huán)播放 則變?yōu)椴豢蒚alk的NPC
{
allowTalk = false;
canTalk = false;
}
}
isTalking = false;
}
}
private void CleanData() //走出對(duì)話區(qū)域重置當(dāng)前文本
{
if (!canTalk && isTalking)
{
GameObject canvas = GameObject.Find("Canvas");
Transform panel = canvas.transform.Find("NPCTalk_Panel");
textRow = 0;
isTalking = false;
panel.gameObject.SetActive(false);
}
}
}
詳細(xì)邏輯
開啟對(duì)話
private void OnMouseDown() //點(diǎn)擊NPC顯示對(duì)話UI 并重置Txt文本讀取位置
{
if (canTalk)
{
isTalking = true;
GameObject canvas = GameObject.Find("Canvas");
Transform panel = canvas.transform.Find("NPCTalk_Panel");
panel.gameObject.SetActive(true);
textRow = 0;
}
}
? 開啟對(duì)話我使用的是點(diǎn)擊NPC的碰撞體的方式。
? 并且通過canTalk判斷是否能夠?qū)υ?/p>
? canTalk是通過玩家是否在對(duì)話的區(qū)域內(nèi)(TalkArea)進(jìn)行判斷
此處在TalkArea上掛載了一個(gè)TalkCheck腳本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TalkCheck : MonoBehaviour
{
public GameObject npc;
private void OnTriggerEnter2D(Collider2D other)
{
if (npc.GetComponent<NPC_Talk>().allowTalk)
{
npc.GetComponent<NPC_Talk>().canTalk = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
npc.GetComponent<NPC_Talk>().canTalk = false;
}
}
我一開始在TalkCheck.cs腳本中使用的是事件的方式,進(jìn)出碰撞體都會(huì)發(fā)送一個(gè)事件給NPC_Talk.cs腳本,但是后期實(shí)踐的時(shí)候如果存在兩個(gè)以上的NPC,那使用事件就會(huì)造成錯(cuò)誤。所以還是用了比較繁瑣的辦法,將canTalk放到TalkCheck.cs中進(jìn)行判斷。
這里我還加了一個(gè)allowTalk來給玩家自行控制是否開啟NPC的對(duì)話功能,如果玩家關(guān)閉allowTalk,那canTalk會(huì)一直處于關(guān)閉狀態(tài)。
顯示對(duì)話
接著上文所述,玩家點(diǎn)擊NPC后開啟對(duì)話,為了顯示文本,所以下面寫了ShowText()函數(shù)
private void showText() //鏈接txt文本與UI界面Text 并且逐行讀取顯示 讀取完畢隱藏UI
{
GameObject canvas = GameObject.Find("Canvas");
Transform panel = canvas.transform.Find("NPCTalk_Panel");
Text text = canvas.transform.Find("NPCTalk_Panel/NPCWord").gameObject.GetComponent<Text>();
string[] str = talkTxt[txtOrder].text.Split('\n');
if (Input.GetMouseButtonDown(0) && isTalking)
{
canvas.transform.Find("NPCTalk_Panel/NPCName").gameObject.GetComponent<Text>().text = npcName;
canvas.transform.Find("NPCTalk_Panel/Sprite").gameObject.GetComponent<Image>().sprite = this.GetComponent<SpriteRenderer>().sprite;
text.text = str[textRow];
textRow = textRow + 1;
}
if (textRow == str.Length)
{
panel.gameObject.SetActive(false);
textRow = 0;
txtOrder = txtOrder + 1; //第一個(gè)文本播完后 加載第二個(gè)文本
if(txtOrder == talkTxt.Length)
{
txtOrder = 0; //全部文本播完后 重置文本指針
if(!isLoop) //如果為不循環(huán)播放 則變?yōu)椴豢蒚alk的NPC
{
allowTalk = false;
canTalk = false;
}
}
isTalking = false;
}
}
首先將獲取的第一個(gè)文本按行拆分,并且存入str[]中備用。
string[] str = talkTxt[txtOrder].text.Split('\n');
當(dāng)開始對(duì)話后,點(diǎn)擊左鍵即可按行顯示文本內(nèi)容,其中isTalking為之前定義的一個(gè)bool變量,其狀態(tài)代表玩家是否在對(duì)話中。
if (Input.GetMouseButtonDown(0) && isTalking)
{
canvas.transform.Find("NPCTalk_Panel/NPCName").gameObject.GetComponent<Text>().text = npcName;
canvas.transform.Find("NPCTalk_Panel/Sprite").gameObject.GetComponent<Image>().sprite = this.GetComponent<SpriteRenderer>().sprite;
text.text = str[textRow];
textRow = textRow + 1;
}
當(dāng)一個(gè)文本讀完之后,txtOrder會(huì)加一,也就是讀取的txt文件從talkTxt[i]變成了talkTxt[i+1]。也就是開始讀取下一個(gè)文件
當(dāng)txtOrder和txt文件數(shù)相等時(shí),也就是讀完了所有的文件,將會(huì)重置至第一個(gè)文件。
其中isLoop函數(shù)代表該NPC是否可以循環(huán)播放文本,玩家可以手動(dòng)設(shè)置。
如果不能循環(huán)播放,則在播放完后控制allowTalk,使NPC無法對(duì)話。
if (textRow == str.Length)
{
panel.gameObject.SetActive(false);
textRow = 0;
txtOrder = txtOrder + 1; //第一個(gè)文本播完后 加載第二個(gè)文本
if(txtOrder == talkTxt.Length)
{
txtOrder = 0; //全部文本播完后 重置文本指針
if(!isLoop) //如果為不循環(huán)播放 則變?yōu)椴豢蒚alk的NPC
{
allowTalk = false;
canTalk = false;
}
}
isTalking = false;
}
頭頂標(biāo)識(shí)
頭頂標(biāo)識(shí)為一個(gè)表現(xiàn)向的元素,當(dāng)玩家進(jìn)入TalkArea時(shí)即開啟。
供大家參考。
private void ShowSign() //生成頭頂標(biāo)識(shí)
{
if (canTalk)
{
this.talkSign.SetActive(true);
}
else
{
this.talkSign.SetActive(false);
}
}
頭頂標(biāo)識(shí)
頭頂標(biāo)識(shí)為一個(gè)表現(xiàn)向的元素,當(dāng)玩家進(jìn)入TalkArea時(shí)即開啟。
供大家參考。
[外鏈圖片轉(zhuǎn)存中…(img-6kUZhC1w-1637120093347)]文章來源:http://www.zghlxwxcb.cn/news/detail-403268.html
private void ShowSign() //生成頭頂標(biāo)識(shí)
{
if (canTalk)
{
this.talkSign.SetActive(true);
}
else
{
this.talkSign.SetActive(false);
}
}
后話
由于本人不是程序員,本職工作是一個(gè)小策劃,所以希望大家不要介意我混亂的代碼邏輯。
其中還有很多優(yōu)化的地方,比如現(xiàn)在的txt文件需要在最后一行再加一個(gè)回車。如果大家有什么好的改進(jìn)方案,歡迎交流。文章來源地址http://www.zghlxwxcb.cn/news/detail-403268.html
到了這里,關(guān)于Unity3d 制作一個(gè)簡(jiǎn)單的NPC對(duì)話系統(tǒng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!