Phaser - error: Cannot read property 'on' of null - javascript

I'm working on a simple game using Phaser.
My code:
preload() {
this.load.image('player', 'img/player.png');
}
create() {
var player = this.add.sprite(100,100,'player');
player.inputEnabled = true;
player.input.on('pointerdown', () => {
this.scene.stop('ThisScene');
this.scene.start('NextScene');
})
}
The game should switch from one scene to another when you click on the sprite 'player'. Unfortunately, this gives an error: Cannot read property 'on' of null
Any ideas?

The reason it doesn't work is that you've got a mixture of Phaser 2 and Phaser 3 code here. The inputEnabled = true comes from Phaser 2, and the use of the on event listener is from Phaser 3.
You don't state which version you're using, though, so I'll assume 3 and fix the code for that:
preload() {
this.load.image('player', 'img/player.png');
}
create() {
var player = this.add.sprite(100,100,'player');
player.setInteractive();
player.on('pointerdown', () => {
this.scene.start('NextScene');
})
}
When a Game Object is enabled for input, you listen for the events directly on the Game Object itself, not the input component.

Have you check if the pointerdown event is working? Let's try to replace the line player.inputEnabled = true; with the line player.setInteractive();. Then, do a console.log('pointerdown event working') inside the event listener:
create() {
var player = this.add.sprite(100,100,'player');
player.setInteractive();
player.input.on('pointerdown', () => {
console.log('pointerdown event working');
this.scene.stop('ThisScene');
this.scene.start('NextScene');
})
}
Let me know if that works...If not, we'll know that the event is firing & we'll take it from there.

Related

Is there a way I can dynamically bind a string and the text it outputs without using setInterval?

Is there a way I can dynamically bind a string and the text it outputs without using setInterval? I want it to be similar to Angular and Vue though I want to do this with vanilla JS. I want to be able to open the console and change the value at any time and see the change output on my element. Thank you in advance!
I think your only two options are:
A. Edit the element directly, e.g.
myPublicElemeVariable.innerText = 'Bla'
B. Use a setter (or Proxy):
obj = {
get str() { return this.myStr; }
set str(val) {
elem.innerText = val;
this.myStr = val;
}
}
C. Just use a function/method!
If you mean you want change to be event-driven, there is already a very simple event framework in javascript - the EventTarget class as demonstrated by this Code Sandbox
//define a watchable thing
class ValueTarget extends EventTarget {
constructor(value = "") {
super();
this.setValue(value);
}
getValue() {
return this._value;
}
setValue(value) {
this._value = value;
this.dispatchEvent(new CustomEvent("change", { detail: { value } }));
}
}
//get the page elements
const inputElement = document.querySelector("input");
const outputElement = document.querySelector("h1");
//create a watchable thing
const fieldTarget = new ValueTarget("");
//wire events that will change the watchable
inputElement.addEventListener("input", function (e) {
fieldTarget.setValue(e.target.value);
});
//receive notifications from the watchable
fieldTarget.addEventListener("change", (e) => {
outputElement.textContent = e.detail.value;
});
You may be as well to build your own given how simple it is - maintains a list of listeners and calls them when notified. My work recently needed such a thing which I knocked up in Typescript at https://github.com/cefn/lauf/blob/main/modules/lauf-store/src/core/watchable.ts#L3-L21 and would therefore be very easy to redo in javascript.

Can't get event.target.id to match in Javascript

I have three buttons nested within the buttons variable a form that are all supposed to play a certain song if the buttons within the nested element are clicked. The code to check which buttons are pressed is:
//buttons variable that the three button elements are nested in
const buttons = document.getElementById('music-choice');
//the buttons themselves
const canonButton = document.getElementById('canonButton');
const moonlightButton = document.getElementById('moonlightButton');
const _40thButton = document.getElementById('40thButton');
//variables connected to the audio elements
const canonMusic = document.querySelector('audio[id="canon"]')
const moonlightMusic = document.querySelector('audio[id="moonlight"]')
const _40thMusic = document.querySelector('audio[id="40th"]')
buttons.addEventListener('click', musicPlayer(event)); //this is js.20
function musicPlayer(event) {
if (event.target.id === 'moonlightButton') { //this is js.23
moonlightMusic.play();
}
else if (event.target.id === 'canonButton') {
canonMusic.play();
}
else if (event.target.id === '40thButton') {
_40thMusic.play();
}
}
However, everytime I try to run this code I get this error message in the console:
Uncaught TypeError: Cannot read property 'target' of undefined
at musicPlayer (index.js:23)
at index.js:20
When I use the console log of those event objects, it says that the event.target.id is moonlightButton, so I would assume that having it equal to the string moonlightButton would trigger those if and else if statements but that isn't the case. I have tried this with classnames and values and have no luck with any of them so far. I'm new to Javascript so any help would be appreciated.
As vlaz has pointed out, I was trying to pass in a nonexistent parameter to my event listener.

Trouble referencing object from within function - adobe animate canvas

