nkholski

Exploding sprites

Recommended Posts

I just finished an effect to make enemies in my game explode into pieces when getting killed. What it does is to create an emitter at the coordinates of the enemy bursting subparts extracted from the sprite. I think the result was kind of cool so I spent some extra time cleaning up the code a bit so I can contribute with something (after being given so much from Phaser and the forums). 

 

Edit: Basic online demo

 

metroidExplode.png

"Before and after"

 

After the preloader finishes it allows you to define explosions (entityExplode.setup) based on sprites in a SpriteAtlas (I'm using TexturePacker). The entityExplode.setup will add references to subparts of a selected subsprite but not add or alter any of the imagedata.

 

When defining a new state you need to call entityExplode.init in Create.

 

If the setup was correctly done you will be able to explode a sprite with entityExplode.play (you need to remove the sprite yourself after calling play if you don't want both the sprite and the explosion to show).

 

If you call entityExplode.bounceAndFade in your state's update function the pieces will bounce on a tilemap layer and fade away before being removed.

 

Any suggestion on how to improve this is appreciated. You are free to use and modify the code any way you want.

 

Notes:

* A lot of hard coded values fits my game well, but is probably not the best fit for everyone (such as size of the parts, speed of the emitter or number of parts to send out).

* I haven't checked out the Atlas object-stuff enough to know what everything does, I'm just cloning the original frame and changes the things that makes this work (including duplicating UUID). 

* There is for sure things that could improve the emitter. I would love to find a way to get the pieces to stop sliding on the ground and I would prefer that the stopped their rotation once on ground.

entityExplode = {    init: function (that) { // Called from Create for any state that will use entityExplode        that.explosionEmitters = game.add.group();    },        setUp: function (explodeName, frameName, atlasName, fragmentSize) { // Can be called anytime after Preloader finishes (at least after the atlas json is loaded)        // Prepare a possible explosion so that can be called by the play method later.        // explodeName = name of the explosion to create        // frameName = frame name in atlas to build from (I only define one explosion per entity since the sprite get distorted and it doesn't matter much if the entity was jumping or walking or something else when exploding.)         // atlasName = name of atlas to use        // fragmentSize = size of fragment in pixels (default = 8)        var sourceFrame = false;        var xcount, ycount, temp;        var frameNames = [];        if (!("explosionFrames" in this)) {            this.explosionFrames = [];        }        if (explodeName in this.explosionFrames) {            return; // No double-trouble        }        if (frameName in game.cache._images[atlasName].frameData._frameNames) {            sourceFrame = game.cache._images[atlasName].frameData._frames[game.cache._images[atlasName].frameData._frameNames[frameName]];        } else {            console.log("ERROR! FrameName not found");            return        }        if (typeof (fragmentSize) === "undefined") {            fragmentSize = 8;        }        xcount = Math.floor(sourceFrame.width / fragmentSize);        ycount = Math.floor(sourceFrame.height / fragmentSize);        temp = JSON.parse(JSON.stringify(sourceFrame)); // Kind of a hacky way to copy an object        for (var x = 0; x < xcount; x++) {            for (var y = 0; y < ycount; y++) {                temp.name = explodeName + "_explode" + (x * ycount + y);                temp.x = sourceFrame.x + x * fragmentSize;                temp.y = sourceFrame.y + y * fragmentSize;                temp.width = ((temp.x + fragmentSize) > (sourceFrame.x + sourceFrame.width)) ? (sourceFrame.x + sourceFrame.width - temp.x) : fragmentSize; // stay within the sprite-size                temp.height = ((temp.y + fragmentSize) > (sourceFrame.y + sourceFrame.height)) ? (sourceFrame.y + sourceFrame.height - temp.y) : fragmentSize;                // Add the fragment to the atlas data (this works, but it also just duplicates a lot of values I don't know what they do)                game.cache._images[atlasName].frameData._frameNames[temp.name] = game.cache._images[atlasName].frameData._frames.length;                game.cache._images[atlasName].frameData._frames.push(JSON.parse(JSON.stringify(temp)));                frameNames.push(temp.name);            }        }        this.explosionFrames[explodeName] = {            atlas: atlasName,            frames: frameNames // A list of framenames to select from when doing an explosion!        }        game.explosionFrames = this.explosionFrames;    },            play: function (entity, explodeName, that, hitFrom) { // Called to initate explosion of entity        // entity is the object to explode, typically sprite        // explodeName is a explosion previously defined in setUp        // hitFrom is optional, from right or left (could be replaced by sprite object and then calculate the direction of explosion from velocity of impacting bullet using trigometry but I don't need that)        var explode = game.add.emitter(entity.x, entity.y, 100); // Add the explosion at the entity coordinates        if (!(explodeName in this.explosionFrames)) {            console.log("Error: Nothing to explode!");            return;        }        explode.bounce.setTo(0.5, 0.5);        if (hitFrom === "right") {            explode.setXSpeed(-100, -10);        } else if (hitFrom === "left") {            explode.setXSpeed(10, 100);        } else {            explode.setXSpeed(-50, 50);        }        explode.setYSpeed(-100, -200);        explode.makeParticles(game.explosionFrames[explodeName].atlas, game.explosionFrames[explodeName].frames, 100, 250, 100, true);        explode.particleFriction = 10; // Makes no difference!        explode.start(true, 2000, 1, 20, 20)        game.time.events.add(Phaser.Timer.SECOND * 3, function () {            explode.destroy();        });        that.explosionEmitters.add(explode);    },    bounceAndFade: function (that, tilemapLayer) { // Called from update        for (var i in that.explosionEmitters.children) {            that.physics.arcade.collide(that.explosionEmitters.children[i], tilemapLayer);            that.explosionEmitters.children[i].forEachAlive(function (p) {                p.alpha = p.lifespan / that.explosionEmitters.children[i].lifespan;            });        }    }}

Share this post


Link to post
Share on other sites

A picture paints a 1000 words as they say, a demo paints a 1000 more!

 

That looks pretty good, I can see myself using that somehow.  The advantage of jsfiddle would be that you don't have the burden of maintaing the link.

 

If I were you, I'd put it up on github and assign a license.

Share this post


Link to post
Share on other sites

Yes. I think so, but not without modifying the code quite a lot and I don't think I will add support for it. If it's a sprite sheet I think you could copy the content of game.cache._images[nameofimage] and change frameHeight and frameWidth to a size that can divide the sprite to an even number. If it's just one image I guess you could try to reproduce a spriteAtlas or the method I just suggested and set the url to match whatever the image you want to explode refers to. I'm not sure if any of this will work, and I will stick to my spriteAtlas solution for myself.

Share this post


Link to post
Share on other sites

Thanks. I uploaded the latest version I could find on my hard disk to Git. It's the unmodified version from that one I used in "Robotic Conflict" (http://dev.niklasberg.se/roboticConflict/). It will contain bugs for sure and probably some ugly hacks just to get it to work. This version can blow up both sprites and tiles on a tilemap, and requires Phaser 2.4.x (The code I posted in the first post will not work in Phaser 2.4+ because of changes on how cache works). If there is a demand I might update it and make a proper repository.

Gist: https://gist.github.com/nkholski/533cfbf0272a3c41273c

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.