2012年08月16日

あらためてAIMSでSTGを作る

ikafont.jpg
以前グラディウスっぽいゲームを作るという企画があって
こんな感じのフォントも作ってたりしたんですが、
色々あってほかのゲームを作ることにしました。

冬コミにも申し込んだし新しい企画のほうの制作日誌でもつけようかと思います。

自機のプログラムは大体できていて
ステージスクリプトを読み込んでそれに従って敵が出たりするとこまで動いています。

ステージクリア時のリザルト画面をどう実現するべきなのか悩んでいるところです。
ステージシーンの中でやるべきなのか?
別のシーンをステージと平行動作させるのがいいのか。

あとステージごとに別のシーンにするほうがいいんだろうな・・・。

2012年04月01日

新作同人ゲームタイトル「もなこSOS」

monaco_sos.png

久しぶりのブログ更新がエイプリルフールネタっていうのも何ですが・・・。
チャンピオンで連載中の「りびんぐでっど!」が好きすぎてこんな画像を作ってしまいました。

ある程度歳いってるひとなら元ネタが何かはすぐわかってもらえるのではないでしょうか。

可能ならば本当にこのタイトルでSTGを作りたいですね。


ちなみに今のチャンピオンでは、これと「空が灰色だから」が大好きです。

2011年02月15日

AIMSでグラディウスっぽいSTGを作る(11)

list1.gif
drawGraphicList()を使うことでグラフィックチップを帯状に変形させて表示することができます。
この画像のような誘導レーザーっぽいものを表現するのに向いた機能です。

しかしながら当たり判定矩形の配置方法が通常のグラフィックチップとは違うようなので調べてみました。

通常のチップはアクターの回転や拡大に応じて当たり判定の大きさや位置も変化します。
ただし45度回転させても矩形がひし形になったりはしません。(詳細はAIMS本 P.67)

帯ポリゴンに関しても基本的にはそれに従っているのですが、
原点の位置や初期角度の違いが影響して、おかしく見えるようです。

list2.gif
帯ポリゴンに使用する画像は縦に分割されるため、縦長にしておく必要があります(1参照)。
これに当たり判定矩形を設定するには2の位置にするのが自然でしょう。
ところがそのつもりで矩形を設定してゲーム内で表示すると3の位置に当たり判定が発生します。

3の状態は元画像と同じ方向を向いているので回転していないように見えますが、
実はアクター、帯ポリゴンともに270度回転させた状態です。
回転していない場合は4のように右向きになります。

なお、この時のアクターの原点は5のあたりにあります。

ということで、矩形を帯ポリゴンの中に設定したい場合は、
見た目よりも左にしなければならないようです。
具体的に何ドットなのかまではまだはっきりわかっていません。

2011年02月02日

AIMSでグラディウスっぽいSTGを作る(10)

もうAIMSならではのコーディングが必要なところが少なくなってきました。
とりあえず以前やったマップチップとの当たり判定についてです。

以前のものはマップチップ番号が0か、それ以外かで判定していました。
これを例えばチップ番号16番以下は当たり判定なしとすれば
すり抜けられる背景を実現できますが、当たり判定の境界となるチップ番号を
固定にするとチップ作成の自由度が減りますし
可変としても、やはりすり抜け可能なチップを左に集める必要がありますし、
値をいくつにしたかという情報が必要になります。

と、いうことでこれらを解決する手段の一つとして
当たり判定情報を別のマップデータとして用意する方法を試してみます。

sample5.jpg
AIMSで用意されているマップエディタはレイヤーを作成することができ
それぞれに別のマップチップ画像を割り当てられます。

sample6.jpg
これで見た目と当たり判定の情報を完全に分離することができます。
もちろんデータ量は2倍になるわけですが気にしないことにします。


このようなレイヤーを持ったデータをfmf形式で出力すると
lua側では地形データが mapchip.data[0] に、当たり判定データが mapchip.data[1] に
格納されます。

当たり判定データも mapAllocate() で lua に渡してもいいのですが
表示には使わないにもかかわらずレイヤーをひとつ占有してしまうので
オススメできません。
そのまま配列として扱うのがいいでしょう。

以前は mapRead() でチップ番号を読み取っていましたが
そのかわりにインデックスを自分で計算して参照位置を決めます。
mapRead(x, y)

mapchip.data[1][x + y * mapchip.mapWidth]

2011年01月28日

AIMSでグラディウスっぽいSTGを作る(9)

sample4.jpg
今回はゲームオーバーの作り方です。
これはゲーム次第で全然違ってくるところですから、ひとつの例ということで。

