I am creating my first game using Phaser and it is running fine in desktop.
But it is lagging on android phones.
can anyone tell me what could be the reasons?
game is very small
smaller than 2mb.
Images used in game is also very tiny pngs.
is there anyway to find out any leaks in my code.
my main js file.
var buttetSpwanSpeed;
var bulletSpeed;
var enemySpwanSpeed;
var enemySpeed;
var golis;
var enemies;
var enemyLoop;
var scoreText;
var powers;
var bulletSize;
setStart();
//game phaser
var game=new Phaser.Game(window.innerWidth, window.innerHeight, Phaser.CANVAS,"gamearea");
var BootState={
//loding accets
preload: function(){
this.load.image('LodingScreen', 'assets/desimulga.png');
this.load.image('background', 'assets/blue.png');
},
create: function(){
game.state.start("LoadingState");
},
};
var LoadingState={
//loding acc
preload: function(){
bg=this.game.add.tileSprite(0,0,600,300,'background');
bg.height = game.height;
bg.width = game.width;
LodingScreen=this.game.add.sprite(this.game.world.centerX,this.game.world.centerY,'LodingScreen');
LodingScreen.anchor.setTo(0.5);
LodingScreen.scale.setTo(0.5,0.5);
this.load.image('spaceship', 'assets/player.png');
this.load.image('goli', 'assets/bullet.png');
//load ememies
this.load.image('enemy1', 'assets/enemies/enemy1.png');
this.load.image('enemy2', 'assets/enemies/enemy2.png');
this.load.image('enemy3', 'assets/enemies/enemy3.png');
this.load.image('enemy4', 'assets/enemies/enemy4.png');
this.load.image('enemy5', 'assets/enemies/enemy5.png');
this.load.spritesheet('power1', 'assets/power/bulletUp.png',34,33,4);
this.load.image('restart', 'assets/restart.png');
this.load.spritesheet('blast', 'assets/explosion.png',400,400,8);
game.load.audio('fire', 'assets/music/bullet.mp3');
game.load.audio('killed', 'assets/music/killed.mp3');
//game.load.audio('bg_music', 'assets/music/background.mp3');
game.load.audio('death_music', 'assets/music/death.mp3');
game.load.audio('start_music', 'assets/music/start.mp3');
},
create: function(){
game.time.events.add(Phaser.Timer.SECOND * 2, function(){
bg.kill();
LodingScreen.kill();
game.state.start("PreGameState");
},this);
},
};
var PreGameState={
//loding accets
create: function(){
game.scale.refresh();
bg=this.game.add.tileSprite(0,0,600,300,'background');
bg.height = game.height;
bg.width = game.width;
Startb=this.game.add.text(this.game.world.centerX,this.game.world.centerY, 'TAP TO START' , { fontSize: '32px', fill: 'yellow' });
Startb.anchor.setTo(0.5);
Startb.scale.setTo(0.5,0.5);
ship=this.game.add.sprite(this.game.world.centerX,this.game.world.height*0.4,'spaceship');
ship.scale.setTo(0.4);
ship.anchor.setTo(0.5);
game.physics.arcade.enable(ship);
bg.inputEnabled=true;
start_music = game.add.audio('start_music');
start_music.allowMultiple = true;
start_music.addMarker('start_music', 0, 30);
bg.events.onInputDown.add(function(){
bg.inputEnabled=false;
Startb.kill();
start_music.play("start_music");
// game.physics.arcade.moveToXY(ship, this.game.world.centerX, this.game.world.height*0.8, 300, 3000);
// game.add.tween(ship).to( { y: game.world.height*0.8 }, 3000, Phaser.Easing.Sinusoidal.InOut, true);
var tween = game.add.tween(ship).to({
x: [this.game.world.centerX, this.game.world.width*0, this.game.world.width, this.game.world.centerX],
y: [this.game.world.height*0.4, this.game.world.height*0.5, this.game.world.height*0.6, this.game.world.height*0.8],
}, 2000,Phaser.Easing.Quadratic.Out, true).interpolation(function(v, k){
return Phaser.Math.bezierInterpolation(v, k);
});
game.time.events.add(Phaser.Timer.SECOND * 2, function() {
bg.kill();
ship.kill();
game.state.start("GameState");
} ,this);
}, this);
},
};
var GameState={
//loding accets
preload: function(){
},
create: function(){
//background
this.background=this.game.add.tileSprite(0,0,600,300,'background');
this.background.height = game.height;
this.background.width = game.width;
this.background.inputEnabled=true;
this.background.input.enableDrag(true);
this.background.input.startDrag = function(pointer) {
pointer.shipStart = new Phaser.Point(GameState.ship.x, GameState.ship.y);
Phaser.InputHandler.prototype.startDrag.call(this, pointer);
};
this.background.input.updateDrag = function(pointer) {
GameState.ship.x = pointer.shipStart.x - pointer.positionDown.x + pointer.x;
GameState.ship.y = pointer.shipStart.y - pointer.positionDown.y + pointer.y;
GameState.background.x=0;
GameState.background.y=0;
};
//ship
this.ship=this.game.add.sprite(this.game.world.centerX,this.game.world.height*0.8,'spaceship');
this.ship.scale.setTo(0.4);
this.ship.anchor.setTo(0.5);
game.physics.arcade.enable(this.ship);
// this.ship.inputEnabled=true;
// this.ship.input.enableDrag(true);
//score
this.scoreText = this.game.add.text(16, 16, 'Kills: 0', { fontSize: '32px', fill: '#fff' });
//background Music
// music = game.add.audio('bg_music');
//music.play('', 0, 1, true);
//bullet sound
bullet_sound = game.add.audio('fire');
bullet_sound.allowMultiple = true;
bullet_sound.volume=0.5;
bullet_sound.addMarker('fire', 0, 0.5);
//Killed sound
killed_sound = game.add.audio('killed');
killed_sound.allowMultiple = true;
killed_sound.addMarker('killed', 0, 0.5);
//death music
death_music = game.add.audio('death_music');
death_music.allowMultiple = true;
death_music.addMarker('death_music', 0, 10);
//groups of bullets and enemies
golis=game.add.group();
enemies=game.add.group();
powers=game.add.group();
//fire bullet loop
fireLoop=game.time.events.loop(Phaser.Timer.SECOND*1/buttetSpwanSpeed, fireBullet, this);
//this.game.input.onTap.add(fireBullet, this);
//create ememy loop
enemyLoop=game.time.events.loop(Phaser.Timer.SECOND*1/enemySpwanSpeed, createEnemy, this);
//change ememy speed and enemy spwan speed loop
enemySpeedLoop=game.time.events.loop(Phaser.Timer.SECOND*1.5, changeEnemySpeed, this);
//give powerup
powerUp=game.time.events.loop(Phaser.Timer.SECOND*20, powerFun, this);
},
update: function(){
//scrolling background
this.background.tilePosition.y+=2;
//keybord control
if (game.input.keyboard.isDown(Phaser.Keyboard.UP))
{
this.ship.y-=2;
}
if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN))
{
this.ship.y+=2;
}
if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT))
{
this.ship.x+=2;
}
if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT))
{
this.ship.x-=2;
}
//dont go out
if(this.ship.y<0+this.ship.height/2)
{
this.ship.y=0+this.ship.height/2;
}
if(this.ship.y>this.game.world.height-this.ship.height/2)
{
this.ship.y=this.game.world.height-this.ship.height/2;
}
if(this.ship.x<0+this.ship.width/2)
{
this.ship.x=0+this.ship.width/2;
}
if(this.ship.x>this.game.world.width-this.ship.width/2)
{
this.ship.x=this.game.world.width-this.ship.width/2;
}
//check for collisions
game.physics.arcade.overlap(golis,enemies,b_e_collide,null,this);
game.physics.arcade.overlap(this.ship,enemies,s_e_collide,null,this);
game.physics.arcade.overlap(this.ship,powers,s_power1_collide,null,this);
},
};
//setting start game conditions
function setStart(){
buttetSpwanSpeed=2;
bulletSpeed=2000;
enemySpwanSpeed=1;
enemySpeed=300;
score=0;
bulletSize=1.2
}
//fire bullet function
function fireBullet(){
goli=this.game.add.sprite(this.ship.x,this.ship.y-this.ship.height/2,'goli');
goli.anchor.setTo(0.5);
goli.scale.setTo(bulletSize,1);
goli.checkWorldBounds = true;
goli.outOfBoundsKill = true;
//adding to group
golis.add(goli);
game.world.moveDown(goli);
game.physics.arcade.enable(goli);
goli.body.collisonWorldBounds=true;
goli.body.velocity.y=-bulletSpeed;
bullet_sound.play("fire");
}
//create enemy function
function createEnemy(){
enemyNo=game.rnd.integerInRange(1, 5);
x1=game.rnd.integerInRange(0,this.game.world.width);
x2=game.rnd.integerInRange(0,this.game.world.width);
enemy=this.game.add.sprite(x1,10,'enemy'+enemyNo);
enemy.anchor.setTo(0.5);
enemy.scale.setTo(0.4);
enemy.checkWorldBounds = true;
enemies.add(enemy);
enemy.outOfBoundsKill = true;
game.physics.arcade.enable(enemy);
enemy.body.collisonWorldBounds=true;
enemy.angle=90;
enemy.no=enemyNo;
//moving enemy
angleRedian=game.physics.arcade.moveToXY(enemy, x2, this.game.world.height+enemy.height, enemySpeed,0);
angleDegree=angleRedian*57.2958;
enemy.angle=90+angleDegree;
}
//runs when bullet collide to enemy
function b_e_collide(goli,enemy){
//blast
blast=this.game.add.sprite(enemy.x,enemy.y,'blast');
blast.anchor.setTo(0.5);
blast.scale.setTo(0.5);
var explosion=blast.animations.add('explosion');
blast.animations.play('explosion',30,false,true);
//killing
goli.kill();
enemy.kill();
//update scores
if(enemy.no<4)
{
score+=1;
killed_sound.play('killed');
}
this.scoreText.text = 'Kills: ' + score;
}
//runs when ship collide to enemy
function s_e_collide(ship,enemy){
blast=this.game.add.sprite(enemy.x,enemy.y,'blast');
blast.anchor.setTo(0.5);
blast.scale.setTo(0.5);
var explosion=blast.animations.add('explosion');
blast.animations.play('explosion',10,false,true);
ship.kill();
enemy.kill();
//music.stop();
this.scoreText.kill();
death_music.play("death_music");
game.time.events.remove(fireLoop);
game.time.events.add(Phaser.Timer.SECOND * 2, function() {
fianlScore = this.game.add.text(this.game.world.centerX,this.game.world.centerY, 'KILL: '+score, { fontSize: '32px', fill: 'yellow' });
fianlScore.anchor.setTo(0.5);
gameOverText = this.game.add.text(this.game.world.centerX,this.game.world.centerY - fianlScore.height, 'GAME OVER', { fontSize: '32px', fill: 'red' });
gameOverText.anchor.setTo(0.5);
//restart button
restart=this.game.add.sprite(this.game.world.centerX,this.game.world.centerY + fianlScore.height+10,'restart');
restart.anchor.setTo(0.5);
restart.scale.setTo(0.05,0.05);
restart.inputEnabled = true;
restart.events.onInputDown.add(restartGame, this);
game.time.events.stop();
}, this);
}
//runs when ship collide power1
function s_power1_collide(ship,power){
power.kill();
game.time.events.remove(fireLoop);
fireLoop=game.time.events.loop(Phaser.Timer.SECOND*1/10, fireBullet, this);
game.time.events.add(Phaser.Timer.SECOND * 10, function(){
game.time.events.remove(fireLoop);
fireLoop=game.time.events.loop(Phaser.Timer.SECOND*1/buttetSpwanSpeed, fireBullet, this);
},this);
}
function changeEnemySpeed()
{
if(enemySpeed<=900)
{
enemySpeed+=5;
}
if(enemySpwanSpeed<=3)
{
enemySpwanSpeed+=0.025;
}
enemyLoop.delay=Phaser.Timer.SECOND*1/enemySpwanSpeed;
}
//send power up
function powerFun()
{
x1=game.rnd.integerInRange(0,this.game.world.width);
x2=game.rnd.integerInRange(0,this.game.world.width);
power=this.game.add.sprite(x1,10,'power1');
power.anchor.setTo(0.5);
var shine=power.animations.add('shine');
power.animations.play('shine',5,true,true);
power.checkWorldBounds = true;
power.outOfBoundsKill = true;
powers.add(power);
game.physics.arcade.enable(power);
power.body.collisonWorldBounds=true;
game.physics.arcade.moveToXY(power, x2, this.game.world.height+power.height, 400,0);
powerDelay=game.rnd.integerInRange(20,35);
powerUp.delay=Phaser.Timer.SECOND*powerDelay;
}
function restartGame(){
setStart();
game.time.events.start();
game.state.start("PreGameState");
}
game.state.add("GameState",GameState);
game.state.add("BootState",BootState);
game.state.add("LoadingState",LoadingState);
game.state.add("PreGameState",PreGameState);
game.state.start("BootState");
since the game is so small in size I think it should run smoothly on mobile devices.
it runs good on some high end mobile devices but gets slow as time progresses.
this is my first game so I am not very good with game designing concepts.
Hi I currently got this issue fixed myself for my games cross platform.
Things I did were
Load alll sprites right at my main screen/or loading screen
If you have reusable sprites use the command .kill() not .destory() which will take more memory to reload sprite unless the sprite is not coming back to screen then it’s ok to destroy.
Recycle everything let’s say you use a bullet asoon as it hits the wall boom kill and get it back.
And sad part in some cases even by doing sometimes it’ll still lag and have some glitches here and there you just have to see at what point is happening. Use break points to see exactly what sprites are causing it and consider also if the image doesn’t have transparency to use .jpeg
Good luck hope this helps
If it works fine on your desktop, most likely it's an issue with the asset sizes being too big. Reduce all of your assets 1/2 of the size. Phaser has to load all of the assets into cache, then it reduces the pixel size of those files in the engine.
In the mobile phone the processing capacity is lower than in the desktop.
Generally the architecture of a mobile Cpu is usually optimized to save energy. When you are on a desktop, the x86 or x64 architecture is optimized for processing of data.
Therefore it is very important to test the performance of the application on the target device.
You need to reduce creation of objects, loading images or objects when the game is playing. I have better experience with my game just hidging and showing the same objects (enemies), instead of destroying objects (Enemies or Visual elements) and creating again while play. Because when a game create a new instance, the CPU load the same again. This change no make diference in high CPU but is very better in low CPU. Maybe it help you.
I am using this code now, but it returns a blank transparent image:
$(document).ready(function(){
$("#t").click(function() {
html2canvas(document.querySelector("#bracelet_maker"),
{ onrendered: function(canvas){
src = canvas.toDataURL();
window.open(src); } });
});
});
If I use game.canvas.toDataUrl(); it returns a black image.
This is how the game is started in #bracelet_maker div
var game = new Phaser.Game(width,height,Phaser.AUTO,"bracelte_canvas",{
preload:preload,
create:create,
update:update });
Using Phaser.AUTO will allow either Phaser.WEBGL or Phaser.CANVAS be used, depending upon what your browser supports.
If you switch over to Phaser.CANVAS when creating your new Phaser.Game you should be able to use access the canvas.
For example, within Phaser by binding off the S key:
function create() {
// ...
var screenshotKey = game.input.keyboard.addKey(Phaser.Keyboard.S);
screenshotKey.onDown.add(function () { window.open(game.canvas.toDataURL());
}
There's also preserveDrawingBuffer which should allow you capture from WebGL as well, but it needs to be set early on in the process.
var game = new Phaser.Game(800, 600, Phaser.AUTO, '', {
preload: preload, create: create, update: update
});
game.preserveDrawingBuffer = true;
My goal is to make a sprite bigger than the screen and have the user scroll to see the different parts of it, so I wanted to ask if Phaser had any sprite eventListener-functions such as:
var canvas = window.document.getElementsByTagName('canvas')[0],
prevX = 0, prevY = 0, mouseDown = false;
where canvas can be used as
canvas.addEventListener('mousedown',function(e){
});
canvas.addEventListener('mousemove',function(e){
});
Here is how I did it.
In your update function:
if (this.game.input.activePointer.isDown) {
if (this.game.origDragPoint) {
// move the camera by the amount the mouse has moved since last update
this.game.camera.x += this.game.origDragPoint.x - this.game.input.activePointer.position.x;
this.game.camera.y += this.game.origDragPoint.y - this.game.input.activePointer.position.y;
}
// set new drag origin to current position
this.game.origDragPoint = this.game.input.activePointer.position.clone();
}
else {
this.game.origDragPoint = null;
}
If using the camera is OK for you, you can try this Phaser 2 plugin: https://jdnichollsc.github.io/Phaser-Kinetic-Scrolling-Plugin/
With this plugin you can enable vertical and horizontal scroll to simulate scrolling into the canvas element used by Phaser, also you can customize the movement before to initialize the plugin, example:
var game = new Phaser.Game(400, 400, Phaser.AUTO, '', {
init: function () {
//Load the plugin
this.game.kineticScrolling = this.game.plugins.add(Phaser.Plugin.KineticScrolling);
},
create: function () {
//Configure the plugin
this.game.kineticScrolling.configure({
kineticMovement: true,
timeConstantScroll: 325, //really mimic iOS
horizontalScroll: true,
verticalScroll: false,
horizontalWheel: true,
verticalWheel: false,
deltaWheel: 40
});
//Starts the plugin
this.game.kineticScrolling.start();
//Changing the world size
this.game.world.setBounds(0, 0, 800, 800);
},
stopScrolling: function () {
//Stop the plugin
this.game.kineticScrolling.stop();
}
});
Also there's a fork for Phaser 3, check the repo from GitHub.
Regards, Nicholls
I used TexturePacker to create a spritesheet with a JSON file, this is my bit of code:
var game = new Phaser.Game(800, 600, Phaser.AUTO, '', {
preload: preload,
create: create,
update: update
});
function preload() {
game.load.atlas('seyan_f_torch', '../sprite_hashes/seyan_f_torch.png', '../sprite_hashes/seyan_f_torch.json');
};
function create() {
var seyan_f_torch = game.add.sprite(20, 20, 'seyan_f_torch', '00219001.png');
console.log(Phaser.Animation.generateFrameNames('00', 219016, 219023, ''));
seyan_f_torch.animations.add('walk-s', Phaser.Animation.generateFrameNames('00', 219016, 219023, '.png'), 10, true);
// seyan_f_torch.animations.play('walk-s');
};
function update() {
};
Everything works fine and this is the bit of json code:
"00219001.png":
{
"frame": {"x":782,"y":139,"w":37,"h":58},
"rotated": true,
"trimmed": true,
"spriteSourceSize": {"x":39,"y":67,"w":37,"h":58},
"sourceSize": {"w":120,"h":240},
"pivot": {"x":0.5,"y":0.5}
},
But when the image loads it loads like this:
Any information on how to get it to turn the right way would be great thanks.
Edit:
It appears Phaser does not have support for rotated atlas so if you use Texture pack in my case, uncheck rotate images.
https://github.com/photonstorm/phaser/issues/2359#issuecomment-189161540
From the Phaser forum:
// Set anchor to the center of your sprite
sprite.anchor.setTo(.5, .5);
// Invert scale.x to flip left/right
sprite.scale.x *= -1;
// Invert scale.y to flip up/down
sprite.scale.y *= -1;
currently this is causing the (image) fadeout function to end, and then the fade in function fires. i need the images to crossfade and the opacity of each image to overlap. im having trouble getting this work. thoughts?
_initFade: function () {
this._timer = Y.later(this._intervalDuration, this, this._startPeriod, [], false);
},
_startPeriod: function () {
this._timer = Y.later(this._intervalDuration, this, this._fadeOut, [], true);
this._fadeOut();
},
_fadeOut: function(){
var host = this.get('host');
this._animOut.set('node', host._getCurrentBlock());
this._animOut.once('end', this._fadeIn, this);
this._animOut.run();
},
_fadeIn: function(){
var host = this.get('host'),
blocks = host.get('blocks'),
index = host.get('index');
index = host._moveIndex(host._getNextIndex());
var nextBlock = blocks.item(index);
this._transparent(nextBlock);
host.syncUI();
this._animIn.set('node', nextBlock);
this._animIn.run();
},
YUI doesn't support multiple animations which run in sync. But have a look at the 'tween' event of Y.Anim. It is called for every frame of the animation. So you can fade one image using the animation and adjust the opacity of the second image during the tween event.
For example, I use the tween event to animate multiple items simultaneously:
var someNode = Y.Node.create("<div></div>"); // invisible div to animate
Y.one(document.body).appendChild(someNode);
var anim = new Y.Anim({
node: someNode,
duration: 0.25,
from: { color: 'red' },
to: { color: 'white' }
});
anim.on('tween', function(event){
Y.StyleSheet().set('input.text.error', { backgroundColor: someNode.getStyle('color') });
// other animations
});