Flappy Bird game supporting local multiplayer of up to 2 playersGarbage collection in JavaScript multiplayer...
How much RAM could one put in a typical 80386 setup?
How to format long polynomial?
Why can't I see bouncing of a switch on an oscilloscope?
How to draw a waving flag in TikZ
How old can references or sources in a thesis be?
Why are electrically insulating heatsinks so rare? Is it just cost?
Can I make popcorn with any corn?
Unknown notation: What do three bars mean?
The Clique vs. Independent Set Problem
How to know the difference between two ciphertexts without key stream in stream ciphers
Why does Kotter return in Welcome Back Kotter?
How is the claim "I am in New York only if I am in America" the same as "If I am in New York, then I am in America?
Test whether all array elements are factors of a number
Approximately how much travel time was saved by the opening of the Suez Canal in 1869?
How to add double frame in tcolorbox?
Compress a signal by storing signal diff instead of actual samples - is there such a thing?
Is it important to consider tone, melody, and musical form while writing a song?
Writing rule stating superpower from different root cause is bad writing
What do the dots in this tr command do: tr .............A-Z A-ZA-Z <<< "JVPQBOV" (with 13 dots)
a relationship between local compactness and closure
Watching something be written to a file live with tail
Schoenfled Residua test shows proportionality hazard assumptions holds but Kaplan-Meier plots intersect
What is the offset in a seaplane's hull?
I'm planning on buying a laser printer but concerned about the life cycle of toner in the machine
Flappy Bird game supporting local multiplayer of up to 2 players
Garbage collection in JavaScript multiplayer gameGame Networking between 2 playersMultiplayer blackjack gameGame prototype with methods to manage playersYet another flappy bird cloneSimple arcade game where you just move the character aroundCard based computer gameNoughts and crosses game for 2 playersReinforcement Learning for Flappy Bird in JavaScriptSimple Flappy Bird Clone using Kivy (with very minimum physics)
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
$begingroup$
The game is hosted here.
GitHub repo
This is my first Phaser 3 game/project and I'm still pretty new to Javascript so I'm sure there are many things I could be doing better. The number 1 thing that I would like to improve about my code is the performance. Then code effectiveness and readability, but performance is priority.
Your feedback is valuable even if you have no experience with PhaserJS whatsoever, because a lot of things that I could probably be doing better only have to do with pure Javascript.
My JS code:
const width = window.innerWidth;
const height = window.innerHeight;
let hiScore = localStorage['hiScore'] || 0;
const config = {
width: width,
height: height,
backgroundColor: 0x50C875,
scene: {
preload,
create,
update,
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 50 },
},
}
}
const gameState = {
gameOver: false,
score: 0,
scoreText: false,
player1AnimationStage: 0,
player2AnimationStage: 0,
player1SpriteSheet: ['upflap', 'midflap', 'downflap',],
player2SpriteSheet: ['player2_upflap', 'player2_midflap', 'player2_downflap',],
player1Y: (height / 2 * 0.5),
player2Y: (height / 2 * 0.5),
secondPlayerSpawned: false,
player1Dead: false,
player2Dead: false,
}
const game = new Phaser.Game(config, 'root');
game.clearBeforeRender = false;
function preload() {
this.load.image('background', 'assets/images/background.png');
this.load.image('ground', 'assets/images/ground.png');
this.load.image('pipe', 'assets/images/pipe.png');
this.load.image('upflap', 'assets/images/upflap.png');
this.load.image('midflap', 'assets/images/midflap.png');
this.load.image('downflap', 'assets/images/downflap.png');
this.load.image('player2_upflap', 'assets/images/player2_upflap.png');
this.load.image('player2_midflap', 'assets/images/player2_midflap.png');
this.load.image('player2_downflap', 'assets/images/player2_downflap.png');
this.load.audio('hit', 'assets/audio/hit.mp3');
this.load.audio('point', 'assets/audio/point.mp3');
this.load.audio('wing', 'assets/audio/wing.mp3');
this.load.audio('die', 'assets/audio/die.mp3');
}
function create() {
gameState.hitSound = this.sound.add('hit');
gameState.pointSound = this.sound.add('point');
gameState.wingSound = this.sound.add('wing');
gameState.dieSound = this.sound.add('die');
// Hide Score Table
document.getElementById('hiScoreTable').style.display = 'none';
const colliderTile = this.physics.add.staticGroup();
gameState.colliderTile = colliderTile.create(50, 0, 'pipe').setScale(0.1, 80).refreshBody();
gameState.colliderTile2 = colliderTile.create(1, 0, 'pipe').setScale(0, 80).refreshBody();
gameState.bgTile = this.add.tileSprite(0, height, width, height, 'background').setScale(2);
gameState.ground = this.physics.add.staticGroup();
gameState.ground.create(0, height, 'ground').setScale((8.6, 1)).refreshBody();
gameState.groundTile = this.add.tileSprite(0, height, width, null, 'ground').setScale(8.6, 1);
gameState.gameOver = false;
gameState.player1 = this.physics.add.sprite(100, gameState.player1Y, 'midflap').setScale(2);
gameState.player1.body.acceleration.y = 1500;
gameState.pipes = this.physics.add.group();
gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: '40px', fontWeight: 'bold', });
gameState.secondPlayerSpawned = false;
// Layers
gameState.groundTile.setDepth(1);
gameState.pipes.setDepth(2);
gameState.scoreText.setDepth(3);
gameState.playSoundMethod = (sound) => {
this.sound.play(sound);
}
const addRowOfPipes = () => {
const hole = Math.floor(Math.random() * 7) + 3;
for (let i = 0; i < 17; i++) {
if (i !== hole && i !== hole + 1 && i !== hole + 2) {
let pipe = gameState.pipes.create(width - 60, i * 50 + 25, 'pipe');
pipe.body.setVelocityX(-200);
pipe.outOfBoundsKill = true;
pipe.body.allowGravity = false;
pipe.body.immovable = true;
this.physics.add.collider(pipe, gameState.colliderTile2, (item) => {
if (i === 16) {
gameState.pointSound.play();
gameState.score++;
if (gameState.scoreText)
gameState.scoreText.destroy();
gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: '40px', fontWeight: 'bold', });
}
item.destroy();
})
if (i === 16) {
pipe.onWorldBounds = true;
}
}
}
}
gameState.fallDown = () => {
if (gameState.player1Dead) {
gameState.player1.y += 5;
if (gameState.player1.y > height)
gameState.player1fallDownCaller.destroy();
}
if (gameState.player2Dead) {
gameState.player2.y += 5;
if (gameState.player2.y > height)
gameState.player2fallDownCaller.destroy();
}
}
addRowOfPipes();
gameState.gameOverMethod = () => {
this.physics.pause();
gameState.scoreText.destroy();
if (gameState.score > hiScore)
localStorage['hiScore'] = gameState.score;
hiScore = localStorage.getItem('hiScore');
document.getElementById('hiScoreTable').style.display = 'initial';
document.getElementById('score').innerHTML = gameState.score;
document.getElementById('hiScore').innerHTML = hiScore;
birdAnimation.destroy();
gameState.gameOver = true;
this.add.text();
pipeGen.destroy();
gameState.player1.setVelocityY(150);
gameState.player1.setVelocityX(0);
if (gameState.secondPlayerSpawned) {
gameState.player2.setVelocityY(150);
gameState.player2.setVelocityX(0);
}
if (gameState.score > 10) {
if (gameState.score > 20) {
if (gameState.score > 30) {
displayMedal('gold');
}
displayMedal('silver');
}
displayMedal('bronze');
}
function displayMedal(medal) {
let medalColor;
document.getElementById('medalContainer').style.display = 'initial';
switch (medal) {
case 'bronze':
medalColor = '#cd7f32';
break;
case 'silver':
medalColor = '#c0c0c0';
break;
case 'gold':
medalColor = '#ccac00';
break;
}
document.getElementById('medal').style.backgroundColor = medalColor;
}
gameState.score = 0;
}
gameState.fallDownCaller = (player) => {
if (player === 'player1') {
gameState.player1fallDownCaller = this.time.addEvent({
delay: 10,
callback: gameState.fallDown,
loop: true,
})
} else {
gameState.player2fallDownCaller = this.time.addEvent({
delay: 10,
callback: gameState.fallDown,
loop: true,
})
}
}
gameState.collisionMethod = (player) => {
if (player === 'player1') {
gameState.player1Dead = true;
if ((gameState.player1Dead && gameState.player2Dead) || !gameState.secondPlayerSpawned) {
gameState.gameOverMethod();
}
gameState.fallDownCaller(player);
} else {
gameState.player2Dead = true;
if (gameState.player1Dead && gameState.player2Dead) {
gameState.gameOverMethod();
}
gameState.fallDownCaller(player);
}
}
// Colliders
gameState.player1.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player1, gameState.ground, () => {
gameState.dieSound.play();
gameState.collisionMethod('player1')
});
this.physics.add.collider(gameState.player1, gameState.pipes, () => {
gameState.hitSound.play();
gameState.collisionMethod('player1');
});
// Initialize input keys
gameState.cursors = this.input.keyboard.createCursorKeys();
const pipeGen = this.time.addEvent({
callback: addRowOfPipes,
delay: 1500,
callbackScope: this,
loop: true,
})
// Animation
const animateBird = () => {
gameState.player1AnimationStage++;
if (gameState.player1AnimationStage > 2)
gameState.player1AnimationStage = 0;
if (gameState.secondPlayerSpawned) {
gameState.player2AnimationStage++;
if (gameState.player2AnimationStage > 2)
gameState.player2AnimationStage = 0;
}
gameState.player1.setTexture(gameState.player1SpriteSheet[gameState.player1AnimationStage]);
if (gameState.secondPlayerSpawned)
gameState.player2.setTexture(gameState.player2SpriteSheet[gameState.player2AnimationStage]);
}
const birdAnimation = this.time.addEvent({
callback: animateBird,
delay: 100,
callbackScope: this,
loop: true,
})
}
function update() {
if (!gameState.gameOver) {
gameState.bgTile.tilePositionX += 0.1;
gameState.groundTile.tilePositionX += 1;
}
// Press spacebar to fly up
if (gameState.cursors.space.isDown) {
if (gameState.gameOver) {
this.scene.restart();
} else {
gameState.wingSound.play();
gameState.player1.setVelocityY(-350);
}
}
const spawnSecondPlayer = () => {
gameState.secondPlayerSpawned = true;
gameState.player2 = this.physics.add.sprite(100, gameState.player2Y, 'player2_midflap').setScale(2);
gameState.player2.body.acceleration.y = 1500;
gameState.player2.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player2, gameState.ground, () => {
gameState.dieSound.play();
gameState.collisionMethod('player2')
});
this.physics.add.collider(gameState.player2, gameState.pipes, () => {
gameState.hitSound.play();
gameState.collisionMethod('player2');
});
}
if (!gameState.secondPlayerSpawned) {
if (gameState.cursors.shift.isDown) {
spawnSecondPlayer();
}
} else {
if (gameState.cursors.shift.isDown) {
if (gameState.gameOver) {
this.scene.restart();
} else {
gameState.player2.setVelocityY(-350);
}
}
}
}
javascript game ecmascript-6 phaser.io
$endgroup$
add a comment |
$begingroup$
The game is hosted here.
GitHub repo
This is my first Phaser 3 game/project and I'm still pretty new to Javascript so I'm sure there are many things I could be doing better. The number 1 thing that I would like to improve about my code is the performance. Then code effectiveness and readability, but performance is priority.
Your feedback is valuable even if you have no experience with PhaserJS whatsoever, because a lot of things that I could probably be doing better only have to do with pure Javascript.
My JS code:
const width = window.innerWidth;
const height = window.innerHeight;
let hiScore = localStorage['hiScore'] || 0;
const config = {
width: width,
height: height,
backgroundColor: 0x50C875,
scene: {
preload,
create,
update,
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 50 },
},
}
}
const gameState = {
gameOver: false,
score: 0,
scoreText: false,
player1AnimationStage: 0,
player2AnimationStage: 0,
player1SpriteSheet: ['upflap', 'midflap', 'downflap',],
player2SpriteSheet: ['player2_upflap', 'player2_midflap', 'player2_downflap',],
player1Y: (height / 2 * 0.5),
player2Y: (height / 2 * 0.5),
secondPlayerSpawned: false,
player1Dead: false,
player2Dead: false,
}
const game = new Phaser.Game(config, 'root');
game.clearBeforeRender = false;
function preload() {
this.load.image('background', 'assets/images/background.png');
this.load.image('ground', 'assets/images/ground.png');
this.load.image('pipe', 'assets/images/pipe.png');
this.load.image('upflap', 'assets/images/upflap.png');
this.load.image('midflap', 'assets/images/midflap.png');
this.load.image('downflap', 'assets/images/downflap.png');
this.load.image('player2_upflap', 'assets/images/player2_upflap.png');
this.load.image('player2_midflap', 'assets/images/player2_midflap.png');
this.load.image('player2_downflap', 'assets/images/player2_downflap.png');
this.load.audio('hit', 'assets/audio/hit.mp3');
this.load.audio('point', 'assets/audio/point.mp3');
this.load.audio('wing', 'assets/audio/wing.mp3');
this.load.audio('die', 'assets/audio/die.mp3');
}
function create() {
gameState.hitSound = this.sound.add('hit');
gameState.pointSound = this.sound.add('point');
gameState.wingSound = this.sound.add('wing');
gameState.dieSound = this.sound.add('die');
// Hide Score Table
document.getElementById('hiScoreTable').style.display = 'none';
const colliderTile = this.physics.add.staticGroup();
gameState.colliderTile = colliderTile.create(50, 0, 'pipe').setScale(0.1, 80).refreshBody();
gameState.colliderTile2 = colliderTile.create(1, 0, 'pipe').setScale(0, 80).refreshBody();
gameState.bgTile = this.add.tileSprite(0, height, width, height, 'background').setScale(2);
gameState.ground = this.physics.add.staticGroup();
gameState.ground.create(0, height, 'ground').setScale((8.6, 1)).refreshBody();
gameState.groundTile = this.add.tileSprite(0, height, width, null, 'ground').setScale(8.6, 1);
gameState.gameOver = false;
gameState.player1 = this.physics.add.sprite(100, gameState.player1Y, 'midflap').setScale(2);
gameState.player1.body.acceleration.y = 1500;
gameState.pipes = this.physics.add.group();
gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: '40px', fontWeight: 'bold', });
gameState.secondPlayerSpawned = false;
// Layers
gameState.groundTile.setDepth(1);
gameState.pipes.setDepth(2);
gameState.scoreText.setDepth(3);
gameState.playSoundMethod = (sound) => {
this.sound.play(sound);
}
const addRowOfPipes = () => {
const hole = Math.floor(Math.random() * 7) + 3;
for (let i = 0; i < 17; i++) {
if (i !== hole && i !== hole + 1 && i !== hole + 2) {
let pipe = gameState.pipes.create(width - 60, i * 50 + 25, 'pipe');
pipe.body.setVelocityX(-200);
pipe.outOfBoundsKill = true;
pipe.body.allowGravity = false;
pipe.body.immovable = true;
this.physics.add.collider(pipe, gameState.colliderTile2, (item) => {
if (i === 16) {
gameState.pointSound.play();
gameState.score++;
if (gameState.scoreText)
gameState.scoreText.destroy();
gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: '40px', fontWeight: 'bold', });
}
item.destroy();
})
if (i === 16) {
pipe.onWorldBounds = true;
}
}
}
}
gameState.fallDown = () => {
if (gameState.player1Dead) {
gameState.player1.y += 5;
if (gameState.player1.y > height)
gameState.player1fallDownCaller.destroy();
}
if (gameState.player2Dead) {
gameState.player2.y += 5;
if (gameState.player2.y > height)
gameState.player2fallDownCaller.destroy();
}
}
addRowOfPipes();
gameState.gameOverMethod = () => {
this.physics.pause();
gameState.scoreText.destroy();
if (gameState.score > hiScore)
localStorage['hiScore'] = gameState.score;
hiScore = localStorage.getItem('hiScore');
document.getElementById('hiScoreTable').style.display = 'initial';
document.getElementById('score').innerHTML = gameState.score;
document.getElementById('hiScore').innerHTML = hiScore;
birdAnimation.destroy();
gameState.gameOver = true;
this.add.text();
pipeGen.destroy();
gameState.player1.setVelocityY(150);
gameState.player1.setVelocityX(0);
if (gameState.secondPlayerSpawned) {
gameState.player2.setVelocityY(150);
gameState.player2.setVelocityX(0);
}
if (gameState.score > 10) {
if (gameState.score > 20) {
if (gameState.score > 30) {
displayMedal('gold');
}
displayMedal('silver');
}
displayMedal('bronze');
}
function displayMedal(medal) {
let medalColor;
document.getElementById('medalContainer').style.display = 'initial';
switch (medal) {
case 'bronze':
medalColor = '#cd7f32';
break;
case 'silver':
medalColor = '#c0c0c0';
break;
case 'gold':
medalColor = '#ccac00';
break;
}
document.getElementById('medal').style.backgroundColor = medalColor;
}
gameState.score = 0;
}
gameState.fallDownCaller = (player) => {
if (player === 'player1') {
gameState.player1fallDownCaller = this.time.addEvent({
delay: 10,
callback: gameState.fallDown,
loop: true,
})
} else {
gameState.player2fallDownCaller = this.time.addEvent({
delay: 10,
callback: gameState.fallDown,
loop: true,
})
}
}
gameState.collisionMethod = (player) => {
if (player === 'player1') {
gameState.player1Dead = true;
if ((gameState.player1Dead && gameState.player2Dead) || !gameState.secondPlayerSpawned) {
gameState.gameOverMethod();
}
gameState.fallDownCaller(player);
} else {
gameState.player2Dead = true;
if (gameState.player1Dead && gameState.player2Dead) {
gameState.gameOverMethod();
}
gameState.fallDownCaller(player);
}
}
// Colliders
gameState.player1.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player1, gameState.ground, () => {
gameState.dieSound.play();
gameState.collisionMethod('player1')
});
this.physics.add.collider(gameState.player1, gameState.pipes, () => {
gameState.hitSound.play();
gameState.collisionMethod('player1');
});
// Initialize input keys
gameState.cursors = this.input.keyboard.createCursorKeys();
const pipeGen = this.time.addEvent({
callback: addRowOfPipes,
delay: 1500,
callbackScope: this,
loop: true,
})
// Animation
const animateBird = () => {
gameState.player1AnimationStage++;
if (gameState.player1AnimationStage > 2)
gameState.player1AnimationStage = 0;
if (gameState.secondPlayerSpawned) {
gameState.player2AnimationStage++;
if (gameState.player2AnimationStage > 2)
gameState.player2AnimationStage = 0;
}
gameState.player1.setTexture(gameState.player1SpriteSheet[gameState.player1AnimationStage]);
if (gameState.secondPlayerSpawned)
gameState.player2.setTexture(gameState.player2SpriteSheet[gameState.player2AnimationStage]);
}
const birdAnimation = this.time.addEvent({
callback: animateBird,
delay: 100,
callbackScope: this,
loop: true,
})
}
function update() {
if (!gameState.gameOver) {
gameState.bgTile.tilePositionX += 0.1;
gameState.groundTile.tilePositionX += 1;
}
// Press spacebar to fly up
if (gameState.cursors.space.isDown) {
if (gameState.gameOver) {
this.scene.restart();
} else {
gameState.wingSound.play();
gameState.player1.setVelocityY(-350);
}
}
const spawnSecondPlayer = () => {
gameState.secondPlayerSpawned = true;
gameState.player2 = this.physics.add.sprite(100, gameState.player2Y, 'player2_midflap').setScale(2);
gameState.player2.body.acceleration.y = 1500;
gameState.player2.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player2, gameState.ground, () => {
gameState.dieSound.play();
gameState.collisionMethod('player2')
});
this.physics.add.collider(gameState.player2, gameState.pipes, () => {
gameState.hitSound.play();
gameState.collisionMethod('player2');
});
}
if (!gameState.secondPlayerSpawned) {
if (gameState.cursors.shift.isDown) {
spawnSecondPlayer();
}
} else {
if (gameState.cursors.shift.isDown) {
if (gameState.gameOver) {
this.scene.restart();
} else {
gameState.player2.setVelocityY(-350);
}
}
}
}
javascript game ecmascript-6 phaser.io
$endgroup$
add a comment |
$begingroup$
The game is hosted here.
GitHub repo
This is my first Phaser 3 game/project and I'm still pretty new to Javascript so I'm sure there are many things I could be doing better. The number 1 thing that I would like to improve about my code is the performance. Then code effectiveness and readability, but performance is priority.
Your feedback is valuable even if you have no experience with PhaserJS whatsoever, because a lot of things that I could probably be doing better only have to do with pure Javascript.
My JS code:
const width = window.innerWidth;
const height = window.innerHeight;
let hiScore = localStorage['hiScore'] || 0;
const config = {
width: width,
height: height,
backgroundColor: 0x50C875,
scene: {
preload,
create,
update,
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 50 },
},
}
}
const gameState = {
gameOver: false,
score: 0,
scoreText: false,
player1AnimationStage: 0,
player2AnimationStage: 0,
player1SpriteSheet: ['upflap', 'midflap', 'downflap',],
player2SpriteSheet: ['player2_upflap', 'player2_midflap', 'player2_downflap',],
player1Y: (height / 2 * 0.5),
player2Y: (height / 2 * 0.5),
secondPlayerSpawned: false,
player1Dead: false,
player2Dead: false,
}
const game = new Phaser.Game(config, 'root');
game.clearBeforeRender = false;
function preload() {
this.load.image('background', 'assets/images/background.png');
this.load.image('ground', 'assets/images/ground.png');
this.load.image('pipe', 'assets/images/pipe.png');
this.load.image('upflap', 'assets/images/upflap.png');
this.load.image('midflap', 'assets/images/midflap.png');
this.load.image('downflap', 'assets/images/downflap.png');
this.load.image('player2_upflap', 'assets/images/player2_upflap.png');
this.load.image('player2_midflap', 'assets/images/player2_midflap.png');
this.load.image('player2_downflap', 'assets/images/player2_downflap.png');
this.load.audio('hit', 'assets/audio/hit.mp3');
this.load.audio('point', 'assets/audio/point.mp3');
this.load.audio('wing', 'assets/audio/wing.mp3');
this.load.audio('die', 'assets/audio/die.mp3');
}
function create() {
gameState.hitSound = this.sound.add('hit');
gameState.pointSound = this.sound.add('point');
gameState.wingSound = this.sound.add('wing');
gameState.dieSound = this.sound.add('die');
// Hide Score Table
document.getElementById('hiScoreTable').style.display = 'none';
const colliderTile = this.physics.add.staticGroup();
gameState.colliderTile = colliderTile.create(50, 0, 'pipe').setScale(0.1, 80).refreshBody();
gameState.colliderTile2 = colliderTile.create(1, 0, 'pipe').setScale(0, 80).refreshBody();
gameState.bgTile = this.add.tileSprite(0, height, width, height, 'background').setScale(2);
gameState.ground = this.physics.add.staticGroup();
gameState.ground.create(0, height, 'ground').setScale((8.6, 1)).refreshBody();
gameState.groundTile = this.add.tileSprite(0, height, width, null, 'ground').setScale(8.6, 1);
gameState.gameOver = false;
gameState.player1 = this.physics.add.sprite(100, gameState.player1Y, 'midflap').setScale(2);
gameState.player1.body.acceleration.y = 1500;
gameState.pipes = this.physics.add.group();
gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: '40px', fontWeight: 'bold', });
gameState.secondPlayerSpawned = false;
// Layers
gameState.groundTile.setDepth(1);
gameState.pipes.setDepth(2);
gameState.scoreText.setDepth(3);
gameState.playSoundMethod = (sound) => {
this.sound.play(sound);
}
const addRowOfPipes = () => {
const hole = Math.floor(Math.random() * 7) + 3;
for (let i = 0; i < 17; i++) {
if (i !== hole && i !== hole + 1 && i !== hole + 2) {
let pipe = gameState.pipes.create(width - 60, i * 50 + 25, 'pipe');
pipe.body.setVelocityX(-200);
pipe.outOfBoundsKill = true;
pipe.body.allowGravity = false;
pipe.body.immovable = true;
this.physics.add.collider(pipe, gameState.colliderTile2, (item) => {
if (i === 16) {
gameState.pointSound.play();
gameState.score++;
if (gameState.scoreText)
gameState.scoreText.destroy();
gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: '40px', fontWeight: 'bold', });
}
item.destroy();
})
if (i === 16) {
pipe.onWorldBounds = true;
}
}
}
}
gameState.fallDown = () => {
if (gameState.player1Dead) {
gameState.player1.y += 5;
if (gameState.player1.y > height)
gameState.player1fallDownCaller.destroy();
}
if (gameState.player2Dead) {
gameState.player2.y += 5;
if (gameState.player2.y > height)
gameState.player2fallDownCaller.destroy();
}
}
addRowOfPipes();
gameState.gameOverMethod = () => {
this.physics.pause();
gameState.scoreText.destroy();
if (gameState.score > hiScore)
localStorage['hiScore'] = gameState.score;
hiScore = localStorage.getItem('hiScore');
document.getElementById('hiScoreTable').style.display = 'initial';
document.getElementById('score').innerHTML = gameState.score;
document.getElementById('hiScore').innerHTML = hiScore;
birdAnimation.destroy();
gameState.gameOver = true;
this.add.text();
pipeGen.destroy();
gameState.player1.setVelocityY(150);
gameState.player1.setVelocityX(0);
if (gameState.secondPlayerSpawned) {
gameState.player2.setVelocityY(150);
gameState.player2.setVelocityX(0);
}
if (gameState.score > 10) {
if (gameState.score > 20) {
if (gameState.score > 30) {
displayMedal('gold');
}
displayMedal('silver');
}
displayMedal('bronze');
}
function displayMedal(medal) {
let medalColor;
document.getElementById('medalContainer').style.display = 'initial';
switch (medal) {
case 'bronze':
medalColor = '#cd7f32';
break;
case 'silver':
medalColor = '#c0c0c0';
break;
case 'gold':
medalColor = '#ccac00';
break;
}
document.getElementById('medal').style.backgroundColor = medalColor;
}
gameState.score = 0;
}
gameState.fallDownCaller = (player) => {
if (player === 'player1') {
gameState.player1fallDownCaller = this.time.addEvent({
delay: 10,
callback: gameState.fallDown,
loop: true,
})
} else {
gameState.player2fallDownCaller = this.time.addEvent({
delay: 10,
callback: gameState.fallDown,
loop: true,
})
}
}
gameState.collisionMethod = (player) => {
if (player === 'player1') {
gameState.player1Dead = true;
if ((gameState.player1Dead && gameState.player2Dead) || !gameState.secondPlayerSpawned) {
gameState.gameOverMethod();
}
gameState.fallDownCaller(player);
} else {
gameState.player2Dead = true;
if (gameState.player1Dead && gameState.player2Dead) {
gameState.gameOverMethod();
}
gameState.fallDownCaller(player);
}
}
// Colliders
gameState.player1.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player1, gameState.ground, () => {
gameState.dieSound.play();
gameState.collisionMethod('player1')
});
this.physics.add.collider(gameState.player1, gameState.pipes, () => {
gameState.hitSound.play();
gameState.collisionMethod('player1');
});
// Initialize input keys
gameState.cursors = this.input.keyboard.createCursorKeys();
const pipeGen = this.time.addEvent({
callback: addRowOfPipes,
delay: 1500,
callbackScope: this,
loop: true,
})
// Animation
const animateBird = () => {
gameState.player1AnimationStage++;
if (gameState.player1AnimationStage > 2)
gameState.player1AnimationStage = 0;
if (gameState.secondPlayerSpawned) {
gameState.player2AnimationStage++;
if (gameState.player2AnimationStage > 2)
gameState.player2AnimationStage = 0;
}
gameState.player1.setTexture(gameState.player1SpriteSheet[gameState.player1AnimationStage]);
if (gameState.secondPlayerSpawned)
gameState.player2.setTexture(gameState.player2SpriteSheet[gameState.player2AnimationStage]);
}
const birdAnimation = this.time.addEvent({
callback: animateBird,
delay: 100,
callbackScope: this,
loop: true,
})
}
function update() {
if (!gameState.gameOver) {
gameState.bgTile.tilePositionX += 0.1;
gameState.groundTile.tilePositionX += 1;
}
// Press spacebar to fly up
if (gameState.cursors.space.isDown) {
if (gameState.gameOver) {
this.scene.restart();
} else {
gameState.wingSound.play();
gameState.player1.setVelocityY(-350);
}
}
const spawnSecondPlayer = () => {
gameState.secondPlayerSpawned = true;
gameState.player2 = this.physics.add.sprite(100, gameState.player2Y, 'player2_midflap').setScale(2);
gameState.player2.body.acceleration.y = 1500;
gameState.player2.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player2, gameState.ground, () => {
gameState.dieSound.play();
gameState.collisionMethod('player2')
});
this.physics.add.collider(gameState.player2, gameState.pipes, () => {
gameState.hitSound.play();
gameState.collisionMethod('player2');
});
}
if (!gameState.secondPlayerSpawned) {
if (gameState.cursors.shift.isDown) {
spawnSecondPlayer();
}
} else {
if (gameState.cursors.shift.isDown) {
if (gameState.gameOver) {
this.scene.restart();
} else {
gameState.player2.setVelocityY(-350);
}
}
}
}
javascript game ecmascript-6 phaser.io
$endgroup$
The game is hosted here.
GitHub repo
This is my first Phaser 3 game/project and I'm still pretty new to Javascript so I'm sure there are many things I could be doing better. The number 1 thing that I would like to improve about my code is the performance. Then code effectiveness and readability, but performance is priority.
Your feedback is valuable even if you have no experience with PhaserJS whatsoever, because a lot of things that I could probably be doing better only have to do with pure Javascript.
My JS code:
const width = window.innerWidth;
const height = window.innerHeight;
let hiScore = localStorage['hiScore'] || 0;
const config = {
width: width,
height: height,
backgroundColor: 0x50C875,
scene: {
preload,
create,
update,
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 50 },
},
}
}
const gameState = {
gameOver: false,
score: 0,
scoreText: false,
player1AnimationStage: 0,
player2AnimationStage: 0,
player1SpriteSheet: ['upflap', 'midflap', 'downflap',],
player2SpriteSheet: ['player2_upflap', 'player2_midflap', 'player2_downflap',],
player1Y: (height / 2 * 0.5),
player2Y: (height / 2 * 0.5),
secondPlayerSpawned: false,
player1Dead: false,
player2Dead: false,
}
const game = new Phaser.Game(config, 'root');
game.clearBeforeRender = false;
function preload() {
this.load.image('background', 'assets/images/background.png');
this.load.image('ground', 'assets/images/ground.png');
this.load.image('pipe', 'assets/images/pipe.png');
this.load.image('upflap', 'assets/images/upflap.png');
this.load.image('midflap', 'assets/images/midflap.png');
this.load.image('downflap', 'assets/images/downflap.png');
this.load.image('player2_upflap', 'assets/images/player2_upflap.png');
this.load.image('player2_midflap', 'assets/images/player2_midflap.png');
this.load.image('player2_downflap', 'assets/images/player2_downflap.png');
this.load.audio('hit', 'assets/audio/hit.mp3');
this.load.audio('point', 'assets/audio/point.mp3');
this.load.audio('wing', 'assets/audio/wing.mp3');
this.load.audio('die', 'assets/audio/die.mp3');
}
function create() {
gameState.hitSound = this.sound.add('hit');
gameState.pointSound = this.sound.add('point');
gameState.wingSound = this.sound.add('wing');
gameState.dieSound = this.sound.add('die');
// Hide Score Table
document.getElementById('hiScoreTable').style.display = 'none';
const colliderTile = this.physics.add.staticGroup();
gameState.colliderTile = colliderTile.create(50, 0, 'pipe').setScale(0.1, 80).refreshBody();
gameState.colliderTile2 = colliderTile.create(1, 0, 'pipe').setScale(0, 80).refreshBody();
gameState.bgTile = this.add.tileSprite(0, height, width, height, 'background').setScale(2);
gameState.ground = this.physics.add.staticGroup();
gameState.ground.create(0, height, 'ground').setScale((8.6, 1)).refreshBody();
gameState.groundTile = this.add.tileSprite(0, height, width, null, 'ground').setScale(8.6, 1);
gameState.gameOver = false;
gameState.player1 = this.physics.add.sprite(100, gameState.player1Y, 'midflap').setScale(2);
gameState.player1.body.acceleration.y = 1500;
gameState.pipes = this.physics.add.group();
gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: '40px', fontWeight: 'bold', });
gameState.secondPlayerSpawned = false;
// Layers
gameState.groundTile.setDepth(1);
gameState.pipes.setDepth(2);
gameState.scoreText.setDepth(3);
gameState.playSoundMethod = (sound) => {
this.sound.play(sound);
}
const addRowOfPipes = () => {
const hole = Math.floor(Math.random() * 7) + 3;
for (let i = 0; i < 17; i++) {
if (i !== hole && i !== hole + 1 && i !== hole + 2) {
let pipe = gameState.pipes.create(width - 60, i * 50 + 25, 'pipe');
pipe.body.setVelocityX(-200);
pipe.outOfBoundsKill = true;
pipe.body.allowGravity = false;
pipe.body.immovable = true;
this.physics.add.collider(pipe, gameState.colliderTile2, (item) => {
if (i === 16) {
gameState.pointSound.play();
gameState.score++;
if (gameState.scoreText)
gameState.scoreText.destroy();
gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: '40px', fontWeight: 'bold', });
}
item.destroy();
})
if (i === 16) {
pipe.onWorldBounds = true;
}
}
}
}
gameState.fallDown = () => {
if (gameState.player1Dead) {
gameState.player1.y += 5;
if (gameState.player1.y > height)
gameState.player1fallDownCaller.destroy();
}
if (gameState.player2Dead) {
gameState.player2.y += 5;
if (gameState.player2.y > height)
gameState.player2fallDownCaller.destroy();
}
}
addRowOfPipes();
gameState.gameOverMethod = () => {
this.physics.pause();
gameState.scoreText.destroy();
if (gameState.score > hiScore)
localStorage['hiScore'] = gameState.score;
hiScore = localStorage.getItem('hiScore');
document.getElementById('hiScoreTable').style.display = 'initial';
document.getElementById('score').innerHTML = gameState.score;
document.getElementById('hiScore').innerHTML = hiScore;
birdAnimation.destroy();
gameState.gameOver = true;
this.add.text();
pipeGen.destroy();
gameState.player1.setVelocityY(150);
gameState.player1.setVelocityX(0);
if (gameState.secondPlayerSpawned) {
gameState.player2.setVelocityY(150);
gameState.player2.setVelocityX(0);
}
if (gameState.score > 10) {
if (gameState.score > 20) {
if (gameState.score > 30) {
displayMedal('gold');
}
displayMedal('silver');
}
displayMedal('bronze');
}
function displayMedal(medal) {
let medalColor;
document.getElementById('medalContainer').style.display = 'initial';
switch (medal) {
case 'bronze':
medalColor = '#cd7f32';
break;
case 'silver':
medalColor = '#c0c0c0';
break;
case 'gold':
medalColor = '#ccac00';
break;
}
document.getElementById('medal').style.backgroundColor = medalColor;
}
gameState.score = 0;
}
gameState.fallDownCaller = (player) => {
if (player === 'player1') {
gameState.player1fallDownCaller = this.time.addEvent({
delay: 10,
callback: gameState.fallDown,
loop: true,
})
} else {
gameState.player2fallDownCaller = this.time.addEvent({
delay: 10,
callback: gameState.fallDown,
loop: true,
})
}
}
gameState.collisionMethod = (player) => {
if (player === 'player1') {
gameState.player1Dead = true;
if ((gameState.player1Dead && gameState.player2Dead) || !gameState.secondPlayerSpawned) {
gameState.gameOverMethod();
}
gameState.fallDownCaller(player);
} else {
gameState.player2Dead = true;
if (gameState.player1Dead && gameState.player2Dead) {
gameState.gameOverMethod();
}
gameState.fallDownCaller(player);
}
}
// Colliders
gameState.player1.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player1, gameState.ground, () => {
gameState.dieSound.play();
gameState.collisionMethod('player1')
});
this.physics.add.collider(gameState.player1, gameState.pipes, () => {
gameState.hitSound.play();
gameState.collisionMethod('player1');
});
// Initialize input keys
gameState.cursors = this.input.keyboard.createCursorKeys();
const pipeGen = this.time.addEvent({
callback: addRowOfPipes,
delay: 1500,
callbackScope: this,
loop: true,
})
// Animation
const animateBird = () => {
gameState.player1AnimationStage++;
if (gameState.player1AnimationStage > 2)
gameState.player1AnimationStage = 0;
if (gameState.secondPlayerSpawned) {
gameState.player2AnimationStage++;
if (gameState.player2AnimationStage > 2)
gameState.player2AnimationStage = 0;
}
gameState.player1.setTexture(gameState.player1SpriteSheet[gameState.player1AnimationStage]);
if (gameState.secondPlayerSpawned)
gameState.player2.setTexture(gameState.player2SpriteSheet[gameState.player2AnimationStage]);
}
const birdAnimation = this.time.addEvent({
callback: animateBird,
delay: 100,
callbackScope: this,
loop: true,
})
}
function update() {
if (!gameState.gameOver) {
gameState.bgTile.tilePositionX += 0.1;
gameState.groundTile.tilePositionX += 1;
}
// Press spacebar to fly up
if (gameState.cursors.space.isDown) {
if (gameState.gameOver) {
this.scene.restart();
} else {
gameState.wingSound.play();
gameState.player1.setVelocityY(-350);
}
}
const spawnSecondPlayer = () => {
gameState.secondPlayerSpawned = true;
gameState.player2 = this.physics.add.sprite(100, gameState.player2Y, 'player2_midflap').setScale(2);
gameState.player2.body.acceleration.y = 1500;
gameState.player2.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player2, gameState.ground, () => {
gameState.dieSound.play();
gameState.collisionMethod('player2')
});
this.physics.add.collider(gameState.player2, gameState.pipes, () => {
gameState.hitSound.play();
gameState.collisionMethod('player2');
});
}
if (!gameState.secondPlayerSpawned) {
if (gameState.cursors.shift.isDown) {
spawnSecondPlayer();
}
} else {
if (gameState.cursors.shift.isDown) {
if (gameState.gameOver) {
this.scene.restart();
} else {
gameState.player2.setVelocityY(-350);
}
}
}
}
javascript game ecmascript-6 phaser.io
javascript game ecmascript-6 phaser.io
asked Mar 29 at 9:18
merkur0merkur0
353
353
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
What if you want to add the functionality for up to 3 players?
You'd have to create a 'player3AnimationStage, player3SpriteSheet etc. It's also inside the 'gameState', which arguably makes sense, but still could be separated into it's own class.
For example:
class Player
{
constructor(spriteSheet, animationStage)
{
this.SpriteSheet = spriteSheet;
this.AnimationStage = animationStage;
}
}
const gameState = {
player1: new Player(...);
player2: new Player(...);
Or better yet, have an array of Players. Try to code your game so that it does not matter how many players there are. (E.g iterate through the list of players).
Your colours could be made into an ENUM, or a class with score, colorName, colorCode.
I'd suggest declaring some variables at the top, to make maintenance easier.
Such as key div elements (hiScoreTable). (Or even just the ids of the elements).
Images.
Try to avoid 'maigc numbers' by using named variables. For example, what is '17' here?:
for (let i = 0; i < 17; i++)
Avoiding 'magicNumbers' also decreases code duplication and makes maintenance easier. For example, to increase player speed currently we'd have to change it in at least 2 places.
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216462%2fflappy-bird-game-supporting-local-multiplayer-of-up-to-2-players%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
What if you want to add the functionality for up to 3 players?
You'd have to create a 'player3AnimationStage, player3SpriteSheet etc. It's also inside the 'gameState', which arguably makes sense, but still could be separated into it's own class.
For example:
class Player
{
constructor(spriteSheet, animationStage)
{
this.SpriteSheet = spriteSheet;
this.AnimationStage = animationStage;
}
}
const gameState = {
player1: new Player(...);
player2: new Player(...);
Or better yet, have an array of Players. Try to code your game so that it does not matter how many players there are. (E.g iterate through the list of players).
Your colours could be made into an ENUM, or a class with score, colorName, colorCode.
I'd suggest declaring some variables at the top, to make maintenance easier.
Such as key div elements (hiScoreTable). (Or even just the ids of the elements).
Images.
Try to avoid 'maigc numbers' by using named variables. For example, what is '17' here?:
for (let i = 0; i < 17; i++)
Avoiding 'magicNumbers' also decreases code duplication and makes maintenance easier. For example, to increase player speed currently we'd have to change it in at least 2 places.
$endgroup$
add a comment |
$begingroup$
What if you want to add the functionality for up to 3 players?
You'd have to create a 'player3AnimationStage, player3SpriteSheet etc. It's also inside the 'gameState', which arguably makes sense, but still could be separated into it's own class.
For example:
class Player
{
constructor(spriteSheet, animationStage)
{
this.SpriteSheet = spriteSheet;
this.AnimationStage = animationStage;
}
}
const gameState = {
player1: new Player(...);
player2: new Player(...);
Or better yet, have an array of Players. Try to code your game so that it does not matter how many players there are. (E.g iterate through the list of players).
Your colours could be made into an ENUM, or a class with score, colorName, colorCode.
I'd suggest declaring some variables at the top, to make maintenance easier.
Such as key div elements (hiScoreTable). (Or even just the ids of the elements).
Images.
Try to avoid 'maigc numbers' by using named variables. For example, what is '17' here?:
for (let i = 0; i < 17; i++)
Avoiding 'magicNumbers' also decreases code duplication and makes maintenance easier. For example, to increase player speed currently we'd have to change it in at least 2 places.
$endgroup$
add a comment |
$begingroup$
What if you want to add the functionality for up to 3 players?
You'd have to create a 'player3AnimationStage, player3SpriteSheet etc. It's also inside the 'gameState', which arguably makes sense, but still could be separated into it's own class.
For example:
class Player
{
constructor(spriteSheet, animationStage)
{
this.SpriteSheet = spriteSheet;
this.AnimationStage = animationStage;
}
}
const gameState = {
player1: new Player(...);
player2: new Player(...);
Or better yet, have an array of Players. Try to code your game so that it does not matter how many players there are. (E.g iterate through the list of players).
Your colours could be made into an ENUM, or a class with score, colorName, colorCode.
I'd suggest declaring some variables at the top, to make maintenance easier.
Such as key div elements (hiScoreTable). (Or even just the ids of the elements).
Images.
Try to avoid 'maigc numbers' by using named variables. For example, what is '17' here?:
for (let i = 0; i < 17; i++)
Avoiding 'magicNumbers' also decreases code duplication and makes maintenance easier. For example, to increase player speed currently we'd have to change it in at least 2 places.
$endgroup$
What if you want to add the functionality for up to 3 players?
You'd have to create a 'player3AnimationStage, player3SpriteSheet etc. It's also inside the 'gameState', which arguably makes sense, but still could be separated into it's own class.
For example:
class Player
{
constructor(spriteSheet, animationStage)
{
this.SpriteSheet = spriteSheet;
this.AnimationStage = animationStage;
}
}
const gameState = {
player1: new Player(...);
player2: new Player(...);
Or better yet, have an array of Players. Try to code your game so that it does not matter how many players there are. (E.g iterate through the list of players).
Your colours could be made into an ENUM, or a class with score, colorName, colorCode.
I'd suggest declaring some variables at the top, to make maintenance easier.
Such as key div elements (hiScoreTable). (Or even just the ids of the elements).
Images.
Try to avoid 'maigc numbers' by using named variables. For example, what is '17' here?:
for (let i = 0; i < 17; i++)
Avoiding 'magicNumbers' also decreases code duplication and makes maintenance easier. For example, to increase player speed currently we'd have to change it in at least 2 places.
answered Mar 29 at 17:17
dustytrashdustytrash
2066
2066
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216462%2fflappy-bird-game-supporting-local-multiplayer-of-up-to-2-players%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown