2016年09月05日

インベーダーゲームを作ってみる(3)インベーダーの移動処理

かつてマイコン少年だったころ、インベーダーゲームで強く印象に残った処理が2つありました。

(1)グラフィカルな敵が多数動いている。処理が大変そう
(2)陣地が敵弾にだんだん崩される様子がリアル。あらかじめ用意した画像を使っているのではなさそう

今回は、このうち(1)のインベーダーの移動処理を扱います。

インベーダー群は、左右に行進しつつ、インベーダーが画面の端まで行くと、一段下へ移動します。
画面最下端へインベーダーが到達したらゲームオーバーです。

従って、移動処理では

(1)画面左右端に到達したインベーダーが居るかどうかの判定
(2)画面最下端に到達したインベーダーが居るかどうかの判定

を行う必要があります。

また、画面の端まで到達できるのは生きているインベーダーだけなので、これらの判定処理は、全ての(生きている)インベーダーについて行う必要があります。
また、「どんなゲームだったのか」で書いたとおり、インベーダーは一度に一匹しか動きません。

これらのことを踏まえ、今回はインベーダーの移動処理を以下のように実装しました。

(1)インベーダーの座標は、全体を一括して扱う(一匹ごとの座標は持たない)
(2)インベーダーの生死はフラグ(の配列)で判定する(ブロック崩しと同じ)
(3)インベーダーを一匹移動させる都度、画面左右下端への到達をチェックする
(4)インベーダーがすべて移動し終わったときに、行進方向の変更や下降の処理を行うかどうか判定する

なお、(1)のインベーダーの座標は全体の「現在位置」「次に動くべき位置」で表し、個別のインベーダーの位置は「次に動かすインベーダーの番号」で表現します。
図にすると下のようになります。この図ではインベーダーは右から左へ移動しています。
iv07.png

インベーダー全体としては(cur_left, cur_top)から(nxt_left, nxt_top)へ移動するのですが、実際には0番〜(nxt_update -1)番までが移動済みで、nxt_update版〜49番までが現在位置に留まっています。

インベーダーの番号は左下が0番で、右へ進むごとに+1、最後の49番は右上のインベーダーです。
オリジナルのインベーダーゲームの映像をよく見ると、最初に動き出すインベーダーは左下で、そこから右隣が順次移動し、次は下から2列目の左端から動き始めていますので、そのような順で番号を振りました。

インベーダーが端に到達したかどうかは、インベーダーを1匹移動させるごとに、そのときの描画座標でチェックします。
(オリジナルのインベーダーでは、画面の端にドットがあるかどうかで判定していたらしいです。)

コードはGitHubで公開していますが、上の説明に対応する部分を載せておきます。

まずインベーダーを表すデータ構造です。

struct aliens_t {
boolean exist[ALN_NUM];
uint8_t alive; // number of aliens alive
int16_t cur_left;
int16_t cur_top;
int16_t nxt_left;
int16_t nxt_top;
int16_t v; // left move or right move
uint8_t nxt_update; // which invader will be moved at the next tick
uint8_t pose; // index num for iv?_img[]
int16_t bottom; // be updated in aliens_draw
boolean move_down; // down-move flag (updated in aliens_draw)
boolean touch_down; // an alien touched bottom line flag
int8_t status;
uint8_t hit_idx; // which invader is been hit
} g_aliens;


そして、移動ルーチンが以下です。
動かすべきインベーダーが見つかれば(最初のif文)、その番号をnxt_updateに代入して終わりです。
49番まで行っても見つからなければ、生存しているすべてのインベーダーを移動し終えたので、最下端に到達したか(touch_down)の確認、X座標の更新、左右端へ到達した場合はY座標の更新を行います。
void aliens_move(struct aliens_t *a) {
uint8_t u = a->nxt_update;
do {
u++;
} while ((u < ALN_NUM) && (!a->exist[u]));

if (u < ALN_NUM) {
a->nxt_update = u;
}
else { // all aliens have moved
if (a->bottom >= SCRN_BOTTOM)
a->touch_down = true;
for (u = 0; (u < ALN_NUM) && (!a->exist[u]) ; u++);
a->nxt_update = u;
a->pose = (a->pose + 1) % 2;
a->cur_left = a->nxt_left;
a->nxt_left += a->v;
a->cur_top = a->nxt_top;
if (a->move_down) { // this flag had been set in aliens_draw()
a->move_down = false;
a->v = -a->v;
a->nxt_top = a->cur_top + ALN_VMOVE;
a->nxt_left = a->cur_left + a->v;
}
}
}

