Help us understand the problem. What is going on with this article?

RPGツクールMVとJavaScriptライブラリーを連携してみよう【Chart.js】

More than 3 years have passed since last update.

はじめに

RPGツクールMV発売から1年が経過し、どういった点が得意で何が不得意なのか、みなさんおおよそ把握してきたのではないかと思ってます。

ゲームの根幹を担っているのは HTML5,CSS3,JavaScript というWebベースの技術で、事前コンパイルするプログラムに比べて若干の速度面の見劣り、変数の動的型付やブラウザごとの実行エンジンの違いにより、やや不安定であるという面はあります。

ただ、それを補って余りあるほどの魅力がこのツクールMVにはあります!
今回、ツクールMV実行言語 JavaScript の魅力でもある豊富なライブラリを使って何か面白いことができないかと考え、Chart.jsというグラフ描画用のライブラリに行き着きました。

何故Chart.jsなのか

実際に取り上げるにあたり以下のようなことが決め手になりました。

  • ライブラリとして実現できることがはっきりしていてわかりやすい。
  • 値からグラフを作成するという用途はゲームにも向いている。
  • 現時点でMITライセンスであり、手軽に使用可能である。
  • 楽しそう。

導入方法

さて、ここからが本題になります。
Chart.jsをダウンロードしてから適用するまでをサクッとご紹介します。

ダウンロード

まずこちらのサイトに行き、Documentationをクリック。

SnapCrab_NoName_2016-11-15_21-46-53_No-00.png

Chart.js CDN のリンクをクリック。

SnapCrab_NoName_2016-11-15_22-54-32_No-00.png

3番目のChart.jsが圧縮されておらず読みやすい状態のものですので、ソースを見る方はこちらのCopyをクリック。
ソースの中身とかどうでもいいので1ミリ秒でも早く!という方はChart.min.jsのCopyをクリック。

SnapCrab_NoName_2016-11-26_17-23-43_No-00.png

URLがクリップボードにコピーされるので、ブラウザのURL欄にペーストして「Enter」を押しましょう。

SnapCrab_NoName_2016-11-26_17-33-18_No-00.png

ソースの中身がブラウザに表示されるので、右クリックして名前をつけて保存をクリック。

SnapCrab_NoName_2016-11-26_17-39-26_No-00.png

保存用の画面が表示されるので、任意の場所に保存してください。

SnapCrab_NoName_2016-11-26_17-42-26_No-00.png

以上で、ダウンロードについては終了です。

ツクールMVへの適用

簡易的な方法

あまり体裁を気にしなければ超簡単です。
pluginsフォルダに突っ込んで、プラグイン管理の画面でONにしてください。
簡単ですね^^

SnapCrab_NoName_2016-11-26_17-51-55_No-00.png

SnapCrab_NoName_2016-11-26_17-56-37_No-00.png

ちゃんとした?方法

Chart.jsはプラグインでなくライブラリの扱いだから、pluginsに入れるなんてもってのほかだ!というめんどく……意識高い方は、pluginsと同じフォルダ内にあるlibsフォルダに入れて、index.htmlを書き換えましょう。

SnapCrab_NoName_2016-11-26_18-26-58_No-00.png

以上で、ツクールMVへの適用手順は終了です。

グラフの作成

さて、ここから実際にChart.jsを使ってツクールにグラフを書いてみましょう。
今回は、ステータス画面の部分をテキストと数字からグラフに置き換えてみます。

SnapCrab_NoName_2016-11-28_22-58-43_No-00.png

まずは、導入するグラフ部分を作っていきましょう。
手早く確認するために、一旦ツクールから離れて通常のHTMLに描画して体裁を整えていきます。

HTMLはChart.jsとグラフ描画のためのmyChart.jsを導入してmyChart.js内のメソッドを呼んでいるだけです。

■ HTML 内

<!DOCTYPE html>
<html>
    <head>
        <script src="js/Chart.js"></script>
        <script src="js/myChart.js"></script>
    </head>
    <body>
        <script>
           makeChart() 
        </script>
    </body>
