phaserjs / phaser

Phaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.
https://phaser.io
MIT License
37.24k stars 7.1k forks source link

Phaser doesn't work with Webpack and require #1974

Closed mkristo closed 8 years ago

mkristo commented 9 years ago

Earlier I made an (ugly) pull request https://github.com/photonstorm/phaser/pull/1923 that made Phaser work with Webpack, but since 7a6de818e1c8742f0e4e3eb0ffb3dc80a280a0e5 I'm sorry to say that this doesn't work anymore.

I'm getting

Uncaught ReferenceError: PIXI is not defined

from https://github.com/photonstorm/phaser/blob/6139071ebc918ddd8a1201279ebc7c70e645de8e/src/Phaser.js#L357

For repro you can use the Webpack boilerplate found here https://github.com/photonstorm/phaser/pull/1924

photonstorm commented 9 years ago

Yes the way it was previously bundled was completely wrong - PIXI should never have been inside the Phaser UMD, so it's now properly split out. The grunt custom build task has a new flag --split true which will generate Phaser, Pixi, P2 and Creature to their own stand-alone build files, each with the wrappers they need. So you can now bundle them together however you want. As long as they're allowed to exist globally they will be much happier. PIXI needs to come before Phaser in any requirement chain.

mkristo commented 9 years ago

I understand now. Yes, this makes much more sense.

But when installing Phaser 2.4.2 via npm it seems like PIXI is still bundled with (at least) the no-physics build. I think it would be desirable to provide the split up modules as well.

Also, we should probably update the Webpack boilerplate to require both PIXI and Phaser. I'd be happy to do this, but the PIXI module needs to be available before this is done.

the-simian commented 9 years ago

FYI, I can currently get Phaser to build by doing this:

global.PIXI = require('pixi.js');
global.p2 = require('p2');
global.Phaser = require('phaser');

In this case, PIXI is set to version

"pixi.js": "2.2.x",

I will add, though that this no longer works in 2.4.3. because the CanvasPool mixin is not defined on PIXI. If you require the CanvasPool file in between PIXI and Phaser, of course, it mixies in correctly, and works.

I'm aware that this involves setting some globals, which isn't ideal- but ti does allow you to use npm to serve all the modules.

I'm going to update my slush generator with these changes as soon as 2.4.3 is on master

photonstorm commented 9 years ago

Just to say that I've got a guide to setting up Phaser with Webpack which I'll publish shortly.

I'd just like to add one thing though: don't ever use any version of Pixi that you find in npm with Phaser, as it won't work. You have to use the version we've built specifically or so much will break.

p2 on the other hand should be safe to pull in from npm instead of the build included with Phaser as long as you use the exact same version that Phaser bundles with it, any deviation from that is highly likely to break it.

the-simian commented 9 years ago

I noticed that the mixins you were using with PIXI were located in the utils directory. My question is, could we still use the npm version, as long as we mix in all the addons (like the CanvasPool). Are you actually changing the source code itself, currently - or are you modifying PIXI entirely by adding new properties?

If you're doing the latter, it could be possible to still use the npm PIXI and then just require a file you provide in the source that then mixes in all your modifications to PIXI. Of course, the same rules apply - you have to match the right version. I know PIXI is well into v3, but you are (last I checked) locking pixi on v 2.2.8. This could be useful because that way you'd really only have to maintain the parts that deviate from pixi.js source. So to be clear, I am imagining that you could require pixi with npm, and then require a file that basically requires all the addons in the right order, and just plug all the functionality you need right in. You would have the latitude to update or replace existing methods if you needed to. If I knew more specifically the parts that deviate from PIXI I would be happy submit a PR that basically works this way.

If I'm way off, and this wouldn't work in your opinion, I'd like to know more. Any amount you can elaborate on what's different about this PIXI from the core pixi (of the same version) I'd love to know.

In the meantime, is the most correct file to include from the Phaser source for PIXI currently

src/pixi/Pixi.js

right? You'd still need the CanvasPool and stuff like that afterwards, I would assume.

Thank you for taking time to respond to all these issues, I really appreciate the work you're putting in on Phaser!

photonstorm commented 9 years ago

The PIXI source in the Phaser repo is vastly different to the last published version of Pixi 2. We've added and fixed so much that isn't in the official repo it's not even close any more.

photonstorm commented 9 years ago

Here's how to use Phaser with Webpack:

1) You have to use a custom build of Phaser. You cannot use one of the builds from the dist folder. Here is the grunt custom task I used:

