いんわんのブログ

JavaScriptでゲーム開発!【サンプル付き・初心者向け】

JavaScriptでゲーム開発

プログラミング言語のJavaScriptを学習中の方やこれから学ぼうという方の中には

 

「普通に勉強してもつまらないからゲームを作りたい!」

 

と思っている方もけっこういるんじゃないかと思います。

 

この記事では初心者にもわかるようにJavascriptを使ったゲームの作り方やどんなゲームが作れるかなどを解説していきます。

 

プログラミング言語のJavaScriptとは?

JavaScriptはウェブサイト上でアニメーションなどの動きをつけたり、何らかの操作に対してアクションを起こすようなプログラムを作るのに使われているプログラミング言語です。

もちろんゲームだって作ることができます。

JavaScriptで作ったゲームはブラウザ上で動くのでブラウザゲームやWEBゲームなどと呼ばれます。

 

プログラミング言語としては学習難度が他の言語よりも比較的低くく入門者向きと言われています。

ブラウザ上で動作するため開発環境の構築も必要なくテキストエディターさえあれば始められる手軽さも初心者向きと言えます。

しかし、初心者向けと言われているからと言っても簡単というわけではないです。

 

かなりいろんなことができる言語なので極めれば一生仕事に困ることは無いでしょう(筆者は残念ながらそんなレベルではないです)。

 

HTMLのcanvasタグにゲームを作る

HTML5からcanvasというタグが追加されました。

このcanvasタグは線や図形、画像などを表示して動かすことに特化したエリアをブラウザのページに作成することができます。

そのエリアにJavaScriptを使ってプログラミングで画像などを描画したり動かしたりします。

 

ゲーム専用というわけではなくブラウザ上でグラフの表示や動きのある背景の表示などいろいろなところに使われています。

 

ちなみに、ゲームはこのcanvasタグに表示されるため、他の学習系サイトの記事に書いてあるようなHTMLとCSSの学習は基本的に必要ありません。

(※canvasを使わずにHTML要素を使ってゲームを作る場合にはHTMLとCSSの学習が必要です。ただし、canvasほど自由にゲームは作れません。また、canvasにゲームを作るにしてもUIだけHTML/CSSで作る方法もあります。)

 

JavaScriptでどんなゲームが作れる?

基本的にはJavaScriptでもcanvas上でなら一般的なゲームはほとんどなんでも作ることができます。

コントローラーに対応したゲームも作れます。

 

JavaScriptはブラウザ上で動作するため直接本体の処理能力を利用できずに処理が遅いと言われていましたが、現在はWebGLという高速に画像を処理してくれる機能を使うことができます。

WebGLを使えばかなり激しい描画のゲームも作ることができるようになっています。

 

Javascriptで作られたゲーム

筆者もJavaScriptでミニゲームを作っています。

JavaScriptで作られたゲームを見てみたい方はぜひ一度遊びに来てください。

リンクページにもライバルのブラウザゲームサイトを多く載せているのでそちらも参考にしてください。

サイト自体もJavaScriptでいろいろやっているのであちこち触ってみてください(変な仕掛けとかもあります)。

 

JavaScriptで作ったゲームもスマホアプリにできる

JavaScriptで作るゲームはブラウザ上で動きます。

一見スマホアプリとは全く無縁のようですが、実はブラウザ上で動くならブラウザの機能込みでアプリ化してしまえばスマホアプリにできます。

そのような形で作られたものをハイブリッドアプリと言います。

 

このハイブリッドアプリを簡単に作れるようにしてくれているサービスとしてMonacaがあります。

ハイブリッドアプリを作るには専門の知識が必要ですがMonacaを使えばさっくり出来てしまいます。

 

JavaScriptで作ったゲームをMonacaでスマホアプリにする方法はこちらの記事に書いています。

 

JavaScriptのゲーム開発用ライブラリ

JavaScriptだけで一からすべてをプログラミングしてゲームを作っていくことはできますが、それはあまりにも大変です。

 

