Timer not working inside function in Phaser 3 - javascript

I have a spawn function which performs some task. Before the function returns, I would like to delay some another function call.
I tried using time.addEvent but with no luck as it does not seem to work within the spawn function. However the timer works perfectly inside the create function.
My code so far:
create(){
newMethod = spawn.bind(this);
newMethod();
}
function spawn(){
//do stuff
timer = this.time.addEvent({
delay: 3000,
callback: functionDelay,
loop: false
});
}
function functionDelay(){
console.log("Works!");
}

var delayText;
var delayedEvent;
class myScene extends Phaser.Scene {
constructor (config)
{
super(config);
}
preload ()
{
this.load.image('dude', 'sprites/phaser-dude.png')
}
create ()
{
delayText = this.add.text(50, 50);
delayedEvent = this.time.delayedCall(3000, this.spawn, [], this);
}
spawn()
{
var sprite = this.add.sprite(300, 50, 'dude')
}
update()
{
delayText.setText('Event.progress: ' + delayedEvent.getProgress().toString().substr(0, 4));
}
}
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
loader: {
baseURL: 'https://cdn.jsdelivr.net/gh/samme/phaser-examples-assets#v2.0.0/assets/',
crossOrigin: 'anonymous'
},
width: 800,
height: 600
};
var game = new Phaser.Game(config);
game.scene.add('myScene', myScene, true);
<script src="//cdn.jsdelivr.net/npm/phaser#3.17.0/dist/phaser.min.js"></script>

Related

Can I fire Phaser 3 events from DOM?

