Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do we use Asset Manger with Google Play's Install Time Asset Packs? #1091

Open
jimmymjing opened this issue Jan 6, 2022 · 4 comments
Open

Comments

@jimmymjing
Copy link

This is a feature request. I hope there would be a way to use asset manager to deal with install-time Asset Packs for Google Play.
Now I'm using PlayAssetDelivery ANE to get install time Asset Packs, but it is an AssetFile object. Is there a way to pass it to the Asset Manager so that the asset management flow would be the same for both iOS and Android? Thanks so much!

ref:https://github.com/airsdk/ANE-PlayAssetDelivery/wiki

@PrimaryFeather
Copy link
Contributor

PrimaryFeather commented Jan 8, 2022

I've never used asset packs yet, so I must admit I don't have any experience with them.

However, I think it should be possible to add support for them without too much fuss. The AssetManager supports custom "DataLoader" classes. E.g., the Zipped Assets extension creates a custom data loader to provide support for zip files. This would be similar.

The idea is to create a data loader that has access to the (already loaded) Asset Packs. Instead of loading the data from a disk or an URL, as is the default, it could return it from the asset pack instead. So you'd add the assets to the AssetManager with a custom url (e.g. assets.enqueue('assetPack://hero/head.png')). If such an URL is found by the custom DataLoader, it accesses the asset pack and returns the relevant content; otherwise, the standard loader takes over.

I know this is only a rough sketch, but perhaps it helps already? I recommend you take a look at the zip extension I mentioned above – it should be a good starting point!

Let me know what you think of that! 😄 Of course, I'd be happy to help.

@jimmymjing
Copy link
Author

jimmymjing commented Jan 9, 2022

Hi, thanks so much for pointing out the direction! But the thing is that the Asset Pack contains many subfolders. (e.g "textures/page/general/nomipmap") Just like the way how AssetManager works, I wish to load all assets within a certain subfolder. (and the ability to unload it)
Here's the way how to currently load assets in folders:

public static function loadMultipleAsset(ass:Array,useMipMap:Boolean,autoLoad:Boolean=true,onComplete:Function=null, onError:Function=null, onProgress:Function=null):void{
	var tAM:AssetManager;
	for(var i:int=0,len:int=ass.length;i<len;i++){
		tAM = am.getAssetManager(ass[i]);
		increSubAMCount(ass[i]);
		if(tAM == null){
			tAM = new AssetManager(contentScaleFactor);
			tAM.textureOptions.mipMapping = useMipMap;
			var path:String = ass[i];
			path = AssetPackManager.getAssetAbsPath(path);
			tAM.enqueue(appDir.resolvePath(path));
			am.enqueueSingle(tAM,path);
		}
	}
	if(autoLoad){
		if(i == 0){
			onComplete();
		}else{
			am.loadQueue(onComplete,onError,onProgress);
		}
	}
}

The problem is that with Install-time Asset Packs, there's no way to get the file path so appDir.resolvePath(path) won't find the path.


As you pointed out, I created a loader class. Is it correct?

public class InstallTimeAssetPackLoader extends DataLoader {
    public function InstallTimeAssetPackLoader() {
        super();
    }

    override public function load(url:String, onComplete:Function,
                                  onError:Function, onProgress:Function = null):void
    {
        if (AssetPackManager.pad) {
            var asset:AssetFile = AssetPackManager.pad.openInstallTimeAsset( url );
            if (asset != null)
            {
                // Load some text content from the asset
//                var content:String = asset.readUTFBytes( asset.bytesAvailable );
                execute(onComplete, asset.readObject);

                /*var components:Array = filename.split(".");
                var extension:String = components.pop();
                var basename:String = components.join(".");
                execute(onComplete, file.content, null, basename, extension);*/

                asset.close();
            }else{
                super.load(url, onComplete, onError, onProgress);
            }
        } else {
            super.load(url, onComplete, onError, onProgress);
        }
    }
}

@PrimaryFeather
Copy link
Contributor

PrimaryFeather commented Jan 10, 2022

But the thing is that the Asset Pack contains many subfolders. (e.g "textures/page/general/nomipmap") Just like the way how AssetManager works, I wish to load all assets within a certain subfolder. (and the ability to unload it)

Ah, I see! Makes sense, and I see how this complicates things. 😉

When you call assets.enqueue(appDir.resolvePath("textures")), then the AssetManager simply looks at which files are available in that folder, and adds each of those files individually. Since asset packs have no way of telling you what's inside, we can't do it like that – as you noticed.

What I'm typically doing in such situations is to add some kind of manifest or index file at the root of a repository (asset pack). This could be created at build time.

So the asset pack would have a file like this in its root; let's call it manifest.json.

"files": [
  "textures/page/general/nomipmap/hero.png",
  "textures/page/general/nomipmap/antagonist.png",
  "textures/page/specific/mipmap/bunny.png",
  "..."
]

If you are able to create such a file during the build process, you could create a helper class that wraps the AssetFile object, giving you options to list what's inside or to return all files that start with a certain prefix.

That's the class that would then be used by the DataLoader to actually get the data. You could then group the assets together into multiple AssetFactories, just as you're doing now.

Again, sorry that I can't provide any source code, but I hope you get the idea. 😄

As you pointed out, I created a loader class. Is it correct?

Hm ... my guess is that this line will be a problem:

execute(onComplete, asset.readObject);

onComplete expects a ByteArray, but readObject tries to restore an object. So you should change it like this:

var bytes:ByteArray = new ByteArray();
asset.readBytes(bytes, 0, asset.bytesAvailable);
execute(onComplete, bytes);

(You could also just call onComplete(bytes) – the reason I used execute in my code is just to be on the safe side if onComplete is null; but I don't think that can happen.)

Otherwise, I think that should work!

I hope my notes are helpful! All the best, Jimmy!

@jimmymjing
Copy link
Author

Thanks so much! I will give it a shot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants