はじめに
ニコ生ゲームを作りたい!けどプログラミングは初めてという人に向けて、覚えるべきJavaScript知識をまとめました。
というのはニコ生ゲームを作る方法を解説した公式サイト Akashic Engine では、 JavaScriptの知識があることを前提に、ゲームエンジン固有の機能紹介をしている からです。
つまり、何か分からないことがあっても、ゲームエンジン固有の疑問は公式サイトで解決できるのですが、そこに書いてあるコードはJavaScriptの知識がないと理解できない事態が発生してしまうのです。
これにより、いざゲームを作ろうとしても「分からないことをいくら探しても書いてない」(JavaScriptの知識を公式サイトで探そうとしている)、「ググっても出てこない」(ゲームエンジンの知識をJavaScriptの解説記事で探そうとしている)という事態が起こってしまっているのです。
そこで、JavaScriptは初めてという人向けに、これを覚えておけばおおよそニコ生ゲームができる知識をまとめました。
対象読者
- これからJavaScriptの勉強を始める人
- 自分でJavaScriptのコードを書いたことがない人
- これからニコ生ゲームを作ってみようとしている人
- ゲームエンジン「Akashic Engine」の存在を始めて聞いた人
- 導入 にしたがって、コーディング環境を用意できた人
値
「値」とは、 42 とか "I have a pen." などのデータを表したものです。 JavaScript では自分で定義した値を保存して利用していくことができます。数字や文字はわかりやすいですね。他にも複雑なデータを格納できる仕掛けがあります。
配列
配列とは値を複数格納できる値です。 [<値1>, <値2>, ...] という文法で書きます。ゲームでは色々な値がたくさん登場して、それを一括で管理したいときが多々あります。たとえばマップにいる敵一覧、シューティングゲームの弾一覧など。このあと「変数」という値の格納方法を紹介するのですが、コードをスッキリさせるためにこの配列というものが使われています。
例
[1, 2, 3, 4, 5]
オブジェクト
オブジェクトは複数の「値」を一つにまとめたものです。 { <key1>: <値1>, <key2>: <値2>, ...} という文法で書きます。 配列との違いは「キー」というラベルが付いていることです。同じような性質をもった値をバラバラにしないでまとめて管理するために使われます。たとえば、RPGで主人公のつよさ、すばやさ、HP、MPといったパラメータを表現するのに使われます。それぞれ値があって、それぞれの値に意味があるとき使われます。
例
{
power: 50,
speed: 30,
hp: 130,
mp: 0
}
余談ですが、キー名は小文字(少なくとも先頭文字は小文字)という雰囲気があります。これは他の人が読んだときに読みやすくするためのお作法です。郷に入っては郷に従え。
関数
JavaScriptでは関数も値です。関数の使い方は後で説明しますが、関数自身も他と同じ値ということを忘れないでください。
変数
定義する
変数はさきほど紹介した値を格納(セーブ)する概念です。 <変数の種類> <変数名> = <値>; という文法で値を保存できます。変数の種類は固定値のconst、後から値を変更できる let、なんでもありの var があります。 var は昔使われていたもので、最近は const か let で書くことが推奨されています。というのは固定値かどうか見た目で分かったほうが、コードが読みやすいからです。思いがけないバグを防ぐための機能です。
const name = "山田太郎"; // 固定値
let money = 10000; // あとから変わりうる
const items = ["やくそう", "どくけしそう", "ひのきのぼう"]; // 配列
const profile = {
power: 50,
speed: 30,
hp: 130,
mp: 0
}; // オブジェクト
上記の // <文字列> はコメントです。後述。
変数名も小文字(少なくとも先頭文字は小文字)という雰囲気があります。
変更する
<変数名> = <値> で、変数の値を変更したり、他の変数にコピーできます。
let money = 10000;
money = 100;
const name = "山田太郎";
name = "麻生太郎"; // エラー! const のため
const nickname = name; // 変数のコピー
配列の一部を変更する
配列の特定の値(要素といいます)を変更するには <配列名>[<番号>] = <値> と書きます。先頭要素が0番目な点に注意。
const items = ["やくそう", "どくけしそう", "ひのきのぼう"];
items[1] = "まんげつそう"
// items は ["やくそう", "まんげつそう", "ひのきのぼう"];
const な配列でも中の要素を変えることができます。
え?配列の一部の要素を削除したいって?
色々ありますが、もう一度配列を作り直す方法を紹介します。これは後で出てくる関数が関係して、JavaScriptの提供機能と相性がいいからです。
let items = ["やくそう", "どくけしそう", "ひのきのぼう"]; // let で
items = ["やくそう", "ひのきのぼう"]; // ほら減った!
オブジェクトの一部を変更する
オブジェクトの場合、 <変数名>.<キー名> だったり、 <変数名>["キー名"] で対応する値を変更できます。
const profile = {
hp: 130,
mp: 0
};
profile.hp = 180;
profile["hp"] = 200;
const なオブジェクトでも中の値を変えることができます。
コメント
// <文字列> か /* <文字列> */ でコードとは無関係の注釈を記述できます。コードだけ書いても人間は忘れてしまうので、人間のためにある機能です。
let money = 50; // 所持金
/* 主人公の所持品。買い物で増やせる。使うとなくなる */
const items = ["ひのきのぼう"];
関数
定義する
こっから少し難しくなります。関数はある入力をもとに、特定の処理をして、結果を出力する値です。 (<入力値1>, <入力値2>, ...) => { /* 処理 */; return <結果>; } と書きます。入力値のことを仮引数と言うので、ググるときのために覚えておいてください。
たとえば主人公と敵のプロフィールをもとに、敵に与えるダメージを計算する処理を関数で書いてみましょう。
// 主人公のプロフィール
const hero = {
power: 50, // 攻撃力
defence: 10, // 守備力
hp: 150
};
// 敵のプロフィール
const enemy = {
power: 5, // 攻撃力
defence: 1, // 守備力
hp: 10
};
/* 攻撃者 attacker が、相手 defender に攻撃したとき、相手が食らうダメージを求めます */
const getDamage = (attacker, defender) => {
// ダメージ = 攻撃力 - 相手の守備力
return attacker.power - defender.defence;
};
余談ですが、関数名も小文字(少なくとも先頭文字は小文字)という雰囲気があります。
一応、下記の書き方もできますが、もろもろの経緯で最近は上記の書き方が主流です。ググると現状では半々くらいだけど、だんだん上記になっていくんじゃないかな(筆者の見解)
/* 攻撃者 attacker が、相手 defender に攻撃したとき、相手が食らうダメージを求めます */
function getDamage (attacker, defender) {
// ダメージ = 攻撃力 - 相手の守備力
return attacker.power - defender.defence;
}
使う
一度定義した関数を使うには <関数名>(<入力値1>, <入力値2>, ...) とします。入力値のことを引数と呼ぶのでググるときのために覚えておいてください。
const hero = { /* 略 */ }; // 主人公のプロフィール
const enemy = { /* 略 */ }; // 敵のプロフィール
// 関数の定義
const getDamage = (attacker, defender) => {
return attacker.power - defender.defence;
};
// 関数を使って、くらったダメージを求める。
const damage = getDamage(hero, enemy);
// 主人公のHPを減らす
hero.hp = hero.hp - damage;
ニコ生ゲーム制作でよく使う関数
自分で定義してもいいけど、JavaScript自体が提供している関数もあります。
Math.floor(<数値>)
小数を切り捨てて整数にする。ニコ生ゲームのスコアは整数にした方がいい雰囲気。四捨五入は Math.round(<数値>)、切り上げは Math.ceil(<数値>)
<配列>.push(<値>)
配列の末尾に値を追加する
<配列>.forEach(<関数>)
配列の各要素にたいして処理を実行します。
// 所有アイテムを画面に表示する
const items = ["ひのきのぼう", "おなべのふた"];
items.forEach(
// i には配列の各要素が格納されます。
(i) => {
// 以下はAkashic Engineの命令
const entity = new g.Sprite({ /* 略 */ });
scene.append(entity);
}
);
<配列>.map(<関数>)
配列の各要素に決まった処理をして別の配列を出力します。
// 所有アイテムから描画用エンティティを作成する
const items = ["ひのきのぼう", "おなべのふた"];
const entities = items.map(
// i には配列の各要素が格納されます。
(i) => {
// 以下はAkashic Engineの命令
const entity = new g.Sprite({ /* 略 */ });
return entity; // この値が出力される配列に格納されます。
}
);
// entities は [g.Sprite, g.Sprite]
アイテム情報から描画アイテムエンティティにまとめて変換したいときなどに使います。
<配列>.filter(<関数>)
配列の中で特定の条件をみたす要素だけを新たな配列にして出力する。
// すべてのクエスト
const quests = [
{
level: 0,
name: "はじめてのおつかい"
},
{
level: 9999,
name: "魔王討伐"
}
];
const myLevel = 1; // 自分のレベル
// 今受諾できるクエスト
const myQuests = quests.filter(
// q には quests の各要素が格納されて呼び出される。
(q) => {
// 自分のレベル以下のレベルのクエストは受諾可能
return myLevel >= q.level
});
/*
myQuests = {
level: 0,
name: "はじめてのおつかい"
}
要求レベルが高い魔王クエストは削除されている
*/
条件式
条件に応じて処理を変えるには if を使います。
if(<条件文>) { /* 条件がなりたつとき */ } else { /* 条件がなりたたないとき */ }
たとえば、ダメージ計算で相手の守備力を上回らなければミス扱いするにはこうします。
/* 攻撃者 attacker が、相手 defender に攻撃したとき、相手が食らうダメージを求めます */
const getDamage = (attacker, defender) => {
// 自分の攻撃力 > 相手の守備力ならダメージ
if (attacker.power > defender.defence) {
return attacker.power - defender.defence;
} else {
// ミス!
return 0;
}
};
繰り返し
配列の出番です。配列の全要素に対して処理するには for 文を使います。
// 所有アイテムを画面に表示する
const items = ["ひのきのぼう", "おなべのふた"];
for (const i of items) {
// i には配列の要素が格納されます。
// 以下はAkashic Engineの命令
const entity = new g.Sprite({ /* 略 */ });
scene.append(entity);
}
※for は他にも書き方があるのですが、ニコ生ゲーム制作でよく使う文法に絞って紹介してます。
実践
公式サイトのサンプルコード(簡略版)を読んでみよう
今まで紹介した知識を総動員して、実際にニコ生ゲームのコードを見てみましょう。
以下はクリックしたら点が増えるコードです。こんな感じのコードがAkashic Engineの公式サイトのサンプルにいきなり登場します。
ここまで読んできたなら、なんとなく雰囲気を掴めるはずです。
module.exports = () => {
const scene = new g.Scene({ game: g.game });
scene.onLoad.add(() => {
// 得点
let score = 0;
// 得点を画面に描画できるようにする
const scoreLabel = new g.Label({
scene: scene,
font: new g.DynamicFont({
game: g.game,
fontFamily: "sans-serif",
size: 50
}),
text: "得点: " + score
});
scene.append(scoreLabel);
// クリックしたら点が増える
scene.onPointDownCapture.add(() => {
score = score + 1;
scoreLabel.text = "得点: " + score;
scoreLabel.invalidate();
});
});
};
解説
module.exports = () => { /* 略 */ };
module.exports はなんぞやですが、要はニコ生ゲームは自身が定義したでっかいひとつの関数を module.exports に設定することで動く原理なのです。
new g.Scene({ game: g.game })
new はいつ使うか Akashic Engineの公式サイトに出てくるので、ここではそんなものかと思ってください。ポイントは { game: g.game }。オブジェクトですね!Akashic Engineではよくオブジェクトを作らせてこういう引数に要求してきます。オブジェクトの定義の仕方に慣れておきましょう。
scene.onLoad.add(() => { /* 略 */ });
初期化処理を定義した関数 () => { /* 略 */ } を scene.onLoad.add() 関数の入力値に設定します。関数は値でしたね。値なので、add()という関数の入力値(引数)にできます。add()すると、あとはゲームエンジン側で準備ができた段階で自分が設定した初期化処理を呼び出してくれます。Akashic Engineはよくユーザが定義した関数を引数に要求してくる(特にユーザー入力時処理、遅延実行処理)ので、関数の定義の仕方に慣れておきましょう。
scene.onPointDownCapture.add(() => { /* 略 */ });
上記と同じっぽいですが、実は重要な仕掛けが隠れてます。どこでしょう?
それは score = score + 1; です。ただ得点変数を変更しているだけのように見えますが、よく見てください、 score って その関数の外側で定義してますよね?ここで score を変えると let score = 0 で定義した score の値も変わります。当たり前のように見えますが、 関数の中で関数の外にある変数を変更できる というのは一見見落としがちだけど、超頻出するテクニックなのです。覚えておきましょう。クロージャーって言います。
まとめ
- ニコ生ゲームを作るためのゲームエンジン Akashic Engine は JavaScript の知識があることを前提にかかれている
- なので JavaScript を始めたばかりの人は、調べようにも JavaScript の知識なのか、ゲームエンジンの知識なのか分からず、情報を得られにくい
- 本記事ではニコ生ゲームによく使われる JavaScript の知識を紹介した
- 値: 配列(順番にならべる)、オブジェクト(キーがある)、関数
- ゲーム制作するとたくさんデータが登場するので、配列やオブジェクトにまとめると便利
- コメントを書いておくと後から見返すときに読みやすくて便利
- 関数には値を入力(引数)に指定することで、新たな値を得ることができる
- Akashic Engineはよく引数にオブジェクトや関数を要求する