Curved laser beams with HTML5 canvas quadraticCurveTo() method

Posted on August 9, 2012 | 4 minute read

In my previous post I talked about making a laser beam using the HTML5 canvas stroke() method. I mentioned the next thing I wanted to do being adding a curve to the beam to make it look more fluid as it moves (and yes, I know lasers do not curve. Mine does. Shut up. It’s a magic laser. Or it may not even be a “laser” when I’m done with it)

Well, the curve now works. There are still lots of tweaks to be made and lots to clean up, but now I have a laser beam that curves as the player moves and then snaps back to the player on the x axis. Craig asked for a video earlier and it’s easier to explain this one with a video anyway, so here’s a quick one showing what it looks like. Everything is a placeholder and I’ve disabled the actual power meters for the lasers for now in order to be able to test it without having to keep building up charge.

There are two types of levels shown here. The first is a placeholder “normal” level and the second is a placeholder “boss” level. All the art stuff is going to change, but this gives you an idea of what the laser beam bend currently looks like. Ignore the “Free Version” watermark, which comes from the screen capture software I used.

[youtube http://www.youtube.com/watch?v=Zz3XpnKrWag]

Performance

Joshua asked about performance. You can see that the frame rate on this isn’t great. This seems to be exclusive to my MacBook Air (and started way before the laser beam was put in). The screen capture software also seemed to slow things down a little. Other laptops and desktops I’ve tested so far run this at 60 fps. Now I just need to find another MBA to check the fps there. I do thrash the crap out of this laptop and a restart does usually help the frame rate, but not completely.

The main code for the laser is already in the previous post. The only thing that really changed in that code is a line in the stroke. Instead of lineTo():

        ig.system.context.lineTo(this.pos.x,this.pos.y);

I’m now using quadraticCurveTo():

ig.system.context.quadraticCurveTo(this.curveX,this.curveY,this.pos.x,this.pos.y);

I also added this to make the tip of the laser rounded instead of square for aesthetic purposes:

ig.system.context.lineCap = "round";

So above you can see that another set of coordinates was added to make the curve - this.curveX and this.curveY.

The laser.js update() has been changed to this. I’m not going to explain it too much as even though this did my head in, the end result I think is pretty self-explanatory when read with my previous post about the laser beam (all of the curve stuff is at the top):

    update: function() {	
        if (ig.game.player.vel.x != 0) {
            if (ig.game.player.vel.x > 0) {
                this.vel.x = ig.game.player.vel.x - 300;
            }

            else if (ig.game.player.vel.x < 0) {
                this.vel.x = ig.game.player.vel.x + 300;
            }

            if (this.curveX < this.pos.x - 10) {
                this.curveX += 5;
            }

            else if (this.curveX > this.pos.x + 10) {
                this.curveX -= 5;
            }
        } 

        else {
            if (this.curveX < this.pos.x) {
                this.curveX += 5;
            }
            else if (this.curveX > this.pos.x) {
                this.curveX -= 5;
            }

            if (this.pos.x < ig.game.player.pos.x + ig.game.player.size.x / 2) {
                this.vel.x = 100;
            }

            else if (this.pos.x > ig.game.player.pos.x + ig.game.player.size.x / 2) {
                this.vel.x = -100;
            }
        }

        this.curveY = this.pos.y + this.size.y / 2;
        this.size.y = ig.game.player.pos.y - this.pos.y;

        // For every frame the laser beam is alive, 
        // it drains power.
        if (this.kind === 'fire') {
            ig.game.laser.incrementLaser(-0.05,0);
            if (ig.game.laser.fireLaser < 1) {
                this.kill();
                console.log('killing beam');
            }
        }

        else if (this.kind === 'water') {
            ig.game.laser.incrementLaser(0,-0.05);
            if (ig.game.laser.waterLaser < 1) {
                this.kill();
                this.laserBeam = null;
            }
        }	

        this.parent();

        // If the laser beam is not touching an enemy entity (a "block"), its vel.y is -500
        if (ig.game.currentLevel === Level1) {
            for (var i = 0; i < ig.game.allBlocks.length; i++) {
                var block = ig.game.allBlocks[i];
                if( !this.touches(block)) {
                    this.vel.y = -500;
                }	
            }	
        }

        else if (ig.game.currentLevel === LevelBoss) {
            for (var i = 0; i < this.allHitareas.length; i++) {
                var hitArea = this.allHitareas[i];
                if( !this.touches(hitArea)) {
                    this.vel.y = -500;
                }	
            }
        }
    },

As you can see right now this is really messy, with crap all over the place. I just got it to a working state and now it’s a matter of tweaking it to make it look exactly as I need and cleaning it up and such. Also keep in mind that the bend in the laser is aesthetic¬†only. The shape of the entity itself is still rectangular, so the collision box does not bend with the laser. The bend is just subtle enough and snaps back quickly enough for this to not really be visible or be a hindrance, I think.




Categories:game dev dev games
comments powered by Disqus