一週間に一つミニゲームを作り続けるのに役立つひどいゲームをかろうじてましなゲームにする力

最近一週間で一つゲームを作るってのにチャレンジしてるよ、という記事をちょいちょい見かける。

ゲームはアイデアだけでは面白いかどうかわからないので、できる限りすぐに遊べる状態に持っていくのが大切。他の人が遊べるようにすればフィードバックがもらえる。そういった取り組みができるよう、毎週一つゲームを作ってリリースするチャレンジをするのがいいよという話。

推奨することとして、ゲームは良かろうが悪かろうがWebサイトでリリースしてコメントをもらう、一度リリースしたものはもういじらない、毎週全く違うものを作るようにする、作って分かったことを日記に残す、ということが挙げられている。

上の記事を受けて、実際に14週に渡ってゲームを作った人の記事。作ったもの、うまくいったところ、うまくいかなかったところ、学んだことが、各週について書いてある。

HTML5のゲームを8週に渡って作った人の、どうやれば短時間でゲームを作り続けられるかのコツ。とてもゲームと呼べないものでいいからとにかく小さく作れ、作りたいものに応じた適切なフレームワークを選べ、音やグラフィックスを作るツールや素材サイトを見つけろ、アニメーションやエフェクトなどでゲームをジューシーに'jucify'しろ、完成直前に友達や家族にテストプレイしてもらえ、あたりがコツらしい。

私も最近2ヶ月で9個ミニゲーム作った。

一週間に一つとか決めてないので、リリースペースは適当です。あと各ゲームを作って分かったこととか全然まとめてません。そろそろ力尽きそう。

一つ分かったことは、一週間に一つとかいうペースで作るんだと、ある程度作ったところで「これはひどいクソゲーだなあ」ということに気づいても、それをまるごと捨てて別のものを作るのは時間的に難しいということ。なので目の前にあるひどいゲームを、なんとかかろうじて遊ぶことはできる程度にはましなゲームに仕立てる能力があると、とても役に立つ。

一つ例を挙げると、2月に作ったSUM10というゲームとCALC +-*/というゲーム、最初に作った時は一つのゲームだった。SUM10みたいなフィールドに、1〜10の数字と+-*/の四則演算が置いてあって、フィールド上のカーソルが動くとそれら数字と四則演算がCALC +-*/のようにスタックに積み上げられ、ヒューレットパッカードの計算機よろしく逆ポーランド記法で計算され、その数字が10の整数倍になると点が入る、というゲームだった。

作って遊んで分かったのだが、このゲームは死ぬほど難しい。適切な数値と演算を限られたフィールド上から揃えることが不可能に近く、それをリアルタイムにこなすなんて並の人間には無理っぽく、ひどいクソゲーだった。

なので2つのゲームに分けた。片方は最初から数値だけがスタックに積み上げられており、四則演算だけをプレイヤーが入力して目標の数字にするCALC +-*/、もう一つは数字が敷き詰められたフィールド上をカーソルを動かして足した数が10の整数倍になると点が入るSUM10。こうすることで、それぞれのゲームに難しさが分散されて、かろうじて遊べる形になった。

難しすぎる1つのゲームをそこそこ難しい2つのゲームに分けて遊べるようにする、ってのは後段の泥縄処理としてはかなり美味しい話で、そんなにうまいこといくことはまれだ。だけど、それ以外にも、スコアリングルールを変えるとか、敵と自分をひっくり返すとか、操作対象のキャラを変えるとか、キャラの数をやたらと増やすとか、そういったことをすることで、ゲームの備えるギミックはそのままでかろうじて遊べるゲームになることはままある。

短時間でミニゲームを作ってると、そういった最後の悪あがき力も鍛えられるという点は、想定していなかった利点ではある。自作ゲームを味見して、ちょっとしたフレーバーを加えることでゲームの味を調える、土壇場のゲーム調整能力は、ミニゲーム制作にとっては重要なポイントであるかも。

2013年作ったゲーム遊んだゲーム

ちょうど20個なので例年の30個ペースには届いとらん。

その代わりに今年はオレオレゲームエンジンとかDSLとか作った。

ってその代わりとか言えたもんじゃないぞ。「ゲームエンジンを作るなゲームを作れ」を合言葉に邁進しないと、ゲームは永遠に完成しないのだ。

遊んで面白かったゲームは、あんまり遊べてないが、

