isobe_yakiのブログ

ニコ生ゲーム開発者向けの記事を書きます

Canvasを使ってニコ生ゲームを作ろう

キャンバスの詳細は以下から↓ developer.mozilla.org

AkashicEngineでは内部でキャンバスを使っているが、何らかの目論見から隠蔽されており、実際にはかなり制限された描画命令しか使えないようになっている。

そういう厳しい制約の中で工夫してゲームを作るから面白いんじゃないか!という人もいるとは思うがプレイヤーには関係ないしやっぱり表現力が欲しい場面もあるだろう。 そこで、ブラウザの秘められた力を解放すべくキャンバスを直接使ったゲームを作ってみよう。

AkashicEngineの描画について

矩形描画しかサポートしてない!

AkashicEngineは描画バックエンドとして2Dと3D両方使える。game.jsonにあるrenderersという設定項目で指定できるようになっているとのこと。

つまりキャンバスへの描画にはCanvasRenderingContext2DWebGLRenderingContextのどちらかが使われることになる。しかし、MDNのリファレンスを見れば分かると思うがこの2つのAPIは全く互換が無くほとんど共通化ができない。共通化できそうなのはせいぜいライン描画と矩形描画くらいである。そのためAkashicでは矩形描画しかサポートしなかったのではないかと思われる。*1

g.Rendererインタフェース

AkashicEngineで画像を描画したい場合通常はg.Spriteクラスを使うと思うが、それ以外にRendererとSurfaceを使う方法がある。

g.Rendererを取得するにはg.game.renderersから取得するかg.Eを継承してrenderSelfを実装し、引数として受け取る方法がある。

取得したRendererのRenderer.drawImageを呼び出して描画したいSurfaceオブジェクトを渡せばよい。

g.Surfaceを作成するにはg.game.resourceFactory.createSurface(width, height)を使う。

以下は100*100のSurfaceを作成し、キャンバスAPIで模様を描画したものをRendererで表示するサンプルコードである。

const canvas = g.game.resourceFactory.createSurface(100, 100);
const ctx = canvas.context()._context;    // !ここハック
ctx.fillStyle = 'magenta';
ctx.fillRect(0, 0, 100, 100);
ctx.fillStyle = 'yellow';
ctx.beginPath();
ctx.arc(50, 50, 40, 0, Math.PI * 2);
ctx.fill();
// Surfaceを原点に描画するだけのクラス
class Canvas extends g.E {
    constructor(surface, params) {
        super(params);
        this.surface = surface;
    }

    renderSelf(renderer) {
        renderer.drawImage(this.surface, 0, 0, this.surface.width, this.surface.height, 0, 0);
    }
}
new Canvas(canvas, { scene, parent: scene, local: true });

上記コードの結果

ここではg.Eを継承したが拡大回転なども行うならg.Spriteを継承した方が便利なのでそちらもおススメ。その場合はSpriteのコンストラクタに渡すパラメータのsrcにSurfaceインスタンスを指定すればよい。

パネワンでの使用例

1.パネル画像

See the Pen ブロック by z0ero (@z0ero) on CodePen.

パネルの画像は単純なパス描画で立体感のある矩形を描画した後、テキスト描画を利用して図柄を表示している。ただし、テキスト描画は環境ごとに見た目が変わる可能性があるのでほんとはあまり推奨しない。

2.ランクボード

See the Pen ランクボード by z0ero (@z0ero) on CodePen.

成績発表の背景画像もキャンバスで生成している。arcToを駆使して丸角矩形のパスを作成し、createLinearGradientでグラデーション塗りつぶし+線の太さを変えて3回strokeすることで実現している。グラデーション塗りつぶしのような高度な2D描画機能が使えるのもキャンバスの恩恵である。

3.背景エフェクト

See the Pen Untitled by z0ero (@z0ero) on CodePen.

ここまでの応用でアニメーションも実装できる。応用といっても単にキャンバスを毎フレーム再描画するだけなので割と簡単である。ただ、落とし穴としてg.game.modified();を毎フレーム呼んでないとrenderSelfが呼ばれなくてアニメーションしないという不具合があったのでその辺だけ注意が必要。ちなみにこの背景エフェクトでは結構テクいこともしている。例えば各パーティクルの移動速度計算が重くならないように関数近似で軽量化を行ったり(どういう計算かは忘れた)、コンポジットモードをいじることで光の軌跡を残すようにしたりしている*2

広がるアイデア

パネワンでは限定的にしかキャンバスを活用しなかったが、ぶっちゃけAkashicの描画機能を一切使わず、全描画をキャンバスで行うことも可能だ。そこで既存の発想にとらわれないアイデアを考えてみよう!

1.お絵かきゲーム

ユーザーが描いた絵を共有して遊べるゲームだ。お題で絵を描いたり、描いた絵を戦わせたりするゲームなどが既に存在しているが、そういったゲームの場合、点や線などを大量のエンティティで表現するよりはキャンバスを使ってしまう方が自然だろう。頑張ればマルチで絵チャなんかも作れると思う。

2.3Dゲーム

canvas要素は3D描画も可能である。WebGLの知識が必要になるが、Three.jsなどのライブラリを利用すれば比較的簡単に3D表現もできるのではないだろうか。

3.ベクターアニメーション

AkashicEngineでは実験機能だが3.2.0以降でSVG画像も使えるようになっている。しかしあくまで静止画として利用可能なだけで、滑らかに変形するアニメーションなどは多分できない。キャンバスAPIなら高度な図形描画機能が豊富なのでフニャフニャと変形するような動きのキャラクターなども比較的まともな速度で表現できるのではなかろうか。

See the Pen ベクターキャラモーション by z0ero (@z0ero) on CodePen.

まとめ

  • g.Spriteなどを継承してrenderSelfをオーバーライドすることで独自にキャンバス描画した画像を普通のエンティティとしてAkashic上で表示できる
  • キャンバスAPIの強力な2D描画や3D描画機能が使えるのでブラウザゲーム相当の表現が可能である
  • よほど特殊機能でない限りブラウザやニコ生アプリ上で問題なく使える

*1:とは言えバックエンドにwebglが使われてるところは無さそうだし、2dならほぼ全てのブラウザが対応している状況で敢えてwebglが選択されるようなケースがあるのかは疑問である。

*2:しかも環境によってはサポートしていないモードもあるのでその対策も入れている