从零开始带你开发橙光游戏AVG框架(仿 葬花 )

来源

从零开始带你开发橙光游戏AVG框架【55课数 收费】
从零开始带你开发橙光游戏AVG框架 unity教程【16课数 免费】

介绍

QuickSheet使用

bug 包报错

可能是我换了untiy版本的原因
Manual sovle
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第1张图片

bug ICSharpCode.SharpZipLib重复

导了一个文件夹,有自己的库,也包含GameFramework、UnityGameFramework,暂时先删掉GameFramework、UnityGameFramework就完事了

bug 中文乱码

VS(Visual Studio)更改文件编码
没改utf-8之前修改过,再改成utf-8没用。
再从压缩包拉出来这些乱码的来覆盖掉,也一样没用

stars 无参事件注册

/// 存档,确定取消
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);
	}
	/// 
	/// 获得标签为 tag 的 物体 身上的 T 组件
	/// 
	/// 
	/// 
	/// 
	/// 
	/// 
	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 字段

	/// 悬停音效
	public AudioSource EnterMusic;
	/// 点击音效
	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;


/// 存档,确定取消
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

方便脚本名,标签名,节点名,三者对应
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第2张图片

bug 修改 标签 Polt 为 Plot

从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第3张图片
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第4张图片

watch 带标签 CG

类似 AVG/MainCanvas/LoadCGPoltPanel/BG/CG鉴赏/ObjectPanel/PolePre/PoltBG/Polt

modify 新建脚本LoadCGPlotPanel.cs

位置是 AVG/MainCanvas/LoadCGPlotPanel

modify 筛选卡

modify AudioMgr.cs

AudioMgr.cs单例,并且挂到GameStart的子节点上,并且对相关引用进行调整
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第5张图片

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_狂乱之宴") );
	}

	/// 播放音频
	public void PlayMusic(int id) 
	{
        
        audioSrc.clip = AudioList[id]; 
        audioSrc.Play();  
    }


	/// 停止播放播放音频
	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 估计用到,后面报错再安装
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第6张图片

modify AudioSource与AudioMgr

01 将所有 AudioSource 放到 AudioMgr 的子节点上,并且改名了3个。
02 后面有移动了 节点 AVG 上的 AudioSource 到 AudioMgr 的子节点 AVGSrc 上
03 类似AVG/MainCanvas/LoadCGPlotPanel/BG/音乐赏析/BGMusic,也有一个AudioSource

暂时不清楚为什么用那么多AudioSource,放内存应该用字典。(需要同时放?)
原版放在 SettingPanel 上
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第7张图片

watch SettingPanel 关系了 音量

也就是做好 SettingPanel 和 AudioMgr 的关系
就是将相关代码 放 AudioMgr,让 SettingPanel 调用(Slider嘛)
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第8张图片

modify EnterPanel通过AudioMgr调用AudioSource

前面的 EnterPanel 用到了 这两个 AudioSource ,也放到 AudioMgr 上,通过访问 AudioMgr 来调用
改名为 EnterBtnSrc, DownBtnSrc,省下两个 Tag

两个AudioSource移动 改名 引用 删Tag

从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第9张图片

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 需要传参的事件注册

所以看了
框架搭建 决定版:理论强化篇(第三季) 53课数

自顶向下

自顶向下用方法

自底向上 之一 委托

自底向上用委托、事件

委托要注册,注销。
存在 回调地狱
要声明委托
缺点是,多个子节点都需要访问父节点,每个子节点都要声明委托,麻烦。

父节点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改为枚举,增加可读性

/// 
/// None=1,
/// QuitGame = 2,
/// ReturnMainPanel = 3,
/// SL = 5,
/// Loading = 6,
/// IsLoadPlotBack = 7,
/// IsLoadCG = 8,
/// 
public enum EventID
{
	None = 1,
	QuitGame = 2,
	ReturnMainPanel = 3,
	/// 存档
	SaveFile = 5,
	/// 打开存档面板,常听的SL大法
	OpenSLPanel = 6,
	/// 剧情
	LoadPlot = 7,
	/// CG(计算机动画)
	LoadCG = 8,

}

