tomek7 Posted June 23, 2016 Share Posted June 23, 2016 Hi All, I am trying with my friend to develop our first game using Phraser. We decided to have it in an Isometric view, after creating first simple map we tested it on PC and game was running smoothly however when we tried to run it on a mobile device (LG G2) FPS dropped down dramatically to less then 1 frame per second, to run it on mobile we tried: 1) cordova 2) intel xdk 3) cocoon 4) cordova+crosswalk Intel xdk was the best but still far from acceptable level. The question is how to build or what to change in our code started in phaser.js to make it work? Here is our code. Any help will be highly appreciated! var width = window.innerWidth; var height = window.innerHeight; var obstacleGroup, player; var marker, itemGroup; var floorGroup; var exitMarker; var grassGroup; var check; var controls; var cN, cS, cE, cW, cSE, cNE, cSW, cNW; var way; var water = []; //Initialize function var init = function () { var game = new Phaser.Game(width, height, Phaser.AUTO, 'test', null, false, true); var keyboard = new Phaser.Keyboard(game); var cursors; var BasicGame = function (game) { }; BasicGame.Boot = function (game) { }; BasicGame.Boot.prototype = { preload: function () { game.load.image('tree', 'images/tiles/tree.png'); game.load.image('trees', 'images/tiles/trees.png'); game.load.image('tree2', 'images/tiles/tree2.png'); game.load.image('gold', 'images/tiles/find1_gold.png'); game.load.image('exit', 'images/tiles/exit.png'); game.load.image('house', 'images/tiles/house.png'); game.load.image('E', 'images/controls/E.png'); game.load.image('N', 'images/controls/N.png'); game.load.image('NE', 'images/controls/NE.png'); game.load.image('NW', 'images/controls/NW.png'); game.load.image('S', 'images/controls/S.png'); game.load.image('SE', 'images/controls/SE.png'); game.load.image('SW', 'images/controls/SW.png'); game.load.image('W', 'images/controls/W.png'); game.load.spritesheet('terrain','images/tiles/terrain.png', 64, 64); game.load.spritesheet('characterAnim','images/tiles/vehicle.png', 128, 128); game.time.advancedTiming = true; // Add the Isometric plug-in to Phaser game.plugins.add(new Phaser.Plugin.Isometric(game)); // Set the world size game.world.setBounds(0, 0, 2048, 1024); // Start the physical system game.physics.startSystem(Phaser.Plugin.Isometric.ISOARCADE); // set the middle of the world in the middle of the screen game.iso.anchor.setTo(0.5, 0); }, create: function () { // set the Background color of our game game.stage.backgroundColor = "0x000000"; // create groups for different tiles floorGroup = game.add.group(); itemGroup = game.add.group(); grassGroup = game.add.group(); obstacleGroup = game.add.group(); // set the gravity in our game game.physics.isoArcade.gravity.setTo(0, 0, -500); var renderedPositions = new Array(32); for (var i = 0; i < renderedPositions.length; i++) { renderedPositions[i] = new Array(32); } for (var i = 0; i < 32; i++) { renderedPositions[31][i] = game.add.isoSprite(31*32, i*32, 0, 'terrain', 18, obstacleGroup); renderedPositions[31][i].anchor.set(0.5); game.physics.isoArcade.enable(renderedPositions[31][i]); renderedPositions[31][i].body.collideWorldBounds = true; renderedPositions[31][i].body.immovable = true; renderedPositions[30][i] = game.add.isoSprite(30*32, i*32, 0, 'terrain', 18, obstacleGroup); renderedPositions[30][i].anchor.set(0.5); game.physics.isoArcade.enable(renderedPositions[30][i]); renderedPositions[30][i].body.collideWorldBounds = true; renderedPositions[30][i].body.immovable = true; water.push(renderedPositions[30][i]); renderedPositions[29][i] = game.add.isoSprite(29*32, i*32, 0, 'terrain', 18, obstacleGroup); renderedPositions[29][i].anchor.set(0.5); game.physics.isoArcade.enable(renderedPositions[29][i]); renderedPositions[29][i].body.collideWorldBounds = true; renderedPositions[29][i].body.immovable = true; water.push(renderedPositions[29][i]); renderedPositions[28][i] = game.add.isoSprite(28*32, i*32, 0, 'terrain', 8, obstacleGroup); renderedPositions[28][i].anchor.set(0.5); game.physics.isoArcade.enable(renderedPositions[28][i]); renderedPositions[28][i].body.collideWorldBounds = true; renderedPositions[28][i].body.immovable = true; } // create the floor tiles var grasses = [2,7,12,17,22]; for (var xt = 0; xt < 32; xt++) { for (var yt = 0; yt < 32; yt++) { if (!renderedPositions[xt][yt]) { renderedPositions[xt][yt] = game.add.isoSprite(xt*32, yt*32, 0, 'terrain', grasses[Math.floor(Math.random() * 5)], floorGroup); renderedPositions[xt][yt].anchor.set(0.5); } } } // create a house object which will be our ending point in the game var house = game.add.isoSprite(300, 100, 0, 'house', 0, obstacleGroup); house.anchor.set(0.5); game.physics.isoArcade.enable(house); house.body.collideWorldBounds = true; house.body.immovable = true; // create collectible items marker = game.add.isoSprite(rndNum(300), rndNum(300), 0, 'gold', 0, itemGroup); game.physics.isoArcade.enable(marker); marker.body.collideWorldBounds = true; marker.anchor.set(1.0); // create the exit marker next to the house object exitMarker = game.add.isoSprite(400, 400, 0, 'exit', 0, itemGroup); game.physics.isoArcade.enable(exitMarker); exitMarker.body.collideWorldBounds = true; exitMarker.anchor.set(0.5); exitMarker.alpha = 0.5; // create control button sprites on the screen cNW = game.add.sprite(0, 100, 'NW'); cNW.fixedToCamera = true; cNW.inputEnabled = true; cNW.events.onInputDown.add(onDown, this); cNW.events.onInputOver.add(onDown, this); cNW.events.onInputUp.add(onUp, this); cNW.events.onInputOut.add(onUp, this); cW = game.add.sprite(0, 176, 'W'); cW.fixedToCamera = true; cW.inputEnabled = true; cW.events.onInputDown.add(onDown, this); cW.events.onInputOver.add(onDown, this); cW.events.onInputUp.add(onUp, this); cW.events.onInputOut.add(onUp, this); cSW = game.add.sprite(0, 252, 'SW'); cSW.fixedToCamera = true; cSW.inputEnabled = true; cSW.events.onInputDown.add(onDown, this); cSW.events.onInputOver.add(onDown, this); cSW.events.onInputUp.add(onUp, this); cSW.events.onInputOut.add(onUp, this); cN = game.add.sprite(76, 100, 'N'); cN.fixedToCamera = true; cN.inputEnabled = true; cN.events.onInputDown.add(onDown, this); cN.events.onInputOver.add(onDown, this); cN.events.onInputUp.add(onUp, this); cN.events.onInputOut.add(onUp, this); cS = game.add.sprite(76, 252, 'S'); cS.fixedToCamera = true; cS.inputEnabled = true; cS.events.onInputDown.add(onDown, this); cS.events.onInputOver.add(onDown, this); cS.events.onInputUp.add(onUp, this); cS.events.onInputOut.add(onUp, this); cNE = game.add.sprite(152, 100, 'NE'); cNE.fixedToCamera = true; cNE.inputEnabled = true; cNE.events.onInputDown.add(onDown, this); cNE.events.onInputOver.add(onDown, this); cNE.events.onInputUp.add(onUp, this); cNE.events.onInputOut.add(onUp, this); cE = game.add.sprite(152, 176, 'E'); cE.fixedToCamera = true; cE.inputEnabled = true; cE.events.onInputDown.add(onDown, this); cE.events.onInputOver.add(onDown, this); cE.events.onInputUp.add(onUp, this); cE.events.onInputOut.add(onUp, this); cSE = game.add.sprite(152, 252, 'SE'); cSE.fixedToCamera = true; cSE.inputEnabled = true; cSE.events.onInputDown.add(onDown, this); cSE.events.onInputOver.add(onDown, this); cSE.events.onInputUp.add(onUp, this); cSE.events.onInputOut.add(onUp, this); // create control functions for the control buttons function onDown(sprite, pointer) { switch(sprite.key) { case "N": case "S": case "SE": case "SW": case "NW": case "NE": case "E": case "W": way = sprite.key; } } function onUp(sprite, pointer) { way = undefined; } controls = game.add.group(); controls.add(cN); controls.add(cS); controls.add(cW); controls.add(cE); controls.add(cNE); controls.add(cNW); controls.add(cSE); controls.add(cSW); controls.alpha = 0.9; // Creste the player player = game.add.isoSprite(25*32, 3*32, 0, 'characterAnim', 0, obstacleGroup); player.alpha = 1.0; // add the animations from the spritesheet player.animations.add('S', [0], 10, true); player.animations.add('SW', [1], 10, true); player.animations.add('W', [2], 10, true); player.animations.add('NW', [3], 10, true); player.animations.add('N', [4], 10, true); player.animations.add('NE', [5], 10, true); player.animations.add('E', [6], 10, true); player.animations.add('SE', [7], 10, true); player.anchor.set(0.5); // enable physics on the player game.physics.isoArcade.enable(player); player.body.collideWorldBounds = true; game.time.advancedTiming = true; game.debug.text(game.time.fps, 0, 0, 'red'); game.camera.follow(player); }, update: function () { // Move the player var speed = 100; if (way === 'SE')// || (cursors.down.isDown && cursors.right.isDown)) { player.body.velocity.x = speed; player.body.velocity.y = 0; player.animations.play('SE'); } else if (way === 'SW')// || (cursors.down.isDown && cursors.left.isDown)) { player.body.velocity.y = speed; player.body.velocity.x = 0; player.animations.play('SW'); } else if (way === 'NW')// || (cursors.up.isDown && cursors.left.isDown)) { player.body.velocity.x = -speed; player.body.velocity.y = 0; player.animations.play('NW'); } else if (way === 'NE')// || (cursors.up.isDown && cursors.right.isDown)) { player.body.velocity.y = -speed; player.body.velocity.x = 0; player.animations.play('NE'); } else if (way === 'N')// || cursors.up.isDown) { player.body.velocity.y = -speed; player.body.velocity.x = -speed; player.animations.play('N'); } else if (way === 'S')// || cursors.down.isDown) { player.body.velocity.y = speed; player.body.velocity.x = speed; player.animations.play('S'); } else if (way === 'E')// || cursors.right.isDown) { player.body.velocity.x = speed; player.body.velocity.y = -speed; player.animations.play('E'); } else if (way === 'W')// || cursors.left.isDown) { player.body.velocity.x = -speed; player.body.velocity.y = speed; player.animations.play('W'); } else { player.body.velocity.x = 0; player.body.velocity.y = 0; player.animations.stop(); } game.physics.isoArcade.collide(obstacleGroup); game.physics.isoArcade.overlap(marker, player ,function(e){ e.destroy(); }); check = game.physics.isoArcade.overlap(exitMarker, player ,function(e){ }); game.iso.topologicalSort(obstacleGroup); }, render: function () { } }; game.state.add('Boot', BasicGame.Boot); game.state.start('Boot'); // generate random number function rndNum(num) { return Math.round(Math.random() * num); } }; // window.onload can work without <body onload=""> window.onload = init; main.js Link to comment Share on other sites More sharing options...
DevJ Posted June 30, 2016 Share Posted June 30, 2016 Set it to Phaser.CANVAS instead of Phaser.AUTO in the following line and see if it makes any difference: var game = new Phaser.Game(width, height, Phaser.CANVAS, 'test', null, false, true); VitaZheltyakov 1 Link to comment Share on other sites More sharing options...
VitaZheltyakov Posted June 30, 2016 Share Posted June 30, 2016 Do as described in the commentary above. And turn off the GPU-backlist in Crosswalk Link to comment Share on other sites More sharing options...
tomek7 Posted July 8, 2016 Author Share Posted July 8, 2016 hi guys, thank you for your advice. We tried both, turning off the GPU-backlist in Crosswalk as well as Phaser.CANVAS but it did not help. I will appreaciate any other suggestions as we can`t move forward without solving this issue ;/ Link to comment Share on other sites More sharing options...
totor Posted July 8, 2016 Share Posted July 8, 2016 how does your game behaves in html5 only with the G2? how large are the images, sounds etc. some phones like phaser.WEBGL better than phaser.CANVAS. How works the samples from phaser.io ? tomek7 1 Link to comment Share on other sites More sharing options...
nicof Posted July 9, 2016 Share Posted July 9, 2016 There is maybe some tips, like removing the render function completely. Using spritesheet and Cocoon in OpenGl (removing the game.debug.text and using the dev app debugger instead) You should not make the way variable mutate to undefined type (the javascript just in time compiler won't like that), setting it to empty string would be better '' The iso plugin maybe really expensive on mobile. But you should remotely CPU profile your game with chrome for mobile, you'll see where are the bottleneck, it's also important to look at the timeline to check memory management. tomek7 1 Link to comment Share on other sites More sharing options...
tomek7 Posted July 27, 2016 Author Share Posted July 27, 2016 Images are not that big, we only have simple map (generated from a tilset) and couple simple graphics to go with it so it shouldn`t be a problem for the engine to display it smoothly on LG G2 phone. We also tried to correct the code as per your advise but it didn`t help. Below I attached the whole game, perhaps we are missing out on something very obvious. Can someone please have a look? We are desperate to get this game running.... Link to comment Share on other sites More sharing options...
mcolman Posted July 27, 2016 Share Posted July 27, 2016 The game looks like it's running fine after inspecting in chrome dev tools, I guess the phone is really struggling. Few things I noticed. Using debug.text is REALLY slow on webgl. Instead use a normal text object for your debugging. Most of your images non POT, so webgl needs to convert them before uploading them to the gpu. Make sure your images are powers of 2. But tbh, I really would have expected the game to run fine with the CANVAS renderer. Have you tried viewing the Phaser demos on that phone? Link to comment Share on other sites More sharing options...
nicof Posted July 27, 2016 Share Posted July 27, 2016 I just wonder what do you do with var keyboard = new Phaser.Keyboard(game); on a mobile ? Link to comment Share on other sites More sharing options...
tomek7 Posted July 29, 2016 Author Share Posted July 29, 2016 Hi Nicof, game is being developed on different platforms including PC, this is why this line of code has been inserted. If this affects the performance on mobile we will remove it. Mcolman, thank you for your time, I appreciate every advise. We will try to run demo on mobile this week so I will let you know. 1. I am not sure what you mean by saying images non POT? Wat POT stands for? 2. By saying the power of 2 did you mean to create tilesets with even numbers? 3. Is this game running well on your mobile? Link to comment Share on other sites More sharing options...
nicof Posted July 30, 2016 Share Posted July 30, 2016 1. POT = Power of Two 2. the image you load should be of the size 2x2, 4x4, 8x8, 16x16, 32x32, 64x64, 128x128, 256x256, 512x512, 1024x1024 etc... (up to 4096x4096 on mobile) Link to comment Share on other sites More sharing options...
tomek7 Posted August 10, 2016 Author Share Posted August 10, 2016 Hi guys, has anyone tried to run it on a mobile? Perhaps low fps is caused by isometric plugin? Mcolman, Nicog, all ground tiles are the POT, map is very simple so far so there must be a bottle neck somwhere ;/ We can`t move forward without solving this problem, please help. Link to comment Share on other sites More sharing options...
samme Posted August 10, 2016 Share Posted August 10, 2016 You should try Chrome's CPU profiler. Here's a desktop profile: Link to comment Share on other sites More sharing options...
Milton Posted August 10, 2016 Share Posted August 10, 2016 And what does this tell you? I think you're better of with a Canvas debugger/inspector. I suspect there are just too many draw calls. No idea how Phaser handles this, but the OP should try getting everything into 1 draw call... Link to comment Share on other sites More sharing options...
Recommended Posts