JavaScriptにはゲームを作るためのライブラリ(またはゲームエンジン、フレームワークと呼ばれてたりする)というものがいくつか公開されていて無料で使うことができます。

 

基本的にはこれらのライブラリを使ってゲームを作ります。

 

JavaScriptのゲーム開発向けのライブラリをいくつか紹介します。

 

enchant.js

enchant.jsはもうかなり古いゲームエンジンなのですが一時期とても人気があったゲームエンジンです。

現在は更新が停止してしまい公式サイトも無くなってしまいましたが今でも使うことはできます。

 

とても人気があったためネット上に多くの情報が残っているので初心者でも独学で調べて作りやすいです。

 

筆者も最初はこのenchant.jsから入りました。

筆者がenchant.jsで作ったゲーム ▷ コスモファイター

 

初心者でもすぐにゲームを開発できるテンプレートを公開しているので良かったら使ってみてください。

 

PixiJS

ブラウザ上で高速に画像を描画できるWebGLという技術を使って画像を描画してくれる機能を持った2D描画に特化したライブラリです。

ゲーム用ライブラリではないため必要な部分を自分で作らなくてはいけませんがとても扱いやすいです。

音楽再生用のpixi-sound.jsも公開されていて一緒に使うことができます。

 

筆者は現在PixiJSを使ってゲームを作っています。

筆者がPixiJSで作ったゲーム ▷ 連射ソルジャー

 

簡単なゲームエンジンを作ったので使ってみてください。

 

phaser.js

上記のpixi.jsをコアに作られたゲームエンジンです。

JavaScriptのゲームエンジンとしては世界で一番利用されているらしいのですが国内での情報はかなり少ないように思います。

 

JavaScriptのゲーム開発環境を作る

実際にゲームを作るための準備をしましょう。

JavaScriptの開発環境はとても簡単でプログラミング用のテキストエディターとブラウザがあればOKです。

 

テキストエディターを用意する

JavaScriptはwindowsのメモ帳でも書けますが専用のテキストエディターがあった方が作業が楽になります。

最近はVisual Studio Codeというエディターが人気のようなのでこれを使いましょう。

 

ブラウザを用意する

ブラウザはwindowsならChromeかEdge、Macならsafariを使ってください。

他のブラウザでもほとんど同じに動作しますが、ごくまれに挙動が違うことがあります。

とりあえず一番利用者が多いブラウザで開発しましょう。

 

ファイルの構成と起動の仕方

JavaScriptはウェブページ上で起動するので簡単なHTMLファイルを作ります。

名前はindex.htmlなど「○○.html」の形になっていればOKです。

<!DOCTYPE HTML>
<html lang="ja">
<head>
    <title>サンプル</title>
    <meta charset="utf-8">
    <script src="main.js"></script>
</head>
<body>
    <canvas id="canvas" style="background-color: black;"></canvas>
</body>
</html>

<canvas>の部分にゲームが表示されます。

JavaScriptのプログラムは○○.js(例ではmain.js)というファイルを作ってそこに書いていきます。

 

このindex.htmlとmain.jsを一つのフォルダに入れて管理してください。

 

そして起動するときはindex.htmlをブラウザにドラッグ&ドロップすると起動します。

 

サンプルプログラム(フルスクラッチ)

最後にちょっとしたサンプルプログラムを載せておくので作り方を簡単に紹介します。

(このサンプルは素のJavaScriptだけで作っているので見た目の割にけっこう複雑です。ライブラリを使えばもっと楽に作れます。)

 

簡単なフレーム更新プログラム

サンプル画像

まずは簡単なフレーム更新プログラムを作ってみました。

sampleプログラム

