Hatena::ブログ(Diary)

ABAの日誌

Back to ABA Games
Twitter (abagames)
カレンダー
 

2015-01-11

手続き脳人間がWeb向け関数型言語elmを使ってゲームを書こうとしてみた

がまだ私には難しすぎる気がするよ……

elmはHaskellに似た構文を持つ関数型言語のAltJS。コンパイルするとJavaScriptが生成されるのでブラウザ上で動くゲームも作れる。なのでごく簡単なミニゲームをelmで作ってみた。

ゲームライブラリ相当の部分を除くと250行強というところなので、コードの分量的にはCoffeeScriptで書くのと似たようなものかちょい長めというところかなあ。でもコードを書く際には関数型言語ならではのかなり違う発想が求められるので、なかなか苦労する点も多い。elmについて、このミニゲームを書いた時に気づいた点をメモしておく。

elmいいところ

サンプルにPongがある

なんでelmを使ってみようかと思ったかというとオフィシャルにPongを作るサンプルがあるからですよ(ただし上記のサンプルまだver.0.13の書き方なので注意。今の最新のelmは0.14)。elmは単なる関数型言語ではなくて、 Functional **Reactive** Programmingのための言語で、その特性を活かしてゲームを作るためのエッセンスが、このPongの作り方に書いてある。

elmではSignalと呼ばれる、時間で変化する値を関数型言語の枠組みで扱うための仕組みがある。時間で変化する値とは、例えばゲームのプレイヤーからのキー入力など。

まだ理解が追いついてないのだが、Signalでキー入力を扱うと、時系列のキー入力状態がリストとして管理される。キーが入力されるとその情報がこのリストの末尾に追加される。

Signalにはこのリストを過去から畳み込むための関数foldpがある。過去から畳み込む!もうこの時点でお前は何を言っているんだ感があるが、要はこれら入力に応じて今から次の未来への変換を行う関数を発火させて、別のSignalを作る仕組み。まあゲームでいうところの毎フレームのUpdate処理だ。

elmのmain関数は画面上のElementのSignalとして定義されている。なのでUpdate処理で生成されるSignalからElementのSignalを作れば、ゲームの画面出力が得られる。なので必要なのは入力や時間経過のSignalをfoldpでゲーム状態のSignalにした上でそれをElementのSignalにmapする関数、とか言い始めるとよく分からなくなるのでPongのサンプルを見て下さい。とにかくPongのサンプルを見ればelmでゲームを作る方法がすぐに分かって素晴らしい。

コアライブラリで基本的な表示と入力が扱える

コアライブラリにあるGraphics.Collageでキャンバス上に書く丸や四角や線、Mouseでマウス入力、Keyboardでキー入力が扱えるので、ゲームに必要な基本的な入出力は外部ライブラリの助けを借りなくても実現できる。関数型言語のProcessingっぽい位置づけとも言える。

Keyboard.wasdっていう関数があるのが、elmでゲームを作りましょうという意図が感じられていいね。

ランタイムエラーが少なくなる

関数型言語はコンパイルが通ればランタイムエラーがほとんど無いとよく言われるが、それは確かに感じた。ただそれが型のチェックのおかげなのか、関数型言語のおかげなのかはよく分からん。型チェックが欲しいだけならTypeScriptでもいいしなあ。

タイムトラベルデバッガ

elmを使って楽しいのがこのタイムトラベルデバッガ。普通にゲーム書くだけで、それを自由にポーズしたり、時間を巻き戻したりすることができる。関数出力を監視するDebug.watchと組み合わせることで、妙な挙動をしたところとその前後を簡単に調べることができる。これは便利。

elmイマイチなところ

なんでも型エラー

ささいな間違いがなんでも型エラー (Type mismatch)になる。しかもエラーメッセージが不親切。

