Unity中从Asset Bundle加载Sprite到物品列表按钮的可行性问询
问题
我有一个包含3D模型和Icon Sprite的Asset Bundle文件,想把其中的Sprite用作物品列表按钮的图标。之前没做过这类操作,想确认能不能直接把Asset Bundle里的Sprite赋值给UI按钮的Source Image;如果不行的话,有哪些替代方案?
目前我已经实现了从服务器加载物品列表和Asset Bundle的逻辑,按钮现在用的是默认矩形图,需要从Asset Bundle里取出Sprite并设置到按钮的Source Image上。相关代码如下:
API逻辑文件
//----------------------------------------------------------------------------------------------------------- public void LoadContent(string name, UnityAction<GameObject> callback) { api.GetBundleObject(name, callback, SpawnableFurnitureMother.transform); } //----------------------------------------------------------------------------------------------------------- public void GetItemList(UnityAction<List<string>> callback) { StartCoroutine(GetItemListRoutine(callback)); } //----------------------------------------------------------------------------------------------------------- IEnumerator GetItemListRoutine(UnityAction<List<string>> callback) { UnityWebRequest www = UnityWebRequest.Get(ItemList); yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.ConnectionError) { Debug.Log("Network error"); } else { string rawText = www.downloadHandler.text; //split string by comma string[] items = rawText.Split(','); //remove empty values and convert to list List<string> itemList = items.Where(x => !string.IsNullOrEmpty(x)).ToList(); //return list to caller callback.Invoke(itemList); } } //----------------------------------------------------------------------------------------------------------- public void GetBundleObject(string assetName, UnityAction<GameObject> callback, Transform bundleParent) { StartCoroutine(GetDisplayBundleRoutine(assetName, callback, bundleParent)); } //----------------------------------------------------------------------------------------------------------- IEnumerator GetDisplayBundleRoutine(string assetName, UnityAction<GameObject> callback, Transform bundleParent) { string bundleURL = BundleFolder + assetName + "-"; //append platform to asset bundle name #if UNITY_ANDROID bundleURL += "Android"; #else bundleURL += "IOS"; #endif Debug.Log("Requesting bundle at " + bundleURL); //request asset bundle UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(bundleURL); yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.ConnectionError) { Debug.Log("Network error"); } else { AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(www); if (bundle != null) { string rootAssetPath = bundle.GetAllAssetNames()[0]; mySystem.AddToLog("Spawning"); GameObject arObject = Instantiate(bundle.LoadAsset(rootAssetPath) as GameObject, bundleParent); bundle.Unload(false); callback(arObject); arObject.SetActive(false); } else { Debug.Log("Not a valid asset bundle"); } } }
按钮实例化逻辑
public void SpawnObject(List<string> itemList) { foreach (string item in itemList) { //load button prefab into scene GameObject buttonObject = Instantiate(ButtonPrefab, ButtonParent); //set button text // buttonBehavior.LoadIcon(item, callback, ButtonParent); buttonBehavior = buttonObject.GetComponent<ButtonBehavior>(); buttonBehavior.Init(item); //set button action Button uiButton = buttonObject.GetComponent<Button>(); // mySystem.AddToLog("Checking uiButton: " + (uiButton != null)); uiButton.onClick.AddListener(() => { if (!ObjectWithIDExists(currentObjectID) && placementIndicator != null) { ActivateMenu(false); LoadContent(item, (SpawnableFurnitureChild) => { // logic for instantiating the 3D model itself }); } }); } }
PHP文件
<?php $folder = "AssetBundles/"; $mask = "*Android*"; $files = glob("" . $folder . $mask); foreach ($files as $file) { $file_name = basename($file,substr($mask,1)); $name = explode("-", $file_name); echo $name[0].","; } ?>
回答
一、直接赋值Sprite到按钮Source Image完全可行
可以直接从Asset Bundle中加载Sprite资源,赋值给UI按钮的Image组件sprite属性。当前你的API逻辑仅加载了Bundle中的GameObject(3D模型),只需新增加载Sprite的方法即可实现需求。
二、具体修改方案
- 在API逻辑中添加加载Sprite的方法
新增协程方法专门加载Bundle内的Sprite,需注意指定正确的Sprite资源名称(或通过LoadAllAssets<Sprite>()获取所有Sprite):
public void GetBundleSprite(string assetName, UnityAction<Sprite> callback) { StartCoroutine(GetBundleSpriteRoutine(assetName, callback)); } IEnumerator GetBundleSpriteRoutine(string assetName, UnityAction<Sprite> callback) { string bundleURL = BundleFolder + assetName + "-"; #if UNITY_ANDROID bundleURL += "Android"; #else bundleURL += "IOS"; #endif UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(bundleURL); yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.ConnectionError) { AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(www); if (bundle != null) { // 假设Sprite在Bundle中的资源名为"Icon",请根据实际名称修改 Sprite iconSprite = bundle.LoadAsset<Sprite>("Icon"); // 若Sprite在Sprite Atlas中,需先加载Atlas再获取Sprite // SpriteAtlas atlas = bundle.LoadAsset<SpriteAtlas>("IconAtlas"); // Sprite iconSprite = atlas.GetSprite("ItemIcon"); callback?.Invoke(iconSprite); // 注意:不要立即卸载Bundle,后续加载3D模型还会用到;如需卸载,确保Sprite已被引用 // bundle.Unload(false); } } }
- 修改按钮实例化逻辑,加载并设置Sprite
在SpawnObject方法中调用新增方法获取Sprite,赋值给按钮的Image组件:
public void SpawnObject(List<string> itemList) { foreach (string item in itemList) { GameObject buttonObject = Instantiate(ButtonPrefab, ButtonParent); buttonBehavior = buttonObject.GetComponent<ButtonBehavior>(); buttonBehavior.Init(item); // 获取按钮的Image组件 Image buttonImage = buttonObject.GetComponent<Image>(); // 加载Sprite并设置 api.GetBundleSprite(item, (sprite) => { if (sprite != null && buttonImage != null) { buttonImage.sprite = sprite; buttonImage.preserveAspect = true; // 保持Sprite比例 } }); // 原按钮点击逻辑不变 Button uiButton = buttonObject.GetComponent<Button>(); uiButton.onClick.AddListener(() => { if (!ObjectWithIDExists(currentObjectID) && placementIndicator != null) { ActivateMenu(false); LoadContent(item, (SpawnableFurnitureChild) => { // 3D模型实例化逻辑 }); } }); } }
三、替代方案(若直接加载Sprite出现问题)
- 方案一:拆分Bundle,单独打包Sprite
将Icon Sprite与3D模型分开打包,单独加载Sprite Bundle,既减少单个Bundle体积,也避免加载3D模型时的资源浪费。 - 方案二:加载Texture2D转Sprite
若Bundle中只有Texture2D没有Sprite,可将Texture2D转换为Sprite后赋值:
Texture2D iconTex = bundle.LoadAsset<Texture2D>("IconTex"); Sprite iconSprite = Sprite.Create(iconTex, new Rect(0,0,iconTex.width,iconTex.height), new Vector2(0.5f,0.5f)); buttonImage.sprite = iconSprite;
- 方案三:用Sprite Atlas管理图标
将所有物品图标打包到Sprite Atlas中,再把Atlas放入Asset Bundle,加载Atlas后通过名称获取Sprite,适合大量图标的场景,可优化Draw Call。
内容的提问来源于stack exchange,提问作者cyberkotleta
相关产品推荐
相关产品推荐

