# AVG_Learning **Repository Path**: ooooinfo/AVG_Learning ## Basic Information - **Project Name**: AVG_Learning - **Description**: 记录学习unity3d制作avg游戏 - **Primary Language**: C# - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2023-02-12 - **Last Updated**: 2023-02-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AVG_Learning #### 介绍 avg框架学习记录 #### 软件架构 unity3D工程 #### 前期记录 ##### 1.设置UI (UI素材的选取很重要,最好是自己制作) > 素材需要专业美术人员制作 ##### 2.UIPanel (绑定在canvas上,控制对话的进行) > 【小知识】 > > 使用UIPanelTestClass测试UIPanel > > ![image-20200703004558689](README.assets/image-20200703004558689.png) > > 【说明】 > > 在UIPanelTestClass中,创建一个UIPanel的对象uiPanel,在Update()函数中,通过uiPanel调用UIPanel类中的方法,测试是否能够正常运行 > > ##### 3.UIManager (控制每一句对话,人物的显示与否) > ![image-20200707003257465](README.assets/image-20200707003257465.png) > > 新建一个Empty GameObject,命名为GameManager > > 新建UIManager.cs,控制UI系统的各个控件 > > 也能控制整个游戏进程的有序进行 ##### 4.AVG Data (使用额外的数据结构存储对话) > 原本使用的数据结构为List > > 考虑到还要控制人物的显示与否,按钮的显示与否,按钮上的文字内容等等->需要新建一个数据结构class**AVGData** > > AVGData有一个属性为contents,数据类型为List > > > > 【注意】 > > DialogContent是AVGData里面的另一个类 > > 【DialogContent类声明】 > > ```c# > [System.Serializable] > public class DialogContent > { > public string dialogText; > public bool charaADisplay; > public bool charaBDisplay; > } > > ``` > > 三个属性分别控制:对话、角色A是否显示、角色B是否显示 ##### 5.Scriptable Object (因为AVGData没有生命周期这一说法,所以不需要继承MonoBehaviour) > 【AVGData的类声明】 > > ```c# > // 加入这个标签,使其能够在unity中create->AVG Asset Config > [CreateAssetMenu()] > public class AVGData : MonoBehaviour { // 没有生命周期,MonoBehaviour > > public List contents; > > //public bool charaADisplay; > //public bool charaBDisplay; > > } > ``` > > 修改为: > > ```c# > // 加入这个标签,使其能够在unity中create->AVG Asset Config > [CreateAssetMenu()] > public class AVGData : ScriptableObject { // 没有生命周期,MonoBehaviour > > public List contents; > > //public bool charaADisplay; > //public bool charaBDisplay; > > } > ``` > > 一、ScriptableObject类概述、属性及函数 > 首先提供官方参考文档:API-Reference-ScriptableObject & Scriptable-Manual & SctiptableObject-Candycat > > 概述: ScriptableObject类直接继承自Object类;它和MonoBehaviour是并列的,都继承自Object(但MonoBehaviour并不是直接继承自Object),会在后面介绍ScriptableObject和MonoBehavior的比较; > > 脚本化对象就是一个数据容器,可以用来存储大量的数据,它是可序列化的,这个特点也正决定了它的主要用途;一个主要用处就是通过将数据存储在ScriptableObject对象中来减少工程以及游戏运行时因拷贝值所造成的内存占用; > ———————————————— > 原文链接:https://blog.csdn.net/qq_36383623/article/details/99649941 ##### 6.Import Excel (使用QuickSheet工具导入Excel表格,生成对应的数据结构) > 自动生成对应的数据结构类型Story01(与excel表格的名字相同) > > ```c# > using UnityEngine; > using System; > using System.Collections; > using System.Collections.Generic; > > /// > /// !!! Machine generated code !!! > /// > /// A class which deriveds ScritableObject class so all its data > /// can be serialized onto an asset data file. > /// > [System.Serializable] > public class Story01 : ScriptableObject > { > [HideInInspector] [SerializeField] > public string SheetName = ""; > > [HideInInspector] [SerializeField] > public string WorksheetName = ""; > > // Note: initialize in OnEnable() not here. > public Story01Data[] dataArray; > public List dataList; > > void OnEnable() > { > //#if UNITY_EDITOR > //hideFlags = HideFlags.DontSave; > //#endif > // Important: > // It should be checked an initialization of any collection data before it is initialized. > // Without this check, the array collection which already has its data get to be null > // because OnEnable is called whenever Unity builds. > // > if (dataArray == null) > dataArray = new Story01Data[0]; > > } > > // > // Highly recommand to use LINQ to query the data sources. > // > > } > > ``` > > ```c# > using UnityEngine; > using System.Collections; > > /// > /// !!! Machine generated code !!! > /// !!! DO NOT CHANGE Tabs to Spaces !!! > /// > [System.Serializable] > public class Story01Data > { > [SerializeField] > string dialogtext; > public string Dialogtext { get {return dialogtext; } set { dialogtext = value;} } > > [SerializeField] > bool charaadisplay; > public bool Charaadisplay { get {return charaadisplay; } set { charaadisplay = value;} } > > [SerializeField] > bool charabdisplay; > public bool Charabdisplay { get {return charabdisplay; } set { charabdisplay = value;} } > > [SerializeField] > bool ischoice; > public bool Ischoice { get {return ischoice; } set { ischoice = value;} } > > [SerializeField] > string btnatext; > public string Btnatext { get {return btnatext; } set { btnatext = value;} } > > [SerializeField] > string btnamsg; > public string Btnamsg { get {return btnamsg; } set { btnamsg = value;} } > > [SerializeField] > string btnbtext; > public string Btnbtext { get {return btnbtext; } set { btnbtext = value;} } > > [SerializeField] > string btnbmsg; > public string Btnbmsg { get {return btnbmsg; } set { btnbmsg = value;} } > > } > ``` > > 这种Story01数据类型提供了两个类成员变量:数组Array和List > > > > 通过excel表格可以方便的编辑对话和自定义数据结构 > > ![image-20200705222850991](README.assets/image-20200705222850991.png) > > ##### 7.AVG Machine (**完成状态机的设计,设定三个状态:OFF,TYPING(为下一次做打字机作铺垫),PAUSED**) > ![image-20200705224806147](README.assets/image-20200705224806147.png) > > 分析: > > UIManager管理UI的初始化、Show和Hide,控制对话的进行。 > > UIPanel控制每个游戏对象的显示和对应的内容。 > > AVGAssetConfig控制人物的图片的材质(Texture)。 > > > > 【升级】使用AVGMachine来管理游戏对象,使用三个状态管理游戏进程 > > ![image-20200705230418797](README.assets/image-20200705230418797.png) > > 使用UIManager控制AVG Machine > > > > 三种状态,通过函数来转换状态 > > ![image-20200705233147599](README.assets/image-20200705233147599.png) > > Typing状态是下一节需要的 > > 在Start()函数中,state初始化为OFF,justEnter初始化为true(第一次进入某个状态),开始游戏后,发现当前状态为OFF,则从OFF状态转为TYPING状态 > > 然后开始游戏,使用Update函数,判断当前的状态: > > 如果是第一次进入OFF状态,则隐藏canvas画布和所有其他的游戏对象 > > 如果是第一次进入TYPING状态,则显示canvas和其他游戏对象,加载对话内容,进入PAUSED状态(文字内容已经加载完成) > > 如果是第一次进入PAUSED状态,不做任何事情(等待用户下一次鼠标左键点击——位于UIManager的Update()函数中,调用UserClicked())->如果已经显示完所有的对话,重置为OFF状态;如果没有,则回到TYPING状态 > > > > 对Update()函数的理解【待补充】 ##### 8.Typing Effect (实现打字机效果) > > > ![image-20200706001354394](README.assets/image-20200706001354394.png) > > 借助Time.deltaTime和Substring函数,控制文字随时间逐个显示(timerValue的值在第一次进入TYPING时,初始化为0) > > ```c# > void UpdateContentString() > { > timerValue += Time.deltaTime; > string tempString = targetString.Substring(0, Mathf.Min( Mathf.FloorToInt(timerValue * typingSpeed), targetString.Length)); > panel.SetContentText(tempString); > } > ``` > > 使用下面的函数判断是否完成文本的所有显示 > > ```c# > private void CheckTypingFinished() > { > if(state == STATE.TYPING) > { > //GoToState(STATE.PAUSED); > if (Mathf.FloorToInt(timerValue * typingSpeed) >= targetString.Length) > { > if (data.dataList[curLine].Ischoice) > { > GoToState(STATE.CHOICE); > //return; > // return;可有可无 > } > else > { > GoToState(STATE.PAUSED); > } > } > } > } > > ``` ##### 9.Button Click(1) (添加==按钮==游戏对象,增加AVGMachine的状态) > ![image-20200706004011093](README.assets/image-20200706004011093.png) > > 第一次进入Choice状态,加载Button和设置button的文字内容 > > ```c# > private void CheckTypingFinished() > { > if(state == STATE.TYPING) > { > //GoToState(STATE.PAUSED); > if (Mathf.FloorToInt(timerValue * typingSpeed) >= targetString.Length) > { > if (data.dataList[curLine].Ischoice) > { > GoToState(STATE.CHOICE); > //return; > // return;可有可无 > } > else > { > GoToState(STATE.PAUSED); > } > } > } > } > ``` > > 判断当前对话是否有选项(Ischoice==true),如果有,则进入CHOICE状态;没有则进入PAUSED状态 > > > > button对象的onclick()函数 > > ![image-20200706010257791](README.assets/image-20200706010257791.png) > > 设置:当button被点击后,触发AVGMachine的ProcessMSG()方法,将自己传给ProcessMSG(btnA) > > ```c# > public void ProcessMSG(GameObject obj) > { > switch(obj.name) > { > case "GoHome": > print("Go home is pressed."); > Story01 tempStory = Resources.Load("Story02"); // Story02.asset改为Story02 > data = tempStory; > > Init(); > justEnter = false; > LoadCharaTexture(asset.charaATex, asset.charaBTex); > GoToState(STATE.TYPING); > break; > > case "GoShopping": > print("Go shopping is pressed."); > GameObject.FindGameObjectWithTag("door").SendMessage("OpenDoor"); > GoToState(STATE.OFF); > break; > default: > break; > } > } > } > > ``` > > 使用switch判断按钮的name,判断点击的是哪个按钮,执行对应的逻辑 ##### 10.Button Click(2) (点击按钮,加载另一段对话) > ![image-20200706011028278](README.assets/image-20200706011028278.png) > > 在原有的表格中,新增一个sheet,使用quicksheet工具,导入新增的内容 > > 并将所有的asset放入Resources文件夹(unity中,可以通过文件名直接调用Resources中的asset文件) > > > > ProcessMSG中,加载了第二段对话后,要重新初始化【==待改进==】可以封装成一个方法 ##### 11.Integration(1) (制作简单的ARPG) > 导入Standard Assets(建议从官网下载对应版本的unity package) > > 添加3D场景 > > 添加ThirdPersonController,添加FreeLookCameraRig,设置FreeLookCameraRig的Target为ThirdPersonController > > 添加一个小方块,设置Is Trigger属性,添加对应的脚本My Trigger(碰到该方块,触发AVG场景,删除之前使用键盘6触发AVG场景的代码) > > > > 在ARPG界面,未触发AVG之前,鼠标应该是隐藏的(在UIManager中初始化) > > 在触发AVG之后,显示鼠标(便于点击按钮) ##### 12.Integration(2) (添加一扇门,点击Go shopping按钮之后,打开门) > 【BUG】 > > 如果控制人物重复进入Trigger方块,会一直重新载入AVG > > 【修改】 > > AVGMachine,StartAVG()函数中添加状态判断,当状态为OFF时,才重新加载 > > > > 使用MyDoor脚本控制门的开启和关闭 > > > > 【小知识】 > > 不清楚变量的值应该具体设置为多少,可以在游戏中测试之后,查看效果,再设置变量speed和maxY的值 > > > > 在AVGMachine中,ProcessMSG()函数中,点击GoShopping后,给tag为door(要自己在unity中添加)的游戏对象发送一个信息“OpenDoor”,改变speed变量,而Update函数会一直运行,使门的位置改变(注意要设定位置的阈值)【这是比较简单的方法,应该还有其他的方法】 > > > > 结束对话后,进入OFF状态,隐藏对话框 #### 问题 上传gitee之后,因为之前Assets文件夹有.git缓存,所以产生了云端服务器打不开Assets文件夹的情况(已解决) #### 下一步 学习将视频放入unity3D中、点击对应的选项->存储相应的值、unity3D连接mysql数据库 #### 安装教程 1. 使用unity3d 2018.1.1f1打开 #### 使用说明 1. xxxx 2. xxxx 3. xxxx #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request