我目前正在開發一款游戲,角色可以在其中進行對話。有幾個對話選項,但對話樹非常簡單。一個關鍵方面是玩家能夠在句子中間打斷對方并改變談話的程序。
作為序言,我已經從 youtube 上學到了我所知道的一切,而且我一直在使用越來越復雜的 if 陳述句,所以我在這里嘗試一些新的東西。
所以我第一次嘗試使用我所知道的:呼叫和 if 陳述句。
public class SubtitleSystem : MonoBehaviour
{
public float subtitleTimeBuffer;
public string[] dialogue;
public string[] specialDialogue;
public float[] subTiming;
public float[] specialSubTiming;
public AudioClip[] diaClips;
public Text sub;
AudioSource player;
int subNum;
int specialSubNum;
// Start is called before the first frame update
void Start()
{
player = gameObject.GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
if (sub.enabled == false) {
SubSystem();
}
}
}
void SubSystem()
{
if(subNum < dialogue.Length)
{
if (subNum != 1)
{
player.clip = diaClips[subNum];
player.Play();
sub.text = dialogue[subNum];
sub.enabled = true;
Invoke("DisableText", subTiming[subNum] subtitleTimeBuffer);
subNum ;
}
else if (subNum == 1)
{
player.clip = diaClips[subNum];
player.Play();
sub.text = specialDialogue[specialSubNum];
sub.enabled = true;
Invoke("DisableText", subTiming[subNum] subtitleTimeBuffer);
Invoke("SpecialSub", specialSubTiming[specialSubNum]);
}
} else
{
Debug.Log("No more dialogue");
}
}
void DisableText()
{
sub.enabled = false;
}
void SpecialSub()
{
sub.text = dialogue[subNum];
subNum ;
}
這是功能性的,雖然笨重且作業量很大,但對于必須為每一行對話找到單獨時間并在字幕行太長時手動拆分的人來說,這是一項非常重要的作業。另一個大問題是不可能完全中斷對話,因為 Invoke 被呼叫并且不管我按下什么都會運行。也許我可以添加某種布爾條件來防止這種情況發生,但是我正在從事的專案將有幾百行對話,所以我需要想出一些我不必輸入每一行的東西檢查員并找到手動計時。
這就是對我來說變得模糊的地方,因為我不熟悉很多這些方法。
Invoke 問題的一個明顯解決方案是使用協程。這些可以被中斷,我什至可以檢查回圈內的輸入以讓玩家中斷。
IEnumerator SubtitleRoutine()
{
while (DialogueIsPlaying)
{
//Display Subtitles
if(Input.GetButton("Interrupt button"){
//interrupt
}
yield return null;
}
//Wait for next piece of dialogue
}
類似的事情就是我想象的。
下一個問題是將對話與某種系統聯系起來,這樣我就可以提取正確的音頻并顯示正確的字幕。在我的第一次嘗試中,這很簡單,因為我創建的四段測驗音頻都是短句,但如果角色說話時間更長,手動中斷對話會很乏味。
或者,我考慮將音頻檔案分解為“字幕長度”,這樣每個音頻檔案都有一個與其直接關聯的字幕字串,但如果對話需要更改,這似乎效率低下且麻煩。
So I thought if I could somehow create a class, that contained all the information needed, then my coroutine could pull in the correct dialogue using it's id (perhaps an integer) and plug in all the information from the object into my coroutine. So something like this:
public class dialogue
{
//Int ID number
//Audiofile
//Who is speaking
//Length
//Subtitle String 1
//Subtitle String 2
//Subtitle String 3
// etc
}
IEnumerator SubtitleRoutine(dialogue)
{
while (DialogueIsPlaying)
{
//Display Subtitles - divide number of subtitle string by Length and display each for result.
if (Input.GetButton("Interrupt button"){
//interrupt audio and subtitles - stop the coroutine
//set correct dialogue Int ID for next correct piece of dialogue and start coroutine with new dialogue playing.
}
yield return null;
}
//Wait for next piece of dialogue
}
Though this is all outside of what I know, from what I've been able to read and understand, this seems like it might work.
So my question is:
- Is this approach going to work?
- If so, where should I look for ressources and help to teach me how?
- If not, what methods should I look at instead?
Thank you so much for your help!
uj5u.com熱心網友回復:
所以我的理解是你想制作一個可中斷的對話系統。您選擇了協程方法,這是一個不錯的選擇,但您并沒有充分發揮它的潛力。當你使用時,StartCoroutine(IEnumerator _enumerator);你會得到一個協程類。如果您存盤它,您可以在以后使用StopCoroutine(Coroutine _routine);它來停止它。因此,您不必使用 while 回圈或 if 陳述句來檢查中斷。
希望這會幫助你。如果沒有,我會發送一些代碼。
uj5u.com熱心網友回復:
在接受編碼導師的一些幫助后,我發現了一個系統,該系統使用自定義類作為資料型別和協程以正確的時間顯示字幕。變數的名稱是丹麥語,但代碼在 Unity 中運行沒有問題。
using System.Collections.Generic;
using UnityEngine;
public class Undertekster
{
public int id;
public AudioClip audioFile;
public float length;
public string[] subtitles;
public bool isMonk;
public SubSystem subsys;
public Undertekster(int id, int dialogClipNummer, float length, string[] subtitles, bool isMonk, SubSystem subsys)
{
this.subsys = subsys;
this.id = id;
this.audioFile = subsys.dialogClip[dialogClipNummer];
this.length = length;
this.subtitles = subtitles;
this.isMonk = isMonk;
}
}
請注意,在構造類以使用 Monobehavior 時使用另一個腳本。這樣我就可以使用在檢查器中創建的陣列將正確的音頻檔案分配給每一行對話。正確的方法可能是以某種方式查找檔案,但這超出了我的范圍。
其次是字幕系統。為了演示,您在游戲中按空格鍵開始對話并按 F 打斷。“字幕”是控制臺中的 Debug.log,但您可以輕松地將它們系結到 UI 中的 Text 物件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class SubSystem : MonoBehaviour
{
[Header("DialogClip")]
public AudioClip[] dialogClip;
[Header("Indstillinger")]
public float subtitleBuffer;
public AudioSource munk;
public AudioSource salamander;
List<Undertekster> alleUndertekster = new List<Undertekster>();
int currentDialogueNumber;
Undertekster currentDia;
float timeDivided;
void Start()
{
currentDialogueNumber = 1;
LoadDialog();
}
public void LoadDialog()
{
alleUndertekster.Add(new Undertekster(1, 0, dialogClip[0].length, new string[] { "Kan du huske mig?" }, true, this));
alleUndertekster.Add(new Undertekster(2, 1, dialogClip[1].length, new string[] { "?h...", "Lidt..." }, false, this));
alleUndertekster.Add(new Undertekster(3, 2, dialogClip[2].length, new string[] { "Jeg er din nabo din idiot!" }, true, this));
alleUndertekster.Add(new Undertekster(4, 3, dialogClip[3].length, new string[] { "Shit!" }, false, this));
}
IEnumerator PlayNextDialogue()
{
int count = 0;
while (munk.isPlaying || salamander.isPlaying)
{
ShowSubtitle(count);
yield return new WaitForSeconds(timeDivided subtitleBuffer);
count ;
yield return null;
}
//yield return new WaitForSeconds(subtitleBuffer);
currentDialogueNumber ;
Debug.Log("Coroutine is stopped and the current dialogue num is " currentDialogueNumber);
StopCoroutine(PlayNextDialogue());
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
InterruptDialogue();
}
if (Input.GetKeyDown(KeyCode.Space))
{
StartDialogue();
}
}
public void StartDialogue()
{
currentDia = alleUndertekster.Find(x => x.id == currentDialogueNumber);
timeDivided = currentDia.length / currentDia.subtitles.Length;
if (currentDia.isMonk)
{
munk.clip = currentDia.audioFile;
munk.Play();
} else if (!currentDia.isMonk)
{
salamander.clip = currentDia.audioFile;
salamander.Play();
}
StartCoroutine(PlayNextDialogue());
}
public void InterruptDialogue() {
StopCoroutine(PlayNextDialogue());
munk.Stop();
salamander.Stop();
currentDialogueNumber ;
StartDialogue();
}
public void ShowSubtitle(int i)
{
if(i <= currentDia.subtitles.Length - 1)
{
Debug.Log(currentDia.subtitles[i]);
} else
{
return;
}
}
}
I chose to put all the dialogue classes into a list so it was easily searchable for the id-numbers. It might have been better to use a Dictionary, but this worked for me and that was good it enough for this project.
With this system, my manuscript writer can put in every line of dialogue with its associated audioclip in the LoadDialog() function and determine who is speaking and in how many pieces the subtitles should be broken into to fit on the screen. They are then displayed one after the other while the audio is playing.
This probably isn't the best solution in the world, but I hope it works for whoever might need it - plus I learned a ton.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/347447.html
上一篇:2D游戲中的健康系統
下一篇:檢查播放器不在場景中(不作業)
