Hey there! I’ve been trying to do a new level for another of my games, but I have stumbled across an issue, copy paste. I’m trying to use, for example, collectStar() across different scenes without having to repeat it again and again. Is there a way to only have 1 instance of the code and being able to call it at any scene?
There are many ways to do that. Create a class with static methods and pass the scene to them. Create a class you instantiate with those methods and pass the scene to it. Create a base class that extends the Phaser scene, then have your scenes extend that base class, like
BaseLevel extends Phaser.Scene
MyLevel extends BaseLevel
You can also create an object literal for a global library.
so, if I have shieldEffect() in the stage scene, I can do shieldEffect extends stage then import it into the other scenes? If so, is there a way to convey all the exports of functions into only 1 import to avoid doing the same process over and over again?
If you choose to do it that way, it’s like this:
class BaseLevel extends Phaser.Scene {
shieldEffect() {
// Do stuff
}
takeDamage() {
// Do stuff
}
}
class MyFirstLevel extends BaseLevel {
constructor() {
super('MyFirstLevel');
}
preload() {
}
create() {
}
update() {
this.shieldEffect();
this.takeDamage();
}
}
class MyOtherLevel extends BaseLevel {
constructor() {
super('MyOtherLevel');
}
preload() {
}
create() {
}
update() {
this.shieldEffect();
this.takeDamage();
}
}
This is basic class inheritance. Whatever you put in the level scenes, will inherit what’s in the base level, so you’re creating an extension of the Phaser scene class with your custom methods. The advantage to that is you don’t have to pass the scene around and it “just works.” The disadvantage is you have to take care of collisions with the Phaser scene API. AKA you can accidentally override the Phaser methods or data.
I’m a bit lost, so in order to have “global functions” i need to make a class that extends to a phaser scene, then all my stage data (a scene) must be on a class that extends to the class with the “global functions”?
It depends on how you want to design the software. Is “global functions” the way you would describe the pattern you’re going for?
Indeed, let me explain a bit on what I want to do:
So, my game is separated per stages, each stage in it’s own .js file. Now, each stage share some stuff regardless of the objects (player, bombs, etc.) and each interaction triggers a function. Now, one thing I could do is just copy paste everything between stages, but that will only make everything much harder to replace or edit. In order to make my workflow faster, I need to only define the function once and be able to be triggered/updated in other scenes (or stages), hence why the “global functions” name.
Ok, thanks. So I described maybe four ways to do that. The inheritance pattern is only one example. That’s not the only way to do it. Probably the preferred way to do this design is to use a class with static methods (a method meaning a function within a class, it’s just a function). You have to pass the scene into each method. An example of a class I’m developing is this:
class Animal {
static species = [];
static populationCheckInterval = 1000;
static overworldMemory = null;
static spawnExclusionAreas = [
{
left: 1376,
top: 3168,
right: 1440,
bottom: 3216
},
{
left: 3288,
top: 3108,
right: 3448,
bottom: 3172
}
];
static captureRules = {
leash: {
type: 'dog',
name: 'dog',
consume: false
},
fish: {
type: 'cat',
name: 'cat',
consume: true
},
fruit: {
type: 'bunny',
name: 'bunny',
consume: true
}
};
static captureItemNames = {
dog: 'a leash',
cat: 'a fish',
bunny: 'an apple'
};
static createPetRecord(animal) {
return {
type: animal.config.type,
color:
animal.config.color || null,
variant:
animal.config.variant || null,
tint:
animal.sprite.isTinted
? animal.sprite.tintTopLeft
: null
};
}
static spawnResidentPets(scene) {
scene.animals = [];
const pets =
gameData.get('npcPets') || [];
pets.forEach((record, index) => {
if(record.home !== scene.data.name) {
return;
}
const Species =
this.species.find((speciesClass) => {
return (
speciesClass.name.toLowerCase() ===
record.type
);
});
if(!Species) {
return;
}
const config = {
x: record.x,
y: record.y,
roamRadius: 48
};
if(record.color) {
config.color = record.color;
}
if(record.variant) {
config.variant = record.variant;
}
if(record.type === 'bunny') {
config.tint = record.tint;
}
const animal =
new Species(
scene,
config
);
/*
* Cat randomly chooses its color in its
* constructor, so restore the delivered color.
*/
if(record.tint === null) {
animal.sprite.clearTint();
} else if(
typeof record.tint === 'number'
) {
animal.sprite.setTint(
record.tint
);
}
if(
Object.prototype.hasOwnProperty.call(
animal,
'tint'
)
) {
animal.tint = record.tint;
}
animal.residentPetIndex = index;
scene.animals.push(animal);
});
this.updateDepths(scene);
}
}
These are not “globals” in the technical sense. To truly make something global in JavaScript, you would attach it to window like:
window.myFunction = function(){
// Do stuff
}
Or just anywhere:
function myFunction() {
console.log('Hello');
}
I would avoid that and use something object-oriented. To use a method in my animal class, you would just do:
Animal.spawnResidentPets(scene);
So you can put that anywhere in any scene. Try that instead of inheritance.
So, I do a class, then inside the class I add a static , which serves as a function right?
So when I go and place it inside a scene i just do, for example, staticFunctions.starCollect() ? Would this also be able react to the var and objects defined inside the scene? Or it’ll break if it’s not defined before importing the class?
In my example, I have added static data to my class (also called properties), so I have a property called species, which is an array and not a function. This represents static data included in the class. So it’s not the static keyword that makes something a function. If you want a static method (aka function), you would put static functionName, like where I have:
static spawnResidentPets(scene) {}
That defines a static method (function) within my Animal class. This is a class I can use without instantiating the class. Like I don’t have to do this:
let a = new Animal(); a.spawnResidentPets(scene);
This isn’t really Phaser stuff, this just has to do with how Object-Oriented Programming works.
Don’t call your class staticFunctions. Think of your program in terms of objects and their methods (functions) and data (properties). I don’t know how things are labeled in your app, but maybe the shieldEffect() is on a Player class, for example.
The static functions in the class can interact with whatever data you pass them, so if you pass the scene, it will have access to all the scene data and it’s scene agnostic in that way, so you can pass it different scenes and it will act on that scene. If there is data outside the scene, you’ll have to pass that as well and yeah I would include these prior to the scenes so they’re available when the scene starts up.
I hope this answers your question. You might want to study a little more about how to use classes and stuff. It’s an involved topic and it’s good to fully grasp it.
I see… this would work if I place it in separate files without an import or export (like how scenes do it)? Also, is the code ok like this, for example?
class reusableActions{
//star function
static collectStar (player, star)
{
this.starCollect.copyPosition(star).play('star_collect');
star.disableBody(true, true);
// Control del sonido
if (!this.starSound.isPlaying) {
this.starSound.play();
} else {
this.starSound.stop();
this.starSound.play();
}
this.score += 100;
this.innerScore += 100;
this.scoreText.setText(`SCORE: ${this.score}`).setVisible(true);
this.levelText.setText(`LEVEL: ${this.level}`).setVisible(true);
if (this.stars.countActive(true) === 0) {
this.stars.children.iterate(child => {
child.respawn();
});
this.sound.play('bomb_fall')
for (let i = this.bombSpawning; i < this.bombsThatShouldSpawn; i++){
const x = Phaser.Math.Between(100, 900);
const bomb = new Bomb(this, x, -10, this.bombs);
}
//this is for the shield
if(!this.effectShield && this.shieldGenObj.countActive(true) == 0){
const shieldProbability = 3 //Phaser.Math.Between(1, 5)
if(shieldProbability == 3){
this.Xshield = Phaser.Math.Between(100, 800);
this.Yshield = Phaser.Math.Between(100, 400);
this.shieldCollect = this.shieldGenObj.create(this.Xshield, this.Yshield, 'shield_box')
this.shieldCollect.anims.play('shield_pw', true)
console.log("this should have spawned a shield box...")
}
}
if(this.invGenObj.countActive(true) == 0){
const invProbability = 3//Phaser.Math.Between(1, 10)
if(invProbability == 3){
this.Xinv = Phaser.Math.Between(100, 800);
this.Yinv = Phaser.Math.Between(100, 400);
this.invCollect = this.invGenObj.create(this.Xinv, this.Yinv, 'star_box')
this.invCollect.anims.play('star_pw', true)
console.log("this should have spawned a candy...")
}
}
}
}
}
If you’re using ES6 modules, it can be a module, you can put:
export class reusableActions
Then you import as usual. Just use a module property on your script tag.
As to your code, it won’t work because you have “this” everywhere. “this” is for use inside an instantiated class. Furthermore, in this case “this” references your Phaser scene. You have to pass that Phaser scene to each static method, like this (this is what goes inside your scene):
reusableActions.collectStar(this, player, star);
Your function signature would change to:
static collectStar(scene, player, star) {}
Inside each static method, replace every reference to “this” with “scene”:
this.starSound.play();
becomes
scene.starSound.play();
I would seriously consider taking an online course to brush up on JavaScript OOP. If you haven’t worked with this, muddling through it will be very frustrating. It’s not a game design or Phaser concept. You have to put yourself in touch with the concepts and syntax of working with classes and objects.
Yeah figured that in the meantime, I’ll see if it works!
It kinda worked? My issue is that this gets greated before creating an object, leading to the error
Uncaught TypeError: can't access property "x", t is undefined, is there any way that the code jumps that until it gets created?
You would have to post like your whole source code for me to troubleshoot that error. You can load your class as soon as you need to. It won’t access anything until you execute something from it.
I can share the Github link if that helps…
Ok, running the project on my server, it has a couple 404s for:
hoots.json
powerups.json
shield.json
invstars.json
Inside the collectStar(), you still have “this”:
this.Xinv = Phaser.Math.Between(100, 800);
this.Yinv = Phaser.Math.Between(100, 400);
Gotta change those to “scene”. You’re calling functions like this:
reusableActions.collectStar(this, this.player, this.star), null, this);
Just do it like this, the null, this parameters don’t do anything:
reusableActions.collectStar(this, this.player, this.star);
As far as the error with x is undefined, the star parameter is undefined. Searching the Game class, I could not find a place where you set “this.star”.
In general, if you get an error saying a variable is undefined, use console.log to log that variable and if it’s undefined inside your function, trace it back to where you set it and console.log it there. If you can’t find where it’s set, that’s the issue.
Ok, I got the error to go, now i have this issue:
Uncaught TypeError: scene.stars.disableBody is not a function. this part of the code is to disable one single star if collected.
scene.stars is a physics group:
this.stars = this.physics.add.group({
classType: Star,
key: 'star',
repeat: 10,
setXY: { x: 12, y: 0, stepX: 70 }
});
disableBody is supposed to be used on a sprite or image, not the group itself. You should look at the Phaser docs for how to disable an entire group, or else loop through the group and disable each child individually.