と思ったら数だけはまあまあ遊んでるな。といってもほとんどコンシューマー機ゲームがなくて、ブラウザ上で遊べるかスチームに置いといてもらわないと面倒で遊ばないという体たらくではある。来年も色々作って色々遊んでいきたい。

ミニゲームのステージを自動生成した時の難易度調整はどうする?

最近はよくて10分遊んでもらえれば上出来みたいなミニゲーム作っているが、それでも数回は繰り返し遊んでもらえるくらいのリプレイアビリティは欲しい。でも面倒な作り込みはしたくない。

と考えた結果、ステージは無限に自動生成、1ステージは10秒くらい、残機無し即死だけどリトライし放題、クリアステージ数はブラウザに保存してコンティニュー可、という作りにしている。ステージをどこまで進められるか、というくらいのリプレイアビリティは得られるといいな、というもくろみ。

自動生成されるステージは面が進むたびに少しずつ難しくならないとハリが無い。かといって単に線形に難しくなっていくとメリハリが無い。それなりの波がありながらも確実に難しくなっていくステージをどうやったら作れるかなあということを考えていて、今のところ下のグラフを使っている。

difficulty graph

横軸はrandom()の0〜1の出目で、縦軸が難度(0が簡単で1が難しい)。ステージ開始前にrandom()を振ってその出目に対応する難度をそのステージの難度として採用する。

このグラフの難度は

Math.pow(random(), 100 / (stage + 1))

で求まっていて、ステージ数が少ないうちは難度の低い下方向に張り付き、ステージ数が進むと上へ膨らむようになっている。なので、先のステージになると、より高い難度が設定されやすくなる、けどたまにrandom()の機嫌によってはそのステージ数にしてはやたら簡単だったり難しくなったりする。

あとステージ全体の難しさが一つの難度で決まるのも楽しくないので、

  • 敵の数
  • 敵が弾を撃つ頻度
  • 弾のスピード

とかのいくつかのパラメタに対してそれぞれrandom()を振って上の式を当てはめた難度を設定する。そうするとあるステージでは敵の数はやたら多いけど弾はあまり撃たないとか、敵は一人しかいないんだけどやたら速い弾をやたら撃つ、とかいうバリエーションが得られる。

後半面になると全てのパラメタが難度1に張り付きがちになるので、全般的に難しい面が連続するようになる。でも難度自体は1が上限なので、単に線形に難度を上げていくよりはリミッタがかかる。

ていう具合にすると、まあ納得できる形で理不尽に難しくなっていくので、そこそこ気に入ってます。最終的には全てのパラメタが難度1に張り付いてカンストするので、無限に難しくなるのは実現できないし、その1に張り付いた難度設定をどの程度にすればいいの、ってところの調整が難しいのが難点。難易度曲線を考えるのは好きなので、またいろいろ考えたい。

ゲームは一日一時間半!

遊ぶ方じゃなくて作る方ね。

31個のゲームを31日で作った人のブログ記事。

They were all made in less than 3 hours (most of them in less than 90 minutes).

ゲームメカニズムに注力して、見た目や音は重視してないとはいえ、ほとんどのゲームを90分以内に作ったというのは偉業だ。

実際90分でゲームを一つ、それもコンスタントに作るってのは、可能なんだろうか。おっさんになると90分くらいでも集中するのが厳しくなるので、例えば90分を3つのパートに分けて考えると、

  • Step1: ゲームの基本ルールを作る(30分)
  • Step2: レベルデザイン、難度設計(30分)
  • Step3: テスト、バランス調整、エフェクト、サウンド(30分)

くらいの配分かなあ。それぞれのステップを具体的に考えてみる。

  • Step1: ゲームの基本ルールを作る(30分)

自機が動くとか、敵が出るとか、ショット撃って敵を倒すとか、そういう基本的な動き、メカニズムを作る。この部分に今までのゲームとちょっと違うギミックが無いと、あまりに平凡なゲームになるので、なにか新しいものを入れたい。となると、30分以内という時間制限はかなり厳しい。

レベルデザインと言っても30分ではまともに作れるわけないので、実際は難度曲線に基づく敵出現パターンの自動生成みたいなアプローチになりがち。適切なレベルを自動生成するってのはそんなに簡単じゃない。となると、30分以内という時間制限はかなり厳しい。

  • Step3: テスト、バランス調整、エフェクト、サウンド(30分)

