JS:爆破エフェクト

JavaScript

CanvasAPIを使った爆破エフェクトイメージです。

実際にゲームに利用した例

ソースコード

エフェクトはEffect.jsとEffectOpacity.jsの2種類あります。
基本的に同じ作りですが、EffectOpacity.jsはパーティクルがだんだん消えていきます。

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
	<script src="Functions.js" type="text/javascript"></script>
	<script src="Effect.js" type="text/javascript"></script>
	<script src="EffectOpacity.js" type="text/javascript"></script>
	<script src="main.js" type="text/javascript"></script>
	<title>爆破エフェクト</title>
</head>
<body>
	<canvas id="canvas" width="500" height="500"></canvas>
</body>
</html>

style.css

/* style.css */
@charset "utf-8";

*{
    margin: 0;
    padding: 0;
}

function.js

// 指定範囲の乱数を生成する関数(min~max)
function randInt(min, max){
    return Math.floor(Math.random() * (max+1-min)+min);
}

// オブジェクトを消去
function deleteObjects(){
    // エフェクト
    for(let i in effects){
        if(!effects[i].isAlive) effects.splice(i, 1);
    }
}

// 多角形を描画する関数(頂点の数、中心x座標、中心y座標、半径、線幅、傾き、色)
const drawPolygon = function(n, cx, cy, r, lineWidth, tilt, color){
    const p = Math.floor(360 / n)
    let theta = -90 + tilt;    // 角度修正(キャンバスでは3時方向が0度扱いのため12時方向を0度とする)
    let polygon = [];

    while(theta<360-90){   // 全ての頂点を求める
        const pos = {
            x: r * Math.cos(theta*Math.PI/180) + cx,
            y: r * Math.sin(theta*Math.PI/180) + cy,
        };
        polygon.push(pos);
        theta += p;    // 次の点の位置
    }
    //console.log({polygon});

    // 多角形を描画する
    g.strokeStyle = color;
    g.lineWidth = lineWidth;
    g.beginPath();
    for(let i=0; i<polygon.length; i++){
        if(i==0){
            g.moveTo(polygon[i].x, polygon[i].y);
        }
        else{
            g.lineTo(polygon[i].x, polygon[i].y);
        }
    }
    g.closePath();  // パスを閉じる
    g.stroke();
}


Effect.js

/* Effect.js : エフェクトクラス*/
 
class Effect{
    // コンストラクタ(x座標, y座標, 発射角度, 速度, 色)
    constructor(x, y, angle, speed, color){
        this.x = x;         // x座標
        this.y = y;         // y座標
        this.angle = angle; // 発射角度
        this.speed = speed; // 速度
        this.color = color; // 色
        this.isAlive = true;

        // 発射角度と速度からxy方向の速度計算
        this.vx = this.speed * Math.cos(this.angle / 180 * Math.PI);
        this.vy = this.speed * Math.sin(this.angle / 180 * Math.PI);
    }

    // 移動メソッド
    move(){
        this.x += this.vx;
        this.vy += 0.1;
        this.y += this.vy;

        // 画面から消えたら消去
        if(this.y < 0 || this.y > canvas.height || this.x < 0 || this.x > canvas.width){
            this.isAlive = false;
        }
    }
    
    // 描画メソッド
    draw(){
        // □を描画
        drawPolygon(4, this.x, this.y, 3, 1, 0, this.color);
    }

    // 更新処理
    update(){
        this.move();
        this.draw();
    }
}

EffectOpacity.js

/*
    EffectOpacity.js
        だんだん消える
*/
 
class EffectOpacity{
    // コンストラクタ(x座標, y座標, 発射角度, 速度, 色)
    constructor(x, y, angle, speed, color){
        this.x = x;         // x座標
        this.y = y;         // y座標
        this.angle = angle; // 発射角度
        this.speed = speed; // 速度
        this.rgbColor = color; // RGB色
        this.opacity = 1.0;     // 透過度
        this.isAlive = true;

        // 発射角度と速度からxy方向の速度計算
        this.vx = this.speed * Math.cos(this.angle / 180 * Math.PI);
        this.vy = this.speed * Math.sin(this.angle / 180 * Math.PI);
    }

    // 移動メソッド
    move(){
        this.x += this.vx;
        this.vy += 0.1;
        this.y += this.vy;

        this.opacity -= 0.02;
        // 画面から消えたら消去
        if(this.y < 0 || this.y > canvas.height || this.x < 0 || this.x > canvas.width){
            this.isAlive = false;
        }
        else if(this.opacity < 0.0){
            this.isAlive = false;
        }
    }
    
    // 描画メソッド
    draw(){
        // □を描画
        this.color = `rgba(${this.rgbColor[0]}, ${this.rgbColor[1]}, ${this.rgbColor[2]}, ${this.opacity})`; // 色
        drawPolygon(4, this.x, this.y, 3, 1, 0, this.color);
    }

    // 更新処理
    update(){
        this.move();
        this.draw();
    }
}

main.js

// main.js

let canvas = null;
let g = null;
let effects = [];    // エフェクト格納用配列
let frameCount = 0; // フレーム数

// 描画更新処理
function mainLoop(){
    // 一定間隔でエフェクトを生成
    if(frameCount % 60 == 0){
        // エフェクト生成
        for(let angle of [0, 30, 60, 90, 120, 150, 180, -30, -60, -90, -120, -150]){
            // Effect
            const effect = new Effect(100, 100, angle, 3, "#555");
            effects.push(effect);

            // EffectOpacity
            const effectOpacity = new EffectOpacity(400, 100, angle, 3, [50, 50, 50]);
            effects.push(effectOpacity);

        }
    }

    // キャンバスクリア
    g.fillStyle = "#eee";
    g.fillRect(0, 0, canvas.width, canvas.height);
    
    // エフェクト描画を更新
    for(let effect of effects){
        effect.update();
    }

    // オブジェクト消去
    deleteObjects();

    // フレームカウント
    frameCount++;

    // フレーム毎に再帰呼び出し
    requestAnimationFrame(mainLoop);
}

window.addEventListener("load", ()=>{
    // キャンバス取得
    canvas = document.getElementById("canvas");
    g = canvas.getContext("2d");

    frameCount = 0;

    // 描画更新処理を開始
    mainLoop();
});

コメント