var canvas, ctx;
var x = 0, y = 0, vx = 1, vy = 1;
//初期処理
function init(){
    canvas = document.getElementById("canvas");
    canvas.width = 640;
    canvas.height = 900;
    ctx = canvas.getContext("2d");//コンテキスト
    
    requestAnimationFrame(update);//フレーム処理のお知らせを送る
}
//四角を描く
function drawRect(x, y, w, h){
    ctx.fillStyle = "white";
    ctx.fillRect(x, y, w, h);
}
//画面描画処理
function render(){
    ctx.clearRect(0, 0, canvas.width, canvas.height);//画面をクリア(前の画面描画を削除)
    x += vx;
    y += vy;
    if(x >= canvas.width - 64){
        vx = -1;
    }else if(x <= 0){
        vx = 1;
    }
    if(y >= canvas.height - 64){
        vy = -1;
    }else if(y <= 0){
        vy = 1;
    }
    drawRect(x, y, 64, 64); 
}
//更新処理
function update(){
    render();
    requestAnimationFrame(update);//フレーム処理のお知らせを送る
}
//開始
window.onload = function(){
    init();
}

このなかで大事なのはrequestAnimationFrameというメソッドです。

これはおよそ60FPSになるような一定の間隔で引数に渡した関数を呼び出してくれるようです。

ただし、requestAnimationFrameは一回こっきりしか働いてくれないようで、ずっと更新処理をし続けるには毎回requestAnimationFrameを呼ぶ必要があるようです。

 

画面が更新されていても何も表示されていなければわからないので四角形を表示させて移動させてみました。DVDプレーヤーのアレみたいなやつを作ってみました。

角に行ったら盛り上がってください(外国で流行っているらしいです)。

 

仮のキャラを表示して作ってみる

シューティングサンプル

とりあえず画面の更新処理さえできてしまえばもうゲームは出来るはずです。

ってことで作ってみたのがこちらです → サンプルプログラム

 

かなり簡素ではありますが一応シューティングゲームになってます。

 

プログラムはこんな感じでやってみました。

var SCREEN_WIDTH = 640;//画面幅
var SCREEN_HEIGHT = 900;//画面高さ
var core, player, bullet, enemy;

class Chara{//キャラクタークラス
    constructor(w, h){
        this.x = 0;
        this.y = 0;
        this.width = w;
        this.height = h;
    }
    render(){
        drawRect(core.ctx, this.x, this.y, this.width, this.height);
    }
}
class Bullet extends Chara{//弾クラス
    constructor(w, h){
        super(w, h);
        this.y = -this.height;
    }
    move(){
        this.y -= 10;
        if(this.y < -this.height){
            this.x = player.x + (player.width - this.width) / 2; 
            this.y = player.y;
        }
    }
}
class Player extends Chara{//プレイヤークラス
    constructor(w, h){
        super(w, h);
        this.x = 300;
        this.y = 500;
    }    
}
class Enemy extends Chara{//敵クラス
    constructor(w, h){
        super(w, h);
        this.x = Math.floor(Math.random() * (SCREEN_WIDTH - this.width));
        this.y = -this.height;
    } 
    move(){
        this.y += 2;
        if(this.y > SCREEN_HEIGHT){
            this.x = Math.floor(Math.random() * (SCREEN_WIDTH - this.width));
            this.y = -this.height;
        }
    }
}
class Core{//ゲーム基幹クラス
    constructor(){
        this.canvas = document.getElementById("canvas");
        this.canvas.width = SCREEN_WIDTH;
        this.canvas.height = SCREEN_HEIGHT;
        this.ctx = this.canvas.getContext("2d");//コンテキスト
        
        this.canvas.addEventListener("mousedown", (e) => {//マウスダウン
            this.isMouseDown = true;
            player.px = e.x;
            player.py = e.y;
        });
        this.canvas.addEventListener("mouseup", (e) => {//マウスアップ
            this.isMouseDown = false;
        });
        this.canvas.addEventListener("mousemove", (e) => {//マウス移動
            if(!this.isMouseDown)return;
            player.x += e.x - player.px;
            player.y += e.y - player.py;
            player.px = e.x;
            player.py = e.y;
        });
        requestAnimationFrame(()=>{this.update();});//フレーム処理のお知らせを送る
    }
    update(){//更新処理
        requestAnimationFrame(()=>{this.update();});//フレーム処理のお知らせを送る
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);//画面をクリア(前の画面描画を削除)
        this.enterframe();//フレーム処理
        this.render();//描画処理
    }
    render(){//描画処理
        enemy.render();
        player.render();
        bullet.render();
    }
    enterframe(){//フレーム処理
        bullet.move();
        enemy.move();
        var r1 = bullet.width / 2;
        var r2 = enemy.width / 2;
        if(circleCollision(bullet.x + r1, bullet.y + r1, r1, enemy.x + r2, enemy.y + r2, r2)){
            enemy.y = SCREEN_HEIGHT;
            bullet.y = -100;
        }
    }
}
function drawRect(ctx, x, y, w, h){//四角を描く
    ctx.fillStyle = "white";
    ctx.fillRect(x, y, w, h);
}
function circleCollision(x1, y1, r1, x2, y2, r2){//当たり判定
    if((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) <= (r1+r2) * (r1+r2)){
        return true;
    }
    return false;
}
//開始
window.onload = function(){
    core = new Core();
    enemy = new Enemy(64, 64);
    player = new Player(64, 64);
    bullet = new Bullet(16, 16);
}