ここでStep2の実装が適切かのテスト、バランス調整を経た後に、パーティクルとかのエフェクトや効果音などを入れるという多大な作業を一気に行う必要がある。となると、30分以内という時間制限はかなり厳しい。

結論:厳しい。

実際はこれらステップの前に、

  • Step0: 30分でルールが、30分でレベルデザインができるゲームを思いつく(任意分)

ってのがあって、ここがうまくこなせるかというのがキモなのだがそうそう思いつかない。

いや、例えば全方位からくる敵を避けるだけとかいうベタなゲームくらいならすぐ作れるけどね、とか思って、

今コレ作ったら57分かかった。コードはたった123行しか無いのに。やっぱりStep1がどんだけ単純でも、Step2の難度設計とか、Step3のバランス調整とかをちょっとでもやろうとすると、それなりに時間がかかってしまうもんだね。

  • Step1: ゲームの基本ルールを作る(15分)
  • Step2: レベルデザイン、難度設計(30分)
  • Step3: テスト、バランス調整、エフェクト、サウンド(45分)

くらいのバランスで、基本ルールを極めてシンプルにしないと、90分で完成するゲームは難しいのかもしらん。

ゲーム開発日記を簡単に作る方法はないかね

Androidゲームを23日で作った記録のブログだって。こういったゲームを作る行程が見られるのはいいね。

ゲームの開発日記的なものが残せるのは、他の人が作り方を学べるところもいいけど、記録する自分自身にとっても、自分は今日これだけ頑張った、っていうことが実感で分かるのが良さそう。

でも、いざこういった記録をちゃんと残そうと思うと結構面倒そう。今日書いたコードの断片を記録して、スクリーンショットを取って、説明を書いて、とかいう作業を、コードを書く以外にも毎日コツコツ続けるのは、なかなか骨が折れそうだ。

せめて今日の作業に関連するコードとかスクリーンショットとかは、ツールで自動的に記録してくれる方法があればいいんだが、ちまたにそういったツールは落ちてないかね。

コミットログ

普通に考えると、バージョン管理システムのコミットログが使えないかと考える。

コミットのコメントとかコードのdiffとか見られるから、基本的な作業内容は分かりそう。ただスクリーンショットを付加する、とかいう用途はあまり考えられてないよね。

継続的インテグレーションツール

Jenkinsに代表される、ビルドやテストを自動化し、開発状況の可視化を助けてくれる、継続的インテグレーションツールとそのプラグインを駆使すれば、所望のものが作れるかもしれない。

ただ個人ゲーム開発のログを取るためだけに使うにはちょっと大げさな気が。

コマンドラインツールを適当に組み合わせる

要はdiffとスクリーンショットが取れればだいたい事足りるから、

なんかでバックアップを取りながら最新のコードとのdiffを適当に記録して、

なんかでスクリーンショットを適当に取る、ってのを実行するスクリプトを用意すればいいかなあ。あとはスクリプトを適当な作業の終わったタイミングで起動したり、IDEのPre/Post-Buildで自動実行するとか。

あんまり決定版というのはないね

diffとかスクリーンショットが集まっても、それをあとで整形しやすいようにMarkdown形式にしたりとかの、後処理用ツールはまた別に必要になりそう。ニッチなニーズだとは思うが、開発日記作成用のツール群、どっかにありませんかね。

Haxeの@:buildマクロを使えばコードにDSL混ぜ放題、かも

Haxeではclass宣言の前に@:buildマクロを書けば、class内の関数とかをビルド時に受け取って、その内容を書き換えるマクロが作れる。

なので、こういうインタプリタ作って、

コードを書いて、

