Addressable 异步图片加载脚本

文章讲述了在Unity中使用Addressable系统来管理资源,特别是如何根据地址动态加载图片并更新Image组件的Sprite。同时,文章讨论了在编辑器模式下实现这一功能的挑战,如Inspector面板的回调问题,以及如何利用OnValidate方法和协程来解决。此外,还介绍了额外的需求,即根据图片资源自动调整Addressable的配置。
摘要由CSDN通过智能技术生成

为什么要这个脚本

1,使用Addressable管理资源话,根据资源的寻址地址,加载对应的图片并且更换image的sprite。

需求

1,根据图片资源的寻址地址,加载对应的图片并且更换image的sprite(刚需),编辑器模式下同样支持(吃力不讨好)。
2,根据图片资源,完成需求1的内容。

实现

开发的时候就想着需求1的实现,当想着完成工具的时候,开发需求2的内容,需求2完成以后,回头想需求1在编辑器模式下实现没什么意义。

字段&属性


		private Image _image;	//图片组件属性
		public Image image
		{
			get
			{
				if (_image == null)
					_image = GetComponent<Image>();
				return _image;
			}
		}

		[SerializeField]
		private Sprite imageSprite; //精灵资源属性

		public Sprite ImageSprite
		{
			get
			{
				return imageSprite;
			}

			set
			{
				imageSprite = value;
				image.sprite = value;	//精灵资源修改时,更换图片的精灵
			}
		}

		[SerializeField]
		private string aai_image = null;	//图片资源寻址地址属性

		public string AAI_Image
		{
			get
			{
				return aai_image;
			}

			set
			{
				if (aai_image != value)
				{
					aai_image = value;
					RefreshImage();			//值修改时,刷新图片
				}
			}
		}

		private IEnumerator coroutinue;		//迭代器,用作协程

需求1

设置寻址地址时,更换图片。毫无疑问使用属性实现,但是实践的时候发现,编辑器模式下对inspector面板赋值,不会触发回调。(monobehaviour里面的字段真的很神奇,有参构造自定义类,标记为可序列化是都能直接实例化,我通过反射都做不到)
所以另辟蹊径,实现OnValidate方法,该方法会在inspector数值发生变化是回调。


		public void OnValidate()
		{
#if UNITY_EDITOR
			RefreshSprite();	//刷新精灵
			RefreshImage();		//刷新图片
#endif
		}
		
		public void RefreshImage()
		{
#if UNITY_EDITOR
			//AssetDatabase.GetAssetPath是Editor命名空间下的方法
			//默认把资源路径作为寻址地址
			//寻址地址和图片精灵寻址地址相同就不用更新
			string imageAssetPath = AssetDatabase.GetAssetPath(image.sprite);
			if (imageAssetPath == aai_image)
			{
				return;
			}
#endif
			//迭代器为空或者迭代器执行完,才刷新图片
			if (coroutinue == null || !coroutinue.MoveNext())
			{
				//迭代器,图片存在并且加载完成后,替换图片的精灵,替换脚本的精灵变量值
				coroutinue = AssetUtil.AA_Exist<Sprite>(aai_image, (sprite) =>
				{
					//编辑器模式下到运行模式下式,会销毁脚本对象,并生成新的脚本对象。
					//所以协程执行时,this可能被销毁。
					if (this != null)
					{
						image.sprite = sprite;
						if (imageSprite != sprite)
						{
							imageSprite = sprite;
						}
					}
				});
				//运行模式下,使用常规的协程开启方法
				if (Application.isPlaying)
				{
					StartCoroutine(coroutinue);
				}
				else if(Application.isEditor)
				{
				//编辑器模式下使用该方法开启协程,不然的话会阻塞进程导致unity编辑器卡死
#if UNITY_EDITOR
					EditorCoroutineUtility.StartCoroutine(coroutinue, this);
#endif
				}
			}
		}

方法补充


		/// <summary>
		/// 可寻址资源如果存在执行委托
		/// </summary>
		/// <param name="path">资源guiid路径</param>
		/// <returns></returns>
		public static IEnumerator AA_Exist<T>(string path, Action<T> action)
		{
			//这个函数只能在主线程上执行,所以用协程调度
			var openHandle = Addressables.LoadResourceLocationsAsync(path, typeof(T));
			//直到条件true时,退出等待
			yield return new WaitUntil(() => openHandle.Status == AsyncOperationStatus.Succeeded);

			if (openHandle.Task.IsCompleted && openHandle.Task.Result.Count > 0)
			{
				LoadAsset<T>(path, action);
			}
		}
		
		/// <summary>
		/// addressable 的加载资源完成后回调
		/// </summary>
		/// <typeparam name="T">加载资源的类型</typeparam>
		/// <param name="path">加载资源的路径</param>
		/// <param name="action">回调委托</param>
		public static void LoadAsset<T>(string path, Action<T> action)
		{
			Addressables.LoadAssetAsync<T>(path).Completed += (open) =>
			{
				action(open.Result);
			};
		}

需求2


		public void RefreshSprite()
		{
			if (imageSprite == image.sprite)
			{
				return;
			}

#if UNITY_EDITOR
			string assetPath = AssetDatabase.GetAssetPath(imageSprite);
			AssetUtil.AA_CreateOrMove("Sprites", assetPath);
			if (aai_image != assetPath)
				aai_image = assetPath;
#endif

		}

方法补充


		/// <summary>
		/// addressable 创建或可寻址资源到Group中
		/// 默认assetPath为资源的寻址地址
		/// </summary>
		/// <param name="GroupName">资源的组名</param>
		/// <param name="assetPath">资源的寻址地址</param>
		public static void AA_CreateOrMove(string GroupName, string assetPath)
		{
#if UNITY_EDITOR
			AddressableAssetSettings addressableSettings = AddressableAssetSettingsDefaultObject.Settings;
			AddressableAssetGroup customGroup = addressableSettings.FindGroup(GroupName);
			if (customGroup == null)
			{
				customGroup = new AddressableAssetGroup();
				customGroup.name = GroupName;
			}

			string guiId = AssetDatabase.AssetPathToGUID(assetPath);
			addressableSettings.RemoveAssetEntry(guiId);
			addressableSettings.CreateOrMoveEntry(guiId, customGroup);
			EditorUtility.SetDirty(addressableSettings);
#endif
		}

总结

在编辑器模式下支持寻址地址更换图片精灵没什么必要,因为根据图片资源更换资源太方便了,还不用资源判空操作。
对于需要在主线程上调度的异步方法(例:Addressables.LoadResourceLocationsAsync(path, typeof(T))),使用协程再合适不过了。
编辑器模式下特有的协程打开方式EditorCoroutine StartCoroutine(IEnumerator routine, object owner),(编辑器模式下到运行模式下式,会销毁脚本对象,并生成新的脚本对象,所以协程执行时,this可能被销毁,从脚本执行的表象上来看是这样的)。
unity的inspector会忽略属性
OnValidate方法再inspector面板刷新后回调

存在的问题

多做了一个吃力不讨好的功能,但是了解了一些问题的处理。
coroutinue变量名换成imageRefreshIterator会更好点。
有时候面板上赋值不会立刻刷新image组件,再次点击unity编辑器任意地方时才刷新。

项目地址
脚本文件:AsyncImage

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值