ゲームにするためには操作が必要なのでmousedown,  mouseup, mousemoveを使って操作できるようにしました。

ただ、これはマウスでの操作にしか反応してくれません。スマホなどのタッチでの操作はまた別に作る必要があります。

 

画像を読み込んで表示する

シューティングサンプル

最後にキャラクターの画像を表示させてみます→サンプル

 

画像はプレイヤー、敵、弾の3つ用意します。

大きさはプレイヤー、敵が64×64、弾が16×16です。それらをimagesフォルダに入れてください。

 

画像の読み込み処理

まずは使う画像をASSETSオブジェクトにまとめて入れておきます。

それぞれの画像の名前とファイルの場所を入れます。

var ASSETS = {
    'img_player': 'images/player.png',
    'img_enemy': 'images/enemy.png',
    'img_bullet': 'images/bullet.png',
};

 

画像の読み込みなんですが、まず画像を扱うにはImageオブジェクトを作成してそこに画像を読み込んで使います。

var image = new Image();
image.src = '画像ファイルの場所';
image.onload = function() {//読み込んだよ
  
}

基本、上記のような感じで書きます。

onloadメソッドは画像の読み込みが完了したら発生するイベントです。大きな画像だったりすると読み込むのに時間がかかったりするので必要な処理は読み込みが終わってからする必要があります。

 

ただ、ゲームだとたくさん画像を扱うので全部読み込んだかどうかの確認が必要です。

 

なので、Coreクラスに以下のような処理をつけました。引数のdataはASSETSです。画像はすべてCoreクラスのassetsオブジェクトに読み込みます。

preload(data){
    var length = Object.keys(data).length;
    var count = 0;
    for(let key in data) {
        this.assets[key] = new Image();
        this.assets[key].src = data[key];
        this.assets[key].onload = ()=>{
            if(++count == length){//全部読み込んだ
                this.onload();//読み込み完了後の処理
            }
        }
    }
}

画像の枚数をカウントしておいて、読み込みの完了した数が画像の枚数と同じになればCoreクラスのonloadメソッドを実行します。

 

それを追加したCoreクラスがこんな感じです。