</html>

myChart.jsはグラフを描画する最低限の内容が入っています。
まず、描画するCanvasを作成し、IDをChartCanvasにしてbodyに突っ込みます。
突っ込んだChartCanvasに対してgetContextを実行し、Canvasに描画するためのAPIにアクセスできるオブジェクトを取得します。

//Canvas作成
var chartCanvas = document.createElement('canvas');
chartCanvas.id = 'ChartCanvas';
document.body.appendChild(chartCanvas);
//context取得
var context = document.getElementById('ChartCanvas').getContext('2d');

次にグラフ用のオブジェクト myChart を作成します。

グラフの種類である type 、データ部の data 、各種オプションを指定する options を指定することにより、任意のグラフが作成可能です。

グラフの type は、棒グラフ、折れ線グラフ、レーダーチャート、ポーラーチャート、円グラフ、ドーナッツ型グラフ、バブルチャートの7種類がありるようですが、パラメータのグラフ化なので、レーダーチャートにします。

グラフの data は、まずデータ項目のラベルである labels にパラメータ名をセットします。

次項目の datasets がグラフ1つの単位で、何個も入れられるよう配列になってますね。
2つ以上作成すると、レーザーチャート2つが重なって複数人のパラメータが表示することなども可能ですが、今回は1人分のみ作ります。

datasets 内の label がそのグラフをあらわす凡例で、次の data が、グラフの実データになります。
data 項目の datasets 内に data があるのが気になりますが仕様です!

options はメモリの開始を0からにするなどのオプションを設定しますが、ひとまず今回は何も入れないでデフォルトにします。

■ myChart.js 内

//グラフ作成
var myChart =
    {
        //グラフの種類
        type: 'radar',
        //データの設定
        data: {
            //データ項目のラベル
            labels: ['攻撃', '防御', '魔法', '魔防', '敏捷', '', '命中', '回避'],
            //データセット
            datasets: [
                {
                    //凡例名
                    label: 'キャラ1',
                    //グラフのデータ
                    data: [200, 110, 150, 80, 120, 130, 120, 98]
                }
            ]
        },
        //オプションの設定
        options: {
        }
    }

一通りの設定が終わったら、Chart.jsのChartメソッドでCanvasにグラフを描画します。

//グラフをCanvasに描画
var objChart = new Chart(context, myChart)

無事グラフが描画されましたが、ツクールMVにこのまま入れるのはどうよ?って感じではありませんか??

SnapCrab_NoName_2016-11-29_1-7-6_No-00.png

というわけで色々なサイトを眺め、公式ドキュメント(英語)を翻訳しながらなんとなくゲームになじむような設定にしました。
背景も見やすいように灰色にします。

■ HTML 内

<!DOCTYPE html>
<html>
    <head>
        <script src="js/Chart.js"></script>
        <script src="js/myChart.js"></script>
    </head>
    <body  bgcolor="#999999">
        <script>
           makeChart() 
        </script>
    </body>
</html>

■ myChart.js 内

