来源
从零开始带你开发橙光游戏AVG框架【55课数 收费】
从零开始带你开发橙光游戏AVG框架 unity教程【16课数 免费】
//
(完整的帧格是5G多,5G多在网盘还是Github都是很麻烦的,有空上传完成了再改)
完成的工程包 提取码:jnag
介绍
QuickSheet使用
bug 包报错
可能是我换了untiy版本的原因
Manual sovle
bug ICSharpCode.SharpZipLib重复
导了一个文件夹,有自己的库,也包含GameFramework、UnityGameFramework,暂时先删掉GameFramework、UnityGameFramework就完事了
bug 中文乱码
VS(Visual Studio)更改文件编码
没改utf-8之前修改过,再改成utf-8没用。
再从压缩包拉出来这些乱码的来覆盖掉,也一样没用
stars 无参事件注册
/// <summary>存档,确定取消</summary>
public class EnterBtn : MonoBehaviour, IPointerDownHandler, IPointerEnterHandler
{
public AudioSource EnterMusic;//悬停音效
public AudioSource DownMusic;//点击音效
private void Start()
{
EnterMusic = gameObject.FindComponentWithTag<AudioSource>(Tags.ENTERMUSIC);
DownMusic = gameObject.FindComponentWithTag<AudioSource>(Tags.DOWNMUSIC);
}
/// <summary>
/// 获得标签为 tag 的 物体 身上的 T 组件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="go"></param>
/// <param name="tag"></param>
/// <returns></returns>
/// <exception cref="System.Exception"></exception>
public static T FindComponentWithTag<T>(this GameObject go, string tag) where T : Component
{
T res= GameObject.FindGameObjectWithTag(tag).GetComponent<T>();
if (res == null)
{
throw new System.Exception("异常");
}
return res;
}
modify 将开始游戏界面的代码。从UIManager中拆出来
就这结构
GameStart
UIMgr等Mgr
Canvas
Panel
modify GamStart.cs
:MonoBehaviour 作为游戏入口,用来控制脚本顺序,存储脚本。
--------------------------------------------------
modify CameraPos.cs
GameStart
/****************************************************
文件:GameStart.cs
作者:lenovo
邮箱:
日期:2023/4/14 19:58:55
功能:游戏入口
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class GameStart : MonoBehaviour
{
#region 字段
AudioMgr audioMgr;
#endregion
#region 生命
void Start()
{
GameObject.FindGameObjectWithTag(Tags.MAINCAMERA).AddComponent<CameraPos>().Init();
//audioMgr.Init();
}
CameraPos
/****************************************************
文件:
作者:WWS
日期:2023/04/14 20:10:21
功能:看起来有摇晃感
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraPos : MonoBehaviour
{
public float X;
public float speed;//速度 鼠标移动
public float rotateX;
public void Init()
{
speed = 0.5f;
}
void Update()
{
X = Input.GetAxis(Constants.Mouse_X); //获得鼠标左右旋转的值
rotateX += X * speed; //乘以速度
//Mathf.Clamp,就是说把一个数值规定在 X 和 Y 的数值之间
X = Mathf.Clamp( rotateX, -5,5);
transform.eulerAngles = new Vector3(0,X,0);
}
}
Tags
/****************************************************
文件:Tags.cs
作者:lenovo
邮箱:
日期:2022/7/15 12:57:58
功能:
*****************************************************/
public static class Tags
{
public const string MAINCAMERA = "MainCamera";
modify LoadCGMgr.cs
未完成 子节点访问父节点的方法,导入QFramework采用事件注册
QFramework 是我跟着 QFramework第三版本 视频做的部分代码,其中包含 事件注册,关键词是 Register,UnRegister,Event,Command
。。。。
点击两个按钮会调用在 确定面板上 的 播放不同音效 的方法.
用 QFramework 可以跑通
。。。。
调用顺序
GameStart
EnterCanvas
EnterPanel
EnterBtn
其中EnterPanel是下文的父节点,EnterBtn是下文的子节点
新建Event
/****************************************************
文件:OverButtonEvent.cs
作者:lenovo
邮箱:
日期:2023/4/15 0:22:20
功能:QFramework 第三版本 的事件
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class OverButtonEvent : Event<OverButtonEvent>
{
}
父节点注册和调用Event
/****************************************************
文件:EnterPanel.cs
作者:lenovo
邮箱:
日期:2023/4/14 23:49:34
功能:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class EnterPanel : MonoBehaviour
{
#region 字段
/// <summary>悬停音效</summary>
public AudioSource EnterMusic;
/// <summary>点击音效</summary>
public AudioSource DownMusic;
#endregion
#region 生命
public void Init()
{
EnterMusic = gameObject.FindComponentWithTag<AudioSource>(Tags.ENTERMUSIC);
DownMusic = gameObject.FindComponentWithTag<AudioSource>(Tags.DOWNMUSIC);
OverButtonEvent.Register(PlayEnterMusic);
ClickButtonEvent.Register(PlayDownMusic);
}
private void OnDestroy()
{
OverButtonEvent.UnRegister(PlayEnterMusic);
ClickButtonEvent.UnRegister(PlayDownMusic);
}
#endregion
#region 系统
#endregion
#region 辅助
public void PlayEnterMusic()
{
EnterMusic.Play();
}
public void PlayDownMusic()
{
DownMusic.Play();
}
#endregion
}
新建Event对应的Command, 将Event塞进Command
/****************************************************
文件:OverButtonEvent.cs
作者:lenovo
邮箱:
日期:2023/4/15 0:22:20
功能:QFramework 第三版本 的事件
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class ClickButtonCommand : ICommand
{
public void Execute()
{
ClickButtonEvent.Trigger();
}
}
子节点调用Command
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>存档,确定取消</summary>
public class EnterBtn : MonoBehaviour, IPointerDownHandler, IPointerEnterHandler
{
#region 系统
public void OnPointerDown(PointerEventData eventData)
{
new ClickButtonCommand().Execute();
}
public void OnPointerEnter(PointerEventData eventData)
{
new OverButtonCommand().Execute();
}
#endregion
}
stars 支持中文
支持中文,先试用下
using System;
public static class ResourcesName
{
public const string Sprites_丹药 = "Sprites/WindowIMG/丹药";
public const string Sprites_五彩石 = "Sprites/WindowIMG/五彩石";
bug 脚本名和节点名没尽量对应
bug 脚本间跨父节点单访问参数
modify Tag的AVGPolt改为PoltCanvas
方便脚本名,标签名,节点名,三者对应
bug 修改 标签 Polt 为 Plot
watch 带标签 CG
类似 AVG/MainCanvas/LoadCGPoltPanel/BG/CG鉴赏/ObjectPanel/PolePre/PoltBG/Polt
modify 新建脚本LoadCGPlotPanel.cs
位置是 AVG/MainCanvas/LoadCGPlotPanel
modify 筛选卡
modify AudioMgr.cs
AudioMgr.cs单例,并且挂到GameStart的子节点上,并且对相关引用进行调整
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioMgr : MonoBehaviour
{
public List<AudioClip> AudioList; //音频播放的数组
public AudioSource audioSrc; //音频播放的组件
#region 单例
private static AudioMgr _instance;
public static AudioMgr Instance
{
get
{
return _instance;
}
set
{
_instance = value;
}
}
#endregion
public void Init()
{
Instance = this;
AudioList=new List<AudioClip>(6);
AudioList.Add( LoadAudioClip("RPGBG") );
AudioList.Add( LoadAudioClip("4_孤独之路") );
AudioList.Add( LoadAudioClip("5_花散曲终") );
AudioList.Add( LoadAudioClip("20絶体絶命の曲") );
AudioList.Add( LoadAudioClip("zh05_燃血之时") );
AudioList.Add( LoadAudioClip("zh08_狂乱之宴") );
}
/// <summary>播放音频</summary>
public void PlayMusic(int id)
{
audioSrc.clip = AudioList[id];
audioSrc.Play();
}
/// <summary>停止播放播放音频</summary>
public void StopMusic()
{
audioSrc.Stop();
}
AudioClip LoadAudioClip(string name)
{
AudioClip audioClip = Resources.Load<AudioClip>("Music/" + name);
if (audioClip == null)
{
throw new System.Exception("异常");
}
return audioClip;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class GameStart : MonoBehaviour
{
#region 字段
public AudioMgr audioMgr;
......
#endregion
#region 生命
void Start()
{
......
//
audioMgr=GetComponentInChildren<AudioMgr>();
audioMgr.Init();
bug IsUsingDeformableBuffer
unity ‘SpriteRenderer’ does not contain a definition for ‘IsUsingDeformableBuffer’ and no accessible extension method ‘IsUsingDeformableBuffer’ accepting a first argument of type ‘SpriteRenderer’ could be found (are you missing a using
Unity spriteRenderer error what i need to do?
我是2020.3.23fc1,尝试了2没用,尝试了1,有用,没报错了,但是 2D Sprite 估计用到,后面报错再安装
modify AudioSource与AudioMgr
01 将所有 AudioSource 放到 AudioMgr 的子节点上,并且改名了3个。
02 后面有移动了 节点 AVG 上的 AudioSource 到 AudioMgr 的子节点 AVGSrc 上
03 类似AVG/MainCanvas/LoadCGPlotPanel/BG/音乐赏析/BGMusic,也有一个AudioSource
暂时不清楚为什么用那么多AudioSource,放内存应该用字典。(需要同时放?)
原版放在 SettingPanel 上
watch SettingPanel 关系了 音量
也就是做好 SettingPanel 和 AudioMgr 的关系
就是将相关代码 放 AudioMgr,让 SettingPanel 调用(Slider嘛)
modify EnterPanel通过AudioMgr调用AudioSource
前面的 EnterPanel 用到了 这两个 AudioSource ,也放到 AudioMgr 上,通过访问 AudioMgr 来调用
改名为 EnterBtnSrc, DownBtnSrc,省下两个 Tag
两个AudioSource移动 改名 引用 删Tag
EnterPanel 调用
/****************************************************
文件:EnterPanel.cs
作者:lenovo
邮箱:
日期:2023/4/14 23:49:34
功能:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class EnterPanel : MonoBehaviour
{
#region 生命
public void Init()
{
OverButtonEvent.Register(PlayEnterMusic);
ClickButtonEvent.Register(PlayDownMusic);
}
private void OnDestroy()
{
OverButtonEvent.UnRegister(PlayEnterMusic);
ClickButtonEvent.UnRegister(PlayDownMusic);
}
#endregion
#region 系统
#endregion
#region 辅助
public void PlayEnterMusic()
{
AudioMgr.Instance.EnterBtnSrc.Play();
}
public void PlayDownMusic()
{
AudioMgr.Instance.DownBtnSrc.Play();
}
#endregion
}
modify 将 UIMgr 的部分代码拆到 EnterPanel
Scene 进入游戏---------------------------------
bug 需要传参的事件注册
自顶向下
自顶向下用方法
自底向上 之一 委托
自底向上用委托、事件
委托要注册,注销。
存在 回调地狱
要声明委托
缺点是,多个子节点都需要访问父节点,每个子节点都要声明委托,麻烦。
父节点PlayerCtrl
namespace FrameworkDesign2021
{
public class PlayerCtrl : MonoBehaviour
{
PlayerAnimationCtrl mAnimationCtrl;
void Start()
{
mAnimationCtrl = transform.Find("Animation").GetComponent<PlayerAnimationCtrl>();
// 注册完成的事件
mAnimationCtrl.OnDoSomethingDone += OnDoSomethingDone;
mAnimationCtrl.DoSomething();
}
void OnDoSomethingDone()
{
Debug.Log("动画播放完毕");
}
void OnDestroy()
{
// 注销完成的事件
mAnimationCtrl.OnDoSomethingDone -= OnDoSomethingDone;
}
}
...
}
子节点 PlayerAnimationCtrl
public class PlayerAnimationCtrl : MonoBehaviour
{
// 定义委托
public Action OnDoSomethingDone = ()=>{};
public void DoSomething()
{
OnDoSomethingDone();
}
}
}
事件 模块(Module层)之间交互
解除限制,使用自有,需要增加小逻辑,是和模块之间的大颗粒度交互,不合适对象之间的小颗粒度交互
命令 对象之间(Model层)交互
带参数
撤销
bug 找到带参的命令
bug canvas不激活GetComonent不到
modify 将UI的事件id改为枚举,增加可读性
/// <summary>
/// None=1,
/// QuitGame = 2,
/// ReturnMainPanel = 3,
/// SL = 5,
/// Loading = 6,
/// IsLoadPlotBack = 7,
/// IsLoadCG = 8,
/// </summary>
public enum EventID
{
None = 1,
QuitGame = 2,
ReturnMainPanel = 3,
/// <summary>存档</summary>
SaveFile = 5,
/// <summary>打开存档面板,常听的SL大法</summary>
OpenSLPanel = 6,
/// <summary>剧情</summary>
LoadPlot = 7,
/// <summary>CG(计算机动画)</summary>
LoadCG = 8,
}
bug Google.GData.Client
Reference has errors ‘Google.GData.Client’.
不明,莫名其妙的坏了,好了
bug 程序集引用丢失
原本是都在根目录
QFramework
QFrameworkData
改成在Plugins/QFramework就报错了,而且会一直在根目录生成QFrameworkData
QFramework
QFrameworkData
modify AnimationEvent
打算拆出来放在一个文件夹AnimationEvent下
bug TLS Allocator ALLOC_TEMP_THREAD
TLS Allocator ALLOC_TEMP_THREAD, underlying allocator ALLOC_TEMP_THREAD has unfreed allocations, size 40
有些线程没释放
[Report All【没用】]](https://blog.csdn.net/wenshuai537/article/details/123558838)
bug Reference has errors ‘Google.GData.Client’.
Assembly ‘Assets/QuickSheet/GDataPlugin/Editor/Google/Google.GData.Extensions.dll’ will not be loaded due to errors:
Reference has errors ‘Google.GData.Client’.
迷,重启电脑后没了
bug 自己的代码库突然就不被CSharp项目识别了
01 我改了Component拓展方法中 using GameObject = UnityEngine.GameObject;
02 整理时将 QuickSheet 移动到 Plugins,报错;又移动出来,报找不到 meta的 错,重启Unity后就好了
03 重启Unity后,不被CSharp项目识别就好了
modify 整理动画事件
将所有动画事件提取出来,以 节点名_动画名 的方式命名脚本,统一放到一个文件夹
如图所示
01 动画 MainAnim 有两个 事件(也就是方法)
02 这两个方法放在一个脚本里,脚本的命名是 要挂的节点名_动画名
03 将该 动画事件脚本 挂载节点上
原版是将所有的 动画事件都放在一个脚本 AnimManager。动画事件又是 0引用 ,所以难管理。
bug 动画事件调用某些属性顺序
01 动画事件调用某些属性
02 属性在Init()方法中赋值
但 01 比 02 快,导致属性还没赋值就被调用
所以先这样,这是跟Siki学院的Ocean老师学RealFrame时的。就是 等人再干活
Crt 是协程的英文Coroutine,我选的 简写
/// <summary>关闭菜单按钮</summary>
public void HideMainBtn()
{
StartCoroutine(HideMainBtnCrt());
}
IEnumerator HideMainBtnCrt()
{
while (UIMgr.Instance == null
|| UIMgr.Instance.MainCanvas == null
|| UIMgr.Instance.MainCanvas.MainPanel == null)//需要时间
{
yield return new WaitForEndOfFrame(); //等一帧
}
UIMgr.Instance.MainCanvas.MainPanel.HideMainBtn();
}
modify 主界面按钮名
动画认名字【改过变黄,识别不了】(这点不好,应该认uuid)
bug 单例为null
点击 开始游戏 出现的, 照理说这时候什么都初始化好了
F5了,Instance为空
。。。。
MonoBehaviour不能有用new的方式
所以用以下
using UnityEngine;
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
protected static T mInstance = null;
public static T Instance
{
get
{
if (mInstance == null)
{
mInstance = FindObjectOfType<T>();
if (FindObjectsOfType<T>().Length > 1)
{
Debug.LogWarning("More than 1");
return mInstance;
}
if (mInstance == null)
{
var instanceName = typeof(T).Name;
Debug.LogFormat("Instance Name: {0}", instanceName);
var instanceObj = GameObject.Find(instanceName);
if (!instanceObj)
instanceObj = new GameObject(instanceName);
mInstance = instanceObj.AddComponent<T>();
DontDestroyOnLoad(instanceObj); //保证实例不会被释放
Debug.LogFormat("Add New Singleton {0} in Game!", instanceName);
}
else
{
Debug.LogFormat("Already exist: {0}", mInstance.name);
}
}
return mInstance;
}
}
protected virtual void OnDestroy()
{
mInstance = null;
}
}
stars SetActive()
将大量的SetActive()改成Show()和Hide()
/****************************************************
文件:ExtendComponent.ShowHide.cs
作者:lenovo
邮箱:
日期:2023/4/22 15:32:47
功能:QFramework第一季
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public static partial class ExtendComponent
{
public static void Show(this GameObject gameObject)
{
gameObject.SetActive(true);
}
public static void Hide(this GameObject gameObject)
{
gameObject.SetActive(false);
}
public static void Show(this Transform transform)
{
transform.gameObject.SetActive(true);
}
public static void Hide(this Transform transform)
{
transform.gameObject.SetActive(false);
}
public static void Show(this MonoBehaviour monoBehaviour)
{
monoBehaviour.gameObject.SetActive(true);
}
public static void Hide(this MonoBehaviour monoBehaviour)
{
monoBehaviour.gameObject.SetActive(false);
}
}
bug unity 运行时busy for卡很久;unity suspendThread loop falied
死锁
猜,断点的地方是用到了映射(自己的一个能得出命名空间、类名、方法名的方法)
modify AVGMachine
将能沉下到各个 Manager 、Canvas、 Panel 的方法沉下去
比如一个方法的参数都是 AudioMgr 中的,直接把这个方法提到 AudioMgr ,再去间接调用
bug C# private不显示
C# private不显示有一会,后面又显示了
modify 区别节点命名
原版是在AVGMainAnim.cs
借点命名都是,如果发生节点丢失很难区分。(虽然有注释)
watch 主菜单花的粒子
/****************************************************
文件:
作者:WWS
日期:2023/04/14 20:10:21
功能:看起来有摇晃感
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraPos : MonoBehaviour
{
public float inputX;
public float speed;//速度 鼠标移动
public float rotateX;
public GameObject Particle;
public void Init()
{
speed = 0.5f;
Particle = gameObject.FindChildDeep("Particle");
ShowParticle();
}
void Update()
{
inputX = Input.GetAxis(Constants.Mouse_X); //获得鼠标左右旋转的值
rotateX += inputX * speed; //乘以速度
inputX = Mathf.Clamp( rotateX, -5,5); //规定在 X 和 Y 的数值之间
transform.eulerAngles = new Vector3(0,inputX,0);
}
void ShowParticle()
{
Particle.Show();
}
}
bug UnityEditor.Graphs.Edge.WakeUp ()
重启Untiy,猜是编译不及时,好像没转圈圈
NullReferenceException: Object reference not set to an instance of an object
UnityEditor.Graphs.Edge.WakeUp () (at <1bfd5359178446f5b1ac53f68c0a5db0>:0)
UnityEditor.Graphs.Graph.DoWakeUpEdges (System.Collections.Generic.List`1[T] inEdges, System.Collections.Generic.List`1[T] ok, System.Collections.Generic.List`1[T] error, System.Boolean inEdgesUsedToBeValid) (at <1bfd5359178446f5b1ac53f68c0a5db0>:0)
UnityEditor.Graphs.Graph.WakeUpEdges (System.Boolean clearSlotEdges) (at <1bfd5359178446f5b1ac53f68c0a5db0>:0)
UnityEditor.Graphs.Graph.WakeUp (System.Boolean force) (at <1bfd5359178446f5b1ac53f68c0a5db0>:0)
UnityEditor.Graphs.Graph.WakeUp () (at <1bfd5359178446f5b1ac53f68c0a5db0>:0)
UnityEditor.Graphs.Graph.OnEnable () (at <1bfd5359178446f5b1ac53f68c0a5db0>:0)
--------------------------------------------------
modify 确定面板 退出游戏
点击悬停音效
原版是直接拖拽节点,我改单例,前面是用无参的事件注册,学会有参在一起改
面板主功能
/****************************************************
文件:EnterPanel.cs
作者:lenovo
邮箱:
日期:2023/4/14 23:49:34
功能:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Random = UnityEngine.Random;
public class EnterPanel : MonoBehaviour
{
#region 字属
Button yesBtn;
Button noBtn; //确认和取消两个按钮
Text contentText;
public GameObject EnterPanelGo; //确认面板
#endregion
#region 生命
public void Init()
{
InitButton();
EnterPanelGo = gameObject;
contentText = gameObject.GetComponentDeep<Text>("ContentText");
}
void InitButton()
{
//以后采用事件注册
//this.RegisterEvent<OverButtonEvent>( PlayEnterMusic);
//this.RegisterEvent<ClickButtonEvent>(PlayDownMusic);
yesBtn = gameObject.GetComponentDeep<Button>("YesBtn");
noBtn = gameObject.GetComponentDeep<Button>("NoBtn");
//
//yesBtn.gameObject.GetComponent<EnterBtn>(); //mark
yesBtn.onClick.AddListener( UIMgr.Instance.YesEvent );
//
//yesBtn.gameObject.GetComponent<EnterBtn>();
noBtn.onClick.AddListener( NoEvent );
}
private void OnDestroy()
{
// OverButtonEvent.UnRegister(PlayEnterMusic);
// ClickButtonEvent.UnRegister(PlayDownMusic);
}
#endregion
#region 系统
#endregion
#region 辅助
public void NoEvent()
{
Close();
}
/// <summary></summary>
public void Close()
{
//当我们按下取消按钮之后
//如果我们做动画了
GetComponent<Animator>().SetBool(AnimatorPara.Quit, true);
//获得 确认面板上的动画控制器
//如果我们没有做动画 只有面板
//EnterObject.SetActive(false); //隐藏确认面板
}
public void SetContentText(string value)
{
contentText.text = value;
}
#endregion
}
面板上的动画事件
/****************************************************
文件:EnterPanel_EnterQuit.cs
作者:lenovo
邮箱:
日期:2023/4/21 20:5:30
功能:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class EnterPanel_EnterQuit : MonoBehaviour
{
#region 辅助
/// <summary>退出确认面板 就是隐藏 确认面板</summary>
public void QuitEnter()
{
UIMgr.Instance.EnterCanvas.Hide();
}
#endregion
}
按钮音效
同时加到下面4个按钮上
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>存档,确定取消</summary>
public class EnterBtn : MonoBehaviour, IPointerDownHandler, IPointerEnterHandler
{
#region 系统
public void OnPointerDown(PointerEventData eventData)
{
//new ClickButtonCommand().Execute(); //无参成功,但是有参失败,所以暂时不用
AudioMgr.Instance.PlayClickBtnMusic();
}
public void OnPointerEnter(PointerEventData eventData)
{
//new OverButtonCommand().Execute();
AudioMgr.Instance.PlayHoverBtnMusic();
}
#endregion
}
效果
stars 按钮监听 GetButtonDeep
在子节点中找命名为 “ResetBtn” 的按钮, 添加 ResetVolume 的事件, 最后将按钮返回
ResetBtn = transform.GetButtonDeep("ResetBtn", ResetVolume);
public static Button GetButtonDeep(this Transform root, string childName, UnityEngine.Events.UnityAction action)
{
Button result = root.GetButtonDeep(childName);
result.onClick.AddListener( action );
return result;
}
/// <summary>
/// 深度查找子对象transform引用
/// </summary>
/// <param name="root">父对象</param>
/// <param name="childName">具体查找的子对象名称</param>
/// <returns></returns>
public static Button GetButtonDeep(this Transform root, string childName)
{
Transform result = null;
result = root.Find(childName);
if (!result)
{
foreach (Transform item in root)
{
result = FindChildDeep(item, childName);
if (result != null)
{
return result.GetComponent<Button>();
}
}
}
return result.GetComponent<Button>();
}
/// <summary>
/// 深度查找子对象transform引用
/// </summary>
/// <param name="root">父对象</param>
/// <param name="childName">具体查找的子对象名称</param>
/// <returns></returns>
public static Transform FindChildDeep(this Transform root, string childName)
{
Transform result = null;
result = root.Find(childName);
if (!result)
{
foreach (Transform item in root)
{
result = FindChildDeep(item, childName);
if (result != null)
{
return result;
}
}
}
return result;
}
--------------------------------------------------
modify 设置面板
子脚本有ScreenSettings 。语言设置没做,屏幕设置好像没有效果。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Linq;
using Slider = UnityEngine.UI.Slider;
using System;
public class SettingsPanel : MonoBehaviour
{
#region 字属
//这部分应该新建一个SLMgr(Save And Load,设置、存档)来保存
public float TextSpeed = 10; //文本显示的速度
public float TextAutoSpeed = 10; //文本自动跳转下一句的速度
public float DialogBoxAlpha = 0.8F; //对话框文本的透明度
//
public List<Slider> SliderLst=new List<Slider>(); //获得滚动条合集
[SerializeField] ScreenSettings ScreenSettings ;
[Header("Button")]
[SerializeField] Button ResetBtn;
[SerializeField] Button BackBtn;
[SerializeField] Button GoMainBtn;
[SerializeField] Button QuitGameBtn;
#endregion
#region 生命
public void Init()
{
TextSpeed = 10;
TextAutoSpeed = 10;
DialogBoxAlpha = 0.8F;
//
InitSliderLst();
//
ScreenSettings = gameObject.GetComponentDeep<ScreenSettings>("ScreenSettings");
ScreenSettings.Init();
//
ReadSettingIfExist();
SetSliderValue(); //把数据赋值给滚动条的value值
//
InitButton();
}
private void Update()
{
UpdateValue();
UpdateSlider();
}
#endregion
#region 辅助
void InitButton()
{
ResetBtn = transform.GetButtonDeep("ResetBtn", ResetVolume);
BackBtn = transform.GetButtonDeep("BackBtn", HideSettingPanel);
GoMainBtn = transform.GetButtonDeep("GoMainBtn", GoMain);
QuitGameBtn = transform.GetButtonDeep("QuitGameBtn", QuitGame);
}
void QuitGame()
{
UIMgr.Instance.QuitGame();
}
void GoMain()
{
UIMgr.Instance.GoMainPanel();
}
/// <summary>隐藏设置界面</summary>
public void HideSettingPanel()
{
SaveValue();
gameObject.Hide();
}
void ReadSettingIfExist()
{
string path = Application.persistentDataPath + "/Setting";
if (File.Exists(path))//判断当前文件路径 是否有 Setting的 文件
{
//如果 有 就执行 读取文件,并且解析文件里面的数据内容
BinaryFormatter bf = new BinaryFormatter();
FileStream fs = File.Open(path, FileMode.Open);
SettingData saveData = (SettingData)bf.Deserialize(fs);//反序列化数据
fs.Close();
SetGameData(saveData);
}
else//如果没有文件
{
SaveValue(); //存储文件的方法
}
}
void InitSliderLst()
{
SliderLst = gameObject.GetComponentsInChildren<Slider>().ToList();
}
/// <summary>更新数据</summary>
public void UpdateValue()
{
AudioMgr.Instance.UpdateValue();
UIMgr.Instance.SetDialogBoxAlpha(DialogBoxAlpha) ; //把对话框文本的透明度赋值
AVGMachine.Instance.TextSpeed = TextSpeed; //把速度赋值过去
}
/// <summary>把滚动条里面的参数 赋值给 当前脚本上面的参数</summary>
public void UpdateSlider()
{
TextSpeed = SliderLst[0].value; //把第一个滚动条的参数赋值给 文字速度
TextAutoSpeed = SliderLst[1].value;
DialogBoxAlpha = SliderLst[2].value;
//
AudioMgr.Instance.TotalVolume = SliderLst[3].value;
AudioMgr.Instance.BgVolume = SliderLst[4].value;
AudioMgr.Instance.EffectVolume = SliderLst[5].value;
AudioMgr.Instance.RoleVolume = SliderLst[6].value;
}
/// <summary>把数据赋值给滚动条的value值</summary>
public void SetSliderValue()
{
SliderLst[0].value = TextSpeed; //把文字打印的速度 赋值给 第一个滚动条
SliderLst[1].value = TextAutoSpeed;
SliderLst[2].value = DialogBoxAlpha;
//
SliderLst[3].value = AudioMgr.Instance.TotalVolume;
SliderLst[4].value = AudioMgr.Instance.BgVolume;
SliderLst[5].value = AudioMgr.Instance.EffectVolume;
SliderLst[6].value = AudioMgr.Instance.RoleVolume;
}
/// <summary>存储数据的方法</summary>
public void SaveValue()
{
SettingData save = CreateSaveGo(); // 接收存档数据信息
BinaryFormatter bf = new BinaryFormatter(); //实例化
FileStream fs = File.Create(Application.persistentDataPath +"/Setting"); //创建一个名为 Setting的文件
bf.Serialize(fs,save); //序列化
fs.Close(); //关掉
}
/// <summary>创建存档数据信息</summary>
public SettingData CreateSaveGo()
{
SettingData save = new SettingData(); //实例化一个 存储数据
save.TextSpeed = TextSpeed;
save.TextAutoSpeed = TextAutoSpeed;
save.WindowAlpha = DialogBoxAlpha;
save.MusicVolume = AudioMgr.Instance.TotalVolume;
save.BGMusic = AudioMgr.Instance.BgVolume;
save.RoleMusic = AudioMgr.Instance.RoleVolume;
save.EffectMusic = AudioMgr.Instance.EffectVolume;
return save;
}
/// <summary>设置游戏参数</summary>
public void SetGameData(SettingData save)
{
TextSpeed = save.TextSpeed ;
TextAutoSpeed = save.TextAutoSpeed ;
DialogBoxAlpha = save.WindowAlpha ;
AudioMgr.Instance.TotalVolume = save.MusicVolume ;
AudioMgr.Instance.BgVolume = save.BGMusic ;
AudioMgr.Instance.RoleVolume = save.RoleMusic ;
AudioMgr.Instance.EffectVolume = save.EffectMusic ;
UpdateValue(); //音频数据更新
}
/// <summary>重置所有选项的按钮</summary>
public void ResetSliderValue(int a1,int a2,float a3,float a4,float a5,float a6,float a7)
{
SliderLst[0].value = a1;
SliderLst[1].value = a2;
SliderLst[2].value = a3;
SliderLst[3].value = a4;
SliderLst[4].value = a5;
SliderLst[5].value = a6;
SliderLst[6].value = a7;
}
/// <summary>重置音量</summary>
public void ResetVolume()
{
ResetSliderValue(10,10,0.7f,0.8f,1,1,1);
}
#endregion
}
bug PlotCanvas Get到但没有初始化
SettingsPanel在MainCanvas下
GamePanel在PlotCanvas下
SettingsPanel在Update中调用GamePanel,也就是PlotCanvas的初始化要在MainCanvas之前(调一下顺序)
/// <summary>更新数据</summary>
public void UpdateValue()
{
AudioMgr.Instance.UpdateValue();
UIMgr.Instance.SetDialogBoxAlpha(DialogBoxAlpha) ; //把对话框文本的透明度赋值
AVGMachine.Instance.TextSpeed = TextSpeed; //把速度赋值过去
}
UIMgr 调用顺序
void InitCanvas()
{
MainCanvas = gameObject.GetComponentDeep<MainCanvas>("MainCanvas");
EnterCanvas = gameObject.GetComponentDeep<EnterCanvas>("EnterCanvas");
PlotCanvas = gameObject.GetComponentDeep<PlotCanvas>("PlotCanvas");
AnimCanvas = gameObject.GetComponentDeep<AnimCanvas>("AnimCanvas");
WhiteCanvasGo = gameObject.Find("WhiteCanvas");
BlackCanvasGo = gameObject.Find("BlackCanvas");
BlackCanvasGo.Hide();
PlotCanvas.Init();//在MainCanvas之前
MainCanvas.Init();
EnterCanvas.Init();
AnimCanvas.Init();
}
modify 屏幕设置
挂在这里,被 SettingsPanel 调用和初始化
/****************************************************
文件:ScreenSettings.cs
作者:lenovo
邮箱:
日期:2023/4/29 23:51:25
功能:屏幕设置
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;
public class ScreenSettings : MonoBehaviour
{
#region 属性
public Button FullScreenBtn;
public Button WindowScreenBtn;
public List<Sprite> SpriteLst = new List<Sprite>(); //图片
#endregion
#region 生命
public void Init()
{
FullScreenBtn = transform.GetButtonDeep("FullScreenBtn",FullScreen); ;
WindowScreenBtn = transform.GetButtonDeep("WindowScreenBtn", WindowScreen);
InitSpriteLst();
FullScreen();
}
#endregion
#region 辅助
void InitSpriteLst()
{
AddSpriteLst("sys_botton_1_full_a");
AddSpriteLst("sys_botton_1_win_a");
AddSpriteLst("sys_botton_1_full_c #9204", "素材/zanghua/Texture2D/");
AddSpriteLst("sys_botton_1_win_c");
}
void AddSpriteLst(string name, string pathPre = "素材/zanghua/Sprite/")
{
Sprite sprite = Resources.Load<Sprite>(pathPre + name);
SpriteLst.Add(sprite);
}
/// <summary>窗口模式</summary>
public void WindowScreen()
{
UnSelectBtn();
Screen.SetResolution(1920, 1080, fullscreen: false);
WindowScreenBtn.GetComponent<Image>().sprite = SpriteLst[3];
}
/// <summary>全屏方法,InitSpriteLst之后</summary>
public void FullScreen()
{
UnSelectBtn();
Screen.SetResolution(1920, 1080, fullscreen: true);
FullScreenBtn.GetComponent<Image>().sprite = SpriteLst[2];
}
/// <summary>取消按钮的激活状态</summary>
public void UnSelectBtn()
{
FullScreenBtn.GetComponent<Image>().sprite = SpriteLst[0];
WindowScreenBtn.GetComponent<Image>().sprite = SpriteLst[1];
}
#endregion
}
--------------------------------------------------
modify 驻足回想 MPCPanel
MPC,Music(音乐) Plot(剧情) CG(计算机动画)
。。。。。。
1 总的tab,有MPC三种
2 散的tab,MPC各自的所有内容
3 MPC在面板上显示的内容
。。。。。。
原版只有 TabGround 和 TabBtn 两个脚本,就1,2都是使用同样的TabGround,所以这部分的初始化区别分开,因为里面的PanelGoLst,面板位置关系不一样(不是都能在子节点找到,拖拽的话丢失麻烦)
TabGround基类
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/// <summary>选项卡基类</summary>
public class TabGround : MonoBehaviour
{
//管理按钮 并且 判断 哪个面板
public List<TabBtn> TabBtnLst = new List<TabBtn>();
/// <summary>存储数据面板</summary>
public List<GameObject> PanelGoLst=new List<GameObject>();
/// <summary>用来存储当前点击的是哪个按钮</summary>
public TabBtn selectTabBtn;
public TabBtn tabBtn1;
#region 生命
protected virtual void Start()
{
InitTabBtnLst();
InitTab1();
InitPanelGoLst();
}
#endregion
#region 辅助
public void OnTabSelected(TabBtn tabBtn)
{
//判断当前点击了哪个按钮,没有点击的按钮就切换成未激活的图片
selectTabBtn = tabBtn; //把点击的按钮 传递过去
ResetTab();
tabBtn.Active();
int tarIdx = tabBtn.transform.GetSiblingIndex();
for (int i = 0; i < PanelGoLst.Count; i++)
{
if (i == tarIdx)
{
PanelGoLst[i].Show();
}
else
{
PanelGoLst[i].Hide();
}
}
}
/// <summary>重置所有按钮的图片</summary>
public void ResetTab()
{
foreach (TabBtn tabBtn in TabBtnLst)
{
if (selectTabBtn != null && tabBtn == selectTabBtn)
{
continue;//跳过本次循环
}
//把按钮的图片切换成对应的 待机未激活 的图片
tabBtn.Idle();
}
}
protected virtual void InitTabBtnLst()
{
}
protected virtual void InitPanelGoLst()
{
}
protected virtual void InitTab1()
{
if (TabBtnLst.Count <= 0)
{
return;
}
tabBtn1 = TabBtnLst[0];
tabBtn1.Active();
OnTabSelected(tabBtn1);
}
#endregion
}
总的TabGround
挂在剧情的父节点或以上,我挂的是MPCPanel节点
/****************************************************
文件:TabGroup0.cs
作者:lenovo
邮箱:
日期:2023/4/30 2:1:34
功能:接受MPCPanel中的最上层的TabGround
*****************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Random = UnityEngine.Random;
public class TabGround0 : TabGround
{
#region 生命
protected override void Start()
{
InitTabBtnLst();
InitTab1();
InitPanelGoLst();
}
#endregion
#region 辅助
protected override void InitTabBtnLst()
{
Transform t = transform.FindChildDeep("BottomRight");
TabBtnLst = t.GetComponentsInChildren<TabBtn>(includeInactive: true).ToList();
foreach (TabBtn btn in TabBtnLst)
{
btn.Idle();
}
}
protected override void InitPanelGoLst()
{
PanelGoLst.Clear();
PanelGoLst.Add( gameObject.FindChildDeep("剧情回放"));
PanelGoLst.Add( gameObject.FindChildDeep("CG鉴赏"));
PanelGoLst.Add( gameObject.FindChildDeep("音乐赏析"));
foreach (GameObject go in PanelGoLst)
{
go.Hide();
}
PanelGoLst[0].Show();
}
#endregion
}
散的TabGround
挂在这两个节点上
PanelGoLst.Add( gameObject.FindChildDeep(“剧情回放”));
PanelGoLst.Add( gameObject.FindChildDeep(“CG鉴赏”));
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/// <summary></summary>
public class TabGround1 : TabGround
{
#region 生命
protected override void Start()
{
InitTabBtnLst();
InitTab1();
InitPanelGoLst();
}
#endregion
#region 辅助
protected override void InitTabBtnLst()
{
List<GameObject> gos= gameObject.FindChildDeep("TabGroud").GetChildrenLst();
foreach (GameObject go in gos)
{
TabBtnLst.Add(go.GetComponent<TabBtn>());
}
foreach (TabBtn btn in TabBtnLst)
{
btn.Idle();
}
TabBtnLst[0].Active();
}
protected override void InitPanelGoLst()
{
PanelGoLst = gameObject.FindChildDeep("PanelGoLst").GetChildrenLst();
}
#endregion
}
TabBtn
挂在所有的按钮上。(1,2,3,4这样的选项卡)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Security;
using System;
public class TabBtn : MonoBehaviour,IPointerDownHandler
{
public TabGround tabGround;
public Image Img;
public Sprite idleSprite;
public Sprite activeSprite;
private void Awake()
{
tabGround = transform.GetComponentInParent<TabGround>();
Img = GetComponent<Image>();
InitSprite();
}
void InitSprite()
{
string pathPre = "素材/zanghua/Sprite/";
int selfIdx = transform.GetSiblingIndex() + 1;
string idlePath = String.Format(pathPre + "sl_menu_page_{0}_a", selfIdx);
string activePath = String.Format(pathPre + "sl_menu_page_{0}_c", selfIdx);
idleSprite =Resources.Load<Sprite>(idlePath);
activeSprite = Resources.Load<Sprite>(activePath);
}
public void OnPointerDown(PointerEventData eventData)
{
//执行当前 UI 的 逻辑
tabGround.OnTabSelected(this);
//TODO 事件注册的做法
//new OnTabSelectCommand().Execute();
}
public void Idle()
{
Img.sprite = idleSprite;
}
public void Active()
{
Img.sprite = activeSprite;
}
}
modify 音乐赏析
/****************************************************
文件:MPCPanel.Music.cs
作者:lenovo
邮箱:
日期:2023/4/30 4:0:58
功能:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;
public partial class MPCPanel : MonoBehaviour
{
#region 属性
[Header("音乐赏析")]
/// <summary>有时面板上看不到按钮事件,测试看的</summary>
[SerializeField] List<Button> btnLst = new List<Button>();
[SerializeField] Button StopMusicBtn;
[SerializeField] int curId;
#endregion
#region 生命
public void InitMusic()
{
curId = -1;
Transform t = transform.FindChildDeep("MusicLst");
for (int i = 0; i < t.childCount; i++)
{
Button btn = t.GetChild(i).GetComponent<Button>();
btn.onClick.AddListener(()=>ClickMusicBtn(btn.transform.GetSiblingIndex()) ); //用i不行
btnLst.Add(btn);//测试看
}
//
StopMusicBtn = transform.GetButtonDeep("StopMusicBtn",() => AudioMgr.Instance.StopMPCMusic());
//
}
#endregion
#region 辅助
void ClickMusicBtn(int i)
{
AudioMgr.Instance.PlayMPCMusic(i);
curId= i;
}
#endregion
}
--------------------------------------------------
继续旅程
删掉原版的TabGround,用TabGroundSL:TabGroundSL ,和SaveLoadPanel ,都是挂在SaveLoadPanel 节点上
SaveLoadPanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine.UI;
/// <summary>用来存储和读取文件</summary>
public class SaveLoadPanel : MonoBehaviour
{
#region 字属
[SerializeField] Image LogoImg;
[SerializeField] Sprite SaveSprite;
[SerializeField] Sprite LoadSprite;
[SerializeField] Button GoMainBtn;
/// <summary>判断当前是存储还是读取</summary>
public bool isSave;
/// <summary>获取自身子集下面的 存档预制体的数组</summary>
public List<SaveSlot> slotList = new List<SaveSlot>();
#endregion
public void Init()
{
LogoImg = gameObject.GetComponentDeep<Image>("LogoImg");
SaveSprite = Resources.Load<Sprite>("素材/zanghua/Sprite/sl_title1");
LoadSprite = Resources.Load<Sprite>("素材/zanghua/Sprite/sl_title2 #9468");
GoMainBtn = transform.GetButtonDeep("GoMainBtn",()=>UIMgr.Instance.GoMainPanel());
}
#region 辅助
/// <summary>把存档预制体 添加进入数据</summary>
public void InitSlot(SaveSlot slot)
{
slotList.Add(slot);
}
/// <summary>把加载界面的logo赋值</summary>
public void SetLogoImgLoad()
{
LogoImg.sprite = LoadSprite;
}
public void SetLogoImgSave()
{
LogoImg.sprite = SaveSprite;
}
/// <summary>加载已拥有的文件数据</summary>
public void LoadFileData()
{
for (int i = 0; i < slotList.Count; i++)
{
if (File.Exists(Application.persistentDataPath + slotList[i].path)) //查询是否有文件 如果有的话
{
BinaryFormatter bf = new BinaryFormatter(); //实例化
FileStream fs = File.Open(Application.persistentDataPath + slotList[i].path, FileMode.Open); //打开i数据文件
SaveData save = (SaveData)bf.Deserialize(fs); //把二进制文件 反序列化 然后在强制转换成Save形式
slotList[i].saveImg.sprite = Resources.Load<Sprite>("BG/" + save.SaveImg); //获取存储预制体上面的图片
slotList[i].Des.text = save.SaveDes; //读取 文本 然后赋值
fs.Close();
}
else
{
//如果没有查询到文件
slotList[i].saveImg.sprite = Resources.Load<Sprite>("BG/" + "黑"); //获取存储预制体上面的图片
slotList[i].Des.text = "没有数据"; //没有数据
}
}
}
/// <summary>二进制存储数据文件</summary>
public void SaveGame(string path, SaveSlot slot)
{
SaveByBin(path, slot);
}
/// <summary>读取游戏数据文件</summary>
public void LoadGame(string path)
{
LoadByBin(path);
}
/// <summary>二进制存储数据</summary>
public void SaveByBin(string path, SaveSlot slot)
{
SaveData save = CreateSaveData(slot);
BinaryFormatter bf = new BinaryFormatter(); //实例化
FileStream fs = File.Create(Application.persistentDataPath + path); //创建文件
bf.Serialize(fs, save);
fs.Close(); //
}
/// <summary>创建存储文件信息</summary>
public SaveData CreateSaveData(SaveSlot slot)
{
SaveData save = new SaveData(); //实例化一下 序列化的脚本
save.CurLine = AVGMachine.Instance.curLine; //把文本下标 保存在save序列化脚本里面
save.StoryID = AVGMachine.Instance.curStoryID;
save.CurPolt = AVGMachine.Instance.TextInfo;
save.Alpha = UIMgr.Instance.PlotCanvas.GamePanel.BG2Alpha; //把背景图片2的透明度 保存在序列化脚本里面
save.BG1 = UIMgr.Instance.PlotCanvas.GamePanel.BG1.sprite.name; //获得 一号背景图片里面使用的图片名称
save.BG2 = UIMgr.Instance.PlotCanvas.GamePanel.BG2.sprite.name; //获得 2号背景图片里面使用的图片名称
save.SaveImg = slot.saveImg.sprite.name; //获得 存储预制体上的图片
save.SaveDes = slot.Des.text; //获得 存储预制体上的文本
return save; //返回存储数据文件
}
/// <summary>读取二进制游戏文件</summary>
public void LoadByBin(string path)
{
if (File.Exists(Application.persistentDataPath + path)) //判断当前的路径是否拥有文件
{
BinaryFormatter bf = new BinaryFormatter(); //实例化
FileStream fs = File.Open(Application.persistentDataPath + path, FileMode.Open); //打开i数据文件
SaveData save = (SaveData)bf.Deserialize(fs); //把二进制文件 反序列化 然后在强制转换成Save形式
fs.Close(); //关闭
SetGame(save); //把反序列化出来的数据文件 传递给setgame
}
}
/// <summary>把读取出来的游戏数据 在赋值回去</summary>
public void SetGame(SaveData save)
{
AVGMachine.Instance.curLine = save.CurLine; //把下标赋值
AVGMachine.Instance.curStoryID = save.StoryID;
AVGMachine.Instance.TextInfo = save.CurPolt;
AVGMachine.Instance.LoadUpdateBG(save.BG1, save.BG2, save.Alpha); //传递 背景1和2的图片名称 和 背景图片2的透明值
AVGMachine.Instance.GoState(TypeState.TYPING); //把当前的状态切换成 typing 打字状态
UIMgr.Instance.PlotCanvas.enabled = true; //显示剧情面板
gameObject.SetActive(false); //关闭自身面板
}
#endregion
}
TabGroundSL:TabGroundSL
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/// <summary></summary>
public class TabGroundSL : TabGround
{
#region 生命
protected override void Start()
{
InitTabBtnLst();
InitTab1();
InitPanelGoLst();
}
#endregion
#region 辅助
protected override void InitTabBtnLst()
{
List<GameObject> gos= gameObject.FindChildDeep("TabGround").GetChildrenLst();
foreach (GameObject go in gos)
{
TabBtnLst.Add(go.GetComponent<TabBtn>());
}
foreach (TabBtn btn in TabBtnLst)
{
btn.Idle();
}
TabBtnLst[0].Active();
}
protected override void InitPanelGoLst()
{
PanelGoLst = gameObject.FindChildDeep("SaveLoadObjLst").GetChildrenLst();
}
#endregion
}
--------------------------------------------------
modify 开始游戏
01 运行原版的,先是 黑屏Canvas 闪一下(显示又隐藏)。MainCanvas不变,MainPanel(主菜单界面)隐藏。然后显示 GamePanel(StartAVG)
02 黑屏Canvas 的滑动,有动画事件,单独提取出来(没引用一个个看动画器看麻烦了),放在BlackCanvas_BlackCanvas (所在的节点名_动画名)
BlackCanvas_BlackCanvas
/****************************************************
文件:BlackCanvas_BlackCanvas.cs
作者:lenovo
邮箱:
日期:2023/4/21 20:23:34
功能:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class BlackCanvas_BlackCanvas : MonoBehaviour
{
#region 辅助(方法顺序就是在动画中的顺序。不要移动顺序)
/// <summary>关闭菜单按钮</summary>
public void HideMainBtn()
{
StartCoroutine(HideMainBtnCrt());
}
/// <summary>开始游戏</summary>
public void StartGame()
{
StartCoroutine(StartGameCrt());//在MonoBehaviour中使用
}
/// <summary>avg开始运行</summary>
public void StartAVG()
{
AVGMachine.Instance.StartAVG();
}
/// <summary>关闭黑屏</summary>
public void HideBlack()
{
gameObject.Hide(); //关闭 自身
}
#endregion
#region 协程
IEnumerator StartGameCrt()
{
//第一种写法
while (UIMgr.Instance == null)//需要时间
{
yield return new WaitForEndOfFrame(); //等一帧
}
UIMgr.Instance.StartGame(); //调用开始游戏方法
}
IEnumerator HideMainBtnCrt()
{
while (UIMgr.Instance == null
|| UIMgr.Instance.MainCanvas == null
|| UIMgr.Instance.MainCanvas.MainPanel == null)//需要时间
{
yield return new WaitForEndOfFrame(); //等一帧
}
UIMgr.Instance.HideMainBtn();
}
#endregion
}
stars enabled
/****************************************************
文件:ExtendComponent.ShowHide.cs
作者:lenovo
邮箱:
日期:2023/4/22 15:32:47
功能:
*****************************************************/
using Codice.Client.BaseCommands;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Experimental.TerrainAPI;
using UnityEngine;
using Random = UnityEngine.Random;
public static partial class ExtendComponent
{
......
public static void Enabled(this Behaviour behaviour)
{
behaviour.enabled = true;
}
public static void Disabled(this Behaviour behaviour)
{
behaviour.enabled = false;
}
}
bug Asset database transaction committed twice!
虽然不影响运行
bug 开始游戏,仍然在主菜单,并且PlotCanvas被隐藏
开始游戏,仍然在主菜单,并且PlotCanvas被隐藏
原版是HideMainBtn,我调用HideMainPanel才能
PlotCanvas初始化不Hide
--------------------------------------------------
modify PlotCanvas.GamePanel 工具按钮(除了 自动、跳过)
GamePanel
/****************************************************
文件:GamePanel.cs
作者:lenovo
邮箱:
日期:2023/4/21 8:41:10
功能:
*****************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SocialPlatforms.Impl;
using UnityEngine.UI;
using Random = UnityEngine.Random;
public class GamePanel : MonoBehaviour
{
#region 字段
[SerializeField] Button NextBtn;
[Header("角色")]
public GameObject RoleCGrpLstGo;
public CanvasGroup RoleACGrp; //获得角色A号位置的 画布组
public CanvasGroup RoleBCGrp; //获得角色B号位置的 画布组
public CanvasGroup RoleCCGrp; //获得角色C号位置的 画布组
public float RoleAAlpha = 0; //A号人物的 Alpha 的值
public float RoleBAlpha = 0; //B号人物的 Alpha 的值
public float RoleCAlpha = 0; //C号人物的 Alpha 的值
//
public CanvasGroup BG1Group; //第一个背景画布
public CanvasGroup BG2Group; //第二个背景画布
[Header("选择按钮")]
public GameObject ChoiceBtnLstGo;
public Button ChoiceBtnA;
public Button ChoiceBtnB;
public Button ChoiceBtnC;
//
[Header("Image")]
/// <summary>对话框背景</summary>
public Image ContentBGImg;
public Image BG1; //获得背景图片 用来替换背景
public Image BG2; //获得背景图片, 只不过这个是第二个背景图片
public Image RoleImg; //获得人物名称 的背景图片
//
[Header("Text")]
/// <summary>对话文本</summary>
public Text DialogText;
/// <summary>用来显示人物名称的文本</summary>
public Text RoleText;
public Text BtnDesText;
[Header("float")]
public float TarSpeed = 2f; //人物显示的渐变速度
public float BG2Alpha = 0; //背景图片2 的 目标透明度
[Header("右下的按钮")]
[SerializeField] Button CloseBtn;
[SerializeField] Button SettingsBtn;
[SerializeField] Button SkipBtn;
[SerializeField] Button AutoBtn;
[SerializeField] Button LoadBtn;
[SerializeField] Button SaveBtn;
[SerializeField] Button ShowPlotBtnLstBtn;
[SerializeField] GameObject PlotBtnLstGo;
[Header("Auto Skip")]
/// <summary>判断是否开始了自动跳转按钮功能</summary>
public bool isAuto;
/// <summary>判断是否开始了快速跳过功能</summary>
public bool isSkip;
/// <summary>自动跳转的待机图片</summary>
public Sprite AutoIdleSprite;
/// <summary>自动跳转的激活图片</summary>
public Sprite AutoActiveSprite;
/// <summary>快速跳转的待机图片</summary>
public Sprite SkipIdleAprite;
/// <summary>快速跳转的激活图片</summary>
public Sprite SkipActiveSprite;
/// <summary>获得自动跳转按钮身上的图片组件</summary>
public Image AutoBtnImg;
/// <summary>获得快速跳转按钮身上的图片组件</summary>
public Image SkipBtnImg;
#endregion
#region 生命
public void Init()
{
InitRoleCanvasGroup();
RoleAAlpha = 0;
RoleBAlpha = 0;
RoleCAlpha = 0;
//
BG1Group = transform.GetComponentDeep<CanvasGroup>(GameObjectName.BG1);
BG2Group = transform.GetComponentDeep<CanvasGroup>(GameObjectName.BG2);
//
NextBtn = transform.GetButtonDeep("NextBtn",AVGMachine.Instance.UserClicked );
InitChoiceBtnLst();
InitPlotBtnLstAndImg();
//
ContentBGImg = transform.GetComponentDeep<Image>(GameObjectName.ContentBGImg);
BG1 = transform.GetComponentDeep<Image>(GameObjectName.BG1);
BG2 = transform.GetComponentDeep<Image>(GameObjectName.BG2);
RoleImg = transform.GetComponentDeep<Image>(GameObjectName.RoleImg);
//
DialogText = transform.GetComponentDeep<Text>(GameObjectName.DialogText);
RoleText = transform.GetComponentDeep<Text>(GameObjectName.RoleText);
BtnDesText = transform.GetComponentDeep<Text>(GameObjectName.BtnDesText);
//
TarSpeed = 2f;
BG2Alpha = 0;
//
AutoIdleSprite = LoadSprite("dialog_menu_auto_a") ;
AutoActiveSprite = LoadSprite("dialog_menu_auto_c");
SkipIdleAprite = LoadSprite("dialog_menu_skip_a");
SkipActiveSprite = LoadSprite("dialog_menu_skip_c");
}
void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
ShowRoleA(1);
ShowRoleB(1);
ShowRoleC(1);
}
if (Input.GetKeyDown(KeyCode.B))
{
ShowRoleA(0);
ShowRoleB(0);
ShowRoleC(0);
}
if (Input.GetKeyDown(KeyCode.C))
{
SetDialogText("jkakfkalgfhjlsglfjhgasljhfgshjafa");
}
if (Input.GetKeyDown(KeyCode.D))
{
SetRoleText(true, "不知道");
}
if (Input.GetKeyDown(KeyCode.E))
{
SetRoleText(false, "不知道");
}
#region 角色A的 透明渐变
//如果A号角色身上的画布的透明度 不等于 我们手动设置的 透明值的话
if (RoleACGrp.alpha != RoleAAlpha)
{
RoleACGrp.alpha = Mathf.Lerp(RoleACGrp.alpha, RoleAAlpha, TarSpeed * Time.deltaTime); //角色A的透明值 使用lerp 渐变到 我们设置的alhpa值
if (Mathf.Abs(RoleACGrp.alpha - RoleAAlpha) < 0.1f) //如果玩家的透明度 减去 我们设置的透明度 的绝对值 小于0.1的话
{
RoleACGrp.alpha = RoleAAlpha; //把目标的透明值 设置给 A号玩家的透明纸
}
}
#endregion
#region 角色B的 透明渐变
if (RoleBCGrp.alpha != RoleBAlpha) //如果A号角色身上的画布的透明度 不等于 我们手动设置的 透明值的话
{
RoleBCGrp.alpha = Mathf.Lerp(RoleBCGrp.alpha, RoleBAlpha, TarSpeed * Time.deltaTime); //角色A的透明值 使用lerp 渐变到 我们设置的alhpa值
if (Mathf.Abs(RoleBCGrp.alpha - RoleBAlpha) < 0.1f) //如果玩家的透明度 减去 我们设置的透明度 的绝对值 小于0.1的话
{
RoleBCGrp.alpha = RoleBAlpha; //把目标的透明值 设置给 A号玩家的透明纸
}
}
#endregion
#region 角色C的 透明渐变
if (RoleCCGrp.alpha != RoleCAlpha) //如果A号角色身上的画布的透明度 不等于 我们手动设置的 透明值的话
{
RoleCCGrp.alpha = Mathf.Lerp(RoleCCGrp.alpha, RoleCAlpha, TarSpeed * Time.deltaTime); //角色A的透明值 使用lerp 渐变到 我们设置的alhpa值
if (Mathf.Abs(RoleCCGrp.alpha - RoleCAlpha) < 0.1f) //如果玩家的透明度 减去 我们设置的透明度 的绝对值 小于0.1的话
{
RoleCCGrp.alpha = RoleCAlpha; //把目标的透明值 设置给 A号玩家的透明纸
}
}
#endregion
#region 背景图片2 渐变显示和隐藏
if (BG2Group.alpha != BG2Alpha)
{
//如果背景图片2 身上的 画布的 透明度 不等于 我们设置的目标透明度
BG2Group.alpha = Mathf.Lerp(BG2Group.alpha, BG2Alpha, TarSpeed * Time.deltaTime); //背景图片2的渐变值
if (Mathf.Abs(BG2Group.alpha - BG2Alpha) < 0.1f)
{
BG2Group.alpha = BG2Alpha; //直接把透明度 赋值给 画布组上面的透明度
}
}
#endregion
}
#endregion
#region 系统
#endregion
#region 辅助
private void InitRoleCanvasGroup()
{
RoleCGrpLstGo = gameObject.FindChildDeep("RoleCGrpLstGo");
RoleACGrp = transform.GetComponentDeep<CanvasGroup>("RoleACGrp");
RoleBCGrp = transform.GetComponentDeep<CanvasGroup>("RoleBCGrp");
RoleCCGrp = transform.GetComponentDeep<CanvasGroup>("RoleCCGrp");
}
Sprite LoadSprite(string spriteName)
{
return Resources.Load<Sprite>("素材/zanghua/Sprite/"+ spriteName);
}
private void IntiPlotImg()
{
SkipBtnImg = SkipBtn.GetComponent<Image>();
AutoBtnImg = AutoBtn.GetComponent<Image>();
}
void InitChoiceBtnLst()
{
ChoiceBtnLstGo = gameObject.FindChildDeep("ChoiceBtnLstGo");
ChoiceBtnA = transform.GetButtonDeep("ChoiceBtnA",()=>AVGMachine.Instance.ProcessChoiceBtnMSG(ChoiceBtnA));
ChoiceBtnB = transform.GetButtonDeep("ChoiceBtnB",()=>AVGMachine.Instance.ProcessChoiceBtnMSG(ChoiceBtnB));
ChoiceBtnC = transform.GetButtonDeep("ChoiceBtnC",()=>AVGMachine.Instance.ProcessChoiceBtnMSG(ChoiceBtnC));
}
void InitPlotBtnLstAndImg()
{
PlotBtnLstGo = gameObject.FindChildDeep("PlotBtnLstGo");
CloseBtn = transform.GetButtonDeep("CloseBtn", HideAnimPoltBtn);
SettingsBtn = transform.GetButtonDeep("SettingsBtn", UIMgr.Instance.OpenSettingsPanel);
SkipBtn = transform.GetButtonDeep("SkipBtn", SkipPlot);
AutoBtn = transform.GetButtonDeep("AutoBtn", AutoPlot);
LoadBtn = transform.GetButtonDeep("LoadBtn", UIMgr.Instance.ShowLoadPanel);
SaveBtn = transform.GetButtonDeep("SaveBtn", UIMgr.Instance.ShowSaveLoadPanel);
ShowPlotBtnLstBtn = transform.GetButtonDeep("ShowPlotBtnLstBtn", ShowPlotBtnLstGo);
IntiPlotImg();
}
/// <summary>更新背景图片 在读取存档之后</summary>
public void LoadUpdateBG(string bg1, string bg2, float alpha)
{
BG1.sprite = Resources.Load<Sprite>("BG/" + bg1); //把背景1和2的图片赋值
BG2.sprite = Resources.Load<Sprite>("BG/" + bg1);
BG2Alpha = alpha; //把透明度赋值给 背景2的透明度
}
public void LoadContent(Story01Data story)
{
ShowRoleA(story.Adisplay);
ShowRoleB(story.Bdisplay);
ShowRoleC(story.Cdisplay);
}
public void ClearContent()
{
ShowRoleA(0);
ShowRoleB(0);
ShowRoleC(0);
}
/// <summary>给对话文本赋值</summary>
public void SetDialogText(string value)
{
DialogText.text = value;
}
public void SetRoleText(bool isShow, string value)
{ //第一个布尔 是判断 是否要显示人物名称的背景图片 第二个 字符串 是 要显示的人物名称
//判断是否要显示人物的名称, 如果要实现 就设置人物的名称
if (isShow)
{
//如果当前要显示人物名称
RoleImg.gameObject.SetActive(true); //显示 人物名称的背景图片
RoleText.text = value; //把传递进来的人物名称 赋值
}
else
{
//如果不显示人物的名称和背景图片
RoleImg.gameObject.SetActive(false); //隐藏人物名称的背景图片
}
}
/// <summary>调用显示按钮的方法</summary>
public void ShowBtnList(Story01Data story)
{
ShowBtnList(story.Ischoice, story.Isa, story.Isb, story.Isc);
}
/// <summary></summary>
public void ShowBtnList(bool value, bool A, bool B, bool C)
{
//显示或者隐藏按钮
ChoiceBtnA.SetActive(value);
ChoiceBtnB.SetActive(value);
ChoiceBtnC.SetActive(value);
// 判断当前按钮在当前剧情中是否要显示或者隐藏
ChoiceBtnA.SetActive(A);
ChoiceBtnB.SetActive(B);
ChoiceBtnC.SetActive(C);
}
/// <summary>隐藏全部的按钮</summary>
public void HideBtnList()
{
//显示或者隐藏按钮
ChoiceBtnA.Hide();
ChoiceBtnB.Hide();
ChoiceBtnC.Hide();
// 判断当前按钮在当前剧情中是否要显示或者隐藏
ChoiceBtnA.Hide();
ChoiceBtnB.Hide();
ChoiceBtnC.Hide();
}
/// <summary>把传递进来的图片 添加到 号位置的图片</summary>
public void ChangeRoleSprite(AVGAssetCfg cfg)
{
RoleACGrp.GetComponent<Image>().sprite = cfg.charaA;
RoleBCGrp.GetComponent<Image>().sprite = cfg.charaB;
RoleCCGrp.GetComponent<Image>().sprite = cfg.charaC;
}
/// <summary>设置按钮里面的内容</summary>
public void SetChoiceBtnText(Story01Data story)
{
ChoiceBtnA.GetComponentInChildren<Text>().text = story.Btnatext ;
ChoiceBtnB.GetComponentInChildren<Text>().text = story.Btnbtext;
ChoiceBtnC.GetComponentInChildren<Text>().text = story.Btnctext;
}
/// <summary>设置按钮的名称</summary>
public void SetChoiceBtnName(Story01Data story)
{
ChoiceBtnA.name = story.Btnaname;
ChoiceBtnB.name = story.Btnbname;
ChoiceBtnC.name = story.Btncname;
}
/// <summary>
/// 更换背景图片
/// </summary>
/// <param name="img">图片类型</param>
/// <param name="img2"><是第二个的背景/param>
/// <param name="alpha">第二个背景图片的alpha透明度</param>
public void ChangeBG(Sprite img, Sprite img2, int alpha)
{
BG1.sprite = img;
BG2.sprite = img2;
BG2Alpha = alpha;
}
/// <summary>判断是否显示人物名称和背景图片</summary>
public void ShowRoleName(Story01Data story)
{
UIMgr.Instance.SetRoleText(story.Isrole, story.Roletext ); //第一个布尔 是判断 是否要显示人物名称的背景图片 第二个 字符串 是 要显示的人物名称
}
#region 判断ABC三个位置的角色 是否显示或者隐藏
bool IsAlphaLegal(int value)
{
if (value != 0 && value != 1)
{
return false;
}
return true;
}
/// <summary>调用显示角色位置的方法</summary>
public void ShowRoleA(int value)
{
if (IsAlphaLegal(value) == false)
{
return;
}
RoleAAlpha = value;
}
/// <summary>调用显示角色位置的方法</summary>
public void ShowRoleB(int value)
{
if (IsAlphaLegal(value) == false)
{
return;
}
RoleBAlpha = value;
}
/// <summary>调用显示角色位置的方法</summary>
public void ShowRoleC(int value)
{
if (IsAlphaLegal(value) == false)
{
return;
}
RoleCAlpha = value;
}
public void SetDialogBoxAlpha(float dialogBoxAlpha)
{
ContentBGImg.GetComponent<CanvasGroup>().alpha = dialogBoxAlpha;
}
/// <summary>隐藏对话框背景</summary>
internal void HideContentBG()
{
ContentBGImg.Hide();
}
internal void HideAnimPoltBtn()
{
PlotBtnLstGo.GetComponent<Animator>().SetBool("Open", false);
}
internal void ShowPlotBtnLstGo()
{
PlotBtnLstGo.Show();
ContentBGImg.Show();
}
internal void HidePlotBtn()
{
PlotBtnLstGo.Hide();
ContentBGImg.Hide();
}
internal void ShowAnimPoltBtn()
{
PlotBtnLstGo.Show();
ContentBGImg.Show();
PlotBtnLstGo.GetComponent<Animator>().SetBool("Open", true);
}
#region 右下按钮
internal void SetPlotBtnDesText(string btnName)
{
BtnDesText.text = btnName;
}
internal void EnabledBtnDesText()
{
BtnDesText.Enabled();
}
internal void DisabledBtnDesText()
{
BtnDesText.Disabled();
}
internal void SkipPlot()
{
if (isSkip)
{
//如果开启了自动跳转
isSkip = false;
//关闭 快速跳转的功能
SkipBtnImg.sprite = SkipIdleAprite; //把图片切换成待机状态
AVGMachine.Instance.isSkip = false; //当前不可以快速跳转剧情
}
else
{
if (isAuto)
{ //如果当前开启了自动状态
//如果开启了自动跳转
isAuto = false;
//关闭自动跳转下一行的功能
AutoBtnImg.sprite = AutoIdleSprite; //图片切换成待机图片
AVGMachine.Instance.isAuto = false; //当前不可以自动跳转到下一句
}
//如果 为false 代表没有开启
isSkip = true; //为true 开启
//执行 快速跳转的功能
SkipBtnImg.sprite = SkipActiveSprite; //把图片切换成激活状态
AVGMachine.Instance.isSkip = true; //当前可以快速跳转剧情
}
}
internal void AutoPlot()
{
if (isAuto)
{
isAuto = false;
//关闭自动跳转下一行的功能
AutoBtnImg.sprite = AutoIdleSprite; //图片切换成待机图片
AVGMachine.Instance.isAuto = false; //当前不可以自动跳转到下一句
}
else
{
//如果 为false 代表没有开启
isAuto = true; //为true 开启
//执行 自动跳转下一行的功能
AutoBtnImg.sprite = AutoActiveSprite; //图片切换成激活图片
AVGMachine.Instance.isAuto = true; //当前可以自动跳转到下一句
}
}
#endregion
#endregion
#endregion
}
modify GameBtn
/****************************************************
文件:
作者:WWS
日期:2023/05/01 17:14:52
功能:PlotCanvas.GamePanel右下的工具按钮
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class GameBtn : MonoBehaviour,IPointerEnterHandler,IPointerExitHandler
{
/// <summary>按钮的名称</summary>
public string BtnName;
#region 系统
public void OnPointerEnter(PointerEventData eventData)
{
UIMgr.Instance.PlotCanvas.GamePanel.EnabledBtnDesText();
UIMgr.Instance.PlotCanvas.GamePanel.SetPlotBtnDesText(BtnName);
}
public void OnPointerExit(PointerEventData eventData)
{
UIMgr.Instance.PlotCanvas.GamePanel.DisabledBtnDesText();
}
#endregion
}
stars SetActive
Button之类
Button
:Selectable
:UIBehaviour
:MonoBehaviour
:Behaviour // Behaviours are Components that can be enabled or disabled.
public static void SetActive(this Behaviour behaviour,bool state)
{
behaviour.gameObject.SetActive(state);
}
modify 处理人物SO
/****************************************************
文件:ResourcesInitRole.cs
作者:lenovo
邮箱:
日期:2023/5/2 15:27:9
功能:初始化Assets/Resources/Role下的两个AVGAssetCfg,防止丢失
*****************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Random = UnityEngine.Random;
public class ResourcesInitRole
{
[MenuItem(DefinePath.MenuItem_AVG + "01 定位Role【AVGAssetCfg】", false, 0)]//按钮在菜单栏的位置
public static void ActiveAVGAssetCfgSO() //文件夹
{
Common.Selection_ActiveObject( "Assets/Resources/Role");
}
[MenuItem(DefinePath.MenuItem_AVG + "01 初始化Role【AVGAssetCfg】", false, 0)]//按钮在菜单栏的位置
public static void InitAVGAssetCfgSO() //文件夹
{
AVGAssetCfg role02 = AssetDatabase.LoadAssetAtPath<AVGAssetCfg>("Assets/Resources/Role/2.asset");
AVGAssetCfg role03 = AssetDatabase.LoadAssetAtPath<AVGAssetCfg>("Assets/Resources/Role/3.asset");
role02.charaA = LoadRoleSprite("cuimianshi");
role02.charaB = LoadRoleSprite("xiao");
role02.charaC = LoadRoleSprite("yinxiao");
role03.charaA = LoadRoleSprite("xiao");
role03.charaB = LoadRoleSprite("cuimianshi");
role03.charaC = LoadRoleSprite("yinxiao");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
private static Sprite LoadRoleSprite(string spriteName)
{
return Resources.Load<Sprite>("RoleIMG/"+spriteName);
}
}
bug SO数据丢失
Resources/Role/2 不会
Resources/Role/3 的数据 sprite 经常在运行游戏后丢失为 none(执行一次选项按钮后)
modify PlotCanvas.LoadDiaTextPanel
这个是之前对话的记录面板,在GamePanel时滚动滑轮触发。
但是问题时好像不是随时记录的
--------------------------------------------------
watch QuickSheet插件的使用
里面有说明文档 Assets/QuickSheet/Doc/Unity-Quicksheet.pdf
01 新建(这两个连锁的)
Excel Settings
Excel Machine
02 新建表格,Excel Machine 中加入表格,确定 Sheet,属性类型。配置Editor,Runtime文件夹路径(不包括Assets)。然后Generate。(我这里报错了)
03 解决小bug后,右键reimport
bug 插件小bug
手动改回来 //
整体待改进--------------------------------------------------
就是节点间的访问
事件注册的使用