///<summary> /// 移除无效的脚本 ///</summary> privatestaticvoidRemoveAllMissingScripts(GameObject go) { Component[] components = go.GetComponents<Component>(); var r = 0; var serializedObject = new SerializedObject(go); var prop = serializedObject.FindProperty("m_Component"); for (int i = 0; i < components.Length; i++) { if (components[i] == null) { string s = go.name; Transform t = go.transform; while (t.parent != null) { s = t.parent.name +"/"+s; t = t.parent; } Debug.Log (s + " has an empty script attached in position: " + i, go);
影响SpriteAtlas是否需要采用Later Bind(触不触发SpriteAtlasManager.atlasRequested接口,通过这个接口结合图集Variant我们可以做到例如不同平台加载不同图集的适配)。反之勾选Include in Build在依赖使用时会自动加载依赖的Sprite Atlas。 这里貌似有些Unity版本有个Bug,勾选Include in Build会导致资源打包冗余
在加载使用SpriteAtlas(仅当设置成不勾选Include in Build)的相关资源时(比如UI Prefab)上诉回调会被触发完成对SpriteAtlas的加载,确保引用的Sprite能正确显示。同时这一机制就允许我们做到不同平台的不同图集资源加载适配这样的功能。
疑问:
Later Bind只是加载SpriteAtlas提供的一个回调接口(无论是主动还是被动(依赖)加载),回调接口内只知道加载了指定的SpriteAtlas却不知道具体的详细用法,这样一来基于对象绑定和索引计数的资源管理系统就无法正确的加载管理了(因为被动(依赖)加载得不到SpriteAtlas的依赖信息)。
// Factory method that generates a playable based on this asset publicoverride Playable CreatePlayable(PlayableGraph graph, GameObject go) { var playable = ScriptPlayable<EventDispatcherPlayable>.Create(graph); playable.GetBehaviour().EventId = EventId; return playable; } }
// Called when the owning graph starts playing // Playable开始执行时 publicoverridevoidOnGraphStart(Playable playable) {
}
// Called when the owning graph stops playing // Playable停止执行时 publicoverridevoidOnGraphStop(Playable playable) {
}
// Called when the state of the playable is set to Play // 触发Playable执行时 publicoverridevoidOnBehaviourPlay(Playable playable, FrameData info) { Debug.Log(string.Format("OnBehaviourPlay() 分发事件 : {0}", EventId)); }
// Called when the state of the playable is set to Paused // Playable暂停执行时 publicoverridevoidOnBehaviourPause(Playable playable, FrameData info) {
}
// Called each frame while the state is set to Play // Playable更新执行时 publicoverridevoidPrepareFrame(Playable playable, FrameData info) {
The Playables API allows for dynamic animation blending. This means that objects in the scenes could provide their own animations. For example, animations for weapons, chests, and traps could be dynamically added to the PlayableGraph and used for a certain duration.
The Playables API allows you to easily play a single animation without the overhead involved in creating and managing an AnimatorController asset.
The Playables API allows users to dynamically create blending graphs and control the blending weights directly frame by frame.
A PlayableGraph can be created at runtime, adding playable node as needed, based on conditions. Instead of having a huge “one-size-fit-all” graph where nodes are enabled and disabled, the PlayableGraph can be tailored to fit the requirements of the current situation.
// Called each frame while the state is set to Play // Playable更新执行时 publicoverridevoidProcessFrame(Playable playable, FrameData info, object playerData) { TryPlayAudio(); }
// Called when the state of the playable is set to Paused // Playable暂停执行时 publicoverridevoidOnBehaviourPause(Playable playable, FrameData info) { Debug.Log($"AudioBehaviour:OnBehaviourPause() 执行指令:{Name}"); if(SourceAudio != null && SourceAudio.clip != null) { SourceAudio.clip = null; } mIsPlayingAudio = false; } ///<summary> /// 尝试执行音乐播放 ///</summary> privatevoidTryPlayAudio() { if(mIsPlayingAudio == false && SourceAudio != null) { if (PlayedAudioType == CustomAudioAsset.AudioType.Bgm) { // TODO: 走背景音乐播放接口 SourceAudio.clip = Resources.Load<AudioClip>(AudioPath); SourceAudio.Play(); } elseif (PlayedAudioType == CustomAudioAsset.AudioType.Sound) { // TODO: 走音效播放接口 SourceAudio.clip = Resources.Load<AudioClip>(AudioPath); SourceAudio.Play(); } mIsPlayingAudio = true; } } }
// Called each frame while the state is set to Play // Playable更新执行时 publicoverridevoidProcessFrame(Playable playable, FrameData info, object playerData) { if(mSourceAudio == null) { mSourceAudio = playerData as AudioSource; } TryPlayAudio(); } ****** }
///<summary> /// Executes a "drop table" on the database. This is non-recoverable. ///</summary> publicintDropTable<T>() { var map = GetMapping (typeof (T));
var query = string.Format("drop table if exists \"{0}\"", map.TableName);
using Sirenix.OdinInspector; using Sirenix.OdinInspector.Editor; using Sirenix.Utilities; using System; using System.Collections; using UnityEditor; using UnityEngine;
//添加MenuItem节点 var custommenuitemdata = new CustomMenuItem.CustomMenuItemContentClass(1, "CustomMenuItemContentClass", null, false); var custommenuitemobj = new CustomMenuItem(menutree, "默认菜单子子节点名", custommenuitemdata); menutree.AddMenuItemAtPath("菜单子节点1", custommenuitemobj);
var customdrawmenuitemdata = new CustomDrawMenuItem.CustomDrawMenuItemContentClass(1, "CustomDrawMenuItemContentClass", null, false); var customdrawmenuitemobj = new CustomDrawMenuItem(menutree, "自定义菜单子子节点名", customdrawmenuitemdata); menutree.AddMenuItemAtPath("菜单子节点2", customdrawmenuitemobj);
using Sirenix.OdinInspector; using Sirenix.OdinInspector.Editor; using Sirenix.Utilities.Editor; using System; using System.Collections.Generic; using UnityEditor; using UnityEngine;
using Sirenix.OdinInspector; using Sirenix.OdinInspector.Editor; using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using UnityEngine;
int recordedCount; if (!mResourceCountMap.TryGetValue(resourcePath, out recordedCount)) { Debug.LogWarning($"ResourceScope未记录资源:{resourcePath}的索引计数,释放失败!"); returnfalse; }
var assetLoader = loader as AssetLoader; if (assetLoader != null) { for (int i = 0; i < releaseCount; i++) { assetLoader.ReleaseAsset(); } returntrue; }
var bundleLoader = loader as BundleLoader; if (bundleLoader != null) { for (int i = 0; i < releaseCount; i++) { bundleLoader.ReleaseAssetBundle(); } returntrue; }
再看看四人帮是如何定义命令模式的: “Encapsulate a request as an object, thereby letting users parameterize clients with different requests, queue or log requests, and support undoable operations”(将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的操作。)
// Use this for initialization voidStart () { // 添加UIController对UI点击事件的响应 mBtnRandomNumber.onClick.AddListener(UIController.Instance.OnBtnRandomNumberClick); }
如何在Unity里实现Data Bidning? Data Binding的核心是在修改数据时能触发回调通知,能将数据和回调绑定起来。 明白了这一点,回顾C#里设置数据一般是通过方法或者属性或者直接访问public成员变量,要想实现回调触发,我们可以通过C#里的Property设置(set)的形式去确保回调相应。
///<summary> /// 属性值变化回调 ///</summary> public ValueChangedDelegate OnValueChanged;
///<summary> /// 属性 ///</summary> public T Value { get { return mValue; } set { T oldvalue = mValue; mValue = value; ValueChanged(oldvalue, mValue); } } private T mValue;
publicreadonly BindableProperty<string> Name = new BindableProperty<string>(); publicreadonly BindableProperty<int> AgeDetail = new BindableProperty<int>();
///<summary> /// 初始化View(主要是V到VM之间的绑定) ///</summary> publicvoidinitialize() { #region Bind VM to V Part mViewModel.Name.OnValueChanged = onNameChanged; mViewModel.Age.OnValueChanged = onAgeChanged; #endregion
#region Bind V To VM mInputFieldName.onValueChanged.AddListener(onInputNameChanged); mBtnSave.onClick.AddListener(onSaveClick); #endregion }
#region Bind VM to V Part privatevoidonNameChanged(string oldname, string newname) { mInputFieldName.text = newname; mTxtName.text = newname; }
privatevoidonAgeChanged(int oldvalue, int newvalue) { mTxtAge.text = newvalue.ToString(); } #endregion
#region Bind V To VM privatevoidonInputNameChanged(string newname) { mViewModel.Name.Value = newname; }
///<summary> Name输入文本(用于View输入改变Name) ///</summary> public InputField mInputFieldName;
///<summary> Name值展示文本 ///</summary> public Text mTxtName; ///<summary> Age值展示文本 ///</summary> public Text mTxtAge;
///<summary> Model数据保存按钮 ///</summary> public Button mBtnSave;
///<summary> /// 真实绑定的VM对象访问入口 ///</summary> public MVVMViewModel ViewModel { get { return (MVVMViewModel)mMVVMViewModelContext; } }
///<summary> /// 响应VM绑定变化回调 /// 在这里做V和VM绑定相关的事 ///</summary> ///<param name="oldvm"></param> ///<param name="newvm"></param> protectedoverridevoidOnBindingContextChanged(IMVVMViewModel oldvm, IMVVMViewModel newvm) { base.OnBindingContextChanged(oldvm, newvm); // 解除老的VM相关绑定 if(oldvm != null) { #region Bind VM to V Part MVVMViewModel ovm = oldvm as MVVMViewModel; ovm.Name.OnValueChanged -= onNameChanged; ovm.Age.OnValueChanged -= onAgeChanged; #endregion
#region Bind V To VM mInputFieldName.onValueChanged.RemoveListener(onInputNameChanged); mBtnSave.onClick.RemoveListener(onSaveClick); #endregion }
// 添加新的VM相关绑定 if (ViewModel != null) { #region Bind VM to V Part ViewModel.Name.OnValueChanged += onNameChanged; ViewModel.Age.OnValueChanged += onAgeChanged; #endregion
#region Bind V To VM mInputFieldName.onValueChanged.AddListener(onInputNameChanged); mBtnSave.onClick.AddListener(onSaveClick); #endregion } } #region Bind VM to V Part privatevoidonNameChanged(string oldname, string newname) { mInputFieldName.text = newname; mTxtName.text = newname; }
privatevoidonAgeChanged(int oldvalue, int newvalue) { mTxtAge.text = newvalue.ToString(); } #endregion
#region Bind V To VM privatevoidonInputNameChanged(string newname) { ViewModel.Name.Value = newname; }