bug Google.GData.Client

Reference has errors ‘Google.GData.Client’.
不明,莫名其妙的坏了,好了

bug 程序集引用丢失

原本是都在根目录
QFramework
QFrameworkData

改成在Plugins/QFramework就报错了,而且会一直在根目录生成QFrameworkData
QFramework
QFrameworkData
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第10张图片

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引用 ,所以难管理。
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第11张图片

bug 动画事件调用某些属性顺序

01 动画事件调用某些属性
02 属性在Init()方法中赋值
但 01 比 02 快,导致属性还没赋值就被调用

所以先这样,这是跟Siki学院的Ocean老师学RealFrame时的。就是 等人再干活
Crt 是协程的英文Coroutine,我选的 简写

	/// 关闭菜单按钮
	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)
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第12张图片

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
借点命名都是,如果发生节点丢失很难区分。(虽然有注释)
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第13张图片

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();
	}
}

从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第14张图片

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( PlayEnterMusic);
		//this.RegisterEvent(PlayDownMusic);
		yesBtn = gameObject.GetComponentDeep<Button>("YesBtn");
		noBtn = gameObject.GetComponentDeep<Button>("NoBtn");
		//
		//yesBtn.gameObject.GetComponent();	//mark
		yesBtn.onClick.AddListener( UIMgr.Instance.YesEvent );
		//
		//yesBtn.gameObject.GetComponent();
		noBtn.onClick.AddListener( NoEvent );

	}


	private void OnDestroy()
	{
	//	OverButtonEvent.UnRegister(PlayEnterMusic);
	//	ClickButtonEvent.UnRegister(PlayDownMusic);

	}
	#endregion

	#region 系统

	#endregion

	#region 辅助


	public void NoEvent()
	{
		Close();
	}


	/// 
	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 辅助
	/// 退出确认面板  就是隐藏 确认面板
	public void QuitEnter()
	{
		UIMgr.Instance.EnterCanvas.Hide();
	}
	#endregion

}


按钮音效

同时加到下面4个按钮上
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第15张图片

using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.EventSystems;


/// 存档,确定取消
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

}

效果

从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第16张图片

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;
	}

    /// 
    /// 深度查找子对象transform引用
    /// 
    /// 父对象
    /// 具体查找的子对象名称
    /// 
    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>();
    }




	    /// 
    /// 深度查找子对象transform引用
    /// 
    /// 父对象
    /// 具体查找的子对象名称
    /// 
    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();
	}

	/// 隐藏设置界面
	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();
	}


	/// 更新数据
	public void UpdateValue() 
	{
		AudioMgr.Instance.UpdateValue();
		UIMgr.Instance.SetDialogBoxAlpha(DialogBoxAlpha)  ;   //把对话框文本的透明度赋值
        AVGMachine.Instance.TextSpeed = TextSpeed;   //把速度赋值过去
    }



	/// 把滚动条里面的参数  赋值给   当前脚本上面的参数
	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;
    }



	/// 把数据赋值给滚动条的value值
	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;   
    }


	/// 存储数据的方法
	public void SaveValue() 
	{
        SettingData save = CreateSaveGo();    // 接收存档数据信息 
        BinaryFormatter bf = new BinaryFormatter();  //实例化
        FileStream fs = File.Create(Application.persistentDataPath +"/Setting");  //创建一个名为 Setting的文件
        bf.Serialize(fs,save);  //序列化
        fs.Close();  //关掉
    }


	/// 创建存档数据信息
	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;
    }


	/// 设置游戏参数	
	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();  //音频数据更新
    }


	/// 重置所有选项的按钮
	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;
    }


	/// 重置音量	
	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之前(调一下顺序)

	/// 更新数据
	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 调用和初始化
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第17张图片

