Related
Here is the example code from phaser3 tutorial.
var BootScene = new Phaser.Class({
Extends: Phaser.Scene,
initialize:
function BootScene ()
{
Phaser.Scene.call(this, { key: 'BootScene' });
},
preload: function ()
{
// map tiles
this.load.image('tiles', 'https://raw.githubusercontent.com/liyi93319/phaser3_rpg/main/part1/assets/map/spritesheet.png');
// map in json format
this.load.tilemapTiledJSON('map', 'https://raw.githubusercontent.com/liyi93319/phaser3_rpg/main/part1/assets/map/map.json');
// our two characters
this.load.spritesheet('player', 'https://raw.githubusercontent.com/liyi93319/phaser3_rpg/main/part1/assets/RPG_assets.png', { frameWidth: 16, frameHeight: 16 });
},
create: function ()
{
// start the WorldScene
this.scene.start('WorldScene');
}
});
var WorldScene = new Phaser.Class({
Extends: Phaser.Scene,
initialize:
function WorldScene ()
{
Phaser.Scene.call(this, { key: 'WorldScene' });
},
preload: function ()
{
},
create: function ()
{
// create the map
var map = this.make.tilemap({ key: 'map' });
// first parameter is the name of the tilemap in tiled
var tiles = map.addTilesetImage('spritesheet', 'tiles');
// creating the layers
var grass = map.createStaticLayer('Grass', tiles, 0, 0);
var obstacles = map.createStaticLayer('Obstacles', tiles, 0, 0);
// make all tiles in obstacles collidable
obstacles.setCollisionByExclusion([-1]);
// animation with key 'left', we don't need left and right as we will use one and flip the sprite
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('player', { frames: [1, 7, 1, 13]}),
frameRate: 10,
repeat: -1
});
// animation with key 'right'
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('player', { frames: [1, 7, 1, 13] }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'up',
frames: this.anims.generateFrameNumbers('player', { frames: [2, 8, 2, 14]}),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'down',
frames: this.anims.generateFrameNumbers('player', { frames: [ 0, 6, 0, 12 ] }),
frameRate: 10,
repeat: -1
});
// our player sprite created through the phycis system
this.player = this.physics.add.sprite(50, 100, 'player', 6);
// don't go out of the map
this.physics.world.bounds.width = map.widthInPixels;
this.physics.world.bounds.height = map.heightInPixels;
this.player.setCollideWorldBounds(true);
// don't walk on trees
this.physics.add.collider(this.player, obstacles);
// limit camera to map
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.startFollow(this.player);
this.cameras.main.roundPixels = true; // avoid tile bleed
// user input
this.cursors = this.input.keyboard.createCursorKeys();
// where the enemies will be
this.spawns = this.physics.add.group({ classType: Phaser.GameObjects.Zone });
for(var i = 0; i < 30; i++) {
var x = Phaser.Math.RND.between(0, this.physics.world.bounds.width);
var y = Phaser.Math.RND.between(0, this.physics.world.bounds.height);
// parameters are x, y, width, height
this.spawns.create(x, y, 20, 20);
}
// add collider
this.physics.add.overlap(this.player, this.spawns, this.onMeetEnemy, false, this);
},
onMeetEnemy: function(player, zone) {
// we move the zone to some other location
zone.x = Phaser.Math.RND.between(0, this.physics.world.bounds.width);
zone.y = Phaser.Math.RND.between(0, this.physics.world.bounds.height);
// shake the world
this.cameras.main.shake(300);
// start battle
},
update: function (time, delta)
{
// this.controls.update(delta);
this.player.body.setVelocity(0);
// Horizontal movement
if (this.cursors.left.isDown)
{
this.player.body.setVelocityX(-80);
}
else if (this.cursors.right.isDown)
{
this.player.body.setVelocityX(80);
}
// Vertical movement
if (this.cursors.up.isDown)
{
this.player.body.setVelocityY(-80);
}
else if (this.cursors.down.isDown)
{
this.player.body.setVelocityY(80);
}
// Update the animation last and give left/right animations precedence over up/down animations
if (this.cursors.left.isDown)
{
this.player.anims.play('left', true);
this.player.flipX = true;
}
else if (this.cursors.right.isDown)
{
this.player.anims.play('right', true);
this.player.flipX = false;
}
else if (this.cursors.up.isDown)
{
this.player.anims.play('up', true);
}
else if (this.cursors.down.isDown)
{
this.player.anims.play('down', true);
}
else
{
this.player.anims.stop();
}
}
});
var config = {
type: Phaser.AUTO,
parent: 'content',
width: 320,
height: 240,
zoom: 2,
pixelArt: true,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: false // set to true to view zones
}
},
scene: [
BootScene,
WorldScene
]
};
var game = new Phaser.Game(config);
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/phaser#3.11.0/dist/phaser.js"></script>
Per the tutorial, there are 2 scenes, one for the world map and the other for the battle. I doubt if the code above implement the battle scene. I assume the scene in question is the world scene.
Moving (with arrow keys) the character one or two steps/tiles works well.
Keep moving the character a few tiles more toward the same direction causes the world scene to shake, why is that? How do I fix it?
In line #126 you have a shake function this.cameras.main.shake(300); that makes the game window to shake with a duration of 300ms. To disable it, just comment it out or delete it.
In my code I want to call the function floor2 if the score is more than or equal to 10, and then the function should be called when the player touches the door.
Currently when the player overlaps with the door it does nothing. I have tried moving the if statement about but doesn't seem to do much. I think it's either the if statement mentioned above or the function floor2 causing the error but its a logic error so can't be too sure on where it's coming from
Here is my code:
<html>
<head>
<meta charset="UTF-8" />
<title>Making your first Phaser 3 Game - Part 7</title>
<script src="//cdn.jsdelivr.net/npm/phaser#3.11.0/dist/phaser.js"></script>
<style type="text/css">
body {
margin: 0;
}
</style>
</head>
<body>
<script type="text/javascript">
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
var player;
var platforms;
var cursors;
var score = 0;
var scoreText;
var end;
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('sky', 'assets/l1.png');
this.load.image('ground', 'assets/stone.png');
this.load.image('star', 'assets/wine.png');
this.load.image('bomb', 'assets/skull.png');
this.load.image('door', 'assets/door1.png');
this.load.spritesheet('dude', 'assets/test.png', { frameWidth: 39, frameHeight: 56 });
this.load.spritesheet('dead', 'assets/test2.png', { frameWidth: 39, frameHeight: 38.5 });
this.load.audio("bgm",'assets/constantine.mp3');
this.load.audio("bottle",'assets/bottle.mp3');
}
function create (score)
{
bgm = this.sound.add("bgm", { loop: true});
bgm.volume = 1.5
bgm.play();
bottle = this.sound.add("bottle",{loop:false});
bottle.volume = 0.05;
this.add.image(400, 300, 'sky');
door = this.add.image(50,550,'door');
platforms = this.physics.add.staticGroup();
platforms.create(400, 568, 'ground').setScale(2).refreshBody();
platforms.create(600, 400, 'ground');
platforms.create(50, 250, 'ground');
platforms.create(750, 220, 'ground');
player = this.physics.add.sprite(100, 450, 'dude');
player.setBounce(0.2);
player.setCollideWorldBounds(true);
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('dude', { start: 1, end: 3 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'turn',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 0 }),
frameRate: 20
});
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('dude', { start: 4, end: 6 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'dead',
frames: this.anims.generateFrameNumbers('dead', { start: 0, end: 1 }),
frameRate: 10,
repeat: -1
});
cursors = this.input.keyboard.createCursorKeys();
this.physics.add.collider(player, platforms);
stars = this.physics.add.group({
key: 'star',
repeat: 11,
setXY: { x: 12, y: 0, stepX: 70 }
});
stars.children.iterate(function (child) {
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
this.physics.add.collider(stars, platforms);
this.physics.add.overlap(player, stars, collectStar, null, this)
scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });
bombs = this.physics.add.group();
this.physics.add.collider(bombs, platforms);
this.physics.add.collider(player, bombs, hitBomb, null, this);
if (score >= 10)
{
this.physics.add.overlap(player,door,floor2(score));
}
}
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);
player.anims.play('turn');
}
if (cursors.up.isDown && player.body.touching.down)
{
player.setVelocityY(-330);
}
}
function collectStar (player, star)
{
star.disableBody(true, true);
score += 10;
bottle.play();
scoreText.setText('Score: ' + score);
if (stars.countActive(true) === 0)
{
stars.children.iterate(function (child) {
child.enableBody(true, child.x, 0, true, true);
});
var x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400);
var bomb = bombs.create(x, 16, 'bomb');
bomb.setBounce(1);
bomb.setCollideWorldBounds(true);
bomb.setVelocity(Phaser.Math.Between(-200, 200), 20);
}
}
function hitBomb (player, bomb)
{
this.physics.pause();
player.setTint(0xff0000);
player.anims.play('dead');
end = this.add.text(300, 300, 'game over', { fontSize: '64px', fill: '#808080' });
gameOver = true;
}
function floor2(score)
{
this.physics.pause()
win = this.add.text(300, 300, 'you win', { fontSize: '64px', fill: '#808080' });
gameOver = true;
}
</script>
</body>
</html>
Here is what the game looks like
In Phaser, the create is run once when the game scene is created.
In your create you're setting up the scene and then checking if the score is 10 or more. The player hasn't played the game yet, so their score will be 0, so this.physics.add.overlap(player,door,floor2(score)); isn't run.
At this point the create function is done, and won't be called again, so it'll never check whether the score is 10 or more, and therefore will never add that overlap call.
There's a number of things you could do instead. A few ideas:
Add a check when collecting bottles (collectStar()).
Add a check in update().
Add a function to handle score changes, and check then.
However you implement it, you'll need to check the score outside of create.
I'm new to Phaser 3 as well as game development then I have a problem here. I'm trying to restart a game scene but I get an error saying "Uncaught TypeError: this.scene.restart is not a function" when I run this
var playbtn = this.add.dom(390, 600).createFromCache('play-btn');
playbtn.setPerspective(600);
playbtn.addListener('click');
playbtn.on('click', function (event) {
this.scene.restart();
});
And this is my whole code
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Making your first Phaser 3 Game - Part 10</title>
<script src="https://cdn.jsdelivr.net/npm/phaser#3.24.1/dist/phaser.js"></script>
<style type="text/css">
body {
margin: 0;
}
</style>
</head>
<body>
<script type="text/javascript">
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
},
dom: {
createContainer: true
},
scene: {
preload: preload,
create: create,
update: update,
hitbomb: hitBomb
}
};
var game = new Phaser.Game(config);
var player;
var stars;
var bombs;
var platforms;
var cursors;
var score = 0;
var gameOver = false;
var scoreText;
var level_state;
var level = 1;
var de_star = 0;
var element;
var playbtn;
function preload ()
{
this.load.image('sky', 'assets/sky.png');
this.load.image('ground', 'assets/platform.png');
this.load.image('star', 'assets/bomb.png');
this.load.image('bomb', 'assets/star.png');
this.load.spritesheet('dude', 'assets/dude.png', { frameWidth: 32, frameHeight: 48 });
this.load.html('play-btn', 'assets/play.html');
this.load.html('nameform', 'assets/loginform.html');
}
function 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: 1,
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' });
level_state = this.add.text(16, 50, 'Level: 1', { 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);
}
function 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);
}
}
function collectStar (player, star)
{
star.disableBody(true, true);
// Add and update the score
score += 10;
scoreText.setText('Score: ' + score);
if (de_star==12)
{
gameOver = True;
}
else if (stars.countActive(true) === 0)
{
de_star+=1;
stars = this.physics.add.group({
key: 'star',
repeat: 11-de_star,
setXY: { x: 12, y: 0, stepX: 70 }
});
this.physics.add.collider(stars, platforms);
stars.children.iterate(function (child) {
// Give each star a slightly different bounce
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
// A new batch of stars to collect
stars.children.iterate(function (child) {
child.enableBody(true, child.x, 0, true, true);
});
this.physics.add.overlap(player, stars, collectStar, null, this);
var x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400);
level += 1;
var bomb = bombs.create(x, 16, 'bomb');
bomb.setBounce(1);
bomb.setCollideWorldBounds(true);
bomb.setVelocity(Phaser.Math.Between(-200, 200), 20);
bomb.allowGravity = false;
}
level_state.setText('Level: '+level);
}
function hitBomb (player, bomb)
{
this.physics.pause();
player.setTint(0xff0000);
player.anims.play('turn');
gameOver = true;
var playbtn = this.add.dom(390, 600).createFromCache('play-btn');
playbtn.setPerspective(600);
playbtn.addListener('click');
playbtn.on('click', function (event) {
this.scene.restart();
});
this.tweens.add({
targets: playbtn,
y: 300,
duration: 3000,
ease: 'Power3'
});
}
</script>
</body>
</html>
When the character hits the bomb, the "Play Again" button will pop up, then the game will restart if the player hits that button. These are the things that I aim for...
The project I'm working on is a tutorial example but any advice or solution would help me a lot. Thank you
As #Thomas already point out, it's a problem of this.
You could use a fat arrow
playbtn.on('click', (event) => {
this.scene.restart();
});
or you could pass the context to the 'on' function, i think it's 'game' looking at your code.
playbtn.on('click', function (event) {
this.scene.restart();
}, game);
Here a snippet from the documentation of Phaser's on
/**
* Add a listener for a given event.
* #param event The event name.
* #param fn The listener function.
* #param context The context to invoke the listener with. Default this.
*/
on(event: string | symbol, fn: Function, context?: any): this;
I'm not very familiar with Phaser, but probably this inside the event handler refers to the button. Use a fat arrow to bind the handler function to this from the surrounding scope:
playbtn.on('click', (event) => {
this.scene.restart();
});
I am trying to make an eyeball in matter.js. I have a pupil and an iris that are constrained together. My problem is that I need to make both bodies ignore collision so I can place one on top of the other.
var defaultCategory = 0x0001
var iris = Bodies.circle(0, 0, 20);
var pupil = Bodies.circle(300, 200, 30)
var ground = Bodies.rectangle(400, 380, 810, 60, { isStatic: true });
var bodyA = Bodies.polygon(100, 100, 6, 20,{
collisionFilter: {
mask: defaultCategory
}
});
var bodyB = Bodies.polygon(200, 100, 10, 50,{
collisionFilter: {
category: defaultCategory
}
});
var constraint = Constraint.create({
bodyA: bodyA,
pointA: { x: -10, y: -10 },
bodyB: bodyB,
pointB: { x: -10, y: -10 },
length: 40
});
World.add(world, [pupil, iris, ground, constraint]);
Not sure if there is a better way, but instead of using categories and masks, I used groups and this allows both bodies to overlap, see below:
var Engine = Matter.Engine,
Render = Matter.Render,
Runner= Matter.Runner,
World = Matter.World,
Bodies = Matter.Bodies,
Composites = Matter.Composites,
Common = Matter.Common,
Constraint = Matter.Constraint,
Body = Matter.Body
// no longer using category
var defaultCategory = 0x0001
//using group instead
var group = Body.nextGroup(true)
var engine = Engine.create(),
world = engine.world;
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 400,
wireframes: false
}
});
var ground = Bodies.rectangle(400, 380, 810, 60, { isStatic: true });
var bodyA = Bodies.polygon(100, 100, 6, 20,{
collisionFilter: { group: group }
});
var bodyB = Bodies.polygon(200, 100, 10, 50,{
collisionFilter: { group: group }
});
var constraint = Constraint.create({
bodyA: bodyA,
pointA: { x: -10, y: -10 },
bodyB: bodyB,
pointB: { x: -10, y: -10 },
length: 40
});
World.add(world, [bodyA, bodyB, ground, constraint]);
// World.add(world, [leftEye, leftEyeBall, ground, constraint]);
Engine.run(engine);
Render.run(render);
I am charting different data with RickshawJS. But I need a way to update the chart when a user clicks the #search button. Right now it just creates a new chart below the old one, and that is pretty messy.
The user enters the page and enters some details and clicks the button to chart it. So ideally I'd like to start with an empty chart that isn't shown, but I can't really figure out how to remove the data from the chart and axes and then update it.
I could call $('#chart svg').remove(); on the chart and axes but it seems messy.
$('#search').click(function(event){
event.preventDefault();
var data = utils.malletData();
var graph = new Rickshaw.Graph( {
element: document.querySelector("#chart"),
width: 800,
height: 250,
series: [ {
name: data['name'],
color: 'steelblue',
data: data['series']
} ]
} );
graph.render();
var hoverDetail = new Rickshaw.Graph.HoverDetail( {
graph: graph,
xFormatter: function(x) {
var date = new Date(x).getTime();
return moment(x).format('MMMM Do YYYY, h:mm:ss a');
},
yFormatter: function(y) { return Math.floor(y) + " users" }
} );
var xAxis = new Rickshaw.Graph.Axis.X( {
graph: graph,
orientation: 'bottom',
element: document.getElementById('x_axis'),
tickFormat: function(x) { return moment(x).fromNow(); },
ticks: 7,
tickSize: 1,
} );
xAxis.render();
var ticksTreatment = 'glow';
var yAxis = new Rickshaw.Graph.Axis.Y( {
graph: graph,
orientation: 'left',
tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
ticksTreatment: ticksTreatment,
element: document.getElementById('y_axis'),
} );
yAxis.render();
});
There's no official way to do so. However, you could leverage the fact that arrays in javascript are passed by reference and then update the graph.
Have a look at this demo on fiddle
var data = [
{
data: [ { x: 0, y: 120 }, { x: 1, y: 890 }, { x: 2, y: 38 }, { x: 3, y: 70 }, { x: 4, y: 32 } ],
color: "#c05020"
}, {
data: [ { x: 0, y: 80 }, { x: 1, y: 200 }, { x: 2, y: 100 }, { x: 3, y: 520 }, { x: 4, y: 133 } ],
color: "#30c020"
}
];
var graph = new Rickshaw.Graph( {
element: document.getElementById("chart"),
renderer: 'line',
height: 300,
width: 800,
series: data
} );
var y_ticks = new Rickshaw.Graph.Axis.Y( {
graph: graph,
orientation: 'left',
tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
element: document.getElementById('y_axis'),
} );
graph.render();
$('button#add').click(function() {
data.push({
data: [ { x: 0, y: 200 }, { x: 1, y: 390 }, { x: 2, y: 1000 }, { x: 3, y: 200 }, { x: 4, y: 230 } ],
color: "#6060c0"
});
graph.update();
});