Akashic 関連ツールに組み込みのテンプレートを使って、ランキング対応ゲームの作成をご紹介します。
本格的なニコ生ゲームの作成には、 Akashic Engine を使った JavaScript プログラミングが必要です。 これ以降のページでは、読者が JavaScript の基礎的な知識を持っていると仮定します。
改造編同様、前提として Akashic Engine 関連ツールが必要です。 改造編の インストール のページを参考にインストールを行ってください。
akashic-cli
をインストールしたことで、 CUI ツールで akashic
コマンドが利用できるようになっています。
これを使って、ここで解説するランキング対応ゲームを生成します。
参考: Akashic Engine 入門の 空のゲームの作成
まずは改造編同様、CUI ツールを起動します。適当な作業場所のフォルダに cd
コマンドで移ってください。仮に c:\akashic
というところに移るものとします。以下のようなコマンドになります。
c:
cd \akashic
ここに、今回のゲーム用のフォルダを用意したいと思います。以下のコマンドを実行してください。
mkdir game
cd game
mkdir
で game
というフォルダを作り、 cd
で作業場所フォルダを今作成した新しいフォルダにしたので、このフォルダで Akashic ゲームを生成したいと思います。
以下のコマンドを実行してください。
akashic init -t javascript-shin-ichiba-ranking
akashic init
は、「現在の作業フォルダに Akashic ゲームのを生成する」コマンドです。-t javascript-shin-ichiba-ranking
の部分は、「JavaScript 向け・ランキングゲーム用のテンプレートを使う」という意味の akashic init
コマンドのオプションです。これにより、いくつかランキングゲームの作成に必要な処理が最初から組み込まれたゲームが生成されます。
ここでは使いませんが、
-t
オプションを変えると、内容が空のゲームや TypeScript 向けの雛形などを生成できます。 参考: akashic-cli 利用ガイドの --type オプション
このコマンドを実行すると、対話型のインターフェースで色々聞かれると思いますが、それぞれ以下のように入力して Enter を押してください。
項目 | 入力値 | 備考 |
---|---|---|
width:(320) | 640 | ゲーム画面の横幅 |
height:(320) | 360 | ゲーム画面の縦幅 |
fps:(30) | (入力せずそのまま) | フレームレート |
ニコ生ゲームなので、ゲーム画面が (ニコニコ生放送の一般的な解像度である) 16:9 になるようにしています。この解像度比率にしておけば、映像とぴったり合ったサイズのコンテンツを作成できます。 width
や height
に表示されている (320)
や fps
の (30)
はデフォルト値を意味しているので、 fps
はそのまま Enter を押してもらう事で 30fps になります。
この状態で一度 akashic-sandbox
を実行してみましょう。
akashic-sandbox
http://localhost:3000 にアクセスすると、画面中央でキャラクタが浮かんでいるだけのゲームになっています。
このゲームは次の要素で構成されています:
これ自体は、画像表示や音声再生など、よく使われる機能のサンプルコードとしての意味あいが強く、ゲーム性はほぼありません。 弾を発射しても狙う敵はおらず、このキャラクタも移動するわけでもなく……このままでは画面の連打回数を競うだけのゲーム (余計なキャラクタつき) ですが、この状態でも最低限ランキング対応ゲームとしての形を満たしています。
以下、このテンプレートのコードを題材に、ランキング対応ゲームの作成に必要な作業をご紹介します。
現実にオリジナルのゲームを作る場合は、このコードを削って作成するのが簡単でしょう。具体的なゲーム作成方法は Akashic Engine 入門 (別ページで開きます) の「シングルプレイのゲーム作成」パートを参照してください。以下「入門」ページを読んだ後の読者を想定します。
最初に必要なのは「この Akashic ゲームはランキング対応ゲームである」と表明することです。
これは game.json の environment.nicolive.supportedModes
の値に ["ranking"]
を設定することで行われます。テンプレートコードの game.json では、既に次のように記述されています。
{
// ... その他の記述 ...
"environment": {
"nicolive": {
"supportedModes": ["ranking"]
}
}
}
以前は
environment.nicolive
ではなくenvironment.niconico
プロパティを利用していました。 現在environment.niconico
は非推奨になっています。 詳細は別項 ニコ生ゲーム関連の仕様 を参照してください。
表明した以上はランキング対応でなければなりません。単なるシングルプレイの Akashic ゲームと、ランキング対応ゲームの違いは、大きく次の二つです。
そのためまず決めることは 制限時間 、「このゲームは何秒のゲームなのか」です。
game.json に environment.nicolive.preferredSessionParameters.totalTimeLimit
の項目を記述すると、希望の制限時間を申告することができます。
次の例では、希望制限時間を 30 秒としています。
{
// ... その他の記述 ...
"environment": {
"nicolive": {
"supportedModes": ["ranking"],
"preferredSessionParameters": {
"totalTimeLimit": 30
}
}
}
}
totalTimeLimit
には 20 以上 200 以下の整数を指定できます。
特に指定しなかった場合は、適当なデフォルト値が使われます (現在は 80 秒程度になります)。テンプレートでは特に指定していないので、必要なら上の例を参考に記述を追加してください。
後方互換性のため、
environment.nicolive
がなくenvironment.niconico
がある場合、そちらが参照されます。
制限時間は、上記のように申告さえしておけば (あるいはしなくても) 他の対応は不要です。 ゲームは制限時間に応じて自動的に・強制的に終了されます 。
しかし現実には、「残り時間をゲーム画面に表示したい」といったことはよくあるでしょう。 あるいは残り時間が短くなったら結果発表演出を行いたい、ということも考えられます。
テンプレートのコードでも残り時間の表示を行なっています。
テンプレートの main.js では、 main()
の引数 param
から制限時間を取得しています。次の箇所がそれです。
let time = 60; // 制限時間
if (param.sessionParameter.totalTimeLimit) {
time = param.sessionParameter.totalTimeLimit;
}
この値を使って、次のように scene.update
で 1 フレームごとにカウントダウンしていき、ラベル timeLabel
で画面に表示しています。
const updateHandler = () => {
if (time <= 0) {
// (中略)
scene.update.remove(updateHandler); // このハンドラを削除=カウントダウンを停止
}
// カウントダウン処理
time -= 1 / g.game.fps;
timeLabel.text = "TIME: " + Math.ceil(time);
timeLabel.invalidate();
};
scene.update.add(updateHandler);
制限時間は (game.json で希望値を申告していても) 必ず
param.sessionParameter
から取得してください。というのも、 game.json での記述は、あくまで "希望値の申告" であるためです。 ゲーム実行時の実際の制限時間が保証されるわけではありません。
制限時間は「ゲーム起動から終了までの全ての時間」であることに注意してください。 これには「ゲームリソースのダウンロード時間」なども含まれます。 そのため、制限時間のギリギリで行う演出は、回線状況によって途切れてしまう可能性があります。 終了前に結果発表演出などを挟む場合は、念のため 制限時間の 10 秒程度前までに演出を完了させることを推奨します 。
ランキング対応に必要なもう一つの作業は、スコアを設定することです。
これは g.game.vars.gameState.score
に整数を代入することで行われます。
g.game.vars
は、「ゲーム開発者が自由に使っていい領域」として Akashic Engine が提供している変数で、初期値は空のオブジェクトです。ランキングゲームではここに gameState
という変数が入っていることを期待します。
そのためテンプレートの main.js では、次のようなコードで最初にスコアを 0 点にセットしています。
g.game.vars.gameState = { score: 0 };
テンプレートのゲームは、単に画面がクリックされたら 1 点加算するというものでした。 これは次の記述で実現されています。
scene.pointDownCapture.add(() => {
// 制限時間以内であればタッチ1回ごとにSCOREに+1します
if (time > 0) {
g.game.vars.gameState.score++;
scoreLabel.text = "SCORE: " + g.game.vars.gameState.score;
scoreLabel.invalidate();
}
// ... その他、弾の発射や効果音再生処理
});
++
で score
を 1 加算し、それをラベル scoreLabel
で画面に表示しています。
実際のゲームでは「アイテムを取得した時」「敵を倒した時」「一定時間生き残った時」などお好みの条件でスコアを追加してください。
スコアは 0 以上の整数である必要があります。上限は今のところありませんが、現実的に表示が崩れてしまうので、10 万未満 (5 桁以内) を推奨します。
スコアは、ゲーム中も常に
g.game.vars.gameState.score
に代入してください。つまり「スコアを中間変数に保持しておいて、最後に
g.game.vars.gameState.score
に代入する」というような処理にはしないでください。 というのも、通信遅延などで起動が遅れた場合などでは、最悪score
の代入が間に合わずにゲームが終了されてしまう可能性があるためです。 途中までプレイできていたのに 0 点扱いになってしまうと、プレイヤーからは不具合に見える動作になってしまいます。
前述のとおりランキング対応ゲームは、配信者・各視聴者の手元で個別に実行されるシングルプレイのゲームです。
このため g.game.random
で生成される乱数が、プレイヤー間で一致しません 。
言い換えると、 g.game.random
と g.game.localRandom
がどちらも各プレイヤー固有の乱数を生成してしまいます。
これはマルチプレイゲームと異なる点に注意してください。マルチプレイの場合、エンジンが乱数シードを揃えるため、
g.game.random
の結果はプレイヤー間で一致します。
そこでランキング対応ゲームテンプレートは、プレイヤー間で共通の乱数生成器 (共通乱数生成器) を独自に提供しています。
テンプレートの main.js 内、 main()
の引数が param
の時、共通乱数生成器は param.random
で取得できます。
function main(param) {
const random = param.random; // 共通乱数生成器
const r = random.generate(); // プレイヤー間で共通の乱数値を生成
}
ランダム性を持たせつつ、全プレイヤーが等しい条件で競うようなゲームを作成する場合は、この共通乱数生成器を利用してください。
もちろんトランプの手札や麻雀の配牌のように、プレイヤーごとにランダムに異なる状態を作りたいゲームでは、単に g.game.random
(か g.game.localRandom
) を利用することになるでしょう。
ゲームが作成できたら、ニコ生ゲーム投稿 ページから投稿できます。 ニコ生ゲームを投稿しよう を参照して、投稿してみてください。