例えば関数の引数の数を間違えたというささいな間違いに対して長大な型エラーが出力されたりする。関数に対して一部の引数を与えることができるカリー化でそういった関数も評価できてしまうせいもあるとは思うのだが、そういった間違いも無理やり評価してその結果いろんなところに型エラーを発見したことになってしまっている感じ。よく見ると本質的なエラーが書いてあるところもあるのだが、それ以外のエラー指摘に埋もれてしまっていて発見が非常に困難。

特にelmのコンパイラは型エラーはこのへんの記述に関係あります、というメッセージの下に関数まるごとが表示されたりすることが頻発するので、発見がより困難になる。

関数型への考え方の転換コスト

elmは関数型言語であるからにして代入を許さない。参照透過性を保つためだ。

これはゲームの書き方にいろんな影響を与える。Update処理はゲーム内オブジェクトを書き換えるのでは無く、1フレーム前の状態を見て今のフレームの状態を生成する処理になる。また逐次的な処理を行う際の書き方も、前状態から次状態の値を生成する関数を作り、その値を入力としてさらに次の値を生成する関数、という書き方になりがちで、その受け渡しをするための一時変数名が大量に必要になったりする。

この辺の記述の無駄さ加減がコードを書いている際に気になる。まあそのような逐次的な処理をしないような作りにしましょう、というのが正しいプラクティスなのかもしれんが、手続き脳人間にはなかなかつらい。

パースエラー

elmのパーサーこなれてないよ。特にレコード操作。elmはレコードにすでにあるフィールドをアップデートする時の構文(<-)と、フィールドを追加する時の構文(=)が違うのだが、これを間違った時にそこを指摘せずにその後ろのカンマを指摘したりする。あとアップデートはカンマで複数書けるのに追加は複数書けないとかいう構文自体の謎仕様もある(外部ライブラリのFocusを使えばマシにはなるが)。しかもその仕様を踏んだ時のエラーメッセージがまた謎。

あと型の宣言でのエラーや変数名の重複などについて、その行番号を教えてくれないのは何なんですかね。

結局Signalとは何者だったのか

Signal、結局よく分かってない。今困っているのは乱数のシードに与えるための、ゲーム開始時の時刻を取る方法が分からないこと。Time.timestampでSignal (Time, a)は取り出せるっぽいのだが、このTimeをRandom.initialSeedに流し込むのはどうすればいいんだ。

elm、書き方の発想が手続き型言語と違って書いている分にはいろいろ楽しいけど苦しみも多い。これからどうしようかなあ。もうちょっと継続して使って見るか、やめて手続き脳の山に帰るか、悩ましいところだ。

2014-12-27

今年面白かったゲーム

やけくそランク VS やけくそアイテムのインフレゲーRisk of Rain、タイムパイロット in 21世紀のLUFTRAUSERS、ミニマムA列車のMini Metroあたりは特にオススメ。そして2014年になってもまだスペースハリアーを遊んでいる未来。そして今でも面白い。

トラックバック - http://d.hatena.ne.jp/ABA/20141227

2014-12-23

今年50のゲームを作って分かった面白いゲームを作る方法

なんてのは無いということが。

作ったものは上のページにまとめた。全ゲームのスクリーンショットがアニメGIFになっていて、クリックすればそのゲームが遊べる。個人的な意見としては、左上の方が楽しめて、右下のほうが退屈できます。

すべてブラウザで遊べる昔ながらのミニゲーム。半分Flash、半分HTML5。HaxeとCoffeeScriptで書いた。ソースも置いてあります。

1年で50作れば年の終わり頃には余裕で面白いゲームを狙って作れるようになるかなあと思ったけど、脳内で面白そうと思ったゲームが実際に作るとひどくつまらないということは相変わらず多発するので、やはりイケてるゲームを作る簡単なセオリーなんてものは無い。あるいはまだ見つかって無い。ひたすら作って、遊んで、面白くなるまで直すしかないね。

まあでも昔ながらのミニゲームを作るのに役立ちそうないくつかの方策はあったような気がするので、忘れないようメモしておく。

  • 斬新さを少しずつ削る