I'm trying to fire a Phaser 3 event from DOM like I read on this example I found on internet:
"The game instance is accessible when you create the game, with something like
const game = new Phaser.Game(config);
And you can access everything from your desired scene from this instance
const scene = game.scene.keys.sceneName
And do something like
textareaElement.addEventListener("change", function(event) {
const text = textareaElement.value;
scene.updateGameTextFromTextarea(text);
});
This is my project config:
const config = {
// Phaser.AUTO, intenta usar WebGL y si el navegador no lo tiene, usa canva.
type: Phaser.AUTO,
parent: 'game-container',
width: 650,
height: 522,
scene: [MainScene],
scale: {
mode: Phaser.Scale.FIT
},
dom: {
createContainer: true
},
physics: {
default: 'arcade',
arcade: {
// debug: true,
// gravity: { y: 350 }
}
}
}
// Inicializacion del objeto
game = new Phaser.Game(config)`
I actually can access to game from console, but canĀ“t fire events.
const scene = game.scene.keys.MainScene
scene.greeting()
get:
Uncaught TypeError: Cannot read properties of undefined (reading 'greeting')
at :1:7
Code:
https://github.com/Neffer2/bull-rompecabezas.git
There are two issues:
The first issue is, that the name of the scene is gameScene not MainScene , this is the value from the constructor.
The second issue is the Scene/Game Instance is not populated right away, you would have to wait abit. I simulated this in the demo with the setTimeout, or you can also click the Button to call the same function from the scene.
Here a short Demo:
document.body.style = 'margin:0;';
class MainScene extends Phaser.Scene {
constructor(){
super('gameScene');
}
test(){
console.info('Hello From Scene');
}
}
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
physics: {
default: 'arcade',
arcade: {
gravity:{ y: 100 },
debug: true
}
},
scene: [ MainScene ],
banner: false
};
var game = new Phaser.Game(config);
setTimeout( () => game.scene.keys.gameScene.test(), 1000); // Wait for generation
document.querySelector('button').addEventListener('click', () => game.scene.keys.gameScene.test())
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
<button id="button"> Fire from the DOM </button><br>

is not a function, when function is right above?

i often have the problem that i cant reach the Functions of my js classes inside the js class.
I mostly fix it with this. or a bind(this) but why does this not work? its the exact copy the way i do this in another class.
class Page {
// constructor and props
ShowNewJobPopup() {
var popupNewJobElement = $("<div>").attr("id", "popupNewJob");
console.log(data, element);
}
InitializeFilterElements(filterItems) {
filterItems["Control"].Items.push({
Template: function (itemElement) {
itemElement.append("<div id=\"btnNeuerJob\" style=\"width: 100%; margin-top: 4px;\"></div>");
$("#btnNeuerJob").dxButton({
type: "default",
text: "Neuer Job",
disabled: false,
onClick: function () {
this.ShowNewJobPopup(); // how is this not a function
}.bind(this)
});
}
});
return filterItems;
}
}
when im in the dev console. i can write Page.ShowNewJobPopup (Page is the script this is attached to)
The this is actually not from the class Page.
We rewrite your Page class in the below way and you can see the this is referred to the obj.
class Page {
constructor() {}
ShowNewJobPopup() {
var popupNewJobElement = $("<div>").attr("id", "popupNewJob");
console.log(data, element);
}
InitializeFilterElements(filterItems) {
const obj = {
Template: function (itemElement) {
itemElement.append(
'<div id="btnNeuerJob" style="width: 100%; margin-top: 4px;"></div>'
);
$("#btnNeuerJob").dxButton({
type: "default",
text: "Neuer Job",
disabled: false,
onClick: function () {
this.ShowNewJobPopup();
}.bind(this), // this is referred to obj
});
},
};
filterItems["Control"].Items.push(obj);
return filterItems;
}
}
What you can do is create a variable self to store the this and use it to reference to the class Page later.
class Page {
constructor() {}
ShowNewJobPopup() {
var popupNewJobElement = $("<div>").attr("id", "popupNewJob");
console.log(data, element);
}
InitializeFilterElements(filterItems) {
const self = this; // this here is referred to the class Page
const obj = {
Template: function (itemElement) {
itemElement.append(
'<div id="btnNeuerJob" style="width: 100%; margin-top: 4px;"></div>'
);
$("#btnNeuerJob").dxButton({
type: "default",
text: "Neuer Job",
disabled: false,
onClick: function () {
self.ShowNewJobPopup(); // self is referred to Page Class
},
});
},
};
filterItems["Control"].Items.push(obj);
return filterItems;
}
}
You seem to have several functions with different this contexts nested within each other. It would be much simpler just to create a variable at the top of your class method rather than worrying about passing the right context to each function.
InitializeFilterElements(filterItems) {
const thisPage = this; // SET THE VARIABLE
filterItems["Control"].Items.push({
Template: function (itemElement) {
itemElement.append("<div id=\"btnNeuerJob\" style=\"width: 100%; margin-top: 4px;\"></div>");
$("#btnNeuerJob").dxButton({
type: "default",
text: "Neuer Job",
disabled: false,
onClick: function () {
thisPage.ShowNewJobPopup();
}
});
}
});
return filterItems;
}
The value of this is determined by how a function is called (runtime binding). It can't be set by assignment during execution, and it may be different each time the function is called.
InitializeFilterElements(filterItems) {
const self = this; // self now holds the value of this reference
filterItems["Control"].Items.push({
Template: function (itemElement) {
itemElement.append("<div id=\"btnNeuerJob\" style=\"width: 100%; margin-top: 4px;\"></div>");
$("#btnNeuerJob").dxButton({
type: "default",
text: "Neuer Job",
disabled: false,
onClick: function () {
self.ShowNewJobPopup();
}
});
}
});
return filterItems;
}
This is a classic problem of this in JavaScript. When the Template function is invoked, the reference of this changes to whatever the context of the object, in which the function is invoked.
Checkout the example for the error
A possible fix is
class Page {
// constructor and props
ShowNewJobPopup() {
var popupNewJobElement = $("<div>").attr("id", "popupNewJob");
console.log(data, element);
}
InitializeFilterElements(filterItems) {
var that = this;
filterItems["Control"].Items.push({
Template: function (itemElement) {
itemElement.append("<div id=\"btnNeuerJob\" style=\"width: 100%; margin-top: 4px;\"></div>");
$("#btnNeuerJob").dxButton({
type: "default",
text: "Neuer Job",
disabled: false,
onClick: function () {
this.ShowNewJobPopup(); // how is this not a function
}.bind(that)
});
}
});
return filterItems;
}
}
class A {
test() {
console.log("test")
}
test2() {
let a = []
a.push({
template() {
function abc() {
console.log(this)
this.test()
}
abc.bind(this)()
}
})
return a
}
}
let a = new A()
let arr = a.test2()
//this will work
arr[0].template.bind(a)()
console.log("===========")
arr[0].template()

Phaser pointer events not working in WebView (Xamarin Forms) C#

I have create a simple animation screen using phaser and its rendering perfect and invoking all pointer events like pointerover, pointerout, pointerup and pointerdown on button btn as expected for browser. But when I render same game using WebView its rendering screen but its not invoking any pointer events. To confirm JavaScript is working I have tested by adding eventlistner on window and it was working.
var config = {
type: Phaser.CANVAS,
width: 650,
height: 900,
canvas: document.getElementById('myCustomCanvas'),
scale: {
mode: Phaser.Scale.FIT,
width: 750,
height: 1334,
parent: 'game'
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 }
}
},
scene : {
preload: function(){game.preload(this, 'asset_url');},
create: function(){game.create(this);},
update: function(){game.update(this);},
}
};
function game(config) {
this.start = function(config)
{
this.phaserGame = new Phaser.Game(config);
};
this.create = function()
{
var btn = this.scene.add.sprite(375, 665, 'btn');
btn.setInteractive()
.on('pointerover', () => {
console.log('pointerover');
})
.on('pointerout', () => {
console.log('pointerout');
})
.on('pointerdown', () => {
console.log('pointerdown');
})
.on('pointerup', () => {
console.log('pointerup');
});
};
}
I am using Xamarin Forms WebView to render game in mobile. My setting for WebView is as follow.
var webView = CreateNativeControl();
webView.SetWebViewClient(new HTMLGameWebClient(this));
webView.Settings.LightTouchEnabled = true;
webView.Settings.JavaScriptEnabled = true;
webView.Settings.DomStorageEnabled = true;
webView.Settings.MediaPlaybackRequiresUserGesture = false;
SetNativeControl(webView);
I also have conditional CSS which is used only if canvas is rendering in mobile.
canvas {
width: 100vw !important;
height: unset;
}
Thanks in advance for any helps or suggestions.

Jasmine's BeforeAll inner functions are called after a spec

I am making a game with the Phaser-framework and I am coding automatic tests with Jasmine. All works fine in this code exept the function beforeAll() (called after a it (spec)) The console prints:
test2
test
when it should print test test2. I tried the beforeEach() but it does not make any difference.
describe("Hit Box", function() {
var collide = false;
beforeAll(function() {
game = new Phaser.Game(400, 400, Phaser.AUTO, '', { preload: preload, create: create, render:render}, false, true);
function preload() {
game.load.image('blue', 'assets/magicien100.png');
game.load.image('fire_ball', 'assets/red.png');
}
function create() {
game.physics.startSystem(Phaser.Physics.ARCADE);
game.world.setBounds(0, 0, 400, 400);
game.dummyPlayer = game.add.sprite(100,100,'blue');
game.dummyPlayer.width = 100;
game.dummyPlayer.height = 100;
game.physics.arcade.enable(game.dummyPlayer);
game.dummySpell = game.add.sprite(50, 50, 'fire_ball');
game.dummySpell.width = 75;
game.dummySpell.height = 75;
game.physics.arcade.enable(game.dummySpell);
game.debug.spriteBounds(game.dummyPlayer);
game.debug.spriteBounds(game.dummySpell);
if (game.physics.arcade.overlap(game.dummyPlayer, game.dummySpell)) {
collide = true;
console.log('test');
}
}
function render(){
game.debug.spriteBounds(game.dummyPlayer);
game.debug.spriteBounds(game.dummySpell);
}
});
it("Should have a collision", function() {
expect(collide).toBe(true);
console.log('test2');
});
});
If Phaser.Game is asynchronous, it may still be creating when the it function is run.
Try returning a promise from the beforeAll:
describe("Hit Box", function() {
var collide = false;
beforeEach(function() {
return new Promise(function(resolve, reject) {
game = new Phaser.Game(400, 400, Phaser.AUTO, '', { preload: preload, create: create, render:render}, false, true);
function preload() {
// preload code
}
function create() {
// create code
resolve(); // to complete the promise
}
});
});

jQuery deferred - when multiple timeout tasks have completed

Having a few teething problems with $.deferred, $.when and $.done.
I'm calling a method which has a couple of tasks inside on a timer. I'm looking at getting a callback when everything inside this method has completed, including the stuff in timers, so started looking at $.when() and $.done() to achieve this.
The problem I am getting is the function is firing before the tasks have completed, immediately as the method is called. So, I started playing with $.deferred and resolve(), but haven't managed to get anything working. Without the timers, I can do it.
This is where I call the method:
$.when(cover.start()).done(function() {
console.log("Cover has started.");
});
This is the entire method:
return {
other: function() {},
start: function() {
var dfd = $.Deferred();
el.filter.animate({
"opacity": "0.6", "filter": "alpha(opacity=60)"
}, 2000, "easeInOutCirc", function() {
el.share.removeClass('fa-spin');
setTimeout(function() {
el.share.removeClass('fa-cog').addClass('fa-bars');
},1000);
setTimeout(function() {
el.scroll.animate({
"opacity": "1",
"bottom": "40px"
}, 1200, "easeOutBounce", function() {
var pulseOptions = { opacity: "0" };
setTimeout(function() {
el.scroll.pulse(pulseOptions, {
duration : 400,
pulses: 3,
interval: 500,
returnDelay: 800
});
}, 2000);
dfd.resolve();
});
}, 2000);
return dfd.promise();
});
}
} // end return
As you can see, after my original attempt failed, I added dfd.resolve() to where I want the callback and tried to return the promise. However, the function still fires too early. Where am I going wrong?
The problem is, you need to return promise from the start method
return {
other: function () {},
start: function () {
var dfd = $.Deferred();
el.filter.animate({
"opacity": "0.6",
"filter": "alpha(opacity=60)"
}, 2000, "easeInOutCirc", function () {
el.share.removeClass('fa-spin');
setTimeout(function () {
el.share.removeClass('fa-cog').addClass('fa-bars');
}, 1000);
setTimeout(function () {
el.scroll.animate({
"opacity": "1",
"bottom": "40px"
}, 1200, "easeOutBounce", function () {
var pulseOptions = {
opacity: "0"
};
setTimeout(function () {
el.scroll.pulse(pulseOptions, {
duration: 400,
pulses: 3,
interval: 500,
returnDelay: 800
});
}, 2000);
dfd.resolve();
});
}, 2000);
});
//need to return from start
return dfd.promise();
}
} // end return
Not fishing to steal APJ's rep' but out of interest, you could avoid callback hell by exploiting .delay() and .promise(), both of which relate to the default "fx" animation queue.
Something along the following lines would fix the problem, and would be more readable :
//animation maps
var maps = [];
maps[0] = { 'opacity':0.6, 'filter':'alpha(opacity=60)' };
maps[1] = { 'opacity':1, 'bottom':'40px' };
maps[2] = { 'opacity':0 };
maps[3] = { 'duration':400, 'pulses':3, 'interval':500, 'returnDelay':800 };
//animation functions
var f = [];
f[0] = function () {
return el.filter.animate(maps[0], 2000, "easeInOutCirc").promise();
};
f[1] = function () {
return el.share.removeClass('fa-spin').delay(1000).promise();
};
f[2] = function () {
return el.share.removeClass('fa-cog').addClass('fa-bars').delay(1000).promise();
};
f[3] = function () {
el.scroll.animate(maps[1], 1200, "easeOutBounce").promise();
}
f[4] = function () {
return el.scroll.delay(2000).promise();//delay() could be called on any element. `el.scroll` is arbitrary.
};
f[5] = function () {
el.scroll.pulse(maps[2], maps[3]);
};
return {
other: function () {},
start: function () {
//animation sequence
var p = f[0]().then(f[1]).then(f[2]).then(f[3]);
p.then(f[4]).then(f[5]);
return p;//<<<< and here's the all important return
}
}
Not sure this is 100% correct - might need some work.
It's worth noting that there are performance pros and cons with this approach :
Pros: Reusable animation maps; Reusable functions;
Cons: More liberal use of promises will cause a larger memory spike.

Categories

Resources