一匹のインベーダーを描画する処理は以下の通りです。ほとんど3種類を描き分ける処理だけですが、最後のところで左右端への到達判定(move_down)と、インベーダー群の中で最大(最下端)のY座標の保存(bottom)を行っています。
void aliens_draw(struct aliens_t *a) {
if (!a->exist[a->nxt_update])
return;

uint8_t row = A_ROW(a->nxt_update);
uint8_t col = A_COL(a->nxt_update);
int16_t x = a->nxt_left + col * (ALN_W + ALN_HSPACING);
int16_t y = a->nxt_top + row * (ALN_H + ALN_VSPACING);
uint8_t *img;

switch (row) {
case 0: img = (uint8_t *)aln1_img[a->pose]; break;
case 1:
case 2: img = (uint8_t *)aln2_img[a->pose]; break;
case 3:
case 4: img = (uint8_t *)aln3_img[a->pose]; break;
}
DRAW_BITMAP(x, y, img, ALN_W, ALN_H, WHITE);

if (((x <= SCRN_LEFT) && (a->v < 0)) ||
((x >= SCRN_RIGHT - ALN_W) && (a->v > 0)))
a->move_down = true;

a->bottom = max(a->bottom, y + ALN_H);
}
タグ:Arduboy
posted by boochow at 02:03| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年09月04日

インベーダーゲームを作ってみる(2)Arduboy向けの画面設計

Wikipediaによると、初代のインベーダーゲームは白黒で、画面を縦に使い、解像度は横224×縦260ドットだったそうです。
上下のスペースを得点表示と残機表示に使っています。
iv01.png
(画像はWikipediaより)

Arduboy用の画面は白黒128×64ドットですので、もちろんそのままでは表示できません。
ちなみにゲームボーイの画面は4階調160×144ドットだったそうなので、それよりさらに低スペックです。
2:1という横長のアスペクト比をどう活かすかが難しいところです。

ペイントソフトでドット絵を描きながら、悩んだ末に下図のような画面設計としました。
iv02.png

縦が64ピクセルですので、アスペクト比を4:3とするなら横86ピクセル程度となりますが、わずかに広げて横88ピクセルをゲーム画面に使うことにしました。
残りはスコア表示と残機表示に使っています。

ゲーム画面の中身ですが、インベーダー一匹を横5ドット×縦3ドット、インベーダー同士の隙間を横2ドット、縦3ドットとしました。
従って、インベーダーは横7×縦6ドットの中に一匹ということになります。
ちなみにオリジナルでは、インベーダーは16×16ドットの中に一匹だったようです。

インベーダーは沢山いるほうが本物の雰囲気に近づけるように思いましたので、一匹あたりの表現力は捨てて、インベーダーの数を重視しました。
インベーダーのデザインは、オンラインで公開されている「Pixel Invaders」を参考にしています。(少しだけ変更しましたが。)

Yoda's Video Arcade - Play 8-Bit Retro Game Pixel Invaders - free online retro game | free online game 8-bit style

オリジナルのインベーダーゲームは横11匹×縦5行ですが、一列減らして10匹×5行にしました。
これでも、編隊全体としては横68ドット×縦27ドット必要になります。
iv03.png

インベーダーの移動距離は、横方向は1ドット、縦に下がるときは3ドットとしました。
Wikipediaの画像などを見ると、オリジナルの移動距離は横2ドット、縦8ドットだったようです。
iv04.png


インベーダー以外のキャラクタの移動は、UFOと自機は1ドット、自機弾と敵弾は2ドットです。


