You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的方法即可实现需求。

二、具体修改方案

  1. 在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); 
        }
    }
}
  1. 修改按钮实例化逻辑,加载并设置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:32:03