/****************************************************
    文件: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);
	}


	/// 窗口模式
	public void WindowScreen()
	{
		UnSelectBtn();
		Screen.SetResolution(1920, 1080, fullscreen: false);   
		WindowScreenBtn.GetComponent<Image>().sprite = SpriteLst[3]; 
	}


	/// 全屏方法,InitSpriteLst之后
	public void FullScreen()
	{
		UnSelectBtn();
		Screen.SetResolution(1920, 1080, fullscreen: true);  
		FullScreenBtn.GetComponent<Image>().sprite = SpriteLst[2];  

	}


	/// 取消按钮的激活状态	
	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,面板位置关系不一样(不是都能在子节点找到,拖拽的话丢失麻烦)
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第18张图片
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第19张图片

TabGround基类

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;


/// 选项卡基类

public class TabGround : MonoBehaviour
{
    //管理按钮 并且 判断   哪个面板
    public List<TabBtn> TabBtnLst = new List<TabBtn>();
	/// 存储数据面板
	public List<GameObject> PanelGoLst=new List<GameObject>();   
	/// 用来存储当前点击的是哪个按钮
	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();
            }
        }
    }

	/// 重置所有按钮的图片
	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;
/// 

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 音乐赏析

从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第20张图片

/****************************************************
    文件: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("音乐赏析")]
	/// 有时面板上看不到按钮事件,测试看的
	[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 节点上
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第21张图片

SaveLoadPanel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine.UI;

/// 用来存储和读取文件
public class SaveLoadPanel : MonoBehaviour
{


	#region 字属


	[SerializeField] Image LogoImg;		
	[SerializeField] Sprite SaveSprite; 
	[SerializeField] Sprite LoadSprite; 
	[SerializeField] Button GoMainBtn; 
	/// 判断当前是存储还是读取
	public bool isSave;   
	/// 获取自身子集下面的 存档预制体的数组
	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 辅助


	/// 把存档预制体 添加进入数据
	public void InitSlot(SaveSlot slot)
	{
		slotList.Add(slot);
	}


	/// 把加载界面的logo赋值
	public void SetLogoImgLoad()
	{
		LogoImg.sprite = LoadSprite;
	}

	public void SetLogoImgSave()
	{
		LogoImg.sprite = SaveSprite;
	}

	/// 加载已拥有的文件数据
	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 = "没有数据";    //没有数据
			}
		}

	}


	/// 二进制存储数据文件
	public void SaveGame(string path, SaveSlot slot)
	{
		SaveByBin(path, slot);
	}


	/// 读取游戏数据文件
	public void LoadGame(string path)
	{
		LoadByBin(path);
	}


	/// 二进制存储数据
	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();  //
	}


	/// 创建存储文件信息
	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;   //返回存储数据文件
	}


	/// 读取二进制游戏文件
	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
		}
	}


	/// 把读取出来的游戏数据 在赋值回去
	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;
/// 

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 (所在的节点名_动画名)

从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第22张图片

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 辅助(方法顺序就是在动画中的顺序。不要移动顺序)

	/// 关闭菜单按钮
	public void HideMainBtn()
	{
		StartCoroutine(HideMainBtnCrt());
	}


	/// 开始游戏
	public void StartGame()
	{
		StartCoroutine(StartGameCrt());//在MonoBehaviour中使用
	}

	/// avg开始运行
	public void StartAVG()
	{
		AVGMachine.Instance.StartAVG();
	}

	/// 关闭黑屏
	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")]
	/// 对话框背景
	public Image ContentBGImg;   
	public Image BG1;   //获得背景图片  用来替换背景
	public Image BG2;   //获得背景图片,  只不过这个是第二个背景图片
	public Image RoleImg;   //获得人物名称 的背景图片
							//
	[Header("Text")]
	/// 对话文本
	public Text DialogText;   
	/// 用来显示人物名称的文本
	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")]
	/// 判断是否开始了自动跳转按钮功能
	public bool isAuto;   
	/// 判断是否开始了快速跳过功能
	public bool isSkip;   
	/// 自动跳转的待机图片
	public Sprite AutoIdleSprite;   
	/// 自动跳转的激活图片
	public Sprite AutoActiveSprite;  
	/// 快速跳转的待机图片
	public Sprite SkipIdleAprite;   
	/// 快速跳转的激活图片
	public Sprite SkipActiveSprite;  
	/// 获得自动跳转按钮身上的图片组件
	public Image AutoBtnImg;  
	/// 获得快速跳转按钮身上的图片组件
	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();
	}


	/// 更新背景图片 在读取存档之后
	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);
	}


	/// 给对话文本赋值
	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);   //隐藏人物名称的背景图片
		}

	}

	/// 调用显示按钮的方法
	public void ShowBtnList(Story01Data story)
	{
		ShowBtnList(story.Ischoice, story.Isa, story.Isb, story.Isc);
	}

		/// 
	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);
	}

	/// 隐藏全部的按钮
	public void HideBtnList()
	{
		//显示或者隐藏按钮
		ChoiceBtnA.Hide();
		ChoiceBtnB.Hide();
		ChoiceBtnC.Hide();
		// 判断当前按钮在当前剧情中是否要显示或者隐藏
		ChoiceBtnA.Hide();
		ChoiceBtnB.Hide();
		ChoiceBtnC.Hide();
	}


	/// 把传递进来的图片 添加到 号位置的图片
	public void ChangeRoleSprite(AVGAssetCfg cfg)
	{
		RoleACGrp.GetComponent<Image>().sprite = cfg.charaA;
		RoleBCGrp.GetComponent<Image>().sprite = cfg.charaB;
		RoleCCGrp.GetComponent<Image>().sprite = cfg.charaC;
	}



	/// 设置按钮里面的内容
	public void SetChoiceBtnText(Story01Data story)
	{
		ChoiceBtnA.GetComponentInChildren<Text>().text = story.Btnatext ;
		ChoiceBtnB.GetComponentInChildren<Text>().text = story.Btnbtext;
		ChoiceBtnC.GetComponentInChildren<Text>().text = story.Btnctext;
	}


	/// 设置按钮的名称
	public void SetChoiceBtnName(Story01Data story)
	{
		ChoiceBtnA.name = story.Btnaname;
		ChoiceBtnB.name = story.Btnbname;
		ChoiceBtnC.name = story.Btncname;
	}

	///  
	/// 更换背景图片
	/// 
	/// 图片类型
	/// <是第二个的背景/param>
	/// 第二个背景图片的alpha透明度
	public void ChangeBG(Sprite img, Sprite img2, int alpha)
	{
		BG1.sprite = img;  
		BG2.sprite = img2; 
		BG2Alpha = alpha;  
	}


	/// 判断是否显示人物名称和背景图片
	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;
	}
	/// 调用显示角色位置的方法
	public void ShowRoleA(int value)
	{
		if (IsAlphaLegal(value) == false)
		{
			return;
		}
		RoleAAlpha = value;  
	}

	/// 调用显示角色位置的方法
	public void ShowRoleB(int value)
	{
		if (IsAlphaLegal(value) == false)
		{
			return;
		}
		RoleBAlpha = value;
	}

	/// 调用显示角色位置的方法
	public void ShowRoleC(int value)
	{
		if (IsAlphaLegal(value) == false)
		{
			return;
		}
		RoleCAlpha = value;  
	}

	public void SetDialogBoxAlpha(float dialogBoxAlpha)
	{
		ContentBGImg.GetComponent<CanvasGroup>().alpha = dialogBoxAlpha;  

	}

	/// 隐藏对话框背景
	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
{

	/// 按钮的名称
	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

从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第23张图片

/****************************************************
    文件: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
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第24张图片

02 新建表格,Excel Machine 中加入表格,确定 Sheet,属性类型。配置Editor,Runtime文件夹路径(不包括Assets)。然后Generate。(我这里报错了)
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第25张图片

从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第26张图片

03 解决小bug后,右键reimport
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第27张图片

bug 插件小bug

手动改回来 //
从零开始带你开发橙光游戏AVG框架(仿 葬花 )_第28张图片

整体待改进--------------------------------------------------

就是节点间的访问
事件注册的使用

你可能感兴趣的:(游戏,unity,游戏引擎)