最後の自機が死んだら、爆風が消えた後静止して
「GAME OVER」の文字を出すという仕様にします。

静止させる方法はAIMS本の5-3「複数シーンの並列動作と一時停止」が
とても参考になります。

setSceneFreeze()でシーンを停止させることができます。
これによってそのシーンに属するアクターやスレッドも止まる
(画面表示だけは行われる)ので、停止させるのは簡単です。

停止を再開させるためには並列動作するシーンが必要になるので
setSceneFreeze()の前にaddScene()で gameoverシーンを起動します。

gameoverシーンではcreateTextActor()で文字表示アクターを作成します。
ここで作成したアクターはsetSceneFreeze()の影響を受けずに動作するようです。
OnStepではボタンが押されるまで待ちます。

ボタンが押されたらフリーズを解除し、changeScene()でフリーズを解除したシーンを
タイトル画面のシーンに切り替えます。
そして自分自身をcloseScene()で終了させます。
ここではフリーズしているシーンを操作する必要があるので
グローバル変数などでシーンIDを保持しておかなければなりません。

function game_OnStep()
  if 自機が死んだ then
    setSceneFreeze(V.scene, true)
    addScene("gameover")
  end
end

function gameover_OnStart()
createTextActor(G.font, "GAME OVER", 320, 240,
LAYER_INDICATOR, 255, 255, 255, 255, ALIGN_CENTER)
end

function gameover_OnStep()
if getJoyPressCount(0, BUTTON_TRIG1) == 1 then
setSceneFreeze(V.scene, false)
changeScene(V.scene, "title")
closeScene()
end
end


2011年01月26日

AIMSでグラディウスっぽいSTGを作る(8)

そろそろ自機が死ぬようにしたいのですが
そのためにはシーンをちゃんと作らないといけません。

普通はゲームオーバーになったらタイトル画面に戻ります。
タイトル画面は独立したシーンとして作るのが普通です。
よってシーンを作る必要が出てくるわけです。

公式サンプルのSUiCA32ではサークルロゴ表示をひとつのシーンとして用意し
そこでデータの読み込みをしています。
読み込み自体はローダースレッド内で行い、メインスレッドはそれが終わるのを待ちます。

時間がかかるような大きなファイルの読み込みをメインスレッドで行うと
AIMSが応答なしになってしまうので、この形が推奨されています。

SUiCA32では"LOADING"の文字を表示するためにPXFというアクタークラスが使われていますが、
AIMS1.40にはcreateTextActor()で同等の機能を持ったアクターが使えるので
そっちを使うと楽です。

createTextActor()の引数を見ると、一つのアクターで表示できる文字列は
固定のように思えるかもしれません。
しかし、setActorString()という関数が用意されているのでこれを使えば
いつでも好きなだけ書き換えが可能です。

実際、スコアの表示にも使っていますし。こんな感じで。


function score_OnStep()
setActorString(string.format('1P %8d HI %8d', getScore(), getHiScore()))
end

そんなわけでテキストアクターを使うこと以外は
SUiCA32そのままでいいと思います。

2011年01月16日

AIMSでグラディウスっぽいSTGを作る(7)

sample3.jpg

適当に地形を作って、ミサイルがそれに沿って進むようになりました。

画像ではミサイルが地面から離れて見えますが
これはマップチップ番号が0以外の場合は全て当たり判定を持つようにしているためです。
チップのほとんどが透明であっても判定があるので。

ミサイルの挙動は、
1.落下中かつ、ひとつ下のチップに判定があるなら、水平移動に遷移
2.水平移動中かつ、ひとつ下のチップに判定がないなら、落下中に遷移
となっていて、これをミサイルのOnStep()に記述します。

ミサイルは段差を乗り越えたりはしないので
水平移動中に前方にある地形と接触したら消えなければいけませんが、
これはマップチップ側の当たり判定ロジックがやってくれます。


マップチップが動かない場合(スクロールを除く)は、ここまでで十分なはずです。
が、ミサイルが上を滑っていくような敵(グラIIのホッピングモアイとか)や
背景が動く場合(グラII最終面カニ前のせり上がる床など)には対応できません。

このへんも何とかしたいところです。

2011年01月13日

AIMSでグラディウスっぽいSTGを作る(6)

今回は公式で配布されている SUiCA32 を改造して、地形との当たり判定を実装します。

AIMSにはマップチップの描画機能が用意されているので表示はそれを使いますが、
当たり判定は完全に自前でやることになります。

