23
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Canvasでティウンティウンティウン...

Last updated at Posted at 2015-11-09

はじめに

Canvasを利用して、ライフが無くなった時のゲームオーバーエフェクトを表現してみました。SE(音)は著作権的にアヤシイ気がしたので付けていません。エフェクトはシリーズによってもずいぶん違うかと思いますが、何となく雰囲気が伝われば幸いです。

ティウンティウンとは (ニコニコ大百科)

[動作イメージ]
※エフェクト以外はDOMで表現しています
thiwn_thiwn.gif

なお実際の動作は以下で確認することができます
http://codepen.io/nekoneko-wanwan/pen/JYmaJx

特長・仕様

  • Canvasサイズに依存しない
  • 粒の数・大きさ・色は柔軟に変更可能
  • ※少しラクをしたので、調整には直接関数内の修正が必要ですがご容赦ください

ソースコード

index.html
<!-- 動作イメージに出てくる○キャラは省略 -->
<canvas id="myCanvas" width="500" height="300"></canvas>
canvas.js
var cs       = document.getElementById('myCanvas');
var ctx      = cs.getContext('2d');
var csWidth  = cs.width;
var csHeight = cs.height;

var fps = 60;
var frameTime_ms  = 1000 / fps; // 1フレーム辺りのミリ秒
var frameTime_sec = frameTime_ms / 1000; // 秒数に変換

var startTime = new Date().getTime();

/* 経過時間を返す */
var getTimer = function() {
  return (new Date().getTime() - startTime);
};


/* ティウンティウンティウン...コンストラクタ */
var ThiwnThiwn = function() {
  this.initialize();
};

ThiwnThiwn.prototype = {
  initialize: function() {
    this.x  = 0;
    this.y  = 0;
    this.r  = 20;
    this.vx = 0;
    this.vy = 0;
    this.scale = 0;
    this.alpha = 1;
    this.isBorder = true;
    this.time = getTimer();
  },
  update: function() {
    /* 実行時の経過時間を定義 */
    var nowTime = getTimer();

    /* X座標とY座標を更新 */
    this.x += this.vx * frameTime_sec;
    this.y += this.vy * frameTime_sec;

    /* 透明度を下げていく */
    this.alpha -= 0.4 * frameTime_sec;
    if (this.alpha < 0) {
      this.alpha = 0;
    }

    /* 経過時間によってスタイルを変更 */
    if (nowTime - this.time > 0) {
      this.scale += 3 * frameTime_sec;
      this.isBorder = false;
    }
    if (nowTime - this.time > 300) {
      this.isBorder = true;
      this.scale = 0;
      this.time = nowTime;
    }
  },
  draw: function() {
    ctx.save();
    ctx.translate(this.x, this.y);
    ctx.globalAlpha = this.alpha;

    /* 枠線のみ描画 */
    if (this.isBorder) {
      ctx.beginPath();
      ctx.arc(0, 0, this.r + 7, 0, Math.PI * 2, false);
      ctx.closePath();
      ctx.lineWidth = 3;
      ctx.strokeStyle = '#fff';
      ctx.stroke();
      ctx.closePath();
    }

    /* 中心部を描画 */
    ctx.scale(this.scale, this.scale);
    var grad = ctx.createRadialGradient(0, 0, 0, 0, 0, this.r);
    ctx.beginPath();
    grad.addColorStop(0.8, '#fff ');
    grad.addColorStop(1, 'rgba(255,213,0,0.8)');
    ctx.fillStyle = grad;
    ctx.arc(0, 0, this.r, 0, Math.PI * 2, false);
    ctx.fill();

    ctx.restore();
  }
};

/**
 * コンストラクタから複数のインスタンスを作成して配列を返す
 * @param {num} x: エフェクトを発生させる中心のX座標
 * @param {num} y: エフェクトを発生させる中心のY座標
 * @param {num} deg: インスタンスをいくつ作成するかに使用
 *   45を指定すると、360/45 = 8個生成される
 */
var createCircle = function(x, y, deg) {
    var arr  = [];  // 複数のインスタンスを格納
    var sp   = 200; // アニメーション速度に影響
    var _deg = 0;
    var obj; // インスタンスを格納
    for (; _deg <= 360 * 2; _deg += deg) {
        obj = new ThiwnThiwn();

        /* 2週目以降はスピードを落とすことで、二重の輪を表現 */
        if (_deg > 360) {
            sp = 100;
        }

        obj.x  = x;
        obj.y  = y;
        obj.vx = sp * Math.cos(_deg * Math.PI / 180);
        obj.vy = sp * Math.sin(_deg * Math.PI / 180);

        arr[arr.length] = obj;
    }
    return arr;
};

/**
 * レンダリング
 * @param {arr} arr: createCircleにより複数インスタンスを格納した配列を渡す
 */
var render = function(arr) {
    var obj;
    ctx.clearRect(0, 0, csWidth, csHeight);
    for (var i = 0, l = arr.length; i < l; i++) {
        obj = arr[i];
        obj.update();
        obj.draw();
    }
    requestAnimationFrame(function() {
        render(arr);
    });
};

/* 実行 */
var circles = createCircle(csWidth/2, csHeight/2, 45);
setTimeout(function() {
    render(circles);
}, 100);

作り方・考え方

コンストラクタの用意

  • 丸オブジェクトを生成するコンストラクタを用意します
  • インスタンスには更新時に必要な値を持たせます(x, y座標、スケールなど)
  • インスタンスの共通メソッドとして、以下を用意します
    • どのように値を更新していくかのupdate()
    • どのようにCanvasに描画していくかのdraw()
  • update()内では経過時間を見ることで、動作の細かい制御を可能にしています
  • draw()内では、translate()scale()によりアニメーションを制御しています
  • 描画の度に座標軸のズレを初期化するため、save(), restore()で元に戻してます

インスタンスを生成し、インスタンスを実行するメソッドを用意

  • createCircle()メソッドにより、コンストラクタから複数のインスタンスを生成します
  • render()メソッドにより、一定期間ごとに各インスタンスのupdate(), draw()を一度に実行し続けます

なおrequestAnimationFrameを止める処理は考慮していません

残り

  • 最後にcreateCircle()render()を実行する処理を書いてお終いです
  • サンプルの動作イメージでは、自キャラと敵キャラの位置を監視し、重なった(衝突と見なした)ら上記メソッドを実行するようにしています

終わりに

作り方は自分で考えてみたので、書き方が微妙だったり非効率な可能性があります。もっと良い方法があればご教授くださいませ。

今まで作成した謎なシリーズ

Canvasでビックリマンなシールを書いてみた(光るゾ!)
Canvasで斬!っと画面が斬れるようなエフェクトを書いてみた
Canvasで漫画にあるような吹き出しを書いてみた
Canvasで漫画にあるような "ざわ・・・" を書いてみた
Canvasで漫画にあるような集中線を書いてみた

23
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?