敵弾は、オリジナルでは3種類あるということでしたので、3種類×2通りのグラフィックスを作成しました。(ここはちょっとこだわりポイントです。)
幅1ドットだとさすがに表現力に限界があるので、横2ドットにしています。
オリジナルでは、敵弾は横3ドット縦8ドットを4ドットずつ移動させているように(ビデオを見る限りでは)思われます。

また、自機弾も敵弾も陣地に当たると爆発しますので、そのパターンを用意しています。
敵弾の爆発パターンは下図に載せていませんが、2×2ドットの正方形です。
iv05.png


最後にUFOですが、倒したときに点数がUFOに重なって表示されます。
「UFOの得点」はインベーダーゲームの攻略の非常に大きなポイントだったらしいので、点数の表示は再現したいところです。
UFOは7×3ドットにしたので、3桁の数を表現するにはドット数が足りないのですが、かなり無理やり「50・100・150・300」を表現してみました。
iv06.png
タグ:Arduboy
posted by boochow at 17:09| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年09月03日

インベーダーゲームを作ってみる(1)どんなゲームだったのか

picovaders.png


先日MFT2016で購入したArduboyですが、作ったゲームがブロック崩しの移植だけでは寂しいので、Arduboyをターゲットにしたインベーダーゲームを作ってみました。

もう一応完成していて、コードは以下のGitHubで公開しています。

boochow/picovaders

HTML5用のシミュレータはこちらです。(移動はカーソルキー←→、弾発射はCTRLキー)

picovaders.html


ここでは備忘録を兼ねて、中身の解説を書いておこうと思います。

まず「インベーダーゲームの分析」について情報収集しました。
ゲームそのものについては、Wikipediaや、作者の西角友宏氏のインタビュー記事が詳しいです。

スペースインベーダー・今明かす開発秘話――開発者・西角友宏氏、タイトー・和田洋一社長対談 - 日経トレンディネット

インベーダーゲーム開発秘話! - 週刊アスキー

スペースインベーダー - Wikipedia

Space Invaders - Wikipedia, the free encyclopedia

インベーダー発案

上の記事にもあるように、インベーダーゲームがブロック崩しから発想を得て作られた、というのは有名な話です。

しかし、実際には当時のハードウェアでたくさんのキャラクタを動かすのは大変です。
そのために行われたプログラミング上の工夫が、結果的にインベーダーゲームの独特の画面や、数々の裏技を作り出しているようです。

そのような、インベーダーゲームの実装が垣間見える情報は「2ちゃんねる」が参考になります。

■ 「インベーダー」をトコトン追及したい ■

ここにいろいろ書かれていることの中から、当時のプログラムを知るうえで参考になる情報を引用してみます。

・インベーダは、左下のヤツから1int(1/60秒)に1匹ずつ移動する。
 ゆえに、インベーダーが少なくなると移動が速まる。
・インベーダーの弾は3種類ある。画面上同時に存在するのはこの3つ。
 A.左右非対称にトゲが付いているもの
 B.左右対称にトゲが付いているもの
 C.波形のもの
 弾Aは、砲台に一番近い列から発射される。
 弾B・Cは、あらかじめ決まった列から順次発射される
・UFOは25秒おきに出現する。左右どちらから出るかは、自機のショット
 回数(奇数偶数)で決まる。インベーダーが7匹以下だと出ない。
・UFOの得点は、自機のショット回数で決まる。
 50 50 100 150 100 100 50 300 100 100 100 50 150 100 100 (以降ループ)
・UFOの出現中は、弾Cは発射されない。つまり、この時は同時に2発しか出ない。
・面クリアするごとにインベーダーの初期位置が下がるが、9面をクリア
 すると2面の配置に戻る。
・インベーダの消滅音は毎回微妙に異なる。これは基板上の発振回路のタイミング
 に依存しいるため。(希に2回鳴ることがあるのもこのせい)

インベーダーは、最初に出現したら、まず右方向に動きます。
これを間違えると、名古屋のときに、方向がおかしくなります。

