接上昨天的的继续,今天总结一下游戏界面的实现
1.在新场景创建UI - Canvas
2.创建一个Image作为背景图
3.创建三个大小合适的image,分别放在背景的偏左,中,偏右位置,用于显示立绘
3.在Canvas上创建Panel,用来显示文字,点击切换文本显示,以及放切换其他窗口的按钮
4.创建两个UI - Text,一个用来显示说话者的名字,另一个用来显示对话内容
5.创建几个按钮(先创建Image,附上按钮素材,再添加按钮组件)
然后新建脚本命名为UI_Manager,来控制界面的所有UI,上述创建的物体全部要在脚本中定义好
//static
public static UI_Manager UIManager_Instance;
//public
public Text SpeakerName;//Speaker name
public Text DialogueText;//dialogue displayed on the panel
public GameObject BackgroundImg;//background image
public GameObject LeftChara, CenterChara, RightChara;//characters
因为角色的位置是由脚本解析得到的string确定的,所有定义一个字典,来将立绘位置与立绘建立映射。
public Dictionary<string, GameObject> CharaPosition;//match character position
然后我们之后还需要设置面板,储存读档面板等物体,都先定义好,然后再在OnAwake方法中初始化
//initialize objects
UIManager_Instance = this;//initialize manager
SpeakerName = GameObject.Find("Speaker").GetComponent<Text>();//text object (component)
BackgroundImg = GameObject.Find("Background");//background object
DialogueText = GameObject.Find("Dialogue").GetComponent<Text>();//text object (component)
LeftChara = GameObject.Find("Character_Left");//character
CenterChara = GameObject.Find("Character_Mid");
RightChara = GameObject.Find("Character_Right");
DialoguePanel = GameObject.Find("Text_Panel");//text panel
//
//initialize dictionary
CharaPosition = new Dictionary<string, GameObject> { { "left", LeftChara }, { "center", CenterChara }, { "right", RightChara } };
UI界面就初始化好了,然后来实现相关功能
角色的立绘都放在新建的Resources文件夹中,这个文件夹Unity也认识,可以用Resources.Load方法从文件夹中进行资源的加载。定义LoadCharacter方法,传入角色名字(路径)以及position参数。初始化时把Image的颜色设为(1,1,1,1),后面通过调透明度来控制角色的显示
public void LoadCharacter(string path,string pos)
{
//use a sprite varible
Sprite TempSprite = (Sprite)Resources.Load(path, typeof(Sprite));
//get position to draw character
GameObject character = CharaPosition[pos];
//initialize sprite
character.GetComponent<Image>().sprite = TempSprite;
character.GetComponent<Image>().color = new Color(1, 1, 1, 1);
}
//
这样还不够,显然角色的切换是比较频繁的,最好写一个函数来控制图片透明度,来实现角色的erase操作,如果参数为all,则对所有立绘做相同的操作,若传入的是位置,则更新相应位置(on为显示,off为不显示)
//method to control character display( on ->display off->erase)
public void CharacterDisplay(string pos,string type)
{
if (pos == "all")//process all positions
{
if (type == "on")
{
CharaPosition["left"].GetComponent<Image>().color = new Color(1, 1, 1, 1);
CharaPosition["center"].GetComponent<Image>().color = new Color(1, 1, 1, 1);
CharaPosition["right"].GetComponent<Image>().color = new Color(1, 1, 1, 1);
}
else if (type == "off")
{
CharaPosition["left"].GetComponent<Image>().color = new Color(1, 1, 1, 0);
CharaPosition["center"].GetComponent<Image>().color = new Color(1, 1, 1, 0);
CharaPosition["right"].GetComponent<Image>().color = new Color(1, 1, 1, 0);
}
}
else//process single position
{
if (type == "on")
{
CharaPosition[pos].GetComponent<Image>().color = new Color(1, 1, 1, 1);
}
else if (type == "off")
{
CharaPosition[pos].GetComponent<Image>().color = new Color(1, 1, 1, 0);
}
}
}//display character end
//
然后在之前的textparser脚本中的switch语句添加相应的处理:
else if(TempScript[1]== "setcharacter")
{
if (TempScript[2] != "null")
{
UI_Manager.UIManager_Instance.SetCharacter(TempScript[2], TempScript[3]);
//8.12 update CurrentCharacter info
switch (TempScript[3])
{
case "left":
CurrentCharas[0] = TempScript[2];
break;
case "center":
CurrentCharas[1] = TempScript[2];
break;
case "right":
CurrentCharas[2] = TempScript[2];
break;
default:
break;
}
UI_Manager.UIManager_Instance.CharacterDisplay(TempScript[3], "on");
}
}
else if(TempScript[1]== "erasecharacter")
{
UI_Manager.UIManager_Instance.CharacterDisplay(TempScript[2], "off");
//8.12 update CurrentCharacter info
switch (TempScript[2])
{
case "left":
CurrentCharas[0] = "null";
break;
case "center":
CurrentCharas[1] = "null";
break;
case "right":
CurrentCharas[2] = "null";
break;
default:
break;
}
}
最好添加一个渐变的过程,直接切换会显得很生硬,用一个协程来实现。突然想到,StopCoroutine只能处理不带参数的协程,这里协程编写都没带参数,为了控制切换时间间隔,切换背景的名字,先在前面定义好要传递的参数。
//public (used as coroutines' parameters)
public string Cor_text;
public string Cor_bgname;//new image path
public float Cor_DisplayingSpeed;
public float Cor_Time;
切换背景的协程:
IEnumerator SetBgCoroutine()
{
yield return new WaitForEndOfFrame();
Sprite BackImg = (Sprite)Resources.Load("Background/" + Cor_bgname, typeof(Sprite));
Debug.Log("coroutine is changing bg");
BackgroundImg.GetComponent<Image>().CrossFadeAlpha(0.5f, Cor_Time, true);
yield return new WaitForSeconds(Cor_Time);
//(switch background)
BackgroundImg.GetComponent<Image>().sprite = BackImg;
//
BackgroundImg.GetComponent<Image>().CrossFadeAlpha(1, Cor_Time, true);
yield return new WaitForSeconds(Cor_Time);
}
然后封装成一个函数:关闭上一个协程->修改参数->开启协程
public void SetBackground(string name,float dur_time)
{
StopCoroutine("SetBgCoroutine");//stop last bg coroutine
this.Cor_bgname = name;
this.Cor_Time = dur_time;
//start new coroutine
StartCoroutine("SetBgCoroutine");
}//SetBackground end
背景图片放在Resources下新建的Background文件夹中
这个在之前的半成品项目中已经讲到过,这回就用上次的思路。定义一个bool变量判断是否在显示文字,在之后的按钮脚本中会用到。在定义一个TempText变量来获取目前正在显示的文字
public bool ShowTalkText;//judge if display complete
public string TempText;
IEnumerator ShowDialogueCoroutine()
{
yield return new WaitForEndOfFrame();
ShowTalkText = true;
TempText = "";
//
for(int i=0;i<Cor_text.Length;i++)
{
TempText += Cor_text[i];
//update temptext
this.DialogueText.text = TempText;
yield return new WaitForSeconds(1/ Cor_DisplayingSpeed);
}
ShowTalkText = false;
}
public void DisplayDialogue(string text,float speed)
{
//Stop last dialogue coroutine
StopCoroutine("ShowDialogueCoroutine");
//
this.Cor_text = text;
this.Cor_DisplayingSpeed = speed;
//
StartCoroutine("ShowDialogueCoroutine");
}
//
上一章创建的textparser脚本中也添加对话脚本的处理语句:
// in "textparser"
UI_Manager.UIManager_Instance.UpdateSpeakerName(TempScript[1]);//update name
LineIndex++;
UI_Manager.UIManager_Instance.DisplayDialogue(TempScript[2], UI_Manager.UIManager_Instance.Cor_DisplayingSpeed);//
然后和主界面的操作一样,定义一个ButtonsManager脚本,里面含一个方法OnButtonClickEvent,用switch语句来处理按钮点击事件,以下是当点击显示对话的panel时执行的代码:
case "Text_Panel":
if(UI_Manager.UIManager_Instance.ShowTalkText==true)
{
//stop coroutine and display the whole Dialogue
UI_Manager.UIManager_Instance.StopCoroutine("ShowDialogueCoroutine");
UI_Manager.UIManager_Instance.ShowTalkText = false;
UI_Manager.UIManager_Instance.DialogueText.text = UI_Manager.UIManager_Instance.Cor_text;
}
else
{
TextParser.TextParser_Instance.ParsingText(TextParser.TextParser_Instance.LineIndex);
}
break;
若定义的DisplayText的bool值为true(还没有显示完),则立即停止协程,并将完整的文本直接覆盖DialogueText对话框,再将bool值置为false。
若bool值为false,即显示完全,这时点击按钮就让textparser处理下一行脚本语句了
创建一个空物体BGM_Source,挂一个AudioSource,建立一个脚本BGM_Manager来管理bgm的播放,停止,切换。定义一个游戏物体来获取到AudioSource,以下是定义及初始化内容
//static:
public static BGM_Management BGM_Manager_Instance;
//public:
public AudioSource BGM_Player;
public AudioClip BGM;
public string Current_BGM;
// Use this for initialization
private void Awake()
{
BGM_Manager_Instance = this;
BGM_Player = GameObject.Find("BGM_Source").GetComponent<AudioSource>();
}
再写切换bgm和停止bgm的函数,让textparser来调用,bgm文件是audioclip类型的,同样用Resources.load方法来获取
public void SetBGM(string bgmname)
{
//
Debug.Log(bgmname);
Current_BGM = bgmname;
//Get BGM Resource
BGM = (AudioClip)Resources.Load("Bgm/" + bgmname, typeof(AudioClip));
BGM_Player.clip = BGM;
BGM_Player.loop = true;
BGM_Player.Play();
}
public void StopBGM()
{
BGM_Player.Stop();
}
TextParser中:
else if(TempScript[1]== "setbgm")
{
//Set current BGM
BGM_Management.BGM_Manager_Instance.SetBGM(TempScript[2]);
}
做到这一步就已经可以播放一个完整的故事了,像海猫鸣泣之时那种的就没有选项23333,来看看效果
这个项目也快要结束了,下回继续来写设置面板的编写以及存档读档的实现,溜了溜了