grunt custom --exclude p2,creature,ninja --split true

This will create phaser.js and pixi.js files in the dist folder. Copy those over to your project folder.

2) In Webpack you need to install the script-loader:

npm install script-loader --save-dev

3) In your webpack.config.js flag Phaser and Pixi to use the script loader. There are various ways of doing this, but I used this approach:

module.exports = {

    entry: "./src/entry.js",

    output: {
        path: __dirname,
        filename: "bundle.js"
    },

    module: {
        loaders: [
            { test: /pixi.js/, loader: "script" },
            { test: /phaser.js/, loader: "script" }
        ]
    }

};

4) In my entry.js I just have this:

require('../lib/pixi.js');
require('../lib/phaser.js');
require('./main.js');

main.js contains my test game, which is just a basic Phaser example.

5) Then build it.

Using the split Phaser / PIXI libs and the Webpack script loader I was able to bundle my game up quite happily without any changes to the core Phaser source.

the-simian commented 9 years ago

Thank you for responding so quickly!

I will try this out as soon as possible

lslima91 commented 9 years ago

I'm getting the error Phaser.game is not a function,

photonstorm commented 9 years ago

Check you followed the instructions above exactly as written. If it still doesn't work post your project structure and conf file up.

lslima91 commented 9 years ago

Ok I got it now it works after cleaning my project folder. I didn't bother to check what script loader does..

mkristo commented 9 years ago

I'm happy with the solution provided above.

@photonstorm: Should I close the issue, or do you want it to be open?

photonstorm commented 9 years ago

I'll leave it for now until I've moved the above instructions somewhere more public.

webcaetano commented 9 years ago

I had the same problem days ago. With Webpack + Bower. To be able to use commonJS-style i did this simple config

var webpackConfig = {
    // ...
    externals: {
        "phaser": "Phaser"
    }
    // ...
};

and then

var phaser = require('phaser');

In my opnion. The bower should keep the main like that, but still export all submodules sources. To easly make custom builds from bower overrides.

Or like lodash, exporting allsubmodules in npm. E.g. : https://www.npmjs.com/package/lodash.shuffle

Currently, the only way to do custom build is from grunt

seand88 commented 9 years ago

could we get a more full set of instructions for this in a public place?

amowu commented 8 years ago

I try to write a shim module for ES6 and webpack.. https://github.com/amowu/phaser-shim

kinday commented 8 years ago

Thanks, @amowu! It works!

webcaetano commented 8 years ago

@amowu nice. You guys also may like my phaserjs boilerplate. It's ES6+Gulp+Webpack

Phaser also are called by require(); And stats.js too.

amowu commented 8 years ago

@webcaetano đź‘Ť

dlindenkreuz commented 8 years ago