editor.jpg
マップデータはAIMS付属のMapEditorを使って作成します。
チップのサイズは16*16、マップのサイズは64*64にして、
こんな感じに適当に木を生やしてみました。
(マップチップ画像はマップエディタPlatinumに同梱のサンプルを使っています)

fmf形式でマップデータを出力したら適当なディレクトリに
マップチップ画像と一緒に放り込んでおきます。


次はスクリプトに手を入れます。
ちょうどいい感じにレイヤー9が使われていないのでこれを使います。

#game_common.lua
LAYER_MAPCHIP = 9

マップチップの表示は専用のアクターで行います。
gameシーンの開始時に生成するので game_OnStart()内でcreateActor()を実行します。
このアクターは通常の画像表示をしないので透明のチップハンドル(G.common.clear)を渡しています。

#game.lua
A.mapchip = createActor(G.common.clear, 0, 0, LAYER_MAPCHIP, 'mapchip')

アクターの実体はとりあえずplayer.lua内に定義します。
本当は別ファイルにしたほうがいいんですけど。

#player.lua
function mapchip_OnStart()
G.mapchip = loadGraphic('gfx/empire-3.bmp', 0, 117, 117)
local t = loadFMF('gfx/test.fmf')
mapAllocate(LAYER_MAPCHIP, t.mapWidth, t.mapHeight, G.mapchip, 16, 16, 0, t.data[0])
mapSetWrapMode(LAYER_MAPCHIP, OUTSIDE_NONE, OUTSIDE_WRAP)
mapSetVisible(LAYER_MAPCHIP, true)
setLayerScroll(LAYER_MAPCHIP, 0, 0)
end

function mapchip_OnStep()
local x, y = getLayerScrollX(LAYER_MAPCHIP), getLayerScrollY(LAYER_MAPCHIP)
y = y - 1
setLayerScroll(LAYER_MAPCHIP, x, y)

for index, id in pairs(iLayerActors(LAYER_EBULLET)) do
ex, ey = mapGetWrappedPos(LAYER_MAPCHIP,
bit.arshift(getX(id), 4),
bit.arshift(getY(id) + y, 4))
if mapRead(LAYER_MAPCHIP, ex, ey) ~= 0 then
vanish(id)
end
end
end

function mapchip_OnVanish()
end

suica.jpg
これで、木がスクロールし、敵弾が木に当たると消滅するようになりました。
問題は色々ありますが。
最大の問題は当たり判定の大きさです。
敵弾の中心が当たり判定のあるチップ(16*16ドット)の内部に入った時に接触と見なすため
敵弾がもっと大きい判定を持っている場合に困ってしまいます。

また mapRead() ~= 0 と書いてあるとおり、チップ0番以外は全て当たり判定を持つと
見なすようになっているので、当たり判定を持たない背景が実現できません。

これらも追々解決しなければいけません。

2011年01月11日

AIMSでグラディウスっぽいSTGを作る(5)

敵キャラが出現して、それを破壊できるようになりました。
が、絵面はあまり変わらないのでSSはなし。

このあたりの処理はAIMSならではというところはあまりありません。

ショットやミサイルのOnStepでgetHitLayerを呼んで
当たっている敵の体力を減らしつつ自分はvanishで消滅。
敵のOnStepで体力が0になったらvanishを実行するだけです。
体力の値はVARスロットのどれかをそれ用に割り当てます。


敵のOnVanishでは cause==KILL_ORDERED だったら
爆発エフェクトのアクターを呼び、スコアをカウントアップするようにします。

これでかなりゲームらしさが出てきます。
自機はあいかわらず無敵のままなんですけどね。
自機が死ぬ処理を実装するにはシーンにも手を入れないといけないので
なるべく後回しにしておきたいところです。

2011年01月09日

今年は家の建て替えを予定

春になったら家の建て替えをする予定です。

なにしろ古い家ですからねぇ・・・。
私の部屋なんか、隣に音が筒抜けなので
常時ヘッドホン必須ですし、ジョイスティックも音がうるさいので使えません。

なので建て替えが終わったらXbox360とジョイスティック買って
格ゲーやシューティングをやりまくるんだ・・・!


近所の知り合いが最近タ○ホームで家を建てたばかりで、
「タ○ホームはいいよ」っていうのを真に受けて、家の親も乗り気だったんですけど
いやいや、そこはまずいだろと思って、とある口コミ掲示板のログを親に見せて
思いとどまらせました。

桧○の営業が熱心に足を運んできていて、たぶんここで決まるんじゃないかと思います。
評判もそんなに悪くなさそうですし。


早いところいらない本やCDを処分して荷物を減らしておかないとなぁ・・・。

カレンダー

2012年 8月
Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

twitter