function makeChart() {

    //Canvas作成
    var chartCanvas = document.createElement('canvas');
    chartCanvas.id = 'ChartCanvas';
    document.body.appendChild(chartCanvas);

    //context取得
    var context = document.getElementById('ChartCanvas').getContext('2d');

    //グラフ作成
    var myChart = {
        //グラフの種類
        type: 'radar',
        //データの設定
        data: {
            //データ項目のラベル
            labels: ['攻撃', '防御', '魔法', '魔防', '敏捷', '', '命中', '回避'],
            //データセット
            datasets: [
                {
                    //線のカーブ
                    lineTension: 0,
                    //例
                    label: '',
                    //面の表示
                    fill: true,
                    //背景色
                    backgroundColor: 'rgba(250,241,109,0.5)',
                    //枠線の色
                    borderColor: 'rgba(250,241,109,0)',
                    //結合点の背景色
                    pointBackgroundColor: 'rgba(250,241,109,0)',
                    //枠線の太さ
                    borderWidth: 0,
                    //線の色
                    strokeColor: 'rgba(0,0,0,0)',
                    //値の点の枠線の色
                    pointStrokeColor: 'rgba(255,255,255,0)',
                    //背景色(ホバーしたときに)
                    //hoverBackgroundColor: 'rgba(179,181,198,0.4)',
                    //結合点より外でマウスホバーを認識する範囲(ピクセル単位)
                    pointHitRadius: 100,
                    //グラフのデータ
                    data: [200, 110, 150, 80, 120, 130, 120, 98]
                }
            ]
        },
        //オプションの設定
        options: {
            //グラフ外側の文字色
            scaleFontColor: 'rgba((255,0,0,1)',
            //サイズ自動調整
            //responsive: false,
            //ボーダーライン幅
            borderWidth: 2,
            //スケール
            scale: {
                //タイプ
                type: 'radialLinear',
                //フォントサイズ
                fontSize: 0,
                //ボーダー色
                borderColor: 'rgba(0,0,0,0)',
                //ポイントラベル
                pointLabels: {
                    //フォントサイズ
                    fontSize: 10,
                    //フォントカラー
                    fontColor: 'rgba(255,255,255,1)'
                },
                //目盛
                ticks: {
                    //間隔
                    stepSize: 50,
                    //最大値
                    max: 200,
                    //最低値がゼロ
                    beginAtZero: true,
                    //ラベル表示
                    showLabels: true,
                    //フォントカラー
                    fontColor: 'rgba((255,0,0,1)',
                    //フォントサイズ
                    fontSize: 10,
                    //ラベルバックドロップ表示
                    showLabelBackdrop: false,
                }
            },
            //凡例
            legend: {
                //非表示
                display: false
            },
            //ホバー
            hover: {
                //動作(single, label, dataset)
                mode: 'label'
            }
        }
    }
    //Canvasにグラフ描画
    var objChart = new Chart(context, myChart)
}

某サッカー育成シュミレーションで見たことあるような黄色の透過グラフになりました。
これなら、ゲームで表示しても違和感なさそうですね。

SnapCrab_NoName_2016-11-29_2-4-19_No-00.png

作成したグラフをMVへ導入

さて、いよいよ作成したグラフをツクールMVに導入してみましょう。
ツクールMVの場合も通常HTMLと同じように、作成したCanvas上に描画します。

ツクールMVで今回のグラフを導入するために使えそうなCanvasは、GameCanvasかUpperCanvasになります。
GameCanvasがマップチップやウィンドウ、キャラクターなどを描画している部分です。
UpperCanvasは、プラグインなどで使用されているのをたまに見かけますね。

SnapCrab_NoName_2016-11-30_0-25-41_No-00.png

ひとまず、GameCanvasに描画したらどうなるのかを試してみようと思いましたが、getContext('2d')で取得しようとしてもnullしか返ってきません。
UpperCanvasはちゃんと取得できるみたいですが。

SnapCrab_NoName_2016-11-30_0-39-38_No-00.png

調べてみたら、どうやらgetContext('webgl')で取得できるみたいです。
また、とあるフォロワーさんがMVをCanvasモードにして起動すると取得できることを確認してくださいました。
今回深くは調査してませんが、Canvasの中身がどうも変わるみたいですね。

SnapCrab_NoName_2016-11-30_0-59-54_No-00.png

では、UpperCanvasか?というとまた1点問題があります。
Chart.jsはCanvasに合わせてグラフをいい感じに配置してくれるので、Canvasのどの位置に描画するという記述が無いようなのです。
内部を細かくみるとあるのかもしれませんが、私の実力的に厳しい あまり時間も無いので、今回は新規にChartCanvasを追加してCanvasのサイズと位置をステータス画面に合わせて変更し、その上に描画してみようと思います。(ハメコミ合成的なあれです)

GraphicsがCanvasの管理を担っているので、以下のメソッドにChartCanvasの処理を追加しましょう。