せっかく自作ミニゲーム作るんだから、今までに無かった面白くて斬新なギミックやフィーチャーが入ったゲームが作りたい、という方針で作るとだいたいとても斬新でとても面白くないゲームができる。大事なことはそこであきらめないで、ゲームに盛ってしまった斬新さを少しずつ削って、そこそこ面白くてそこそこ斬新なゲームに仕立てていくこと。保守的な作りによせる、ということが後から出来るようになるとゲーム作りははかどる。

  • 無理やり発想を広げるワンキーゲーム

削る前の斬新さを探すのもそこそこ大変である。そういった場合はとりあえずワンキーゲーム作りをトライ。ワンキーしか使えないという制約が強制的に新しいゲームを作るのに役立つ。ワンキーにどういう動作、仕組みを割り当てるか、という発想の起点が分かりやすいのもいいところ。

  • 誘爆は友達

困ったら何か誘爆させとけ。連鎖で何かが爆発すればみんな笑顔。うまく調理しないとありがちなゲームになる危険性もあるけど。

  • 重力も友達

地面に落ちるとか惑星の引力に引かれるとかの、ゲームに重力を取り入れたり、重力を操作できるギミックを入れたりするのは安直だがいい方策。磁力とかもいい。

昔の名作は名作たる所以があるだけあって楽しいギミックがてんこ盛りである。そういったギミックをつまみ食いしてゲームに取り入れたり、レトロゲーの一部分だけを別ゲーとして仕立てるというのは、ミニゲームを作る際には役立つ。

これは面白いゲームを作るというよりはどうやって手早くゲームを作るかという話だが、プログラムにレベルデザインを自動生成させることに慣れるとミニゲームを短時間で作るにはとても役立つ。自動生成する仕組みそのものも重要だが、自動生成したレベルが理不尽なものにならないように、あらかじめ正解を作っておいてそれ以外の部分を後から埋めるとか、作ったレベルから正解のルートだけを安全に加工するとか、そういうロジックまで書ける方が望ましい。

  • 納得できる終わりをもたらす難度曲線

ミニゲームはゲームがどんどん難しくなってやがてプレイヤーの能力を超えたところでプレイヤーを負かして終わり、という作りが多くなる。その場合、いきなり難しくなって理不尽に負ける、では無くてあともうちょっと頑張れば勝てたのに負けた、くらいの感覚になるように難しくなり具合、いわゆる難度曲線を調整できているのが望ましい。

  • リスク駆動開発

かと言ってゲーム序盤がやたら簡単なのもヒマだ。そういった時は、プレイヤーになんらかのリスキーな行動をさせてそれにボーナスを与えるべし。リスクへのボーナスを適切に与えることで、ゲーム開始1秒から納得性の高いゲームオーバーが提供できる。どういったリスクをプレイヤーに与えようか、という点から新たなギミックを考えるリスク駆動開発もオススメ。

今思いつくのはこんなところ。今年作ったゲームを遊んでくれたり感想をくれたりした方々に感謝いたします。

トラックバック - http://d.hatena.ne.jp/ABA/20141223

2014-12-01

最新ゲーム機の裸眼立体視をマイコン時代のBASICで堪能できるプチコン3号

ニンテンドーDS用プログラミング環境プチコンの第3弾は3DS用である。

しかも裸眼立体視対応である。1000円を握りしめてニンテンドーeショップに駆け込み、プチコン3号を買って、以下のプログラムを打ち込もう。

@1:LOCATE RND(48),RND(30),RND(500)-250:PRINT "バカ":GOTO @1

文字列の表示位置を指定するLOCATEの引数が3つあることに注目。3つ目の引数のZ座標で奥行きを設定することができ、このプログラムで3DSの上画面に飛び出す「バカ」が表示できる。

立体視を手軽に試すことができるという点では、プチコン3号はかなりイケている環境である。コンソール上のテキストだけでなく、スプライトやBGなどもZ座標を設定することで画面から飛び出した位置や奥まった位置に配置できる。ムダに立体なゲームが作りたくなる。

