I have a sound that plays whenever I rollover an object. To make it less annoying I want the sound to play only if it's not playing already. Since I need this with a couple of different sounds I don't want to use a timer (if not absolutely necessary). I found this:
var channel:SoundChannel = snd.play();
channel.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
public function onPlaybackComplete(event:Event)
{
trace("The sound has finished playing.");
}
but I'm not sure I can use it since I have a background music as well. Any tips?
You can use such a trick selectively. Still, if you plan to have a lot of such objects that trigger sounds on mouse-overs, you might decide to have a manager class or data table that would have an entry per such an object, and its field would have a true if the sound is played, and a listener assigned as such would clear the value in that field, deriving the correct entry from registered set of SoundChannels. A premade sound manager would do, but you'd better make a tailored one. An example:
public class SoundManager {
private var _fhOPO:Dictionary;
private var _bhOPO:Dictionary;
// going sophisticated. Names stand for "forward hash once per object" and "backward"
// forward hash stores links to SoundChannel object, backward stores link to object from
// a SoundChannel object.
// initialization code skipped
public static function psOPO(snd:Sound,ob:Object):void
{
// platy sound once per object
if (_fhOPO[ob]) return; // sound is being played
var sc:SoundChannel=snd.play();
_fhOPO[ob]=sc;
_bhOPO[sc]=ob;
sc.addEventListener(Event.SOUND_COMPLETE, _cleanup);
}
private static function _cleanup(e:Event):void
{
var sc:SoundChannel=event.target as SoundChannel;
if (!sc) return; // error handling
var ob:Object=_bhOPO[sc];
_bhOPO[sc]=null;
_fhOPO[ob]=null; // clean hashes off now obsolete references
sc.removeEventListener(Event.SOUND_COMPLETE, _cleanup);
// and clean the listener to let GC collect the SoundChannel object
}
}
Then, whenever you need to play a sound but limit to one instance of channel per object, you call SoundManager.psOPO(theSound,this); providing a button reference instead of this if need be. You can use normal Sound.play() alongside this kind of sound management should you need to, or use another type of management for BGM or other types of sounds should you need to.
I am trying to make an options menu for my game where there are 2 settings. One setting disables all music that are in the game, and the other disables all sound effects. If the user chooses to disable all sound effects and not music, then when he exits the game and comes back to it, it should remember his settings. I have tried numerous times to create this sort of system, but it is not working for me at all. I don't know how to create it. Can anyone please help? I am fairly new at action script.
All sounds are accessed from library
Use SoundChannel to create and control separate sounds.
use a SharedObject to store user choice.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/SoundChannel.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/SharedObject.html
The answer is to use a sound manager class of static functionality, which will have two public Boolean properties which then you can set separately using your options menu. Then, the class will check these booleans each time you want a sound to be played (for this, use its function to play sounds). An example:
public class SoundManager {
private var _musicOn:Boolean;
private var _soundOn:Boolean;
private var _currentMusicChannel:SoundChannel;
private var _currentMusicSelected:Sound; // what to start when music is enabled
public static function get musicOn():Boolean { return _musicOn; }
public static function set musicOn(value:Boolean):void {
if (value==_musicOn) return;
_musicOn=value;
if (_musicOn) _currentMusicChannel=_currentMusicSelected.play();
else if (_currentMusicChannel) {
_currentMusicChannel.stop();
_currentMusicChannel=null;
}
}
public static function get soundOn():Boolean { return _soundOn; }
public static function set soundOn(value:Boolean):void { _soundOn=value; }
// a simple version, as this is an example
public static function playSound(someSound:String):void {
var aSound:Sound=getSoundFromString(someSound); // TODO
// ^ you have to devise a method to convert strings to sounds
if (isMusic(aSound)) {
// TODO, you should have either one music or a set of em, so if you're
// trying to play a music, this should return true, otherwise false
_currentMusicSelected=aSound;
if (_musicOn) _currentMusicChannel=aSound.play();
} else {
// a simpler version, the more advanced version should allow instant mute
if (_soundOn) aSound.play();
}
}
// some other functions are missing from here, as well as sound library and support stuff
}
When I play a sound, usually the first time but sometimes at the succeeding times as well, the current playing animation of my app stalls.
I stored all my Sound objects in static variables in a class called Jukebox. Anywhere in my code I need to play a sound I call a static method of Jukebox called playSfx().
Here's the relevant code:
public static function
playSfx( snd:Sound, vol:Number = 1 ):void
{
_channelSfx = snd.play();
_transform = _channelSfx.soundTransform;
_transform.volume = vol;
_channelSfx.soundTransform = _transform;
}
Any help will be greatly appreciated. Cheers!
Is it possible to save logs of Trace somewhere, so that I can look up traces for players?
Yes, but for that you need a logger function instead of trace(). By the way, if you compile your SWF in release mode, no traces will happen. Say like this one:
private static var COMPLETE_LOG:String='';
public static function log(...args):void {
var i:int; var s:String='\n';
for (i=0;i<args.length;i++) {
if (i>0) s+=' ';
s+=args[i].toString();
}
COMPLETE_LOG+=s;
}
Use log() instead of trace() to capture traces you need. And then say upload that COMPLETE_LOG somewhere.
This is my code in Flash/AS3, in main class.
addEventListener(Event.ENTER_FRAME,function(e:Event){
if(findObject == true){
// I want to remove this ENTER FRAME
}
});
try this:
e.currentTarget.removeEventListener(e.type, arguments.callee)
You shouldn't be doing what you do in the code above.
The mgraph's code has a tiny chance of failing to work as advertised if the currentTarget of the event doesn't have a removeEventListener() method (possible, but very unlikely). From the compiler standpoint though you will be trying to dynamically resolve the method on a generic object which is error prone and should be handled with care. This is hazardous because it shows that the programmer "did not know" what kind of object was she expecting to handle and worked by assumption. Assumptions are great for finding a solution but are equally bad for implementing one.
If you thought of optimizing something in the way you did it, then, just FYI this actually creates a unique (redundant) name in the symbol table (in the compiled SWF file) which causes worse compression of the SWF.
If you are doing this as a matter of experiment, this is fine, but you should avoid such code in real life projects.
One more thing to be aware of: comparison to true constant is 100% useless. If such comparison makes any sense at all (i.e. findObject may evaluate to false any time), then if (findObject) { ... } is equivalent but shorter version of your code.
Last thing, hopefully, the anonymous function is missing return type declaration. It won't really change much in your example, except that you will get compiler warning. Omitting type declaration is, in general, a bad style.
EDIT
public function addEventListener(type:String, listener:Function ...):void
{
this._listeners[type].push(listener);
}
public function dispatchEvent(event:Event):void
{
for each (var listener:Function in this._listeners[event.type])
listener(event);
}
public function removeEventListener(type:String, listener:Function, ...):void
{
delete this._listeners[type][listener];
}
Suppose you actually want to implement IEventDispatcher (instead of using another EventDispatcher - you may have your reasons to do so, one such reason is that native EventDispatcher generates insane amounts of short-lived objects - events, and you may want to reduce that.) But there is no way you can replicate event.target or event.currentTurget in your code because you can't access the object owning the method, so, you would leave that out.
Another example:
public class SomeEvent extends Event
{
private var _target:NotEventDispatcher;
public function SomeEvent(type:String, someTarget:NotEventDispatcher)
{
super(type);
this._target = someTarget;
}
public override function get target():Object
{
return this._target;
}
}
This is something that I actually saw in real world, this was used in either Mate or similar framework to sort of "anonymously" connect all event dispatchers to a single static instance of some "mothership event dispatcher".
I don't necessarily justify this approach, but, technically, nothing stops you from doing either one of these. What I was saying in my post above is that in certain situations the language promises you things, like, if you did:
var dispatcher:IEventDispatcher;
try
{
dispatcher = IEventDispatcher(event.currentTarget);
// now you can be sure this object has removeEventListener
dispatcher.removeEventListener(event.type, arguments.callee);
}
catch (error:Error)
{
// but what are you going to do here?
}
But the most common case would be you subscribing to a bubbling event, in which case, you don't know whether you want to unsubscribe from event.target or event.currentTtarget - because you don't know which one is that you are listening to.
I agree with wvxvw.
Another way to approach your problem is to have a variable to control the "state" of your ENTER_FRAME event:
private var _state:String;
private function init(e:Event):void {
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function loop(e:Event):void {
switch(_state) {
case "play":
// do play stuff
// when you want to pause
// goToPause();
break;
}
}
// you can call the method below from a button or whatever you want
private function goToPause():void {
_state = "pause";
// do some stuff here
// when you are done, switch "_state" back to "play"
}
In this example, you keep listening for ENTER_FRAME, but it only does things when the _state variable is set to "play". You can also remove the event listener in the goToPause method:
private function goToPause():void {
_state = "pause";
removeEventListener(Event.ENTER_FRAME, loop);
}
However, the nice thing about using the "_state" to switch things is that you don't end up having a mess of addEventListeners and removeEventListeners (which is what can happen depending on how complicated your loop gets) that you have to keep track of.
You should not use anonymous function call if you would like to remove listener some time later.
public function main():void
{
//...
//some method, where you add event listener
//...
//adding enterFrame event listener
this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
//...
}
private function enterFrameHandler(e:Event)
{
if(findObject) // " == true" is not really necessary here.
{
// removing enterFrame listener:
this.removeEventlistener(Event.ENTER_FRAME,enterFrameHandler);
}
}
Just for a completeness with the other techniques mentioned here, the function you are creating is a unbound closure, so you can also leverage that concept to reference both your function and dispatcher.
var callback:Function;
var dispacher:IEventDispatcher = this;
addEventListener(Event.ENTER_FRAME, callback = function(e:Event){
if(findObject == true){
dispacher.removeEventListener(Event.ENTER_FRAME, callback);
}
});
Normal closed-over variable rules apply.