//ChartCanvasの作成を追加定義
Graphics._createAllElements = function () {
    this._createErrorPrinter();
    this._createCanvas();
    this._createVideo();
    this._createUpperCanvas();
    this._createRenderer();
    this._createFPSMeter();
    this._createModeBox();
    this._createGameFontLoader();
    this._createChartCanvas();  //追加
};

//ChartCanvasの更新を追加定義
Graphics._updateAllElements = function () {
    this._updateRealScale();
    this._updateErrorPrinter();
    this._updateCanvas();
    this._updateVideo();
    this._updateUpperCanvas();
    this._updateRenderer();
    this._updateChartCanvas();  //追加
    this._paintUpperCanvas();
};

次に、ChartCanvasの作成と更新用のメソッドを作ります。
透過した状態 (opacity = 0) で、UpperCanvasの上(zIndex = 5)に作ります。

//ChartCanvasの作成メソッドを新規作成
Graphics._createChartCanvas = function () {
    this._chartCanvas = document.createElement('canvas');
    this._chartCanvas.id = 'ChartCanvas';
    this._updateChartCanvas();
    document.body.appendChild(this._chartCanvas);
};

//ChartCanvasの更新メソッドを新規作成
Graphics._updateChartCanvas = function () {
    this._chartCanvas.width = 0;
    this._chartCanvas.height = 0;
    this._chartCanvas.style.zIndex = 5;
    this._chartCanvas.style.opacity = 0;
};

これで、ツクールMVにChartCanvasが作成されました。

SnapCrab_NoName_2016-11-30_2-26-9_No-00.png

次に、パラメータを描画している部分で、パラメータのラベルと値を作成するグラフに入れたいので以下のように書き換えます。

//グラフの値とラベルを取得するよう書き換え
Window_Status.prototype.drawParameters = function (x, y) {
    var chart = MyChartData.data
    chart.labels = [];
    chart.datasets[0].data = [];
    for (var i = 0; i < 6; i++) {
        var paramId = i + 2;
        chart.labels.push(TextManager.param(paramId));
        chart.datasets[0].data.push(this._actor.param(paramId));
    }
};

次はステータスが画面を描画する部分に、グラフ作成用のメソッドを追加します。
グラフ作成用のメソッドに、Canvasのサイズや透過度などを設定し、グラフの
描画部分を記述します。

//グラフの描画を追加定義
Window_Status.prototype.refresh = function () {
    this.contents.clear();
    if (this._actor) {
        var lineHeight = this.lineHeight();
        this.drawBlock1(lineHeight * 0);
        this.drawHorzLine(lineHeight * 1);
        this.drawBlock2(lineHeight * 2);
        this.drawHorzLine(lineHeight * 6);
        this.drawBlock3(lineHeight * 7);
        this.drawHorzLine(lineHeight * 13);
        this.drawBlock4(lineHeight * 14);
        createChartObj(lineHeight * 6.5);   //追加
    }
};

//ChartCanvasの体裁を整えてグラフを描画するメソッドを新規作成
var MyChart = null;
createChartObj = function (y) {
    var context = document.getElementById('ChartCanvas').getContext('2d');
    context.canvas.width = 300;
    context.canvas.height = 300;
    context.canvas.style.position = 'relative';
    context.canvas.style.margin = 0;
    context.canvas.style.left = Graphics._canvas.offsetLeft + 'px';
    context.canvas.style.top = Graphics._canvas.offsetTop + y + 'px';
    context.canvas.style.opacity = 1;
    MyChart = new Chart(context, MyChartData);
}

次はグラフの削除部分です。
キャラクターの選択画面の部分で消えるように以下のように定義します。
MyChart.destroy()の部分はChart.jsが提供しているメソッドです。
こちらも、先ほどとは別のとあるフォロワーさんに教えていただきました。
感謝感謝であります。