最下段のインベーダーの最初の出現位置は、ビーム砲台から、

 1面 : −10段目
 2面 : −8段目
 3面 : −6段目
 4・5・6面 : −5段目
 7・8・9面 : −4段目

となります。

そうすれば、名古屋の際、4・5・6面は、
方向が逆になるのも再現されます。

敵の弾、相殺できるのとできないのがあるけど何か法則はあるの?

 当たり判定の順番の問題だったような。
 自弾移動ー>当たり判定ー>消滅  が先か
 敵弾移動ー>当たり判定ー>消滅  が先かという。

因みに、当たり判定は描画データをグラフィック画面とANDする事で
判定していますので、1ドット単位で正確です。


インベーダーは、下から上に向かって波打つように動きますが、一回の描画処理が1/60秒で終わるように、一度に1匹ずつ動かしていたのですね。
スタート時のインベーダーは全部で55匹ですから、1秒近くかけて全体を動かしていることになります。

1/60秒という制約があるのは、昔のブラウン管は電子線が画面を毎秒60回、左から右、上から下へスキャンすることによって描画しており、表示内容の変更は下まで行った電子線を上まで戻す間に行っていたからです。
今では、あまり実態のない制約だと思いますが、ゲームでは今でも1/60秒という周期を便利に使っているようです。
Arduboyも、画面更新速度(フレームレート)は最速で1/60秒ごととなっています。

タグ:Arduboy
posted by boochow at 22:18| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年08月21日

ARMの開発環境「MDK」と「μVision」でmbedのデバッグを試してみた

夏休みもそろそろ終わりですが、最後にARMの開発ツール「MDK」とそのデバッガ「μVision」を試してみました。

ARM(が買収したKeil)はμVisionとコンパイラやライブラリその他もろもろのツールを合わせて「MDK Microcontroller Development Kit」として提供しているようです。
mbedのクラウド上で作成したコードも、MDK用にエクスポートすれば、ローカルでデバッグすることができます。

このとき、ボードがデバッグ用のインタフェース「CMSIS-DAP」をサポートしていれば、ボード上でコードを実行しながらステップ実行やトレースなどの操作を行うことができます。
幸い、私のボード「mbed LPC1768」はCMSIS-DAP対応のファームウェアが提供されていましたので、このデバッグ機能を試用してみました。

既に先人のレポートがネット上にありましたので、それを参照しつつ進めました。
参考にしたのは以下の2つです。

μVision4でデバッグ! | mbed
mbed LPC1768をMDK-ARMでデバッグ - Qiita

1つ目の記事はやや古く、2012年9月の日付です。文中ではμVisionのバージョンが4になっていますが、現在はバージョン5が提供されています。
2つめの記事は2016年1月のもので、1つ目の記事を参照しています。バージョン5を使う場合のはまりどころが記載されており助かりました。


やることは、大まかに言えば

MDKのダウンロード&インストール
MDK v4 Legacy Support for Cortex-M Devicesのダウンロード&インストール
mbed LPC1768のファームウェアをアップデート

が準備で、そのあと

・mbed用に書いたコードをローカル環境へエクスポート
・MDKのデバッガの設定
・コンパイルして実行

という流れになります。


準備のところは飛ばします。ダウンロードしてインストールするだけですので・・・
なお、MDKをダウンロードするとARMからMDKの日本語スタートアップガイドへのリンクが送られてきますので、メールアドレスはきちんと入力しましょう。


インストールができたら、まずはmbedのファイルをローカルへエクスポートします。
対象はとりあえず先日動作確認したLチカにしました。

右クリックメニューから、プログラムのエクスポートを選びます。

uvision01.png


ボードと開発環境の選択が出ます。
開発環境としてKeil uVision4を選んでおきます。
(後述しますが実際にはUVision5でも大丈夫なようです。)

uvision02.png


ダウンロードしたZIPファイルを展開すると、こんな感じになっています。

uvision03.png


.uvprojファイルをダブルクリックするとMDKが立ち上がります。