@photonstorm using script-loader prevents minification because the module is loaded as a string and then evaluated (see https://github.com/webpack/script-loader/issues/1).

i used following webpack config to load pixi with imports-loader, then directly provided as PIXI variable to phaser. files in lib/are built using @photonstorm's method described above https://github.com/photonstorm/phaser/issues/1974#issuecomment-134222165. no probs with minification during build.

{
  resolve: {
    alias: {
      "pixi": path.join(__dirname, "lib/pixi.js"),
      "phaser": path.join(__dirname, "lib/phaser.js")
    }
  },
  module: {
    loaders: [
      {test: /phaser\.js$/, include: path.join(__dirname, 'lib'), loader: 'imports?PIXI=pixi'},
    ]
  }
}

no need to require pixi in my code files:

var Phaser = require("phaser");
// ES6
import Phaser from "phaser"
smr-el commented 8 years ago

None of these are solutions, just leaking bandages. Phaser must evolve accordingly to maintain relevance in an ES6 world. If jQuery can do it, so can Phaser.

hayesmaker commented 8 years ago

^^

tjpalmer commented 8 years ago

Yes, the following should just work: npm install --save phaser followed by import Phaser from "phaser". I've used many other libs this way, and anything else isn't good enough.

crisu83 commented 8 years ago

Thanks @dlindenkreuz, your solution worked perfectly for me.

photonstorm commented 8 years ago

None of these are solutions, just leaking bandages. Phaser must evolve accordingly to maintain relevance in an ES6 world. If jQuery can do it, so can Phaser.

That is what Lazer is for. I have no intention of ever making that change to Phaser, it's too close to end-of-life to be worth the effort.

Given 2.4.5 now has a webpack build option built in, and the README updated to reflect that I'm going to close this issue off now.

pk-nb commented 8 years ago

Taking @dlindenkreuz's approach a different direction—you can also expose the files from node_modules in Phaser 2.4.5+ so you don't have to make a custom build and still get minification. Unfortunately all the files have to be global due to a circular reference (Phaser file pulls PIXI global var, and pixi.js references Phaser from a function). The expose-loader works well here (I could not get the ProvidePlugin to work).

npm install --save phaser expose-loader
var path = require('path');

// Phaser webpack config
var phaserModule = path.join(__dirname, '/node_modules/phaser/');
var phaser = path.join(phaserModule, 'build/custom/phaser-split.js');
var pixi = path.join(phaserModule, 'build/custom/pixi.js');
var p2 = path.join(phaserModule, 'build/custom/p2.js');

module.exports = {
  ...
  module: {
    loaders: [
      { test: /pixi\.js/, loader: 'expose?PIXI' },
      { test: /phaser-split\.js$/, loader: 'expose?Phaser' },
      { test: /p2\.js/, loader: 'expose?p2' },
      ...
    ]
  },
  resolve: {
    alias: {
      'phaser': phaser,
      'pixi': pixi,
      'p2': p2,
    }
  }
};

Then in your entry file:

// commonjs
require('pixi');
require('p2');
require('phaser');

// or es2015
import 'pixi';
import 'p2';
import "phaser";

This is similar to making it an webpack external, but at least webpack can bundle it together and minify it now.

supermrji commented 8 years ago
npm install --save phaser expose-loader
var path = require('path');

// Phaser webpack config
var phaserModule = path.join(__dirname, '/node_modules/phaser/');
var phaser = path.join(phaserModule, 'build/custom/phaser-split.js');
var pixi = path.join(phaserModule, 'build/custom/pixi.js');
var p2 = path.join(phaserModule, 'build/custom/p2.js');
module.exports = {
  ...
  module: {
    loaders: [
      { test: /pixi\.js/, loader: 'expose?PIXI' },
      { test: /phaser-split\.js$/, loader: 'expose?Phaser' },
      { test: /p2\.js/, loader: 'expose?p2' },
      ...
    ]
  },
  resolve: {
    alias: {
      'phaser': phaser,
      'pixi': pixi,
      'p2': p2,
    }
  }
};

// main.js
import 'pixi';
import 'p2';
import "phaser";

Hi there, it makes a alot of comment with author (look at http://imgur.com/a/WamqO mini map yellow comment), does any one know this problem ?

dlindenkreuz commented 8 years ago

@supermrji these comment blocks contain @license and are not removed by uglifyjs. you can pass a config object to webpack.optimize.UglifyJsPlugin with comments: false, this should remove all comments.

donaldpipowitch commented 8 years ago

It would be nice, if phaser could be used without specific configuration for webpack. I often share a generic webpack configuration between multiple projects and it would ease development.

dmassiani commented 8 years ago

I use Phaser from npm and nom I would adding Box2D Plugin but I have many errors from "goog" and goog.global.CLOSURE_UNCOMPILED_DEFINES

var path = require('path')
var webpack = require('webpack')
var BrowserSyncPlugin = require('browser-sync-webpack-plugin')

// Phaser webpack config
var phaserModule = path.join(__dirname, '/node_modules/phaser/')
var phaser = path.join(phaserModule, 'build/custom/phaser-split.js')
var pixi = path.join(phaserModule, 'build/custom/pixi.js')
var p2 = path.join(phaserModule, 'build/custom/p2.js')
var box2D = path.join(__dirname, '/src/libs/box2d-plugin-full.js')

var definePlugin = new webpack.DefinePlugin({
  __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true'))
})

module.exports = {
  entry: {
    app: [
      'babel-polyfill',
      path.resolve(__dirname, 'src/main.js')
    ]
  },
  devtool: 'source-map',
  output: {
    pathinfo: true,
    path: path.resolve(__dirname, 'dist'),
    publicPath: './dist/',
    filename: 'bundle.js'
  },
  watch: true,
  plugins: [
    definePlugin,
    new BrowserSyncPlugin({
      host: process.env.IP || 'localhost',
      port: process.env.PORT || 3000,
      open: false,
      server: {
        baseDir: ['./', './build']
      }
    })
  ],
  module: {
    loaders: [
      { test: /\.js$/, loader: 'babel', include: path.join(__dirname, 'src') },
      { test: /pixi\.js/, loader: 'expose?PIXI' },
      { test: /phaser-split\.js$/, loader: 'expose?Phaser' },
      { test: /p2\.js/, loader: 'expose?p2' }
    ]
  },
  node: {
    fs: 'empty'
  },
  resolve: {
    alias: {
      'phaser': phaser,
      'pixi': pixi,
      'p2': p2,
      'box2D': box2D
    }
  }
}

Can you help me please?

YuraBaev commented 7 years ago
  test: box2d,
  loader: 'imports?this=>window'
ElegantSudo commented 7 years ago

@photonstorm any plans on making phaser more native to ES6? I know you said that you're not planning on doing this, but I'm asking because it's often the case that plans change from year to year. This is a convenience I and the community would really love to have. Thanks in advance, I really appreciate the work you do.

photonstorm commented 7 years ago

@ElegantSudo the issue here (in this thread) isn't to do with not being ES6, it's to do with Phaser being one giant global single var, with no module capability at all, with dependencies to other global based libs. Which makes it a pain to ingest in a module based workflow. Which is why Phaser 3 is fully modular, and itself built with webpack2. The code is still ES5, the module format CJS, but it should make threads like this a thing of the past. ES6 will follow, but first the module based version needs completing. One leads to the other.

ElegantSudo commented 7 years ago

@photonstorm ah, that's what I meant (albeit, didn't communicate correctly). Thanks, and on that note, I'm really looking forward to Phaser 3!

johnborges commented 7 years ago

@pk-nb Thanks! expose-loader solution is what I'll work with for now. Looking forward to Phaser 3.

josfaber commented 7 years ago

I got it working alrite, but the bundle is a whopping 3.9mb. Has anyone managed to create a webpack config and/or 'require scheme' that can separate phaser into chunks for preloading purpose?

photonstorm commented 7 years ago

Not sure if you're talking about Phaser 2 or 3, but it sounds too large in either case unless it's including the source map and comments, in which case it sounds about right. Exclude those and it should be significantly smaller. In Phaser 2 you cannot separate the modules out on their own, in V3 you can.

umeboshi2 commented 7 years ago

@josfaber Yes. I have been able too keep it chunked in a view. I'm using expose loader as mentioned above, and require.ensure: https://github.com/umeboshi2/infidel/blob/2f23c9a978f6bf0c619f6095439c12ee3bc0b1b8/client/applets/phaserdemo/controller.coffee#L9

josfaber commented 7 years ago

Thanks for your replies. I managed to get phaser into a separate bundle, which compresses to 800kb when compiling for production. That will thus be browser-cached inbetween the various pages I'll use it in. I can live with that :)

# webpack.config.js

var path = require("path");
var webpack = require('webpack');

var WebpackUtilsPlugin = require('./plugins/webpackutilsplugin');

// Phaser webpack config
var phaserModule = path.join(__dirname, '/node_modules/phaser-ce/')
var phaser = path.join(phaserModule, 'build/custom/phaser-split.js')
var pixi = path.join(phaserModule, 'build/custom/pixi.js')
var p2 = path.join(phaserModule, 'build/custom/p2.js')

function isExternal(module) {
  var context = module.context;
  if (typeof context !== 'string') {
    return false;
  }
  console.log(context);
  return context.indexOf('node_modules') !== -1;
}

module.exports = {

    entry: {
      "main": "./app/main.js",
      "challenge1": "./app/challenge1/main.js",
      "challenge2": "./app/challenge2/main.js",
    },

    output: {
        path: path.join(__dirname, "web/js"),
        filename: '[name].bundle.js',
        // chunkFilename: '[id].chunk.js'
    },

    plugins: [
      new webpack.optimize.CommonsChunkPlugin({
        name: "vendor",
        filename: 'phaser.bundle.js',
        minChunks: function(module, count) {
          return module.resource && /phaser|pixi|p2/.test(module.resource) && count >= 1
        },
      })
    ],

    module: {
        rules: [
          { test: /pixi\.js/, use: ['expose-loader?PIXI'] },
          { test: /phaser-split\.js$/, use: ['expose-loader?Phaser'] },
          { test: /p2\.js/, use: ['expose-loader?p2'] },
          {
            'test': /\.js$/,
            'loader': 'babel-loader',
            'exclude': /node_modules/,
            'query': {
              'presets': ['env']
            }
          },
        ]
    },

    node: {
      fs: 'empty',
      net: 'empty',
      tls: 'empty'
    },

    resolve: {
      alias: {
        'phaser': phaser,
        'pixi': pixi,
        'p2': p2
      }
    },

    watch: true
};