我如何用Unity3D实现一个Galgame框架(二)

接上昨天的的继续,今天总结一下游戏界面的实现

三.游戏主界面

(一)主要框架

1.在新场景创建UI - Canvas
2.创建一个Image作为背景图
3.创建三个大小合适的image,分别放在背景的偏左,中,偏右位置,用于显示立绘
3.在Canvas上创建Panel,用来显示文字,点击切换文本显示,以及放切换其他窗口的按钮
4.创建两个UI - Text,一个用来显示说话者的名字,另一个用来显示对话内容
5.创建几个按钮(先创建Image,附上按钮素材,再添加按钮组件)
我如何用Unity3D实现一个Galgame框架(二)_第1张图片
然后新建脚本命名为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文件夹中
我如何用Unity3D实现一个Galgame框架(二)_第2张图片

(四)文字显示

这个在之前的半成品项目中已经讲到过,这回就用上次的思路。定义一个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相关处理

创建一个空物体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,来看看效果

我如何用Unity3D实现一个Galgame框架(二)_第3张图片
我如何用Unity3D实现一个Galgame框架(二)_第4张图片

我如何用Unity3D实现一个Galgame框架(二)_第5张图片
我如何用Unity3D实现一个Galgame框架(二)_第6张图片
可以正常解析脚本并反映在UI上了

这个项目也快要结束了,下回继续来写设置面板的编写以及存档读档的实现,溜了溜了

未完待续

你可能感兴趣的:(Unity)