MDK v4 Legacy Support for Cortex-M Devicesをインストールしていないと、下図のようなダイアログが出ますのでインストールしましょう。

uvision04.png


プロジェクトを開いた画面はこのようになっています。
普通のIDEですね。

uvision05.png


まず、「Options for target...」をクリックします。

uvision06.png


そして、Debugタブの「Settings...」ボタンからデバッガの詳細設定に入ります。

uvision06b.png


詳細設定画面で、アダプタを「MBED CMSIS-DAP」に指定します。
また、Debugの設定で「HW RESET」を選び、「Stop after bootloader」をオンにします。

uvision10.png


これで準備完了です。
プロジェクトのリビルドボタンを押してビルドします。

uvision07.png


プロジェクトフォルダの中に.binファイルができますので、これをmbedデバイスのフォルダへコピーします。

デバッグは、ドコモのロゴを思い出させる虫眼鏡のアイコンをクリックします。

uvision08.png


ここで、上で説明した「Stop after bootloader」をオンにしていないと、「The CoreSight debug unit in the target device does not react.」とエラーが出てデバッグが終了してしまいます。
私の環境だけかもしれませんが・・・。

uvision09.png


デバッガが起動すればこんな画面が出てきます。
1行ずつのステップ実行などもできます。

uvision11.png


とりあえず動作までは確認できました。
無償版ではオブジェクトサイズが32KBまでという制限があるので、あまり大きなものは作れませんが、趣味で使うなら十分な気がします。


以下オマケです。

mbedからのエクスポートの際、UVision5も選択することができますので、やってみました。

uvision13.png


プロジェクトファイルを開くと、「LPC1700のパッケージが必要」とメッセージが出るので、インストールします。

uvision12.png


あとは基本的に同じです。
デバッガの設定も忘れずに行います。
posted by boochow at 18:42| Comment(0) | mbed | このブログの読者になる | 更新情報をチェックする

2016年08月20日

Arduboyのシミュレータを試してみた

Arduboyのコミュニティサイトを見ていたら、Arduboy用のスケッチをVisual Studioでコンパイルして動作させるシミュレータがあったので試してみました。

Proton Arduboy Simulator (for Windows Visual C++ 2005) - Arduboy / Development - Community

Protonというクロスプラットホームのゲーム開発プラットホームを開発されている方が作られたようです。
エミュレーションではなくシミュレータなので、ArduboyのライブラリのAPIと互換のライブラリをWindows上に作成し、Arduboy用に書かれたスケッチファイルをコンパイルしてWindows用のアプリを生成します。


Proton Arduboy SimulatorをダウンロードするにはSVNクライアントが必要です。
以下の説明にあるように「TortoiseSVN」をインストールし、ダウンロード先のフォルダで右クリックから「SVN Checkout...」を選択します。
するとリポジトリのURLを入力するウインドウが出るので、下記のリンク先にあるとおり「svn://〜」のURLを入力します。

proton:download [RTsoft Wiki]

arduboysym00.png

ファイルにはArduboyのサンプルにあるブロック崩しが最初から含まれています。
プロジェクトファイルは「ArduboySim\windows」の中にあります。
Visual Studio 2015 Communityで問題なく実行できました。

arduboysym01.png


次に、自分で作ったブロック崩しを動作させてみました。
設定の仕方は以下のページに書かれています。

proton:arduboysim [RTsoft Wiki]

スケッチファイルは「ArduboySim\Arduboy」フォルダの中に、「ardu_main.cpp」というファイル名で入れます。
そして、スケッチファイルの「setup()」と「loop()」をそれぞれ「main_setup()」「main_loop()」という名前に変更します。

次に、Visual Studioでプロジェクトファイルを開き、プロジェクトエクスプローラで「ardu_main.cpp」ファイルを探します。
そして、右クリックで「プロパティ」を選択します。

arduboysym03.png


プロパティウインドウが開いたら、「C/C++」の「プリコンパイル済みヘッダー」を「使用しない」に設定します。

arduboysym02.png


なお、もともとのサンプルに含まれていた「bitmaps.h」「bitmaps.cpp」はプロジェクトから削除しておきます。


