賞状+本到着
コンテストの賞状、革製しおり(参加賞)、入賞賞品の書籍「Arduinoでロボット工作をたのしもう!」が届きました。
- 作者: 鈴木美朗志
- 出版社/メーカー: 秀和システム
- 発売日: 2014/09/29
- メディア: 単行本
- この商品を含むブログ (2件) を見る
なるほど、Arduinoに興味があれば、どっかのスキルレベルに当たるなこれは。
入賞報告
宇宙海賊キャプテン・オニオンは「秀和システム賞」となりました。よく入賞できました。
他の受賞作と見比べると、本作は詰め込み以外に特筆すべき要素に乏しかったですね。
(おにたま氏も詰め込み具合に慣れてきちゃってるのか、コメント短いですのぅ。
まあ、そこだけでも評価頂ければ光栄なんですが。)
さて、今回の受賞作の内、自分が注目していたのは2つ。
一つ目は優秀ゲーム賞のオオツルギ。最優秀も狙えると思ってましたよ。
唸らされたのはソード攻撃の仕様。
シューティングでゴッドバードアタックや科学忍法火の鳥な感じの体当り攻撃を実装するとき、
どうやって自機をコントロールラインに戻すか、扱いに困るんですよね。
オオツルギでは、自機移動範囲が円のため、向い側への跳躍!で自然に解決できているのが凄い。
もちろんあんだけ「カッコイイ」演出というのも評価高いです。
もう一つ注目作だったのは、いつもハイセンスさが光る丸中さんのヌリツヴス。
結果はなんと2賞を同時受賞。
パネルを塗る一筆書き要素は去年の拙作Kacotte!にもあったので、かなり気になる作品でした。
パズル特化時の方向性とかアイデア、ルール拡張性など、深く掘り込む姿勢は見習うべきだなー。
終了判定
とりあえず、一次選考通過の報告。そして、ついでにロジック解説。
128パーセク到達後に敵を殲滅かつ母艦健在である場合、離脱成功としています。
クリア判定処理に関与するコードを抜き出してみると、以下のような感じ。
if(plmode){ プレイ中であれば repeat 99 艦艇オブジェクトを操作するループ gosub *verdup オブジェクト管理テーブルからID=cntのオブジェクト情報を変数にdupする処理。 if(eep){ オブジェクト存在時の処理 flag>>f0 敵ならフラグをクリア if(eep>=1000){ オブジェクトが健在 flag|=(cnt=0) 母艦(cnt=0)であればflagに1をORする。 } } loop } : 進行度が128パーセク以上でflag>0であればゲーム終了判定を出す。
このループでは、すべての艦艇オブジェクトに関して状態値eepを判断し、それぞれの操作(移動、当り判定、爆発、敵生成)などを行います。(クリア判定に関係のない処理は省いています)
同時に、フラグ変数flagの状態を変化させ、母艦健在で敵殲滅の状態になっているかを求めています。
128パーセク到達後に通る箇所でflag>0か判断。
クリア判定が出ていれば、ゲーム終了シーケンス(終了メッセージ表示、ハイスコア更新)を行って、タイトルへ移行します。
まず、ループ前にflagを初期化していません。
敵が一体でも存在(爆発含む)していれば0になるし、flag>0が意味を持つのは128パーセク到達後の1度だけ(1度クリア判定すればタイトルに移行する)なので、省略しても問題がないのです。
実質の初期化は、母艦が健在のときだけ意味のある処理として flag|=(cnt=0) で行っています。母艦が健在なら1をOR。
母艦以外でも該当オブジェクトが健在であれば実行されますが、cnt=0の比較演算が0を返すのでflagは変化しません。
この初期化の前にflagが1だったとしても、先にも書いたように128パーセクに到達していなければ不問となるので不都合はありません。
そもそも、終盤は敵の出現頻度が高く、flagが1のままフレームを持ち越すことがないというのもあります。
そして、敵が存在(健在もしくは爆発)している場合にflagを落す処理は、flag>>f0 。
f0は敵味方の処理振分け用に用意されている変数で、値は味方→0、敵艦→1、敵ミサイル→2 です(*vardupにてcnt/33相当の値を代入)。
つまり、味方以外の処理でここを通ると、f0が0ではないためflagの左シフトが発生。flagは0か1なので、1ビットでも左シフトすれば0クリアされる仕掛けです。
ゲーム終了の処理は、ゲームクリア、ゲームオーバー兼用で、母艦が存在するかどうかでメッセージとハイスコア記録用文字列を変えるだけとしています。
これらの作り込みにより、100Bytes以内のコストでゲームクリア関連処理の追加を実現しています。
弾の発生
通常弾、レーザー、ミサイルの生成処理について。
このゲームでは、登場するキャラクターをオブジェクト管理テーブル(2次元配列)で管理しています。
ID=0は母艦、1〜32は僚艦、33〜65は敵艦、66〜98は敵ミサイル、99不使用、100以上は攻撃オブジェクトに割り当てており、IDが99未満の「艦艇オブジェクト」については、共通のコードで弾の発生を処理しています。
フレーム毎に射撃インターバルカウンタが0かどうかチェックし、0になっていれば、以下のような流れで、攻撃オブジェクト(通常弾/レーザー)もしくはミサイルを生成します。(ミサイルは管理の都合上、艦艇扱い)。
1 ロックオン中の攻撃対象がない(ターゲットID=0)の場合は、対象範囲からランダムにIDを選択し、仮ロックオン。
ロックオンしている攻撃対象の情報を取得。
2-A ミサイル艦の場合
ターゲット(ミサイル)が不在(状態値=0)で、自身が特攻前なら、ミサイル発射処理を実施
射撃インターバルを再設定(固定値)。
該当のIDにミサイル用のパラメータを格納(ミサイル耐久値は、ゲーム進行により増加)。
ロックオンは不要なので、攻撃対象をなしにする。
2-B ミサイル艦以外
攻撃対象の座標を取得し、自身からの距離を計算
「攻撃対象が健在、射程圏内、弾に空きがある」の全てについて成立するかチェック。
成立する場合、攻撃オブジェクトの生成を実施
射撃インターバルを再設定(カウンタ値は、ゲーム進行により減少)。
弾の空きリストから1件取り出して攻撃オブジェクトとしての各種パラメータを格納。
成立しない場合は、自身のロックオン中の攻撃対象をなしにする。
一度ターゲットIDが決まったら条件から外れるまで保持するので、射程圏内に入った攻撃対象をロックオンするようになります。
(ターゲットを撃破するか射程圏外になるまで撃ち続ける)
ランダムに選択するIDの範囲は、艦艇の種類ごとに以下のように割り振っています。
艦艇オブジェクトの種類 | 選択するIDの乱数範囲 |
---|---|
プレイヤー母艦、プレイヤー僚艦(通常弾) | 33〜65:敵艦 |
プレイヤー僚艦(レーザー) | 66〜98:敵ミサイル |
敵艦通常弾 | 0〜32:プレイヤー側全体 |
敵レーザー艦 | 0固定:常にプレイヤー母艦 |
敵ミサイル艦 | 66〜98:敵ミサイル(生成対象として) |
敵ミサイル | 99〜132(この範囲は常に状態値=0なので弾を撃たない) |
母艦を示すID=0は、この処理ではロックオンなしの意味でもあります。
そのため、敵の通常弾攻撃は母艦に対しては消極的で、側に僚艦がいるとそちらをロックオンするようになります。
逆に敵レーザー艦では、攻撃対象をID=0固定とするので、常に母艦をロックオン(発射は距離次第)します。
また、ミサイル艦の場合は、求めたIDは新たに生成するミサイルのオブジェクトIDとして使用しますので、空きオブジェクトのIDを掴めないとそのフレームでの発射はなし、次フレームでの再試行に回されます。
通常弾とレーザーを生成する場合、弾の空きリストからオブジェクトIDを持ってくることによって、ほぼ切れ目なく、等間隔連射が可能になっています。
この弾の空きリストは、1次元配列と、リスト件数で管理しています。
フレーム毎にリスト件数をクリアし、通常弾とレーザーの描画や衝突判定など処理するループの中で、移動カウンタが0のオブジェクトについて空きリスト前方から順に追記。
(リスト件数が示す配列要素に該当IDを記録し、リスト件数を1加算)
次フレームの弾発生処理にて、リスト件数で示されている場所から順次遡って使用(リスト件数を減算しつつ、示されるIDを利用)します。
背景描画
本作の背景は、星を多重スクロール風(4層)で表示しています。
多重といっても描画は各層の重ねあわせではなく、それぞれの星を全くバラバラに縦移動させています。
このような描画をする場合、X座標、Y座標、速度、色などの情報を管理する必要があります。
しかし、各情報を個別の配列で管理したり、乱数値をそれぞれ得たりすると、コード量が膨れてしまいます。
最終的に以下のようなコード(本体はrepeat-loop部分)となりました。(このままスクリプトエディタに張り付けて実行可能です。)
なお、dim省略などの行儀のよくない作りをしており、ヘタに組み込むと弊害が出たりします。参考にする場合は十分注意してください。
*main redraw:await 15:redraw 0:color :boxf repeat 207 ; cntはx座標成分と兼用 dup fs,sppg(cnt) ; sppgはdimしていないが、初回描画で207個分に拡張される if(fs){}else{fs=rnd(32767)} ; 描画色(0-31)、速度(0-3)、Y位置(32倍計算)を1度に乱数取得 tmp=((fs&3)+1)*32:hsvcolor (fs&31)*6,100,tmp+127:pset cnt*3,fs/32 fs+tmp:fs\(16400+cnt) ; Y位置を動かして、下まで行ったら変化を加えて上から繰り返し。 loop goto *main
詳細を見ていきましょう。
repeat 207
207は星の数。半端ですが、後のpsetで3倍した値をX座標の範囲にあてています。
右側ゲージに掛からないよう、X座標を621までに抑えたので、この値。
dup fs,sppg(cnt)
整数型配列sppgによって各星の位置や属性を管理しています。アクセスのコストを下げるためcntで示される要素をfsにdupします。
どこにもdimがありませんが、初回で207個分のループによって配列が自動拡張されることを利用して、配列宣言を省略しています。
if(fs){}else{fs=rnd(32767)}
fsの値が0である場合、乱数値で初期化します。初期化したいのはY位置、色指定、スクロール速度。
関数は高コストなので、rnd()は一回。
Y座標の初期値(32倍計算)、描画色指定(0-31)、速度指定(0-3)のまとまりとして乱数を得てfsに代入します。
最初はどの配列要素も0ですから、ランダムなY座標に星が置かれます。
(厳密には配列拡張が行われた際に反故にされる範囲がありますが、初回描画だけなので無視)
tmp=((fs&3)+1)*32
fsの下2ビット(0-3の4種)に1足して32倍したものを速度値(32,64,96,128)としています。
速度であると同時に、明度の制御にも使っています。
hsvcolor (fs&31)*6,100,tmp+127
hsvcolorによる色指定。
fsの下5ビット(0-31)を6倍して色相値として指定、彩度は100固定、明度は速く動く星ほど大きくします。
pset cnt*3,fs/32
X座標は上にも書いたようにcntの3倍、Y座標はfsを32で割った値。
fs+tmp
Y方向の移動計算。速度値を加算するだけです。
fs\(16400+cnt)
星が画面下に到達した場合に、画面上部に持ってくるための計算。480*32より適当に大きな値でfsを割った余りを代入しています。
速度や描画色も変わってきます。(隣の星と変化の差をつけるためにcntも利用)
このとき余りが丁度0になって、次の描画で乱数値の取得対象になることが稀にあります。
その場合、途中の高さから星が出現するわけですが、ただの背景描画ですから気にする必要はありません。
repeat−loop部分、AXにして220Bytesになります。
パーセク
蛇足事項。
このゲーム、ステージ進行の単位をパーセクにしたため、ワープ時亜空間での戦闘、という裏設定を入れています。
パーセクとは「年周視差が1秒(角度)になる距離」っていう定義で、だいたい3.26光年。ものすごい距離。
128パーセク生き延びるということは、417.3光年の逃避行です。
通常宇宙空間の前提で考えると荒唐無稽な移動距離なので、亜空間なる御都合空間にいるという設定。
パーセクを採用したのは、なにより単位記号が「pc」の2文字であるのと、語感を重視。
光年はlyで1とlが紛らわしいため却下。天文単位(au)は某キャリア名と被ったので除外。
あと、某アニメの宇宙キロとかも捨てがたかったけど、記号がわからなかった。
(キロだけだと、キロメートルかキロマイルかまったく別のキロナントカなのかも不明だし…)
V1.07
バージョンをV1.07に上げました。多分これが最終バージョン。反映待ち。
クリア(離脱成功)時のHSP-TVへのハイスコア登録で、*マークをつけるようにしました。
128pc.に到達しても、敵を殲滅する前に母艦が撃破されるとクリアにはならないため、
クリア時と失敗の両方ともハイスコア登録が128pc.の表記になって区別不能です。
というわけでV1.07では、失敗時「 128pc.」、クリア時「*128pc.」を登録することで対処。
スコア登録時の文字列先頭は、通常空白なので、クリア時のみPOKEで'*'を書き込むことで実現。
この処理のために必要な16Bytesを捻出するため、僚艦ドラッグ時の僚艦ステータス表示を抑止して、
僚艦追加メニューと僚艦ステータス/強化メニューの表示に使用するPOS命令を1本化しました。
ついでに、GameOver時・クリア時のメッセージ文字色が僚艦耐久値ゲージと被っていたので、
個別指定で赤に変更。