englercj / phaser-tiled

A tilemap implementation for phaser focusing on large complex maps built with the Tiled Editor.
MIT License
290 stars 31 forks source link

How can you use this with typescript? #51

Closed JimVliet closed 8 years ago

JimVliet commented 8 years ago

So I was wondering on how to use this with typescript. I am new to web dev, but I have worked with python and java before. Anyway this is my current code:

/// <reference path="lib/phaser.d.ts"/>
/// <reference path="lib/phaser-tiled.d.ts"/>

class RPGame {

    constructor() {
        this.game = new Phaser.Game(800, 600, Phaser.AUTO, 'content', { preload: this.preload, create: this.create });
    }

    game: Phaser.Game;

    preload() {
        this.game.add.plugin(Phaser.Plugin.Tiled);
        this.game.load.pack("test_01", "maps/tilemap-assets.json");
    }

    create() {
        var map = this.game.add.tiledmap("test_01");
    }

}

window.onload = () => {

    var game = new RPGame();

};

This code doesn't work though as you can see here: http://i.imgur.com/xrsR4Rr.png

This is my project folder: http://i.imgur.com/S0y2WBj.png

All the images I need are in the image folder and the tmx file is referering to them correctly.

So from what I read, it seems like you need to generate an asset-pack? and then load the asset pack. So how would you do this?

englercj commented 8 years ago

The definition file probably needs to be updated to extend Phaser.Plugin, I think it extends IStateCycle right now (from a long time ago).

Generating asset packs is not manditory. You can use the plugin just like in the README, without asset packs. Judst use the cacheKey utility to key the items you load.

If you want to generate asset packs though, there is a gulp plugin to help out. I do this because I have a lot of levels with a lot of different resources, so using that plugin I don't have to hard-code the assets.

JimVliet commented 8 years ago

Alright the definition file is updated: http://i.imgur.com/UViYwAx.png

I am still getting errors though: http://i.imgur.com/8wvEyWg.png

It doesn't seem to recognize the methods you added to the load function. Like it doesn't autocomplete etc.

I think it might have to do with typescript, since it seems like you can use the function for normal javascript, but not for typescript.

It looks like this piece of code adds this function:

Tiled.prototype.init = function () {
    Phaser.GameObjectFactory.prototype.tiledmap = GameObjectFactory_tiledmap;
    Phaser.Loader.prototype.tiledmap = Loader_tiledmap;
    Phaser.Loader.prototype.loadFile = Loader_loadFile;
    Phaser.Loader.prototype.jsonLoadComplete = Loader_jsonLoadComplete;
    Phaser.Loader.prototype.xmlLoadComplete = Loader_xmlLoadComplete;
    Phaser.Loader.prototype.processPack = Loader_processPack;

    if (Phaser.Physics.P2) {
        Phaser.Physics.P2.prototype.convertTiledmap = physics.p2.convertTiledmap;
        Phaser.Physics.P2.prototype.convertTiledCollisionObjects = physics.p2.convertTiledCollisionObjects;
    }

    if (Phaser.Physics.Ninja) {
        Phaser.Physics.Ninja.prototype.convertTiledmap = physics.ninja.convertTiledmap;
    }
};

Tiled.prototype.destroy = function () {
    Phaser.Plugin.prototype.destroy.apply(this, arguments);

    Phaser.GameObjectFactory.prototype.tiledmap = originals.gameObjectFactory.tiledmap;
    Phaser.Loader.prototype.tiledmap = originals.loader.tiledmap;
    Phaser.Loader.prototype.loadFile = originals.loader.loadFile;
    Phaser.Loader.prototype.jsonLoadComplete = originals.loader.jsonLoadComplete;
    Phaser.Loader.prototype.xmlLoadComplete = originals.loader.xmlLoadComplete;
    Phaser.Loader.prototype.processPack = originals.loader.processPack;

    if (originals.physics.p2.convertTiledmap) {
        Phaser.Physics.P2.prototype.convertTiledmap = originals.physics.p2.convertTiledmap;
        Phaser.Physics.P2.prototype.convertTiledCollisionObjects = originals.physics.p2.convertTiledCollisionObjects;
    }

    if (originals.physics.ninja.convertTiledmap) {
        Phaser.Physics.Ninja.prototype.convertTiledmap = originals.physics.ninja.convertTiledmap;
    }
};

function GameObjectFactory_tiledmap(key, group) {
    return new Tiled.Tilemap(this.game, key, group);
}

/**
 * Add a new tilemap loading request.
 *
 * @method Phaser.Loader#tilemap
 * @param {string} key - Unique asset key of the tilemap data.
 * @param {string} [url] - The url of the map data file (csv/json)
 * @param {object} [data] - An optional JSON data object. If given then the url is ignored and this JSON
 *      object is used for map data instead.
 * @param {number} [format=Tiled.Tilemap.CSV] - The format of the map data. Either Tiled.Tilemap.CSV
 *      or Tiled.Tilemap.TILED_JSON.
 * @return {Phaser.Loader} This Loader instance.
 */
