finiteadventurer Posted April 14, 2014 Share Posted April 14, 2014 Hi, I've been making my first game in Phaser (using Phaser 2.0.3) and have been looking to ensure that some sounds are garbage collected when they are no longer in use. I was hoping to use Sound.destroy to do so (which I think is a new feature to Phaser 2.0.3)/ However, when I call Sound.destroy, I am getting the following error (as pasted below from Chrome's console): Uncaught TypeError: Cannot read property 'length' of undefined Phaser.Signal.removeAll phaser.js:14369Phaser.Signal.dispose phaser.js:14451Phaser.Sound.destroy phaser.js:43234 I get the same error in all browsers tested (Chrome and Firefox). Has anyone else encountered this issue? Thanks! Link to comment Share on other sites More sharing options...
codevinsky Posted April 14, 2014 Share Posted April 14, 2014 Can you share your code? Link to comment Share on other sites More sharing options...
finiteadventurer Posted April 14, 2014 Author Share Posted April 14, 2014 My code is up in the following github repo: https://github.com/sarahquigley/crawl The latest version of my code (where I have encountered this issue) can be seen on the development branch of the repo, in the file crawl.js, here: https://github.com/sarahquigley/crawl/blob/development/js/crawl.js At this stage, I have just begun experimenting with adding sound here - and had also come to realise that garbage collection is an issue I have not dealt with properly in my code. I thought I would first experiment with garbage collection of sound, then move on to tackle the issue on a larger scale. To point out the most relevant bits of the code. I have an object for my Player, with a music attribute - see below - crawl.js lines 1 - 12:Player = function(game, cursors){ this.game = game; this.cursors = cursors; this.sprite = this.game.add.sprite((this.game.world.centerX) - 21, (this.game.world.centerY) - 16, 'player'); this.game.physics.enable(this.sprite, Phaser.Physics.ARCADE); this.sprite.anchor.setTo(0.5, 0.5); this.sprite.body.collideWorldBounds = true; this.sprite.body.setSize(this.config.bodyWidth, this.config.bodyHeight, this.config.bodyWidth/2, this.config.bodyHeight/2); this.music = this.game.add.audio('player', 1, true); this.music.play();};I was experimenting with Sound.Destroy here - crawl.js lines 209 - 214: gameOver: function(player, baddy){ player.kill(); game.score = this.score; this.player.music.destroy(); game.state.start('Over'); }This function is called when the player collides with the enemy. This kills the player and starts a new state 'over', in which the player can restart the game if they wish. Link to comment Share on other sites More sharing options...
finiteadventurer Posted April 14, 2014 Author Share Posted April 14, 2014 On checking things out further from the Chrome's console, I'm not sure that my own game code is very relevant to this error. The following set of commands, run from the console, lead to the same error:// Create a gameg = Phaser.Game(10, 10, Phaser.AUTO, 'g');// Load some audiog.load.audio('sound', ['assets/audio/Buggybug_Play.mp3', 'assets/audio/Buggybug_Play.ogg'])// Add a sounds = g.add.sound('sound');// Destroy the sounds.destroy(); Link to comment Share on other sites More sharing options...
finiteadventurer Posted April 14, 2014 Author Share Posted April 14, 2014 I've done my best to delve into the Phaser 2.0.3 source code to find an explanation for this error. This is the code for Phaser.Sound#destroy - Phaser 2.0.3 lines 43213 - 43243: /** * Destroys this sound and all associated events and removes it from the SoundManager. * * @method Phaser.Sound#destroy * @param {boolean} [remove=true] - If true this Sound is automatically removed from the SoundManager. */ destroy: function (remove) { if (typeof remove === 'undefined') { remove = true; } this.stop(); if (remove) { this.game.sound.remove(this); } this.markers = {}; this.context = null; this._buffer = null; this.externalNode = null; this.onDecoded.dispose(); this.onPlay.dispose(); this.onPause.dispose(); this.onResume.dispose(); this.onLoop.dispose(); this.onStop.dispose(); this.onMute.dispose(); this.onMarkerComplete.dispose(); }In the code above, note how, when this function is called with parameter remove set to true, Phaser.SoundManager#remove is called. Below is the code corresponding to Phaser.SoundManager#remove - Phaser 2.0.3 lines 43721 - 43744: /** * Removes a Sound from the SoundManager. The removed Sound is destroyed before removal. * * @method Phaser.SoundManager#remove * @param {Phaser.Sound} sound - The sound object to remove. * @return {boolean} True if the sound was removed successfully, otherwise false. */ remove: function (sound) { var i = this._sounds.length; while (i--) { if (this._sounds[i] === sound) { this._sounds[i].destroy(false); this._sounds.splice(i, 1); return true; } } return false; },In the above code, note how, if the sound passed to Phaser.SoundManager#remove is included in the _sounds array, Phaser.Sound#destroy is called on this sound. So when, we call Phaser.Sound#destroy on a sound with the remove parameter set to true, Phaser.Sound#destroy will be called twice. The first time it is called, the function will execute without error. However, the second time it is called, the attempted execution of any of the following lines will result in an error: this.onDecoded.dispose(); this.onPlay.dispose(); this.onPause.dispose(); this.onResume.dispose(); this.onLoop.dispose(); this.onStop.dispose(); this.onMute.dispose(); this.onMarkerComplete.dispose();Here's why: The first time Phaser.Sound#destroy executes (calling Phaser.Signal#dispose on all of the above attributes), the _bindings attribute of all of the above attributes will be deleted. During the second execution of Phaser.Sound#destroy (when Phaser.Signal#dispose is called a second time on all of these attributes), the absence of the _binding attribute of all of the above objects will result in the error I encountered. This is because Phaser.Signal#dispose cannot execute to completion if the _bindings attribute of the object it is called on is undefined. Unless I have misinterpreted things somehow, I think the issue I have encountered is a bug in the implementation of Phaser.Sound#destroy. Link to comment Share on other sites More sharing options...
rolandino Posted May 7, 2014 Share Posted May 7, 2014 I've been seeing the same thing. Looks like a bug to me .... Link to comment Share on other sites More sharing options...
rolandino Posted May 7, 2014 Share Posted May 7, 2014 Looks like there's a workaround: this.game.sound.remove(_sound); // Could just use this.game.sound.removeByKey(key); Link to comment Share on other sites More sharing options...
Recommended Posts