//グラフの削除を追加定義
Window_Selectable.prototype.activate = function () {
    Window_Base.prototype.activate.call(this);
    this.reselect();
    Graphics._clearChartCanvas();  //追加
};

//グラフを削除するメソッドを新規作成
Graphics._clearChartCanvas = function () {
    if (MyChart != null) MyChart.destroy()
    var context = this._chartCanvas.getContext('2d');
    context.canvas.style.opacity = 0
    context.clearRect(context.canvas.style.left, context.canvas.style.top, context.canvas.width, context.canvas.height);
};

これで一通りの処理を記述しましたので、HTMLのときに記述したグラフ部分と合わせて以下のようになりました。

//ChartCanvasの作成を追加定義
Graphics._createAllElements = function () {
    this._createErrorPrinter();
    this._createCanvas();
    this._createVideo();
    this._createUpperCanvas();
    this._createRenderer();
    this._createFPSMeter();
    this._createModeBox();
    this._createGameFontLoader();
    this._createChartCanvas();   //追加
};

//ChartCanvasの更新を追加定義
Graphics._updateAllElements = function () {
    this._updateRealScale();
    this._updateErrorPrinter();
    this._updateCanvas();
    this._updateVideo();
    this._updateUpperCanvas();
    this._updateRenderer();
    this._updateChartCanvas();   //追加
    this._paintUpperCanvas();
};

//ChartCanvasの作成メソッドを新規作成
Graphics._createChartCanvas = function () {
    this._chartCanvas = document.createElement('canvas');
    this._chartCanvas.id = 'ChartCanvas';
    this._updateChartCanvas();
    document.body.appendChild(this._chartCanvas);
};

//ChartCanvasの更新メソッドを新規作成
Graphics._updateChartCanvas = function () {
    this._chartCanvas.width = 0;
    this._chartCanvas.height = 0;
    this._chartCanvas.style.zIndex = 5;
    this._chartCanvas.style.opacity = 0;
};

//グラフの値とラベルを取得するよう書き換え
Window_Status.prototype.drawParameters = function (x, y) {
    var chart = MyChartData.data;
    chart.labels = [];
    chart.datasets[0].data = [];
    for (var i = 0; i < 6; i++) {
        var paramId = i + 2;
        chart.labels.push(TextManager.param(paramId));
        chart.datasets[0].data.push(this._actor.param(paramId));
    }
};

//グラフの描画を追加定義
Window_Status.prototype.refresh = function () {
    this.contents.clear();
    if (this._actor) {
        var lineHeight = this.lineHeight();
        this.drawBlock1(lineHeight * 0);
        this.drawHorzLine(lineHeight * 1);
        this.drawBlock2(lineHeight * 2);
        this.drawHorzLine(lineHeight * 6);
        this.drawBlock3(lineHeight * 7);
        this.drawHorzLine(lineHeight * 13);
        this.drawBlock4(lineHeight * 14);
        createChartObj(lineHeight * 6.5);   //追加
    }
};

//ChartCanvasの体裁を整えてグラフを描画するメソッドを新規作成
var MyChart = null;
createChartObj = function (y) {
    var context = document.getElementById('ChartCanvas').getContext('2d');
    context.canvas.width = 300;
    context.canvas.height = 300;
    context.canvas.style.position = 'relative';
    context.canvas.style.margin = 0;
    context.canvas.style.left = Graphics._canvas.offsetLeft + 'px';
    context.canvas.style.top = Graphics._canvas.offsetTop + y + 'px';
    context.canvas.style.opacity = 1;
    MyChart = new Chart(context, MyChartData);
}

//グラフの削除を追加定義
Window_Selectable.prototype.activate = function () {
    Window_Base.prototype.activate.call(this);
    this.reselect();
    Graphics._clearChartCanvas();  //追加
};

//グラフを削除するメソッドを新規作成
Graphics._clearChartCanvas = function () {
    if (MyChart != null) MyChart.destroy();
    var context = this._chartCanvas.getContext('2d');
    context.canvas.style.opacity = 0;
    context.clearRect(context.canvas.style.left, context.canvas.style.top, context.canvas.width, context.canvas.height);
};