package ;
import neko.Lib;
using totsubefu.Interpreter;
@:build(totsubefu.Interpreter.parse(":b"))
class Main {
	function new() {
		hello();
	}
	@:b function hello() {'
v @_       v
>0"!dlroW"v 
v  :#     < 
>" ,olleH" v
   ^       <
	';}
	static function main() {
		Interpreter.setPrint(Lib.print);
		new Main();
	}
}

ビルドして、

% haxe -main Main.hx -neko Main.n
totsubefu/Interpreter.hx:20: 
_人人 人人 人人_
> 突然のBefunge <
 ̄Y^Y^Y^Y^Y^Y^Y ̄

v @_       v
>0"!dlroW"v 
v  :#     < 
>" ,olleH" v
   ^       <
	

実行できる。

% neko Main.n
Hello, World!

今回はBefunge (wikipedia:Befunge)を混ぜた。

マクロ内でContext.getBuildFields()を呼び出せばクラス内の関数などがArrayで取り出せるので、中身を解析して書き換えてArrayを返せば、関数の中身などをビルド時に書き換えることができる。メタプログラミングによるDSL構築の一種。

この仕組みを使いこなせば、例えばHaxeには無いコルーチン的な仕組みを、後付けで無理やり作ったりできる。

ただ、ExprDef (http://haxe.org/api/haxe/macro/exprdef)を見ると分かるけど、Haxeの持ついろんな構文を解析して書き換えるのは結構大変そう。Haxe自体と密結合できるDSLを混ぜ込むのは手間がかかるかも。

EmscriptenとLDCを使えばD言語のSDLゲームがブラウザで動かせる、かも

とりあえずひどく簡単なサンプルは動いたけど、D言語でclassを書いたらLLVMからJavaScriptへの変換が失敗したので、まだ実用までは壁がありそう。

EmscriptenLLVMからJavaScriptへのコンパイラ

LDCD言語からLLVMへのコンパイラ

なので、D言語LDCLLVMコンパイルし、LLVMEmscriptenJavaScriptコンパイルすれば、ブラウザ上でD言語が動かせる。原理的には。EmscriptenD言語SDLに対応しているのでSDLも動く。原理的には。

Windowsで動かす場合、例えば以下のコードを書く。

import SDL;

immutable width = 640, height = 480;
SDL_Surface *screen;
int ticks = 0;

void update() {
  SDL_LockSurface(screen);
  for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            *(cast(char*)screen.pixels + i*width*4 + j*4 + 0) = cast(char)(j + ticks) % 255;
            *(cast(char*)screen.pixels + i*width*4 + j*4 + 1) = cast(char)(i + ticks) % 255;
            *(cast(char*)screen.pixels + i*width*4 + j*4 + 2) = cast(char)((255-i) + ticks) % 255;
    }
  }
  SDL_UnlockSurface(screen);
  SDL_Flip(screen);
  ticks++;
}

extern(C):
void mainLoop() {
  update();
}

void emscripten_set_main_loop(void function(), int fps, int simulate_infinite_loop);

int main() {
  SDL_Init(SDL_INIT_VIDEO);
  screen = SDL_SetVideoMode(width, height, 32, SDL_SWSURFACE);
  emscripten_set_main_loop(&mainLoop, 60, 1);
  return 0;
}

emscripten_set_main_loopってのは、各フレームごとに呼ぶ出される関数を指定するためのもの。Emscripten使う場合は、SDL_Delayではなく、これを使う必要があるらしい。

あとは以下のようにコンパイルすれば、

ldc2 -O -ISDL hello.d -output-ll

LLVMコンパイルされたhello.llができる。次はこれを、

python emcc -O2 -s ASM_JS=1 hello.ll -o hello.html

とすれば、LLVMから変換されたJavaScriptを含むHTMLファイルができる。'-s ASM_JS=1'オプションをつければ、asm.jsを使ったJavaScriptが生成されるので、せっかくだからasm.jsに対応したFirefox22で動かしたほうが楽しいかも。残念ながらこんくらいの単純なサンプルだとChrome26と比べて別段速くはないが。

とりあえずの問題は、最初にも書いたようにclass書くとEmscriptenがコケるところ。何も含まない空のclass書くだけでコケるのがなんとも困る。LDCとClangでclassの扱いが違うのかね。

あと、Windows上でコンパイルのためのツールを整備するのがかなり面倒。Emscriptenについては、

にある長い手順をこなさないといかんし、LDCは、

というさらに面倒な手順が必要。しかもうちの環境だと5.の最後のninja叩く前に、build.ninjaの'-D NDEBUG'をすべて消さないと、7.のLDCのビルドがコケた。

D言語ブラウザゲームが書けるようになるとかなり便利なので、このまま順当に進化してもらって実用になって欲しいところ。とりあえずclass問題がなんとかなるといいな。あと音周りもだぶんまだだよね?SDL_Mixer対応とか。

追記:

Emscripten自体はSDL_Mixer対応しているらしい。