作ったゲームはサーバ上にアップロード、公開することができ、公開キーを打ち込むことでダウンロードできる。作ったゲームを配るのも簡単だ。

いくつか作ってみた。

3DS用のソフトなので、スクリーンショット付きで3DSやWii用のSNSであるMiiverseに投稿できる。3DS上で見ればスクリーンショットもちゃんと立体だ。

プチコン3号の前のプチコンmkIIの時は、

みたいな感じであまり若者が見当たらなかった印象だが、Miiverseを見ている限り今回は若手がたくさんいる感じ。

小4でmkIIから触ってるという人もいるから、単にMiiverseで若手が可視化されただけかもしれん。

プチコン3号にはあらかじめ用意されたキャラクタや背景、BGMやSEもあるので、ちょっとしたゲームを作るのは簡単で、プログラミング入門環境としてはかなり優秀だ。

言語はBASICなので最近の言語みたいな凝ったことはできないが、複数文をくくれるIF〜ENDIFはあるし、昔ながらのサブルーチン作成手段のGOSUB〜RETURN、関数定義のDEF〜ENDはあるから、頑張ればそこそこきれいで大規模なコードもかけそう。手軽ミニゲームからマジ大作まで、いろんなゲームがプチコン3号上で作られるといいね。

トラックバック - http://d.hatena.ne.jp/ABA/20141201

2014-10-18

リワードがスコアだけとはなんという古めかしいゲームじゃ

昨日ゲームのリスク/リワードのうちリスクの話だけ (http://d.hatena.ne.jp/ABA/20141017#p1)したけど、リワードの話はしなかった。いやしなかったというか、私が最近作ってるミニゲームにおけるリワードは「スコアが入る」以外なにも無いので、しようがなかったというか。

最近のゲームだとそもそもスコアという概念が無いので、別の形でプレイヤーに報いるリワードがあるのが普通だ。アンロックされるキャラクタやステージだったり、プレイヤーを強化できる経験値やカードだったり、あと実績やメダルも。

ゲームそのものは昔ながらの作りでも、これら近代的なリワードを入れることで現代に通用するゲームにすることができる。最近遊んだゲームの中で、これにとても成功しているなあと思ったのは、LUFTRAUSERS。

LUFTRAUSERSはゲーム自体は「これタイムパイロットじゃん」という古典的全方位シューティングなのだが、コンボ中に8つのボートを倒せ!とかいうチャレンジをクリアしていくごとに、自機の武器、機体、エンジンのパーツがいろいろともらえる。死ぬ時に大爆発する機体とか、海面に突っ込んでもダメージが無いエンジンとか、一風変わったパーツをカスタマイズして組み合わせると、ノーマル自機とはかなり違った遊び方ができるようになっている。

ゲーム本体にいろんなチャレンジと、アンロックされるパーツを組み合わせるという作りにすれば、ゲーム本体は昔ながらの古典的な内容でも今風なゲームにすることができるので、みなさんなるべくそういうふうにゲームを作りましょう。

と言ってもこれ作るの面倒なんだよな。適切なチャレンジを設定する、組み合わせて面白いパーツを作りこむ、両方ともそれなりに手間がかかる。あと、チャレンジを閲覧したりパーツをカスタマイズするUIを作るのもおっくうだ。チャレンジが設定できるだけのゲームの幅を持たせるのも難しい。

でも少しは現代風なゲームにしたいと思ったら、プロトタイピングにおいてもこういったアンロックされるなにかも含んだ遊び心地まで確認できることが必要だろうし、ミニゲームにもなんらかのチャンレンジ&アンロックの仕組みが入っているのが理想なのかもしれん。これらをうまいこと手抜きして作れる方策を考えたいね。

トラックバック - http://d.hatena.ne.jp/ABA/20141018
 
旧AbaPage Diary Logs
Back to ABA Games