Im trying to build a static AudioManager which is able to play any music with only referring one instance. However, the same music cannot play again after I dispose it from the instance.
The AudioManager works when run on desktop, but fail when run on android device.
The main function from static AudioManager class:
Music music = null;
public static Music playMusic(String musicName){
if (music!=null){
music.stop();
music.dispose();
}
music = assetManager.get("music/"+musicName,Music.class);
music.setLooping(true);
music.play();
currentMusicName = musicName;
MyDebugger.d("new music !");
return music;
}
The changing from music_A to music_B works, but reusing music_A does not play any music with no errors. (in android device)
Any help please.
You are disposing your music and then trying to reuse the same music instance when you use assetManager.get().
When you use AssetManager to load an asset, you should not be disposing it. You should be unloading it. That also means you cannot reuse it after unloading it. You need to load a new copy.
Music music = null;
public static Music playMusic(String musicName)
{
if (music!=null){
music.stop();
assetManager.unload(music);
}
assetManager.load("music/"+musicName,Music.class);
assetManager.finishLoading();
music = assetManager.get("music/"+musicName,Music.class);
music.setLooping(true);
music.play();
currentMusicName = musicName;
return music;
}
But since you are only ever loading one music instance at a time (judging from your use of the static method), it might be easier not to use the asset manager at all for it.
Be careful using static for anything involving Disposables. Don't forget to dispose it when the game's dispose() is called.
Related
How can I patch actionscript without constantly rebuilding sfw?
There is a fairly large actionscript project that I need to modify and resulting swf is used on a live site. The problem I have is that I need to make quick small updates to the swf and it's not acceptable to update the swf on live site ten time a day (I don't control that part, I need to ask another person to put the result on live site).
What options do I have to workaround that issue? I'm a complete noob when it comes to actionscript and all flash related stuff and I'm not even sure what is possible and what isn't. I'm thinking about the following approaches, which ones are possible/acceptable?
Imagine that live site is on www.livesite.com/game.html and this page loads www.livesite.com/flashgame.swf. In that flashgame.swf among many others there is a class com/livesite/Magic.as that gets instantiated and instance of that class has a member variable xxx123 of class com/livesite/MagicWork.as. I only need to modify this MagicWork class. Now, I simply modify it, build and ask to put updated flashgame.swf live. So, I want to avoid that manual step.
All my ideas can be split in two basic approaches: 1) keep flashgame.swf totally unmodified and then load flashgame.mod.swf that contains alternative implementation of that MagicWork class, then using javascript access internals of instance of that Magic class and update its xxx123 member to be an instance of MagicWork class from flashgame.mode.swf. I'd need to modify game.html to load my javascript so that my js file would load flashgame.mod.swf and patch code inside flashgame.swf. By patching I mean javascript-style overwriting of Magic.xxx123 to a new value. flashgame.mode.swf would ideally reside on my own host that I control. Is that kind of stuff possible, if not what's not possible?
2) I could make one-time change in flashgame.swf so that it would effectively load itself my own code at runtime and patch it's xxx123 member. Is that possible?
I had already written a note about loading runtime shared libraries previously. I'll put the most essential parts of the process here, and add a link to the full article at the end.
You need to tag your main application entry point in the following manner.
[Frame(factoryClass="Preloader")]
public class Main extends Sprite
{
}
Then create a class called Preloader.
public class Preloader
{
public function Preloader()
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.loader_completeHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, this.loader_ioErrorHandler);
var request:URLRequest = new URLRequest("math.swf");
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
loader.load(request, context);
}
private function loader_completeHandler(event:Event):void
{
var mainClass:Class = getDefinitionByName("Main") as Class;
var mainInstance:Main = new mainClass();
this.addChild(mainInstance);
}
}
The full implementation of the Main class is like this.
[Frame(factoryClass="Preloader")]
public function Main()
{
var integer:IntegerArithmetic = new IntegerArithmetic(); // Type declared in math.swf
var operand1:int = 10;
var operand2:int = 10;
var result:int = integer.add(operand1, operand2);
}
Deploying Runtime Shared Libraries
The confusing bit about using a runtime shared library is realizing that the SWF has to be extracted from the SWC at the time of deploying the application. This was not immediately obvious and I ended up spending days placing a compiled SWC file in various locations and wondering why the application was unable to load it at runtime. An obscure article on the Adobe website made explicit this particular step and set things straight.
The full article along with the same example is available at http://www.notadesigner.com/runtime-shared-libraries-with-plain-actionscript/.
What i have
Large amount of swfs without it's sources (so i couldn't modify its)
What i need
To load and play this swfs with my AIR app.
The problem
The problem is that this swfs seems having
Security.allowDomain('*')
in their source, so they would throw
SecurityError: Error #3207: Application-sandbox content cannot access
this feature.
after i load it. I know that Air doesn't need to use this line, but instead of ignoring or warning on it my full app would stop to executing after loading one of this swfs. If only i could try/catch this string, but as i said i don't have an source of that swfs, so the only thing i could do is to modify my AIR app.
What i tried
What i already tried is to catch all errors inside loader by doning
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loaderIOErrorHandler);
private function loaderIOErrorHandler(e:IOErrorEvent):void {
e.preventDefault();
}
but it seems it isn't catch errors inside loader at all
Update
I couldn't share one of this swfs, but here is simulation i made that reproduce problem https://www.dropbox.com/s/0spbdzijfpboi47/problematicSwf.swf?dl=0
Here it's init code
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
Security.allowDomain('*');
tf = new TextField();
tf.text = 'Me loaded!';
addChild(tf);
}
As you could see it is crashing on allowDomain inside loaded swf.
And here is how i load it
var ctx:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
loader.load(new URLRequest(path), ctx);
This is a typical security restriction but it's a very strict one and it's purpose is to make sure the served swf will never be used outside of what it was made for in the first place.
So the short answer to your problem is this: externally loaded swf that are sandboxed with "Security.allowDomain('*');" will not allow a sandboxed AIR app to interact with them in anyway. Instead those swf will be able to interact with the parent AIR app under restrictions via a sandbox bridge.
If truly you cannot modify those swf then you will never be able to add them to a display list in a AIR app or call any of their methods. A sandbox bridge will also be of no use to you.
It's not the answer you want to hear I bet but it's the only one you'll get.
I've a question about the libgdx Skin behavior.
the game I'm developing has a global AssetsManager (from the libgdx suite) that every class can access.
I load different TextureAtlas inside this assets manage
I know that assetManager.dispose() disposes all the resources loaded inside the assets manager.
Now, I would like also to have a Skin (for the GUI) loaded inside the assets manager.
The skin is gonna use several TextureAtlas...
Here's the question: since I'm gonna use skin.addRegion() and since the online API reference about the skin class says "The atlas will not be automatically disposed when the skin is disposed" is it a good idea to load all the TextureAtlasof the skin in the global assets manager?
I'm fearing about the dispose action. Because when I call the assetManager.dispose() both the TextureAtlas and the Skin will be called on the dispose method...but what if the TextureAtlas are disposed before the skin?
Could actually happen any problem about it?
The skin behavior is not so well-defined, I mean...what does the dispose method do?
Thank in advance,
Luca
skin.dispose() calls dispose on any specific resources that are Disposable. But the TextureAtlas itself is not one of the "resources" so it must be manually disposed of separately from the skin.
The only example of a disposable skin resource I can think of is a BitmapFont that does not use the TextureAtlas that you're using with the Skin.
Note that you should never call dispose on something that you loaded with the AssetManager. Instead, you should call manager.unload() on that resource so the AssetManager can properly manage dependencies.
The nice thing about manager.unload() is that it keeps track of how many other resources are dependent on the object and only disposes it when it's clear of dependencies. So if you also load your Skin with the AssetManager, you only ever need to worry about calling manager.unload("mySkin") and it will correctly determine whether the associated TextureAtlas should also be disposed.
But be sure to only call unload() on a resource once per time you called load() on the same resource. AssetManager's internal dependency counting does rely on all your load() and unload() calls mirroring each other one-to-one.
I didn't want to post an anwser but I wasn't able to post a comment with code -_-.
Ok, so if the situtation is this below:
`
assetManager.load("images/guiTextureAtlas", TextureAtlas.class);
assetsManager.load("skin/uiSkin.json", Skin.class)";
assetsManager.finishLoading();
Skin uiSkin = assetManager.get("skin/uiSkin.json");
uiSkin.addRegion(assetManager.get("images/guiTextureAtlas");
`
Is it all fine if I call assetManager.dispose() for disposing all the resources?
I'm creating a game using Libgdx and i write a debugger for that. Now i want to display debugging information in another LwjglApplication.
Is there any way to open multiple windows in Libgdx desktop application ?
Create two classes extending Game or implementing ApplicationListener, one for main game and one for debug window.
In DesktopLauncher class, create two LwjglApplications with objects of aforementioned classes.
public static void main (String[] arg) {
LwjglApplicationConfiguration mainConfig = new LwjglApplicationConfiguration();
new LwjglApplication(new MainGame(), mainConfig);
LwjglApplicationConfiguration debugConfig =new LwjglApplicationConfiguration();
new LwjglApplication(new DebugGame(), debugConfig);
}
Hope this helps.
I have this program, that needs to load a text file, which it can do, but since everything is multi-threaded on AS3, it continues with the program even though its not loaded. Like, if I was to make a function that loads the text, and then have it return the loaded text, it will always return "undefined". I have to add an event listener for when it's done, but the way I have to handle the returning of the text is awkward. Is there any way to make the program just wait, or do nothing until it's loaded?
It sounds like what you're looking for is for the data to load synchronously so that you can just make the loading call and have it return right away, like so:
# (this actually is not actionscript)
fileDataContents = open("file.txt", "r");
then have the fileDataContents immediately available to use.
By design this is not how flash works.
Loading external data in flash is not multithreaded, but rather asynchronous. One very good reason why synchronous loading is not done is that it causes blocking/locking of the program during the operation potentially resulting in a poor user experience.
Take for example, if this is loading a file over the web: what if the user's internet connection had just cut out/hiccupped or had been moved/deleted/modified suddenly on the server? Or what if the file is moderately sized but the user was on dial-up?
This is out of your control in most cases and the resulting behaviour may be that it causes flash to "forever" freeze in the user's browser window as it tries to load, possibly taking down the whole browser with it, ultimately ending in a poor user experience. By blocking/locking the program in that loop you would have no way to recover or respond to the user appropriately.
No, you can't.
Listening for the COMPLETE event like you have now is the correct way to wait and continue the application flow once done. I can't see how this would become awkward - I suggest updating your question to include information about what's making you uncomfortable about the event system so that I can help you from there.
Perhaps you're just not structuring your code in a way that works well with the event system. Here is how you can set up a very clean, straightforward way of loading some data and starting up the application once done.
Here's the document class, which can be used purely as a channel to load your text data which will be forwarded on to a fresh instance of the actual Application class:
public class Main extends Sprite
{
private var _application:Application;
private var _loader:URLLoader;
public function Main()
{
var fileUrl:URLRequest = new URLRequest("file.txt");
_loader = new URLLoader(fileUrl);
_loader.addEventListener(Event.COMPLETE, _textLoaded);
}
private function _textLoaded(e:Event):void
{
_loader.removeEventListener(Event.COMPLETE, _textLoaded);
// Load the main Application with the required text data.
_application = new Application( _loader.data );
}
}
And then your Application, which you can consider your central class.:
public class Application
{
public function Application(textData:String)
{
// Here we have access to the required text, and we can
// begin preparing the main Application.
trace(textData);
}
}