I am having trouble getting a function to reference a movie clip on the stage (thatsRight). I can reference it outside of a function to initially set it visible = false and inside the this.Correct function to visible = true, but calling another function this.removeAndCheck can not reference the same movie clip on the stage. I get the error
"TypeError: undefined is not an object (evaluating
'this.thatsRight.visible = false')"
on the line in the this.removeAndCheck function. This doesn't make sense to me. One function can reference the movie clip but another can not. This code is on frame.
this.thatsRight.visible = false;
this.Correct = function() {
this.thatsRight.visible = true;
setTimeout(this.removeAndCheck, 3000)
}
this.removeAndCheck = function() {
this.thatsRight.visible = false;
this.CheckAllCorrect();
}
I am also have issue with this.CheckAllCorrect() being called. this.CheckAllCorrect() is also on from one but on another action layer.
This is part of a conversion of different as3 flash assets to html5 canvas assets using adobe animate CC. Any help with this would be greatly appreciated.
#Sammeer is correct, this is a scoping issue. Typically I get around this with a Function.bind
setTimeout(this.removeAndCheck.bind(this), 3000);
You might also see local variable binding like this:
var that = this;
setTimeout(function() { that.removeAndCheck(); }, 3000);
Here is some further reading.
This is a scoping issue with the value of the this variable, which is a common mistake in javascript. To avoid these issues completely, just use arrow functions instead:
this.thatsRight.visible = false;
this.removeAndCheck = () => {
this.thatsRight.visible = false;
this.CheckAllCorrect();
}
this.Correct = () => {
this.thatsRight.visible = true;
setTimeout(this.removeAndCheck, 3000)
}

Javascript order of execution: how to detect object creation

I want to instantiate a new MediaElementPlayer object. When it's successfully created, I want to pass the whole object on to another function (my_object.attachEvents). My code is as follows:
var options = {
success: function () {
//point 2
console.log("passing player object", local_player_instance);
my_main_object.attachEvents(local_player_instance);
}
}
//point 1
console.log('player inited', local_player_instance);
local_player_instance.video = new MediaElementPlayer('#video_player', options);
my_main_object.attachEvents = function(local_player_instance) {
local_player_instance.video.play()
}
In Firefox, the assignment at point one is executed before the line at point 2 calls the attach events method.
Im Chrome, point 2 is evaluate first, and as a result when the play method in the attach events function is called it doesn't exist.
My question is, how do I pass successfully pass the MediaElementPlayer to another function when it is created?
The best way to handle this in a cross browser way is
// here's where you'll store a global reference to the player
var globalMediaElement = null;
var options = {
success: function (domNode, mediaElement) {
globalMediaElement = mediaElement;
doStuff();
// you can also get the the player via jQuery here
$('#video_player').player
}
}
// create MediaElement
new MediaElementPlayer('#video_player', options);
function doStuff() {
globalMediaElement.play();
}

Dynamically Auto-Linking instance variables to object functions in javascript / jquery?

I'm trying to create a robust audio player in javascript (& jQuery). I know that there are other players out there, but I'd like to try creating my own (so please don't refer me to jquery plugins). This is essentially what I would like to do:
Main.js:
var player = new Player(AudioObj); // Audio object links to Audio class (not shown)
player.buttons.play = $('play');
player.buttons.pause = $('pause'); // Play and pause ID's link to HTML Document Element
Player.js:
Player = function(Audio) {
this.Audio = Audio;
this.buttons = {};
for(var button in this.buttons) {
button.live('click', this.button); // This is the line I Have NO idea about..
}
}
Player.prototype = {
play : function() {
// Do Something
},
pause : function() {
// Do something
}
}
So essentially, I would like the properties to be pre-linked to object functions when you initialize the Player, and to just have it work when I link it to an HTML element.
Thanks!
Matt Mueller
I think this would be a more OO way to go. Setup two more functions inside Player. One function would register a UI element to a Player action and another to unregister the action. So rather than keeping an explicit button collection you can just lean on jQuery.live and jQuery.die. For example:
function registerAction(selector, action) {
// you could have some logic to map the passed in action
// to the actual function name
$(selector).live('click', action/functionName);
}
function unRegisterAction(selector, [action]) {
// you could have some logic to map the passed in action
// to the actual function name
$(selector).die('click', [action/functionName]);
}
Then, your main.js example from above would become:
var player = new Player(AudioObj); // Audio object links to Audio class (not shown)
player.registerAction('#play', play);
player.registerAction('#pause', pause); // Play and pause ID's link to HTML Document Element
And your Player constructor would become:
Player = function(Audio) {
this.Audio = Audio;
}
Or something like that.
This is not the perfect solution but I find it to be pretty elegant:
Player.js
Player.prototype = {
init: function() {
var Player = this;
// Attach buttons to respected functions
for(var button in this.buttons) {
if(typeof Player[button] === "function")
$(this.buttons[button]).bind('click', {Player : this}, Player[button]);
}
},
play: function(e){
var Player = e.data.Player;
var Audio = Player.Audio;
Audio.play();
},
pause: function(e){
var Player = e.data.Player;
var Audio = Player.Audio;
Audio.pause();
}
}
Main.js
var audio = new AudioCore("UpToYou.mp3");
var player = new Player(audio);
player.buttons.play = $('#play');
player.buttons.pause = $('#pause');
player.init();
This provides a nice way to link buttons to the function without passing in a huge array or providing a bunch of options. I would be VERY happy to have a solution that would NOT require you to call init().

Categories