//以下チャートの定義
var MyChartData = {
    //グラフの種類
    type: 'radar',
    //データの設定
    data: {
        //データ項目のラベル
        labels: [],
        //データセット
        datasets: [
            {
                //線のカーブ
                lineTension: 0,
                //例
                label: '',
                //面の表示
                fill: true,
                //背景色
                backgroundColor: 'rgba(250,241,109,0.5)',
                //枠線の色
                borderColor: 'rgba(250,241,109,0)',
                //結合点の背景色
                pointBackgroundColor: 'rgba(250,241,109,0)',
                //枠線の太さ
                borderWidth: 0,
                //線の色
                strokeColor: 'rgba(0,0,0,0)',
                //値の点の枠線の色
                pointStrokeColor: 'rgba(255,255,255,0)',
                //背景色(ホバーしたときに)
                //hoverBackgroundColor: 'rgba(179,181,198,0.4)',
                //結合点より外でマウスホバーを認識する範囲(ピクセル単位)
                pointHitRadius: '100px',
                //グラフのデータ
                data: []
            }
        ]
    },
    //オプションの設定
    options: {
        //グラフ外側の文字色
        scaleFontColor: 'rgba((255,0,0,1)',
        //サイズ自動調整
        responsive: false,
        //ボーダーライン幅
        borderWidth: 2,
        //スケール
        scale: {
            //タイプ
            type: 'radialLinear',
            //フォントサイズ
            fontSize: 0,
            //ボーダー色
            borderColor: 'rgba(0,0,0,0)',
            //ポイントラベル
            pointLabels: {
                //フォントサイズ
                fontSize: 10,
                //フォントカラー
                fontColor: 'rgba(255,255,255,1)'
            },
            //目盛
            ticks: {
                //間隔
                stepSize: 50,
                //最大値
                max: 200,
                //最低値がゼロ
                beginAtZero: true,
                //ラベル表示
                showLabels: true,
                //フォントカラー
                fontColor: 'rgba((255,0,0,1)',
                //フォントサイズ
                fontSize: 10,
                //ラベルバックドロップ表示
                showLabelBackdrop: false,
            }
        },
        //凡例
        legend: {
            //非表示
            display: false
        },
        //ホバー
        hover: {
            //動作(single, label, dataset)
            mode: 'label'
        }
    }
};

これをツクールMVで実行したところ、ステータス画面にグラフが表示されました。
ヤッタネ!
マウスを各パラメータの上にホバーさせると具体的な数値が確認できたりします。

SnapCrab_NoName_2016-11-30_3-11-8_No-00.png

こちらにファイル版置いておきますので、是非初期プロジェクトなどに導入してグラフを実際にいじってみてください!

説明がメインでしたので、グローバルの競合などは意識しておらず、既存のメソッドも問答無用に上書きしてますので、プラグインではなくテスト版ということでご容赦を><

SnapCrab_NoName_2016-11-30_3-46-16_No-00.png

終わりに

みなさん、最後まで読んでいただき、ありがとうございます。
今回はステータス画面の変更を行いましたが、他のプラグインと合わせることにより、とどめを刺したキャラ
の比率を円グラフにしたり、ゲーム内日にちごとのアイテム相場を折れ線グラフで表したり、データを共有して各プレイヤーごとの回答結果を棒グラフの積層チャートにしたり、数字に関するさまざまな事柄をグラフにして導入できそう! いうワクワク感を少しでも感じていただけたら幸いです。
まだまだ、面白いライブラリが他にもあると思いますので、是非連携に挑戦してみてはいかがでしょう?
だいぶ長くなりましたが、これにて終了とさせていただきます。
本当にありがとうございました!

追記

この記事は RPGツクールMV Advent Calendar 2016 の1日目の記事です。
ツクールMVに関する記事をいろいろな方が掲載していますので、よろしければそちらも是非ご覧ください。
それではノシ

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away