class Core{//ゲーム基幹クラス
    constructor(){
        this.canvas = document.getElementById("canvas");
        this.canvas.width = SCREEN_WIDTH;
        this.canvas.height = SCREEN_HEIGHT;
        this.ctx = this.canvas.getContext("2d");//コンテキスト
        this.assets = [];//アセット保存用配列
        
        this.canvas.addEventListener("mousedown", (e) => {//マウスダウン
            this.isMouseDown = true;
            player.px = e.x;
            player.py = e.y;
        });
        this.canvas.addEventListener("mouseup", (e) => {//マウスアップ
            this.isMouseDown = false;
        });
        this.canvas.addEventListener("mousemove", (e) => {//マウス移動
            if(!this.isMouseDown)return;
            player.x += e.x - player.px;
            player.y += e.y - player.py;
            player.px = e.x;
            player.py = e.y;
        });
    }
    //画像のロード
    preload(data){
        var length = Object.keys(data).length;
        var count = 0;
        for(let key in data) {
            this.assets[key] = new Image();
            this.assets[key].src = data[key];
            this.assets[key].onload = ()=>{
                if(++count == length){//全部読み込んだ
                    this.onload();//読み込み完了後の処理
                }
            }
        }
    }
    update(){//更新処理
        requestAnimationFrame(()=>{this.update();});//フレーム処理のお知らせを送る
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);//画面をクリア(前の画面描画を削除)
        this.enterframe();//フレーム処理
        this.render();//描画処理
    }
    render(){//描画処理
        enemy.render();
        player.render();
        bullet.render();
    }
    enterframe(){//フレーム処理
        bullet.move();
        enemy.move();
        var r1 = bullet.width / 2;
        var r2 = enemy.width / 2;
        if(circleCollision(bullet.x + r1, bullet.y + r1, r1, enemy.x + r2, enemy.y + r2, r2)){
            enemy.y = SCREEN_HEIGHT;
            bullet.y = -100;
        }
    }
}

 

読み込み完了後にゲームが始動

画像の読み込みが完了したらゲームプログラムが動き始めるようにします。

core.onloadメソッドに読み込み完了後の処理を記述します。ここで初めてrequestAnimationFrameを使います。

この後は自動で更新処理が行われるようになります。

//開始
window.onload = function(){
    core = new Core();
    core.preload(ASSETS);
    core.onload = function(){
        enemy = new Enemy(64, 64);
        player = new Player(64, 64);
        bullet = new Bullet(16, 16);
        requestAnimationFrame(()=>{core.update();});//フレーム処理のお知らせを送る
    }
}

 

画像を描画する

Charaクラスのレンダーメソッドに画像を描画するdrawImageメソッドを記述します。

class Chara{//キャラクタークラス
    constructor(w, h){
        this.x = 0;
        this.y = 0;
        this.width = w;
        this.height = h;
    }
    render(){
        core.ctx.drawImage(this.image, 0, 0, this.width, this.height, this.x, this.y, this.width, this.height); 
    }
}

キャラクターはすべてこのCharaクラスを継承しているのでこの一か所変更するだけでOKです。

これで画像を使ったゲームになります。

 

エラーがあった場合の対処の仕方

プログラムを入力して動かなかった場合や途中で止まった場合どこかにおかしなところがあります。

エラーなどを確認するにはChromeの場合は右上にあるメニューからデベロッパーツールを開いて確認できます(Edgeも似たところにあります)。

 

デベロッパーツールの場所

 

デベロッパーツールを開いたら上にあるメニューの「console」をクリックします。

コンソールの場所

 

するとエラーメッセージなどが表示される画面が下に出ます。

エラーメッセージ

赤字でこんな感じのメッセージが表示されていたらエラーです。

右上にファイル名と行番号が表示されているのでそのあたりをよく見てエラーを見つけましょう。

 

エラーやバグというのは嫌なものですが何度も経験するとある程度原因の予測がつくようになってくるので挫けずに頑張って対処してください。

 

まとめ

以上のような感じでJavaScriptでもゲームを作ることができます。

 

紹介したサンプルプログラムは素のJavaScriptだけで作っているので画像を表示して動かすだけでもかなり面倒くさいことになっています。

実際にゲームを作るならゲーム用ライブラリを使った方がかなり楽に作れます。

enchant.jspixi.jsの記事があるので参考にしてください。

 

★作ったゲームをブログに埋める方法

 

★WordPressのゲームサイト用テーマ配布してます

 

関連記事