これで準備OKです。実行するとシミュレータ上でArduboy用スケッチが動作します。
カーソルキーとCTRLとZが十字ボタン・Aボタン・Bボタンに対応します。

arduboysym04.png


実際にはWindows用のプログラムとして動作していますので、ブレークポイントの設定や変数値の確認なども、下図のように通常のWindowsでのデバッグと同様に行えます。
これは素晴らしいですね。

ちなみに、SDL Debug GLの構成で動作しない場合は、ソリューションエクスプローラーのwinArduboySim上で右クリック→プロパティ→全般 で出力ディレクトリが「..\bin\」になっているかどうか確認してください。

arduboysym05.png


なお、「App.cpp」ファイルの「#define RT_ARDU_LANDSCAPE」の行がコメントアウトされているのを有効にすると、Arduboyを横にした状態で動作を確認することができます。
(ただ、残念ながらカーソルキーの動作までは回転してくれませんでした。)

arduboysym06.png
arduboysym07.png

さらに、この実行コードをHTML5ファイルに変換することができます。
さすがクロスプラットホーム用ツールです。
方法は下記のページに書かれています。

proton:html5_setup [RTsoft Wiki]

まずEmscriptenをインストールします。

Download and install − Emscripten 1.36.5 documentation

インストールしたらスタートメニューのEmscriptenから「Emscripten Command Prompt」を開き、「emsdk」と入力して応答があればインストール完了です。
環境変数PATHにEmscriptenのフォルダが追加されているので、いったんPCを再起動します。

HTMLファイルの生成コマンドは、「ArduboySim\html5」フォルダの中の「build_release.bat」です。
ただし、最初から入っている「build_release.bat」はサンプルのブロック崩しのソースファイルに合わせてありますので、編集が必要です。
現時点では56行目あたりからがその場所になります。
REM **************************************** APP SOURCE CODE FILES
REM set APP_SRC=%APP%\App.cpp %ARDUBOY%\ardu_main.cpp %ARDUBOY%\breakout_bitmaps.cpp
set APP_SRC=%APP%\App.cpp %ARDUBOY%\ardu_main.cpp
REM **************************************** END SOURCE

ソースファイルのリストにbreakout_bitmaps.cppというファイルが含まれていますので、これを取り除いています。


また、現在のバージョンはコンパイル時にSDLのインクルードファイルが見つからないというエラーが出ました。
やむなく音声は無しの指定にしました。(下記引用の赤い部分を削除)

2016/9/15:作者さんが修正してくれました。
SET CUSTOM_FLAGS= -DHAS_SOCKLEN_T -DBOOST_ALL_NO_LIB -DPLATFORM_HTML5 -DRT_USE_SDL_AUDIO -DNDEBUG -DC_GL_MODE -s LEGACY_GL_EMULATION=1 --memory-init-file 0 -Wno-switch -D__AVR_ATmega32U4__ -DRT_ARDUDEV -DRT_RUNS_IN_BACKGROUND -DRT_EMTERPRETER_ENABLED -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1


これで、「build_release.bat」ダブルクリックすればHTML5ファイルが生成されます。
(結構時間がかかります。)

結果、こんな感じのHTMLファイルが生成されました。
ちゃんと遊ぶことができます。(実物は→こちら
一緒に生成されたJavaScriptのファイルは3.6MBほどありました。

arduboysym08.png


Arduinoのシミュレータは世の中にはあまりありません。
開発ボードも周辺ボードも数が多いので、実際問題、サポートが困難なのでしょう。
関連のトピックが下記にありました。

Arduino simulator for debugging

しかし、ArduboyはArduinoベースとはいえ、ハードウェアのスペックは固定されており、外付けの機器も無く、用途もゲームのみ、ということでシミュレータが作りやすい条件だったのかもしれません。
かなり実用的なので、ありがたく使わせて頂こうと思います。
タグ:Arduboy
posted by boochow at 18:36| Comment(0) | Windows | このブログの読者になる | 更新情報をチェックする
人気記事