Sunday, May 31, 2015

Woodventure mahjong connect is finished!

 





 Finally, we finished our new HTML5 game Woodventure mahjong connect. Game was made using Phaser engine.

 Woodventure mahjong connect is clone of popular game with mahjong tiles, where you have to connect matching pairs with way made of maximum three horizontal or vertical lines. Play here.

 Now, we are looking for sponsors. If you are interested, conntact me at my gmail address: tomas[dot]rychnovsky[at]gmail[dot]com

 Game features:
  • 18 levels (= 1 hour and 20 minutes if you do not fail in any level),
  • lot of animals - all animated,
  • nice and calm atmosphere,
  • scales well for different screen resolutions (no black belts on iPhone nor iPad). Allows also fullscreen,
  • ready for easy translation
 






    Mobile Income Report #10 - April 2015






    previous parts
      Mobile Income Report #9 - March 2015
      Mobile Income Report #8 - January and February 2015
      Mobile Income Report #7 - December 2014
      Mobile Income Report #6 - November 2014
      Mobile Income Report #5 - October 2014 
      Mobile Income Report #4 - September 2014 
      Mobile Income Report #3 - August 2014
      Mobile Income Report #2 - July 2014
      Mobile Income Report #1 - June 2014
      Apps page - Portfolio (what are my assets?)


     If you do not want to miss any of my Income Reports you can follow me on Twitter. Just click the button above.

     Under Apps page you can see my portfolio. It is list of the games that are then reported in Income Reports. The portfolio is regularly updated with new information (releases for new platforms, new updates, ...)


    What I did in April


    • in April I was working on our second HTML5 game made with Phaser engine.The game names Woodventure and it is mahjong connect clone. We replaced traditional mahjong tiles with pictures of animals and things you can find in woods. All animals are animated so the play board is "live" during game. We finished the game in the beginning of May and now we are negotiating with sponsors. You can try the game here and below is few pictures:



    •  finally, I continued in learning Unity.



    Report


     Here are April figures for paid apps:


     There is decrease from $24,2 in March to $17,4.



     Shards - the Brickbreaker stays the most profitable game.

     There is also decrease in apps supported with ads. From $101,2 in March to $75,9 in April:



     Total numbers for April are $17,4 + $75,9 = $93,3. It is -25,6% decrease compared to March. If last month was very bad, this one is even worse...


    Next


     I am continuing my journey to $750 target. In March I wrote some new Phaser tutorials and started exploring CocoonJS, that will allow us publish Woodventure on appstores.










    Saturday, May 23, 2015

    Phaser tutorial: DronShooter - simple game in Typescript - Part 3

     





    Previous Phaser tutorials:
    Phaser tutorial: DronShooter - simple game in Typescript - Part 2
    Phaser tutorial: adding 9-patch image support to Phaser
    Phaser tutorial: DronShooter - simple game in Typescript - Part 1
    Phaser tutorial: custom easing functions for tweening and easing functions with parameters
    Phaser tutorial: sprites and custom properties for atlas frames
    Phaser tutorial: manage different screen sizes
    Phaser tutorial: How to wrap bitmap text


     This is third and final part of short tutorial series on creating simple Phaser shooter game with Typescript. In Part 1 we ended with drone sprite hanging in the air above postapocalyptic city. In Part 2 we animated it and made it move.
     In this part we will add cannon, missiles and collisions with drones. To do this we have to check input and employ physics. We will use P2 physics engine.


    Adding cannon


     First adjust your State class with adding some constants and private variables and also delete most of the create() method:

    class State extends Phaser.State {

    private static CANNON_SPEED = 2;
    private static MISSILE_SPEED = 6;

    private _cannon: Phaser.Sprite;
    private _cannonTip: Phaser.Point = new Phaser.Point();

    private _space: Phaser.Key;

    // -------------------------------------------------------------------------
    preload() {
    // background image
    this.game.load.image("BG", "bg.jpg");
    // load sprite images in atlas
    this.game.load.atlas("Atlas", "atlas.png", "atlas.json");
    }

    // -------------------------------------------------------------------------
    create() {
    // background
    this.add.image(0, 0, "BG");

    // set physiscs to P2 physics engin
    this.game.physics.startSystem(Phaser.Physics.P2JS);
    }
    }

     Next add new lines into create() method:

        create() {
    // background
    this.add.image(0, 0, "BG");

    // set physiscs to P2 physics engin
    this.game.physics.startSystem(Phaser.Physics.P2JS);

    // cannon - place it in the bottom center
    this._cannon = this.game.add.sprite(this.world.centerX, this.world.height, "Atlas", "cannon");
    // offset it from position
    this._cannon.anchor.setTo(-0.75, 0.5);
    // make it point straight up
    this._cannon.rotation = -Math.PI / 2;

    // cannon base - place over cannon, so it overlaps it
    var base = this.game.add.sprite(this.world.centerX, this.world.height, "Atlas", "base");
    base.anchor.setTo(0.5, 1);

    }

     The first line creates cannon barrel sprite and places it in the middle bootom. Next we adjust anchor of it as shown on this image:

     New anchor is point around which rotations will be done.
     Next, we are rotating it to point straight up. Rotation is -p/2, which may be confusing at first sight. From school we know, that 90 degrees is p/2. But in Phaser the y coordinate is flipped and y axis has values increasing downwards with 0, 0 in top left corner. Here are some values on unit circle (values in parenthesis are alternative values you can use - it does not matter if you rotate -p/2 or 3p/2). The red arc is showing our future limits for cannon movement from -p/4 to -3p/4:
     With last line we add cannon base sprite. This sprite covers part of the cannon barrel and it is static part of cannon.

     If you now compile and run you should see this construct in the bottom of the screen:



    Moving cannon


     Now, let's move it. Add next few lines to create() method:

            //  Game input
    this.game.input.keyboard.addKey(Phaser.Keyboard.LEFT);
    this.game.input.keyboard.addKey(Phaser.Keyboard.RIGHT);
    this._space = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
    // following keys will not be propagated to browser
    this.game.input.keyboard.addKeyCapture([Phaser.Keyboard.LEFT, Phaser.Keyboard.RIGHT, Phaser.Keyboard.SPACEBAR]);

     With this we say which keys we will use and we also prevent propagating presses of these keys to browser - it will be consumed by our game.

     Beside this we have to add update() method to our State class. This method is called every frame and we will check key presses in it:

        update() {
    // shortcut
    var keyboard: Phaser.Keyboard = this.game.input.keyboard;

    // left and right key
    if (keyboard.isDown(Phaser.Keyboard.LEFT)) {
    // calculate frame independent speed - 45 degrees (PI/4) in 1 second adjusted with cannon speed
    this._cannon.rotation -= this.time.elapsedMS * State.CANNON_SPEED / 1000 * (Math.PI / 4);
    } else if (keyboard.isDown(Phaser.Keyboard.RIGHT)) {
    this._cannon.rotation += this.time.elapsedMS * State.CANNON_SPEED / 1000 * (Math.PI / 4);
    } else if (this._space.justDown) { // fire missile

    console.log("Fire missile");
    }

    // limit cannon rotation to left and right to +/- 45 degrees ... -135 to -45 degrees here
    this._cannon.rotation = Phaser.Math.clamp(this._cannon.rotation, -1.5 * Math.PI / 2, -0.5 * Math.PI / 2);
    }

     We check left and right key and if pressed (isDown()), then we adjust cannon rotation. The speed of rotation is p/4 in sec. by CANNON_SPEED, which is 2. So, cannon is rotation with speed p/2 in second.
     Space key is tested differently. We do not want to know, whether it is down, but whether it was pressed. If so, we will lunch missile. Currently, as we do not have missiles yet, we just lunch message to console.
     In the end we check whether cannon rotation is not beyond its limits. We use clamp method from Phaser.Math class for it. This method helps you to keep value between two limits with single line of code.


    Swarm


     Now we have drone and moving cannon. Next, we will spawn swarm of drones. Our dron class is ready from previous part, so this will should be easy. Add few private variables to State class:

        private _drones: Phaser.Group;
    private _dronesCollisionGroup: Phaser.Physics.P2.CollisionGroup;
    private _missiles: Phaser.Group;
    private _missilesCollisionGroup: Phaser.Physics.P2.CollisionGroup;

     And add next lines to create method:

            // allow inpact events
    this.game.physics.p2.setImpactEvents(true);

    // collision groups for drones
    this._dronesCollisionGroup = this.game.physics.p2.createCollisionGroup();
    // collision groups for missiles
    this._missilesCollisionGroup = this.physics.p2.createCollisionGroup();


    // drones group
    this._drones = this.add.group();
    this._drones.physicsBodyType = Phaser.Physics.P2JS;
    this._drones.enableBody = true;

    // create 8 drones
    this._drones.classType = Dron;
    this._drones.createMultiple(8, "Atlas", "dron1");
    this._drones.forEach(function (aDron: Dron) {
    // setup movements and animations
    aDron.setUp();
    // setup physics
    var body: Phaser.Physics.P2.Body = aDron.body;
    body.setCircle(aDron.width / 2);
    body.kinematic = true; // does not respond to forces
    body.setCollisionGroup(this._dronesCollisionGroup);
    // adds group drones will collide with and callback
    body.collides(this._missilesCollisionGroup, this.hitDron, this);
    //body.debug = true;
    }, this);

     First, we are allowing impact events. Early we will also add missiles and we want to check collisions between them and drones. We will want P2 engine call our callback method when there is collision. If we forget to allow impact events our callback will not be called.

     Next, we define two collision groups. One for drones and second for missiles. This helps us easily check collisions between all drones and all missiles.

     For all the drones we create standard Phaser.Group. Setting physicsBodyType and enableBody on group means that these vales will be set on all sprites in this group. Our group contains 8 drones. We create them with call to createMultiple. As we are not creating standard sprites, but we want to create instances of our Dron class, which derives fro Phaser.Sprite, we first set classType to Dron.
     Once drones are created we take one by one and set them. setUp() method randomizes position and movement. In next lines we create its physics shape - circle fits nice to our drone. We also put it into prepared collision group, so each drone is in it and we can refer them all in once as _dronesCollisionGroup. Finally, we say that this drone can collide with _missiles CollisionGroup, which will group similarly all missiles. Part of this setting is callback method if collision occurs. In our case it is hitDron() method. We add it to our State class:

        private hitDron(aObject1: any, aObject2: any) {
    // explode dron and remove missile - kill it, not destroy
    (<Dron> aObject1.sprite).explode();
    (<Phaser.Sprite> aObject2.sprite).kill();
    }

     Callback method hitDrone() is called by P2 engine with two parameters - first is drone that collided and second is object it collided with. All we do, is let the drone explode and we kill missile. If you recall explode() method in our Drone class, you will remember that after explosion animation is finished, drone is killed too.


    Missiles


     Similarly to drones, we add missiles in create() method:

            // missiles group
    this._missiles = this.add.group();
    this._missiles.physicsBodyType = Phaser.Physics.P2JS;
    this._missiles.enableBody = true;

    // create 10 missiles
    this._missiles.createMultiple(10, "Atlas", "missile");
    this._missiles.forEach(function (aMissile: Phaser.Sprite) {
    aMissile.anchor.setTo(0.5, 0.5);
    // physics
    var body: Phaser.Physics.P2.Body = aMissile.body;
    body.setRectangle(aMissile.width, aMissile.height);
    body.setCollisionGroup(this._missilesCollisionGroup);
    body.collides(this._dronesCollisionGroup);
    // body.debug = true;
    }, this);

     Only differences are: shape is not circle, but rectangle and we do not set collision callback here as it is enough if it is called from drone side once. It is enough to say that missiles can collide with all drones grouped in _dronesCollisionGroup.

     While you still can not fire missiles you can compile and run game. You see swarm of drones in the sky. If you would like to check whether you set collision bodies right, you can uncomment "body.debug = true;" when iterating drones and missiles and add this render() method to your State class:

        render() {
    // uncomment to visual debug, also uncommnet "body.debug = true;" when creating missiles and drones
    this._drones.forEach(function (aDron: Dron) {
    this.game.debug.body(aDron);
    }, this);

    this._missiles.forEach(function (aMissile: Phaser.Sprite) {
    this.game.debug.body(aMissile);
    }, this);
    }

     With these color debug circles all deadly drones now looks like jolly carnival balloons:


     And here is last and final piece - launching missiles. Go into update() method and replace console log with this code:

                // get firtst missile from pool
    var missile: Phaser.Sprite = this._missiles.getFirstExists(false);

    if (missile) {
    // calculate position of cannon tip. Put distance from cannon base along x axis and rotate it to cannon angle
    this._cannonTip.setTo(this._cannon.width * 2, 0);
    this._cannonTip.rotate(0, 0, this._cannon.rotation);

    missile.reset(this._cannon.x + this._cannonTip.x, this._cannon.y + this._cannonTip.y);
    (<Phaser.Physics.P2.Body> missile.body).rotation = this._cannon.rotation;
    // life of missile in millis
    missile.lifespan = 1500;
    // set velocity of missile in direction of cannon barrel
    (<Phaser.Physics.P2.Body> missile.body).velocity.x = this._cannonTip.x * State.MISSILE_SPEED;
    (<Phaser.Physics.P2.Body> missile.body).velocity.y = this._cannonTip.y * State.MISSILE_SPEED;
    }

     In first line we ask for free missile in group. It works like pool. When we created missiles, all of them were set exists = false. Now, we want to find first that does not exist, revive it and after it is killed (or its time expires), it falls into exists = false again.

     We check, whether we got any free missile. If yes, we calculate position on the tip of cannon barrel and set with reset() method position of missile. Calling reset sets exists = true. Rotation of the missile is the same as rotation of cannon. Lifetime of missile is limited to 1.5 second. If this time is expired, missile falls automatically into exists = false and is ready for reuse.


    Conclusion


     If you compile and run now, you can move your cannon left to right with arrow keys. With space bar you can launch missiles and shoot drones.
     During tutorial we created very simple game in less than 240 lines - see full listing bellow or download whole project.
     While it is simple, it creates our Dron class derived from Phaser.Sprite, we use animations and custom tweening function, we handle input and also use P2 physics.

     Here is our final game in action:



    Complete code:

    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    class Game extends Phaser.Game {
    // -------------------------------------------------------------------------
    constructor() {
    // init game
    super(640, 400, Phaser.CANVAS, "content", State);
    }
    }

    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    class State extends Phaser.State {

    private static CANNON_SPEED = 2;
    private static MISSILE_SPEED = 6;

    private _cannon: Phaser.Sprite;
    private _cannonTip: Phaser.Point = new Phaser.Point();

    private _space: Phaser.Key;

    private _drones: Phaser.Group;
    private _dronesCollisionGroup: Phaser.Physics.P2.CollisionGroup;
    private _missiles: Phaser.Group;
    private _missilesCollisionGroup: Phaser.Physics.P2.CollisionGroup;

    // -------------------------------------------------------------------------
    preload() {
    // background image
    this.game.load.image("BG", "bg.jpg");
    // load sprite images in atlas
    this.game.load.atlas("Atlas", "atlas.png", "atlas.json");
    }

    // -------------------------------------------------------------------------
    create() {
    // background
    this.add.image(0, 0, "BG");

    // set physiscs to P2 physics engin
    this.game.physics.startSystem(Phaser.Physics.P2JS);

    // cannon - place it in the bottom center
    this._cannon = this.game.add.sprite(this.world.centerX, this.world.height, "Atlas", "cannon");
    // offset it from position
    this._cannon.anchor.setTo(-0.75, 0.5);
    // make it point straight up
    this._cannon.rotation = -Math.PI / 2;

    // cannon base - place over cannon, so it overlaps it
    var base = this.game.add.sprite(this.world.centerX, this.world.height, "Atlas", "base");
    base.anchor.setTo(0.5, 1);


    // Game input
    this.game.input.keyboard.addKey(Phaser.Keyboard.LEFT);
    this.game.input.keyboard.addKey(Phaser.Keyboard.RIGHT);
    this._space = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
    // following keys will not be propagated to browser
    this.game.input.keyboard.addKeyCapture([Phaser.Keyboard.LEFT, Phaser.Keyboard.RIGHT, Phaser.Keyboard.SPACEBAR]);


    // allow inpact events
    this.game.physics.p2.setImpactEvents(true);

    // collision groups for drones
    this._dronesCollisionGroup = this.game.physics.p2.createCollisionGroup();
    // collision groups for missiles
    this._missilesCollisionGroup = this.physics.p2.createCollisionGroup();


    // drones group
    this._drones = this.add.group();
    this._drones.physicsBodyType = Phaser.Physics.P2JS;
    this._drones.enableBody = true;

    // create 8 drones
    this._drones.classType = Dron;
    this._drones.createMultiple(8, "Atlas", "dron1");
    this._drones.forEach(function (aDron: Dron) {
    // setup movements and animations
    aDron.setUp();
    // setup physics
    var body: Phaser.Physics.P2.Body = aDron.body;
    body.setCircle(aDron.width / 2);
    body.kinematic = true; // does not respond to forces
    body.setCollisionGroup(this._dronesCollisionGroup);
    // adds group drones will collide with and callback
    body.collides(this._missilesCollisionGroup, this.hitDron, this);
    //body.debug = true;
    }, this);

    // missiles group
    this._missiles = this.add.group();
    this._missiles.physicsBodyType = Phaser.Physics.P2JS;
    this._missiles.enableBody = true;

    // create 10 missiles
    this._missiles.createMultiple(10, "Atlas", "missile");
    this._missiles.forEach(function (aMissile: Phaser.Sprite) {
    aMissile.anchor.setTo(0.5, 0.5);
    // physics
    var body: Phaser.Physics.P2.Body = aMissile.body;
    body.setRectangle(aMissile.width, aMissile.height);
    body.setCollisionGroup(this._missilesCollisionGroup);
    body.collides(this._dronesCollisionGroup);
    // body.debug = true;
    }, this);
    }

    // -------------------------------------------------------------------------
    update() {
    // shortcut
    var keyboard: Phaser.Keyboard = this.game.input.keyboard;

    // left and right key
    if (keyboard.isDown(Phaser.Keyboard.LEFT)) {
    // calculate frame independent speed - 45 degrees (PI/4) in 1 second adjusted with cannon speed
    this._cannon.rotation -= this.time.elapsedMS * State.CANNON_SPEED / 1000 * (Math.PI / 4);
    } else if (keyboard.isDown(Phaser.Keyboard.RIGHT)) {
    this._cannon.rotation += this.time.elapsedMS * State.CANNON_SPEED / 1000 * (Math.PI / 4);
    } else if (this._space.justDown) { // fire missile
    // get firtst missile from pool
    var missile: Phaser.Sprite = this._missiles.getFirstExists(false);

    if (missile) {
    // calculate position of cannon tip. Put distance from cannon base along x axis and rotate it to cannon angle
    this._cannonTip.setTo(this._cannon.width * 2, 0);
    this._cannonTip.rotate(0, 0, this._cannon.rotation);

    missile.reset(this._cannon.x + this._cannonTip.x, this._cannon.y + this._cannonTip.y);
    (<Phaser.Physics.P2.Body> missile.body).rotation = this._cannon.rotation;
    // life of missile in millis
    missile.lifespan = 1500;
    // set velocity of missile in direction of cannon barrel
    (<Phaser.Physics.P2.Body> missile.body).velocity.x = this._cannonTip.x * State.MISSILE_SPEED;
    (<Phaser.Physics.P2.Body> missile.body).velocity.y = this._cannonTip.y * State.MISSILE_SPEED;
    }
    }

    // limit cannon rotation to left and right to +/- 45 degrees ... -135 to -45 degrees here
    this._cannon.rotation = Phaser.Math.clamp(this._cannon.rotation, -1.5 * Math.PI / 2, -0.5 * Math.PI / 2);
    }

    // -------------------------------------------------------------------------
    render() {
    /*
    // uncomment to visual debug, also uncommnet "body.debug = true;" when creating missiles and drones
    this._drones.forEach(function (aDron: Dron) {
    this.game.debug.body(aDron);
    }, this);

    this._missiles.forEach(function (aMissile: Phaser.Sprite) {
    this.game.debug.body(aMissile);
    }, this);
    */
    }

    // -------------------------------------------------------------------------
    private hitDron(aObject1: any, aObject2: any) {
    // explode dron and remove missile - kill it, not destroy
    (<Dron> aObject1.sprite).explode();
    (<Phaser.Sprite> aObject2.sprite).kill();
    }
    }

    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    class Dron extends Phaser.Sprite {

    // -------------------------------------------------------------------------
    public setUp() {
    this.anchor.setTo(0.5, 0.5);

    // random position
    this.reset(this.game.rnd.between(40, 600), this.game.rnd.between(60, 150));

    // random movement range
    var range: number = this.game.rnd.between(60, 120);
    // random duration of complete move
    var duration: number = this.game.rnd.between(30000, 50000);
    // random parameters for wiggle easing function
    var xPeriod1: number = this.game.rnd.between(2, 13);
    var xPeriod2: number = this.game.rnd.between(2, 13);
    var yPeriod1: number = this.game.rnd.between(2, 13);
    var yPeriod2: number = this.game.rnd.between(2, 13);

    // set tweens for horizontal and vertical movement
    var xTween = this.game.add.tween(this.body)
    xTween.to({ x: this.position.x + range }, duration, function (aProgress: number) {
    return wiggle(aProgress, xPeriod1, xPeriod2);
    }, true, 0, -1);

    var yTween = this.game.add.tween(this.body)
    yTween.to({ y: this.position.y + range }, duration, function (aProgress: number) {
    return wiggle(aProgress, yPeriod1, yPeriod2);
    }, true, 0, -1);

    // define animations
    this.animations.add("anim", ["dron1", "dron2"], this.game.rnd.between(2, 5), true);
    this.animations.add("explosion", Phaser.Animation.generateFrameNames("explosion", 1, 6, "", 3));

    // play first animation as default
    this.play("anim");
    }

    // -------------------------------------------------------------------------
    public explode() {
    // remove movement tweens
    this.game.tweens.removeFrom(this.body);
    // explode dron and kill it on complete
    this.play("explosion", 8, false, true);
    }
    }

    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    function wiggle(aProgress: number, aPeriod1: number, aPeriod2: number): number {
    var current1: number = aProgress * Math.PI * 2 * aPeriod1;
    var current2: number = aProgress * Math.PI * 2 * aPeriod2;

    return Math.sin(current1) * Math.cos(current2);
    }

    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------
    window.onload = () => {
    new Game();
    };