从零开始带你开发橙光游戏AVG框架(仿 葬花 )
来源
从零开始带你开发橙光游戏AVG框架【55课数 收费】
从零开始带你开发橙光游戏AVG框架 unity教程【16课数 免费】
介绍
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;}}#endregionpublic 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 需要传参的事件注册
所以看了
框架搭建 决定版:理论强化篇(第三季) 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改为枚举,增加可读性
/// <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>(); //markyesBtn.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>(); #endregionpublic 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
手动改回来 //
整体待改进--------------------------------------------------
就是节点间的访问
事件注册的使用
从零开始带你开发橙光游戏AVG框架(仿 葬花 )相关推荐
- 教你从头写游戏服务器框架
本文由云+社区发表 作者:韩伟 前言 大概已经有差不多一年没写技术文章了,原因是今年投入了一些具体游戏项目的开发.这些新的游戏项目,比较接近独立游戏的开发方式.我觉得公司的"祖传" ...
- golang游戏服务器框架_教你从头写游戏服务器框架
需求 由于"越通用的代码,就是越没用的代码",所以在设计之初,我就认为应该使用分层的模式来构建整个系统.按照游戏服务器的一般需求划分,最基本的可以分为两层: 底层基础功能:包括通信 ...
- 教你从头写游戏服务器框架 1
本文由云+社区发表 作者:韩伟 前言 大概已经有差不多一年没写技术文章了,原因是今年投入了一些具体游戏项目的开发.这些新的游戏项目,比较接近独立游戏的开发方式.我觉得公司的"祖传" ...
- Silverlight游戏设计(Game Design):(四)从零开始搭建游戏主体框架
思路已俨然清晰,接下来要做的是从零开始搭建游戏的主体框架.Silverlight作为RIA界的新宠儿,不仅在游戏开发方面性能优越,在企业开发领域的优异表现同样日益显著.像我们这些长期从事.NET开发的 ...
- 童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(2)
导语 上一期我们带大家完成了魔塔游戏每一层的初始化画面的制作: 童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(1) 这一期我们会带大家进一步复现我们的魔塔小游戏,主要内容包括英雄类 ...
- 如何从零开始用 C++ 开发一款游戏引擎?
原文链接:https://hackernoon.com/build-a-game-engine-from-scratch-in-c 游戏开发一直很能激励学生学习高级计算机科学.可能有些人认为游戏是孩子 ...
- 童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(3)
导语 上一期我们主要带大家写了勇士类,以及勇士与一些简单的地图元素接触时所触发的事件: 童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(2) 这一期我们会带大家进一步复现我们的魔塔小 ...
- 游戏开发心得——书籍篇——《游戏引擎框架》-专业工具
游戏开发心得--书籍篇--<游戏引擎框架>-专业工具 FOR THE SIGMA FOR THE GTINDER FOR THE ROBOMASTER 简介: 学习<游戏引擎框架&g ...
- arduino nano 蓝牙_探索 Golang 云原生游戏服务器开发,5 分钟上手 Nano 游戏服务器框架...
介绍 Nano 是什么? 轻量级,方便,高性能 golang 的游戏服务器框架. nano 是一个轻量级的服务器框架,它最适合的应用领域是网页游戏.社交游戏.移动游戏的服务端.当然还不仅仅是游戏,用 ...
最新文章
- 20线程测试cpu性能软件,CPU常用跑分软件 你知道那些?
- ueditor初始化
- comparing ORB and AKAZE
- mysql事务和非事物_mysql事务型与非事务型表1.8.5.3. 事务和原子操作
- 吴恩达CS229速查表
- 32乘法运算_算术运算指令
- hibernate运行很慢?查一张只有几条记录的表都要一俩分钟?[问题记录]
- 高斯过程和高斯过程回归
- 软件工程概论网站系统开发基础及目标概述
- postgresql使用pg_rman备份恢复
- JAVA数据类型的强制转换
- 联想无线网卡 linux驱动,联想R7000 Ubuntu无线网卡驱动
- cass打开dwg文件无效_CAD文件打开时图形文件显示无效怎么办
- 学习方法和态度比什么都重要
- PMP-PMBOK图解项目管理(436页PPT、完整版-经典)
- android动态mac地址,Android 版本兼容 — Android 6.0 和 7.0后获取Mac地址
- 禅道项目管理系统安装后显示为空白
- 首都经贸大学计算机科学与技术专业,河北经贸大学经济管理学院的计算机科学与技术专业怎么样啊...
- ArrayList类的详解
- Sentinel持久化