I am learning Phaser 3 by creating a small platform game, everything works as I want, but when the player loads there is some space between him and the ground, what is causing this and how can I fix it? I want him to be standing on the ground without any space between, here is a picture:
https://ibb.co/Px65JMR
and here is my js code:
var config = {
type: Phaser.AUTO,
width: 900,
height: 600,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 }
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
var game = new Phaser.Game(config);
function preload() {
this.load.image('sky', 'assets/world/sky.png');
this.load.image('ground', 'assets/world/ground.png');
this.load.image('platform', 'assets/world/platform.png');
this.load.spritesheet('player', 'assets/characters/player.png', { frameWidth: 32, frameHeight: 48 });
}
function create() {
this.add.image(400, 300, 'sky');
platforms = this.physics.add.staticGroup();
ground = this.physics.add.staticGroup();
platforms.create(600, 400, 'platform');
platforms.create(50, 250, 'platform');
platforms.create(750, 220, 'platform');
ground.create(500, 550, 'ground')
// Creating the player
player = this.physics.add.sprite(50, 350, 'player');
player.setBounce(0.2);
player.setCollideWorldBounds(true);
player.body.setGravityY(300)
this.physics.add.collider(player, ground);
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'turn',
frames: [{ key: 'player', frame: 4 }],
frameRate: 20
});
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('player', { start: 5, end: 8 }),
frameRate: 10,
repeat: -1
});
}
function update() {
cursors = this.input.keyboard.createCursorKeys();
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);
player.anims.play('turn');
}
if (cursors.up.isDown && player.body.touching.down) {
player.setVelocityY(-330);
}
}
this.load.spritesheet('player', 'assets/characters/player.png', { frameWidth: 32, frameHeight: 48 });
Did you make sure that the frameHeight of the sprite is 48 and not 32x32?
Also, I'd recommend you to add debug=true
to your configuration:
arcade: {
debug: true,
gravity: { y: 300 }
}
By doing so, each object will be marked by a purple outline, I bet you will figure it out in no time.
possible solution 1:
try to reduce the second param here:
ground.create(500, 550, 'ground')
to - lets say- 450.
possible solution 2:
ground.create(500, 550, 'ground').setSize(500,500, true);
ground.refresh();
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>
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I have some code written in phaser.js and I need to optimize it, i.e. break it down into functions and remove unnecessary animation calls (the code is completely working, but it just needs to be optimized). I'm just new to js and this library :(
I tried it myself, but then the code stops working for me :(
class Example extends Phaser.Scene {
constructor() {
super();
}
preload() {
this.load.spritesheet("start", "img/bri_big_anim_start.png", {
frameWidth: 392,
frameHeight: 370,
});
this.load.spritesheet("middle", "img/bri_big_anim_middle.png", {
frameWidth: 450,
frameHeight: 430,
});
this.load.spritesheet("finish", "img/bri_big_anim_finish.png", {
frameWidth: 326,
frameHeight: 335,
});
}
create() {
const startAnimation = this.anims.create({
key: "walkStart",
frames: this.anims.generateFrameNumbers("start", { start: 0, end: 4 }),
frameRate: 4,
});
let middleAnimation = this.anims.create({
key: "walkMiddle",
frames: this.anims.generateFrameNumbers("middle", { start: 0, end: 4 }),
frameRate: 4,
});
const finishAnimation = this.anims.create({
key: "walkFinish",
frames: this.anims.generateFrameNumbers("finish", { start: 0, end: 4 }),
frameRate: 4,
});
const spriteStart = this.add.sprite(400, 350, "start").setScale(0.4);
spriteStart.play({ key: "walkStart", repeat: 1 });
this.tweens.add({
targets: spriteStart,
y: 360,
scaleX: 0.9,
scaleY: 0.9,
duration: 2000,
});
spriteStart.on(
"animationcomplete",
function () {
spriteStart.destroy();
let spriteMiddle = this.add.sprite(400, 350, "middle").setScale(0.8);
spriteMiddle.play({ key: "walkMiddle", repeat: 3 });
this.tweens.add({
targets: spriteMiddle,
});
spriteMiddle.on(
"animationcomplete",
function () {
spriteMiddle.destroy();
const spriteFinish = this.add
.sprite(400, 350, "finish")
.setScale(0.8);
spriteFinish.play({ key: "walkFinish", repeat: 1 });
this.tweens.add({
targets: spriteFinish,
y: 100,
x: 300,
scale: 0.1,
duration: 2000,
});
spriteFinish.on(
"animationcomplete",
function () {
spriteFinish.destroy();
let middleAnimation = this.anims.create({
frames: this.anims.generateFrameNumbers("middle", {
start: 0,
end: 1,
}),
frameRate: 1,
});
spriteMiddle = this.add
.sprite(300, 100, "middle")
.setScale(0.1);
spriteMiddle.play({ key: "walkMiddle", repeat: 0 });
this.tweens.add({
targets: spriteMiddle,
});
spriteMiddle.stop();
},
this,
);
},
this,
);
},
this,
);
}
}
const config = {
type: Phaser.AUTO,
parent: "phaser-example",
width: 800,
height: 600,
pixelArt: true,
scene: [Example],
};
const game = new Phaser.Game(config);
Everything I could do
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.spritesheet('start', 'img/bri_big_anim_start.png', { frameWidth: 392, frameHeight: 370 });
this.load.spritesheet('middle', 'img/bri_big_anim_middle.png', { frameWidth: 450, frameHeight: 430 });
this.load.spritesheet('finish', 'img/bri_big_anim_finish.png', { frameWidth: 326, frameHeight: 335 });
}
create ()
{
const startAnimation = this.anims.create({
key: 'walkStart',
frames: this.anims.generateFrameNumbers('start', {start: 0, end: 4}),
frameRate: 4,
});
let middleAnimation = this.anims.create({
key: 'walkMiddle',
frames: this.anims.generateFrameNumbers('middle', {start: 0, end: 4}),
frameRate: 4,
});
const finishAnimation = this.anims.create({
key: 'walkFinish',
frames: this.anims.generateFrameNumbers('finish', {start: 0, end: 4}),
frameRate: 4,
});
const addSprite = (nameSpritePlay, keyPlay, repeatPlay) => {
nameSpritePlay.play({ key: keyPlay, repeat: repeatPlay });
}
const spriteStart = this.add.sprite(400, 350, 'start').setScale(0.4);
// spriteStart.play({ key: 'walkStart', repeat: 1 });
addSprite(spriteStart, 'walkStart', 1);
this.tweens.add({
targets: spriteStart,
y: 360,
scaleX: 0.9,
scaleY: 0.9,
duration: 2000,
});
spriteStart.on('animationcomplete', function () {
spriteStart.destroy();
let spriteMiddle = this.add.sprite(400, 350, 'middle').setScale(0.8);
// spriteMiddle.play({ key: 'walkMiddle', repeat: 3 });
addSprite(spriteMiddle, 'walkMiddle', 3);
this.tweens.add({
targets: spriteMiddle,
});
spriteMiddle.on('animationcomplete', function () {
spriteMiddle.destroy();
const spriteFinish = this.add.sprite(400, 350, 'finish').setScale(0.8);
// spriteFinish.play({ key: 'walkFinish', repeat: 1 });
addSprite(spriteFinish, 'walkFinish', 1);
this.tweens.add({
targets: spriteFinish,
y: 100,
x: 300,
scale: 0.1,
duration: 2000,
});
spriteFinish.on('animationcomplete', function () {
spriteFinish.destroy();
spriteMiddle = this.add.sprite(300, 100, 'middle').setScale(0.1);
// spriteMiddle.play({ key: 'walkMiddle', repeat: 0 });
addSprite(spriteMiddle, 'walkMiddle', 0);
spriteMiddle.stop();
}, this);
}, this);
}, this);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
pixelArt: true,
scene: [ Example ]
};
const game = new Phaser.Game(config);
Optimizie Code can take long, but I would say you could look into the example below, how to chain animations, this would minimize your code, with builtin phaser functionality:
https://phaser.io/examples/v3/view/animation/chained-animation#
the main idea is the playAfterRepeat function
sprite.playAfterRepeat(...);
Here a link to the documentation of this feature.
I personally would load animations from an atlas/json file as seen in this example: https://phaser.io/examples/v3/view/animation/from-animation-json
this keeps the code clean streamlined, and animations can be changed/managed with greater ease.
Or maybe create a helper function like this (but this is just my preference):
generateFrames(key, name){
let animation = this.anims.create({
key: key,
frames: this.anims.generateFrameNumbers(name, { start: 0, end: 4 }),
frameRate: 4,
});
return animation;
}
...
const startAnimation = generateFrames('walkStart', 'start');
const middleAnimation = generateFrames('walkMiddle', 'middle');
const finishAnimation = generateFrames('walkFinish', 'finish');
We have to create a game with Phaser 3 for a university project. We opted for a classic jump and round game.
I chose individual scenes to achieve the 15 levels. Now I have encountered the problem that the run animation does not work in the scene for some reason. However, I don't know exactly why so at least it doesn't make sense to me that it doesn't work. Maybe someone has an idea or knows what I did wrong.
// Level 1
var player;
var stars;
var bombs;
var platforms;
var cursors;
var score = 0;
var gameOver = false;
var scoreText;
class GameScene extends Phaser.Scene {
constructor (config) {
super(config);
//To use the scene in the game
Phaser.Scene.call(this, { key: 'gamescene'});
}
preload () {
this.load.image('sky', 'img/sky.png');
this.load.image('ground', 'img/platform.png');
this.load.image('star', 'img/star.png');
this.load.image('bomb', 'img/bomb.png');
this.load.spritesheet('dude', 'img/dude.png', { frameWidth: 32, frameHeight: 48 });
}
create () {
// A simple background for our game
this.add.image(400, 300, 'sky');
// The platforms group contains the ground and the 2 ledges we can jump on
platforms = this.physics.add.staticGroup();
// Here we create the ground.
// Scale it to fit the width of the game (the original sprite is 400x32 in size)
platforms.create(400, 568, 'ground').setScale(2).refreshBody();
// Now let's create some ledges
platforms.create(600, 400, 'ground');
platforms.create(50, 250, 'ground');
platforms.create(750, 220, 'ground');
// The player and its settings
player = this.physics.add.sprite(100, 450, 'dude');
// Player physics properties. Give the little guy a slight bounce.
player.setBounce(0.2);
player.setCollideWorldBounds(true);
// Our player animations, turning, walking left and walking right.
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'turn',
frames: [ { key: 'dude', frame: 4 } ],
frameRate: 20
});
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }),
frameRate: 10,
repeat: -1
});
// Input Events
cursors = this.input.keyboard.createCursorKeys();
// Some stars to collect, 12 in total, evenly spaced 70 pixels apart along the x axis
stars = this.physics.add.group({
key: 'star',
repeat: 11,
setXY: { x: 12, y: 0, stepX: 70 }
});
stars.children.iterate(function (child) {
// Give each star a slightly different bounce
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
bombs = this.physics.add.group();
// The score
scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });
// Collide the player and the stars with the platforms
this.physics.add.collider(player, platforms);
this.physics.add.collider(stars, platforms);
this.physics.add.collider(bombs, platforms);
// Checks to see if the player overlaps with any of the stars, if he does call the collectStar function
this.physics.add.overlap(player, stars, collectStar, null, this);
this.physics.add.collider(player, bombs, hitBomb, null, this);
}
update() {
if (gameOver)
{
return;
}
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);
player.anims.play('turn');
}
if (cursors.up.isDown && player.body.touching.down)
{
player.setVelocityY(-330);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I am creating a skateboarding game in JavaScript using the Phaser 3 framework. I have loaded the ramp image onto the screen, and I am currently using the "arcade" physics engine in my file. I know I have to use the matter physics engine to have a non-rectangular hitbox. How do I implement it with the triangular hitbox?
I have the .png image file of the ramp, along with the .json file for its hitbox.
I tried following a tutorial off of a website on how to create new hitboxes for the matter physics engine. Everything ended up falling off the screen and I couldn't figure out how to use the .json file for the ramp.
//Configurations for the physics engine
var physicsConfig = {
default: 'arcade',
arcade : {
debug : true //CHANGE THIS TO TRUE TO SEE LINES
}
}
//Game configurations
var config = {
type: Phaser.AUTO,
width: 1200 ,
height: 600,
physics: physicsConfig,
scene: {
preload: preload,
create: create,
update: update
}
}
//Start the game
var game = new Phaser.Game(config);
function preload() {
//Images
this.load.image('sky', 'archery_assets/images/sky.png');
this.load.image('ground', 'skate_assets/images/ground.png');
this.load.image('up_ramp', 'skate_assets/images/up_ramp.png')
//Spritesheets
this.load.spritesheet('player', 'skate_assets/spritesheets/skater.png', {frameWidth: 160, frameHeight: 160});
}
function create() {
//Background
skyImg = this.add.image(600, 300, 'sky');
//Scale the images
skyImg.setDisplaySize(1200, 600);
groundImg = this.add.image(600, 600, 'ground');
groundImg.setDisplaySize(1200, 250);
//Create the player
this.player = this.physics.add.sprite(100, 410, 'player');
this.player.setCollideWorldBounds(true);
//Rolling animation
this.anims.create({
key: 'move',
frames: this.anims.generateFrameNumbers('player', {start: 0, end: 3}),
frameRate: 16,
repeat: -1 // <-- keeps the rolling animation going
});
//Pushing animation
this.anims.create({
key: 'push',
frames: this.anims.generateFrameNumbers('player', {start: 4, end: 8}),
frameRate: 16
});
//Start and keep the rolling animation going
this.player.anims.play('move', true);
//Up ramp (1st ramp)
this.upRamp = this.physics.add.sprite(700, 330, 'up_ramp');
this.upRamp.setSize(320, 150).setOffset(0, 175);
this.upRamp.enableBody = true;
this.upRamp.setImmovable();
this.upRamp.body.angle = 150;
//Input
this.cursors = this.input.keyboard.createCursorKeys();
//Spacebar
this.spacebar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
this.physics.add.collider(this.player, this.upRamp);
}
function update() {
//Set variable for push speed
var playerPushSpeed = 0;
//If the spacebar is pressed
if (this.spacebar.isDown) {
//Play the push animation
this.player.anims.play('push', true);
//Push speed
playerPushSpeed += 175;
//Move player
this.player.setVelocityX(playerPushSpeed);
}
if (this.upRamp.body.touching.left) {
this.player.setVelocityY(-200);
}
}
I need to know how to implement the .png image of the ramp along with its .json hitbox file, so that the player can properly "ride" up the ramp.
You'll have to use the physics: { default: 'matter' } in order to change the hitbox's shape. Use this code snippet for reference:
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
physics: {
default: 'matter',
matter: {
gravity: {
y: 0
},
debug: true
}
},
scene: {
create: create
}
};
var game = new Phaser.Game(config);
function create ()
{
this.matter.world.setBounds();
this.matter.add.rectangle(200, 200, 200, 200, {
chamfer: { radius: [230, 0, 200, 0 ] }
});
this.matter.add.mouseSpring();
}
You could also use a PhysicsEditor, you can check this tutorial on how to implement different shapes.
EDIT:
You can use console.log(this.matter.bodies) to check other available shapes to implement.