I want to reuse assets in multiple scenes, but I get this error:
Texture key already in use
Here is the relevant code. Notice that I want to use the same spritesheet in both scenes. How do I do that without getting this error?
class InventoryScene extends Phaser.Scene {
constructor() {
super({ key: 'InventoryScene', active: true });
}
preload() {
this.load.spritesheet('sheet1', 'assets/sheet1.png',
{ frameWidth: 16, frameHeight: 16 });
}
create() {
this.add.sprite(this.game.canvas.width * .50,
this.game.canvas.height * .50, 'sheet1')
.setFrame(0)
.setInteractive()
.on('pointerdown', () => this.zoomIn())
.setScale(6);
}
}
class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene', active: true });
}
preload() {
this.load.spritesheet('sheet1', 'assets/sheet1.png',
{ frameWidth: 16, frameHeight: 16 });
}
create() {
this.add.sprite(this.game.canvas.width * .50, this
.game.canvas.height * .50, 'sheet1')
.setFrame(0)
.setVisible(false);
}
}
Here is my game config:
var config = {
...
// Order here matters. The first scene is listed last.
scene: [DebugScene, InventoryScene, GameScene, HowToPlayScene, TitleScene]
};
Then I use this.scene.bringToTop('HowToPlayScene') to activate a scene.
PS: I am using Phaser 3
I created for you modified example with Phaser v3.17.
Copy Paste the code below and replace exist one in the page.
Click on 'Run Code' button on the top of the screen and may be have to wait 5-10 seconds:
https://labs.phaser.io/edit.html?src=src/scenes/changing%20scene.js
Scenes will be changed by click on first scene and others in loop.
Pay attention on "face" and "balls" spritesheet that was loaded only in first scene and this.scene.start('sceneB');
var SceneA = new Phaser.Class({
Extends: Phaser.Scene,
initialize:
function SceneA() {
Phaser.Scene.call(this, { key: 'sceneA' });
},
preload: function () {
this.load.image('face', 'assets/pics/bw-face.png');
this.load.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
},
create: function () {
this.add.sprite(400, 300, 'face').setAlpha(0.2);
this.add.sprite(400, 300, 'balls', 3);
this.input.once('pointerdown', function () {
console.log('From SceneA to SceneB');
this.scene.start('sceneB');
}, this);
}
});
var SceneB = new Phaser.Class({
Extends: Phaser.Scene,
initialize:
function SceneB() {
Phaser.Scene.call(this, { key: 'sceneB' });
},
preload: function () {
this.load.image('arrow', 'assets/sprites/longarrow.png');
},
create: function () {
this.add.sprite(400, 300, 'face').setAlpha(1);
this.arrow = this.add.sprite(400, 300, 'arrow').setOrigin(0, 0.5);
this.add.sprite(400, 300, 'balls', 3);
this.input.once('pointerdown', function (event) {
console.log('From SceneB to SceneC');
this.scene.start('sceneC');
}, this);
},
update: function (time, delta) {
this.arrow.rotation += 0.01;
}
});
var SceneC = new Phaser.Class({
Extends: Phaser.Scene,
initialize:
function SceneC() {
Phaser.Scene.call(this, { key: 'sceneC' });
},
preload: function () {
this.load.image('mech', 'assets/pics/titan-mech.png');
},
create: function () {
this.add.sprite(Phaser.Math.Between(0, 800), 300, 'mech');
this.add.sprite(400, 300, 'balls', 3);
this.input.once('pointerdown', function (event) {
console.log('From SceneC to SceneA');
this.scene.start('sceneA');
}, this);
}
});
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: [SceneA, SceneB, SceneC]
};
var game = new Phaser.Game(config);
Related
In my sprite sheet I have two separate frames for when the character is not moving, one is for when she was moving left and then stopped and the other for when she was moving right and then stopped, so i need it to fire up on sort of a key up event. I've seen very similar question here, but the answer didn't work for me, it said to add:
scene.input.keyboard.on('keyup-LEFT', function()
{
});
but it just broke my code, screen goes black and nothing happens, i tried adding that code in either create() and update()
So i tried instead:
this.input.keyboard.on('keyup-LEFT', function()
{
player.setVelocityX(0);
player.anims.play('stopLeft');
});
this.input.keyboard.on('keyup-RIGHT', function()
{
player.setVelocityX(0);
player.anims.play('stopRight');
});
It doesn't break my code, but also nothing happens, I also tried player instead of scene or this, (player is a variable to which my sprite is assigned), but that also broke my code, just like scene did.
BTW, stopLeft and stopRight works fine, because i tested them on normal this.input.keyboard.createCursorKeys() events.
The most common way to implement player movement and/or input is, to put the logic in the update function, and check if the keys are pressed/down (isDown).
I would only use this.input.keyboard, for hot-key like 'ESC' or some special hot-keys.
Here a short working demo:
(for more details checkout this official example)
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 100 }
}
},
scene: {
preload,
create,
update
},
banner: false
};
let player;
let cursor;
let animsKeyText;
function preload() {
this.load.spritesheet('dude', 'https://labs.phaser.io/src/games/firstgame/assets/dude.png', { frameWidth: 32, frameHeight: 48 });
}
function create() {
let floor = this.add.rectangle(0, config.height, config.width, 20, 0xcdcdcd)
.setOrigin(0, .5);
animsKeyText = this.add.text(10, 10, 'current anims-key: ??')
this.physics.add.existing(floor, true);
player = this.physics.add.sprite(100, 10, 'dude')
.setCollideWorldBounds(true);
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'stop-left',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 1 }),
frameRate: 10,
repeat: 0
});
this.anims.create({
key: 'stop-right',
frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 6 }),
frameRate: 10,
repeat: 0
});
this.physics.add.collider(player, floor);
cursors = this.input.keyboard.createCursorKeys();
}
function update() {
if (cursors.left.isDown) {
player.setVelocityX(-160);
player.anims.play('left', true);
}
else if (cursors.right.isDown) {
player.setVelocityX(160);
player.anims.play('right', true);
}
else {
player.setVelocityX(0);
if (player.anims && player.anims.currentAnim) {
// just to prevent of keys "stop-stop....-right" or so
if (player.anims.currentAnim.key.indexOf('stop') == -1) {
let newKey = `stop-${player.anims.currentAnim.key}`;
player.anims.play(newKey, true);
}
}
}
if (player.anims && player.anims.currentAnim) {
animsKeyText.setText(`current anims-key: ${player.anims.currentAnim.key}`);
}
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I'm confused about the 'animationcomplete' notification. I thought I would receive the notification one time when the animation I listen finishes. It turns out I will receive the notification n times. Here is the code.
class BootScene extends Phaser.Scene {
constructor() {
super({ key: 'BootScene' });
}
preload() {
this.load.spritesheet('brawler', 'https://raw.githubusercontent.com/photonstorm/phaser3-examples/master/public/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
}
create() {
this.anims.create({
key: 'win',
frames: this.anims.generateFrameNumbers('brawler', { frames: [ 30, 31 ] }),
frameRate: 8,
repeat: 0,
});
const cody = this.add.sprite(200, 70);
cody.setScale(8);
let btn = this.add.rectangle(500, 70, 200, 150, '#000')
this.add.text(500, 70, 'click')
btn.setInteractive();
btn.on('pointerdown', () => {
console.log('pointerdown');
cody.play('win');
cody.on('animationcomplete', (e) => {
console.log(e.key);
});
});
}
}
var config = {
width: 800,
height: 500,
backgroundColor: '#555',
scene: [BootScene]
}
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I put two console.log in there, one for pointerdown and the other for animationcomplete.
First time I click the btn, I get one pointerdown and one animationcomplete. Second time I click the btn, I get one pointerdown and two animationcomplete.
How do I get just one animationcomplete no matter I click the btn n-th time?
As #Ourobours mentioned you are attaching, on each click, new event handler for animationcomplete.
An Since you can attach multiple event-handlers for the same event, all of them will be executed the next time it fires, ans so on.
You would only have to move the animationcomplete event-handler out of the pointerdown event handler function, and everything should work as you would expect it.
Here a working demo:
class BootScene extends Phaser.Scene {
constructor() {
super({ key: 'BootScene' });
}
preload() {
this.load.spritesheet('brawler', 'https://raw.githubusercontent.com/photonstorm/phaser3-examples/master/public/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
}
create() {
this.anims.create({
key: 'win',
frames: this.anims.generateFrameNumbers('brawler', { frames: [ 30, 31 ] }),
frameRate: 8,
repeat: 0,
});
const cody = this.add.sprite(200, 70);
cody.setScale(8);
let btn = this.add.rectangle(500, 70, 200, 150, '#000')
this.add.text(500, 70, 'click')
btn.setInteractive();
cody.on('animationcomplete', (e) => {
console.log(e.key);
});
btn.on('pointerdown', () => {
console.log('pointerdown');
cody.play('win');
});
}
}
var config = {
width: 800,
height: 500,
backgroundColor: '#555',
scene: [BootScene],
banner:false
}
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I'm a newb, when it comes to developing games, especially with a new language and framework. I'm having trouble at making the camera stick to the player. Not sure where should I put the this.camera.startFollow(); statement so it would work. The game is an asteroids remake, however, the map is larger than 800x600. The game is already set up with socket.io. Here's the code:
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 3200,
height: 2400,
physics: {
default: 'arcade',
arcade: {
debug: false,
gravity: {
x: 0,
y: 0
}
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
function preload() {
this.load.image('ship', 'assets/spaceShips_001.png');
this.load.image('otherPlayer', 'assets/enemyBlack5.png');
this.load.image('star', 'assets/star_gold.png');
this.load.image('backgroundStar1', 'assets/star2.png');
this.load.image('backgroundStar2', 'assets/star3.png');
this.load.image('space', 'assets/deep-space.jpg');
//this.load.image('background', 'assets/background.png');
}
function create() {
var self = this;
this.add.tileSprite(0, 0, 6400, 4800, 'space');
this.physics.world.setBounds(0, 0, 3200, 3200);
//this.cameras.main.setBounds(0, 0, 3200, 3200);
this.socket = io();
this.otherPlayers = this.physics.add.group();
this.socket.on('currentPlayers', function (players) {
Object.keys(players).forEach(function (id) {
if (players[id].playerId === self.socket.id) {
addPlayer(self, players[id]);
//this.camera.startFollow(self.ship); - does not work
} else {
addOtherPlayers(self, players[id]);
}
});
});
this.socket.on('newPlayer', function (playerInfo) {
addOtherPlayers(self, playerInfo);
});
this.socket.on('disconnect', function (playerId) {
self.otherPlayers.getChildren().forEach(function (otherPlayer) {
if (playerId === otherPlayer.playerId) {
otherPlayer.destroy();
}
});
});
this.socket.on('playerMoved', function (playerInfo) {
self.otherPlayers.getChildren().forEach(function (otherPlayer) {
if (playerInfo.playerId === otherPlayer.playerId) {
otherPlayer.setRotation(playerInfo.rotation);
otherPlayer.setPosition(playerInfo.x, playerInfo.y);
}
});
});
this.cursors = this.input.keyboard.createCursorKeys();
this.blueScoreText = this.add.text(16, 16, '', { fontSize: '32px', fill: '#0000FF' });
this.redScoreText = this.add.text(584, 16, '', { fontSize: '32px', fill: '#FF0000' });
this.socket.on('scoreUpdate', function (scores) {
self.blueScoreText.setText('Blue: ' + scores.blue);
self.redScoreText.setText('Red: ' + scores.red);
});
this.socket.on('starLocation', function (starLocation) {
if (self.star) self.star.destroy();
self.star = self.physics.add.image(starLocation.x, starLocation.y, 'star');
self.physics.add.overlap(self.ship, self.star, function () {
this.socket.emit('starCollected');
}, null, self);
});
}
function addPlayer(self, playerInfo) {
self.ship = self.physics.add.image(playerInfo.x, playerInfo.y, 'ship').setOrigin(0.5, 0.5).setDisplaySize(53, 40).setCollideWorldBounds(true);
if (playerInfo.team === 'blue') {
self.ship.setTint(0x0000ff);
} else {
self.ship.setTint(0xff0000);
}
self.ship.setDrag(100);
self.ship.setAngularDrag(100);
self.ship.setMaxVelocity(200);
}
function addOtherPlayers(self, playerInfo) {
const otherPlayer = self.add.sprite(playerInfo.x, playerInfo.y, 'otherPlayer').setOrigin(0.5, 0.5).setDisplaySize(53, 40);
if (playerInfo.team === 'blue') {
otherPlayer.setTint(0x0000ff);
} else {
otherPlayer.setTint(0xff0000);
}
otherPlayer.playerId = playerInfo.playerId;
self.otherPlayers.add(otherPlayer);
}
function update() {
if (this.ship) {
if (this.cursors.left.isDown) {
this.ship.setAngularVelocity(-150);
} else if (this.cursors.right.isDown) {
this.ship.setAngularVelocity(150);
} else {
this.ship.setAngularVelocity(0);
}
if (this.cursors.up.isDown) {
this.physics.velocityFromRotation(this.ship.rotation + 1.5, 100, this.ship.body.acceleration);
} else {
this.ship.setAcceleration(0);
}
this.physics.world.wrap(this.ship, 5);
// emit player movement
var x = this.ship.x;
var y = this.ship.y;
var r = this.ship.rotation;
if (this.ship.oldPosition && (x !== this.ship.oldPosition.x || y !== this.ship.oldPosition.y || r !== this.ship.oldPosition.rotation)) {
this.socket.emit('playerMovement', { x: this.ship.x, y: this.ship.y, rotation: this.ship.rotation });
}
// save old position data
this.ship.oldPosition = {
x: this.ship.x,
y: this.ship.y,
rotation: this.ship.rotation
};
}
}
Solved by adding the following code in the create() function:
window.scene = this;
then changed my commented line
//this.cameras.main.setBounds(0, 0, 3200, 3200);
to
scene.cameras.main.setBounds(0, 0, 3200, 3200);
and lastly added
scene.cameras.main.startFollow(self.ship);
in my addPlayer() function.
How i can draw data-id for each column item in my function updateIndices simillar to updateColumnIndices?
I want to make my grid more dynamic.
I using only javascript and Muuri lib for drag and drop.
Thank you for help.
Muuri is a JavaScript layout engine that allows you to build all kinds of layouts and make them responsive, sortable, filterable, draggable and/or animated. Comparing to what's out there Muuri is a combination of Packery, Masonry, Isotope and Sortable. Wanna see it in action? Check out the demo on the website.
My code for this task under spoiler
function setupItemContainer() {
let itemContentContainer = document.querySelectorAll('.board-column-content');
for(let i = 0; i < itemContentContainer.length; i++)
{
if(!itemContentContainer[i].classList.contains('muuri'))
{
itemContainers.push(itemContentContainer[i]);
muuriItems = new Muuri(itemContentContainer[i], {
itemDraggingClass: 'muuri-item-default-dragging',
dragSortHeuristics: {
sortInterval: 10,
minDragDistance: 5,
minBounceBackAngle: Math.PI / 2
},
dragPlaceholder: {
enabled: true,
duration: 100,
easing: 'ease',
createElement: null,
onCreate: null,
onRemove: null
},
items: '.board-item',
layoutDuration: 400,
layoutEasing: 'ease',
dragEnabled: true,
dragSort: function () {
return columnGrids;
},
dragSortInterval: 0,
dragContainer: document.body,
dragReleaseDuration: 400,
dragReleaseEasing: 'ease',
itemPlaceholderClass: 'muuri-item-placeholder'
})
.on('dragStart', function (item) {
item.getElement().style.width = item.getWidth() + 'px';
item.getElement().style.height = item.getHeight() + 'px';
})
.on('dragReleaseEnd', function (item) {
item.getElement().style.width = '';
item.getElement().style.height = '';
columnGrids.forEach(function (muuriItems) {
muuriItems.refreshItems();
});
})
.on('layoutStart', function () {
muuriColumns.refreshItems().layout();
})
.on('move', updateIndices)
.on('sort', updateIndices);
columnGrids.push(muuriItems);
}
}
}
function setupColumnsContainer() {
muuriColumns = new Muuri(gridElement, {
itemDraggingClass: 'muuri-item-column-dragging',
layoutDuration: 400,
layoutEasing: 'ease',
dragEnabled: true,
dragSortInterval: 0,
dragStartPredicate: {
handle: '.board-column-header'
},
dragReleaseDuration: 400,
dragReleaseEasing: 'ease',
})
.on('move', updateColumnIndices)
.on('sort', updateColumnIndices);
}
function updateColumnIndices() {
muuriColumns.getItems().forEach(function (item, i) {
item.getElement().setAttribute('data-id', i);
});
}
function updateIndices() {
muuriItems.getItems().forEach(function (item, i) {
item.getElement().setAttribute('data-id', i);
});
}
In my game I want to implement a range sort of feature that would restrict players area. I tried to draw a graphic circle and assign it physics properties. Then I setup collision detection between sprite and graphic. The code shows no errors but the collision is not detected.
I followed the following tutorial.
Graphic and Sprite
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,
physics: {
default: 'arcade'
},
scene: {
preload: preload,
create: create,
update:update
}
};
var game = new Phaser.Game(config);
var player;
function preload()
{
this.load.image('dude', 'sprites/phaser-dude.png')
}
function create ()
{
player = this.physics.add.sprite(100, 100, 'dude');
player.setCollideWorldBounds(true);
var graphics = this.add.graphics({ fillStyle: { color: 0xff0000 } });
var circle = new Phaser.Geom.Circle(50, 50, 25);
graphics.fillCircleShape(circle);
this.physics.add.existing(graphics);
cursors = this.input.keyboard.createCursorKeys();
this.physics.add.collider(player, graphics);
}
function update()
{
if (cursors.left.isDown)
{
player.setVelocityX(-160);
}
else if (cursors.right.isDown)
{
player.setVelocityX(160);
}
else if (cursors.down.isDown)
{
player.setVelocityY(160);
}
else if (cursors.up.isDown)
{
player.setVelocityY(-160);
}
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.17.0/dist/phaser.min.js"></script>