function Loader_tiledmap(key, url, data, format) {
    if (typeof format === 'undefined') { format = Tiled.Tilemap.CSV; }

    /*eslint-disable no-eq-null, eqeqeq */
    if (url == null && data == null) {
        console.warn('Phaser.Loader.tiledmap - Both url and data are null. One must be set.');

        return this;
    }
    /*eslint-enable no-eq-null, eqeqeq */

    //  A map data object has been given
    if (data) {
        switch (format) {
            //  A csv string or object has been given
            case Tiled.Tilemap.CSV:
                break;

            //  A json string or object has been given
            case Tiled.Tilemap.TILED_JSON:
                if (typeof data === 'string') {
                    data = JSON.parse(data);
                }
                break;

            //  An xml string or document has been given
            case Tiled.Tilemap.TILED_XML:
                if (typeof data === 'string') {
                    data = utils.parseXML(data);
                }
                break;
        }

        this.game.cache.addTilemap(key, null, data, format);
    }
    else {
        this.addToFileList('tiledmap', key, url, { format: format });
    }

    return this;
}
JimVliet commented 8 years ago

It seems to work with normal javascript, just not with typescript.

this.game.add.plugin(Phaser.Plugin.Tiled); This gives the error:

Error:(16, 30) TS2345: Argument of type 'typeof Tiled' is not assignable to parameter of type 'Plugin'. Property 'active' is missing in type 'typeof Tiled'.

So it seems like you need to create an object of the Tiled plugin class.

englercj commented 8 years ago

I am still getting errors though: http://i.imgur.com/8wvEyWg.png

Yup, typescript has no way for me to extend the Phaser classes I add methods to.

https://github.com/Microsoft/TypeScript/issues/9 https://github.com/Microsoft/TypeScript/issues/819

Nothing I can do about it, you will just have to cast to any.

Error:(16, 30) TS2345: Argument of type 'typeof Tiled' is not assignable to parameter of type 'Plugin'. Property 'active' is missing in type 'typeof Tiled'.

After changing the definition to extend from Plugin this error should go away. I'm using it like this without error:

game.add.plugin(new Tiled(game, game.stage));
JimVliet commented 8 years ago

After changing the definition to extend from Plugin this error should go away. I'm using it like this without error:

What do you mean with this?

englercj commented 8 years ago

If you change your definition from Tiled extends Phaser.IStateCycle to Tiled extends Phaser.Plugin the error you posted there should go away. As it did for me.

The typescript definition says that the method takes a Plugin type. Which means an instance, not the class itself. game.add.plugin(Phaser.Plugin.Tiled) is invalid according to the Phaser typescript definition.

JimVliet commented 8 years ago

I have it like this:

/// <reference path="phaser.d.ts" />

declare module Phaser {
    module Plugin {
        class Tiled extends Phaser.Plugin{

            constructor(game: Phaser.Game, parent: PIXI.DisplayObject);

            active: boolean;
            game: Phaser.Game;
            hasPostRender: boolean;
            hasPostUpdate: boolean;
            hasPreUpdate: boolean;
            hasRender: boolean;
            hasUpdate: boolean;
            parent: PIXI.DisplayObject;
            visible: boolean;

            destroy(): void;
            postRender(): void;
            preUpdate(): void;
            render(): void;
            update(): void;
        }

But I still get errors: http://prntscr.com/ai3d87

And here in the console: http://prntscr.com/ai3dsl

Nothing I can do about it, you will just have to cast to any. What am I supposed to do here?

Anyway, sorry for asking so much, but I am new to typescript and js.

englercj commented 8 years ago

game.add isn't initialized int he constructor, you can't use that object until the boot state. That is the first error you get. This is just how the Phaser lifecycle works.

The second error is just because the first line failed. The functions are patched in when plugins are initialized by Phaser, which never happened because you failed to add it.

What am I supposed to do here?

Cast to any like I said, Typescript doesn't have a way for definitions to add methods to classes so you have to bypass typescript:

(<any>this.game.load).tiledmap(...);

(<any>this.game.add).tiledmap(...);
JimVliet commented 8 years ago

Thanks it works now! I needed to fix one last thing though, in the utils class the name should be optional like this:

static cacheKey(key: string, type: string, name?: string): string; Currently the name is mandatory.

JimVliet commented 8 years ago

My code for other people:

/// <reference path="lib/phaser.d.ts"/>
/// <reference path="lib/phaser-tiled.d.ts"/>

import Tiled = Phaser.Plugin.Tiled;
class RPGame {

    constructor() {
        this.game = new Phaser.Game(800, 600, Phaser.AUTO, 'content', { preload: this.preload, create: this.create });
    }

    game: Phaser.Game;

    preload() {
        this.game.add.plugin(new Tiled(this.game, this.game.stage));
        var cacheKey = Phaser.Plugin.Tiled.utils.cacheKey;
        (<any>this.game.load).tiledmap(cacheKey('test_01', 'tiledmap'), 'maps/test_01.json', null, Phaser.Tilemap.TILED_JSON);
        this.game.load.image(cacheKey('test_01', 'tileset', 'Grass shadow'), 'images/tilesets/002-G_Shadow01.png');
        this.game.load.image(cacheKey('test_01', 'tileset', '066-CF_Ground03'), 'images/tilesets/066-CF_Ground03.png');
    }

    create() {
        var map = (<any>this.game.add).tiledmap('test_01');
    }

}

window.onload = () => {

    var game = new RPGame();

};
englercj commented 8 years ago

Please open a PR with the changes you had to make to the typescript defs, so others can take advantage.

JimVliet commented 8 years ago

Made a pull request

englercj commented 8 years ago

Thanks!