“RUN” それは魔法の言葉

私とコンピュータの思い出を、だらだらと綴ります。最近はHSP3でのゲーム作り日記です

53.キューブ避けゲームを作ろう その4

最後は自機とキューブの当たり判定。

座標値の管理と判定が面倒だなぁと思っていたら、オブジェクト同士の当たり判定をしてくれる便利な命令 GR.COLLISION があった!

これを使えば、簡単に自機とキューブが当たったかを判定できる。
オブジェクト全体の入る矩形同士が重なると、当たったと判定される。
なので、いわゆる自機の判定は1ドットだけ、みたいなのはできない。そういうゲームを作るときは、自前で当たり判定を作らないと行けない。

GR.COLLISIONで、TRUEが返ってきたら、当たったことになるので、ゲームオーバーの処理へGOTOでジャンプ。

 IF GR_COLLISION(自機のオブジェクト,動かしたキューブのオブジェクト) THEN GOTO gameover

BGMは、mp3を垂れ流しできるので、手持ちのグラIIIサントラCDからキューブ面のBGMをmp3化して流すようにした。

!BGMロード・再生(ループ)
AUDIO.LOAD Obgm,"g_bgm.mp3"
AUDIO.PLAY Obgm
AUDIO.LOOP

こうして完成したゲームは、単純ながら、借り物のグラフィックとBGMのおかげでwそれなりに見せられるものになった。
※キャプチャアプリの都合上、ゲーム音声は入っていません。
www.youtube.com

最後は、ソースと画像一式をPCに転送してapkファイルを作成した。
このファイルを実行すれば開発してたタブレット以外で、BASIC!をインストールしていなくても、単独でプログラムが動く。
いつも使ってるメインのスマホにもインストールできて、暇つぶしができるようになった。

なお、こんなしょぼいゲームで遊びたいという奇特な方がいらっしゃったとしても、著作権のクリアができないので、配布いたしません。あしからず。

52.キューブ避けゲームを作ろう その3

星のスクロールができたので、いよいよキューブの部分を作る。

グラフィックのパターンは、面倒なのでネットで拾えた画像から3パターンを抜粋。それを切り替えながら繰り返し表示すると、キューブが回転しているように見えるw

キューブはランダムなY座標に登場するようにした。
そして、自機に向かって来るタイミングもランダムにした。
それっぽければいいのだw
※本家はY軸のラインが複数あって、そのライン上に沿って出現する。気になる人は解説動画へどぞー。

出現したときは、水平、X方向に動くだけ(Y方向は移動量0にする)にして、乱数を発生して向きを変える条件になったら、自機に向かって飛ぶように移動量を決める。

キューブが自機に向かって飛んで来る動きは、三角関数を使って作る。
自機の座標値と、キューブの座標値を元にして、ATAN(アークタンジェント)で角度を求める。
その角度で、X成分とY成分の移動量をsinとcosを使って求める。
それに加速する分の係数を掛けてやれば、次にキューブをXY方向それぞれ、どれだけ動かせばよいかを決められる。
詳しくは、”自機に向かってくる弾 アルゴリズム”とかで、Google先生に聞けば、解説してくれているサイトがあるのでそちらを参考にしてねwww

これをキューブ1個ずつに対して、繰り返し行うと、動きは完成。

キューブの現在の座標、移動量、移動方向、画像などは、配列変数に持たせた。
ループする処理でカウンタをカウントアップして、そのカウンターの値に応じて配列を指定して順次処理するオーソドックスなやり方で作った。

ソースから一部抜粋。

!キューブのビットマップ・初期座標・移動量をセット
FOR i=1 TO 10 %キューブは最大10個まで
 cx[i]=900+(i-1)*400
 cy[i]=50*INT(RND()*9)
 cix[i]=-clength_n % キューブの通常の移動量増分
 ciy[i]=0 % 最初はY方向には動かないので0
 GR.BITMAP.DRAW c_bitmap[i,1],c_obj,cx[i],cy[i]
 GR.BITMAP.DRAW c_bitmap[i,2],c_obj2,cx[i],cy[i]
 GR.BITMAP.DRAW c_bitmap[i,3],c_obj3,cx[i],cy[i]

 !画像を一旦隠す
 GR.HIDE c_bitmap[i,1]
 GR.HIDE c_bitmap[i,2]
 GR.HIDE c_bitmap[i,3]

NEXT

 !キューブを移動
 c_num=MOD(c_count,10)+1

 GR.HIDE c_bitmap[c_num,1]
 GR.HIDE c_bitmap[c_num,2]
 GR.HIDE c_bitmap[c_num,3]

 GR.SHOW c_bitmap[c_num,MOD(c_timing,3)+1] %カウンタを3で割った余りの数で表示するキューブのグラフィックを変更

 !キューブが自機に向かうようにする(100までの乱数を適当に発生させて、15で割り切れるときに向かう)
 IF (MOD(INT(RND()*100),15)=0) & cx[c_num] < 900 & ciy[c_num] = 0 THEN
  angle=ATAN2(jx-cx[c_num],jy-cy[c_num]) %自機とキューブの角度を求める
  cix[c_num] = INT(clength_q*SIN(angle)) %X方向の成分をSINで
  ciy[c_num] = INT(clength_q*COS(angle))   %Y方向の成分をCOSで
 ENDIF

 !キューブを移動
 !キューブの移動量をX、Y方向にプラス(マイナス)して、グラフィックを移動
 cx[c_num]+=cix[c_num]
 cy[c_num]+=ciy[c_num]
 GR.MODIFY c_bitmap[c_num,MOD(c_timing,3)+1],"x",cx[c_num],"y",cy[c_num]

 !キューブが画面外にでた?
 IF (cx[c_num] < 0 | cy[c_num] <0 | cy[c_num] > 500) & c_count > 10 THEN 
  cx[c_num]=1000+INT(RND()*9)*100
  cy[c_num]=50*INT(RND()*9)
  cix[c_num] = -clength_n 
  ciy[c_num] = 0   
 ENDIF
 c_count+=1

<余談>
画像を切り替えたりするのに、昔よくやってた方法。
カウンタの変数を作って0を代入。
ループするたびに、カウンタに1を足す。
そのカウンタを10で割って、余りの数字に1を足すと、1から10までの値が、順番に繰り返される。

0 → 余り0なので → 1
1 → 余り1なので → 2

8→ 余り8なので → 9
9→余り9なので → 10
10→余り0なので → 1
11→余り1なので → 2

割り算の余りを求めるには、MOD関数を使う。たぶんどの言語にも似た命令もしくは演算記号があると思う。
単純にカウンタが10になったら1に戻せばいいのだけど、ゲーム開始から通しで、カウンタ値を使いたいときには、無駄にカウンタを2つ作らなくていい。

51.キューブ避けゲームを作ろう その2

本家グラIIIのキューブ面はこんな感じ。
www.youtube.com

まずは肩慣らしなので、完全再現は目指さず、作るのが面倒そうなところはやらないw

こんなゆるーい仕様で、面白くなるのかもわからず作り始めることにした。

  • 自機は動かすだけ。弾は撃たない
  • キューブは画面の端に行っても溜まらない
  • キューブ同士が接触しても、くっつかないw
  • キューブは適当なタイミングで自機に向かって飛んでくるようにする
  • キューブは、自機に向かって来るときは加速する
  • 1機死んだらガメオベラ(GAMEOVER)
  • 得点は一定時間ごとに増加させる。

タッチパネル操作で自機を動かす部分は作成済みなので、背景に星を表示してスクロールさせるところから作り始めた。なににしても形からww

BASIC!では、点を描画する命令GR.POINTがある。どうやら点も1つのオブジェクトになるらしい。

GR.POINT obj,100,100
GR.RENDER

これで、X=100,Y=100の座標に点が描かれて、オブジェクトobjになる。

星をスクロールするには、点を作るときに指定したオブジェクトobjと、GR.MODIFY命令を使う。

これがちょっと面倒だった。何を変えるかを指定しなくてはならない。
例えば座標を変えたいときは、"x"や"y"と指示した後、変更する値を続ける。
他の描画オブジェクトと共用するためだろうけど、もう少し直接的に扱えればいいのにな。

点の座標をGR.MODIFYで変更して、GR.RENDERで再描画する。

GR.MODIFY obj,"x",200,"y",100
GR.RENDER

星のオブジェクトをたくさん作って、それぞれ色を変えたり、スクロールする速度を変えて表示すれば、それっぽく見えてきたw

www.youtube.com

GR.OPEN 255, 0, 0, 0
GR.SCREEN dis_x,dis_y

GR.SCALE 2,2 %画面比率1/2

!メタリオン画像読み込み
GR.BITMAP.LOAD p_bm_obj, "meta.png" %通常
GR.BITMAP.LOAD p_bm_obj2, "meta_u.png" %上方向
GR.BITMAP.LOAD p_bm_obj3, "meta_d.png" %下方向

DIM p_bitmap[3] %メタリオン画像オブジェクト配列

jx = 400:jy=200 %自機の初期座標
scount=100 %星の数
score=0

GR.BITMAP.DRAW p_bitmap[1], p_bm_obj, jx, jy
GR.BITMAP.DRAW p_bitmap[2], p_bm_obj2, jx, jy
GR.BITMAP.DRAW p_bitmap[3], p_bm_obj3, jx, jy

!画像を一旦隠す
GR.HIDE p_bitmap[1]
GR.HIDE p_bitmap[2]
GR.HIDE p_bitmap[3]

ar=1 %メタリオンの画像番号

!タッチパネルの座標
tx=0:ty=0

GOSUB STAR_BG_INI %星の初期表示

gr.text.size 25
gr.color 255,255,255,255
gr.text.draw scr,0,25,"SCORE:"+USING$("","%10d",int(score))

!ゲーム部
DO 

 !一旦すべて非表示
 FOR j=1 TO 3
  GR.HIDE p_bitmap[j]
 NEXT j

 !自機移動
 GR.TOUCH touched,x,y
 IF tx > 0 | ty > 0 THEN 
  ix = x - tx
  iy = y - ty
 ENDIF

 !自機グラフィック配列番号決定
 ar = 1+ (iy < 0) + (iy > 0)*2

 !自機座標設定
 IF ABS(ix) <= 70 & ABS(iy) <= 70 THEN % 大きな移動量は制限してワープするような動きを抑制
  jx = jx + ix
  IF jx < 0 THEN jx=0
  IF jx >=900 THEN jx=900 

  jy = jy + iy
  IF jy < 10 THEN jy=10
  IF jy >=500 THEN jy=500
 ENDIF

 !自機画像の1パターンだけ表示
 GR.SHOW p_bitmap[ar]

 !自機表示
 GR.MODIFY p_bitmap[ar],"x",jx,"y",jy
 GR.RENDER

 !星のスクロールルーチン呼び出し
 GOSUB STAR_BG

 !タッチしたの座標更新
 tx = x:ty = y
 score += 10
 gr.modify scr,"text","SCORE:"+USING$("","%10d",int(score)) 
 gr.render
 
UNTIL 0

STAR_BG_INI: %星の初期表示サブルーチン
!星は100個ぐらい?
DIM star[scount],star_x[scount],star_y[scount],star_ix[scount]

FOR si=1 TO scount
 star_x[si] = int (RND()*1000) 
 star_y[si] = int (RND()*500)
 star_ix[si] = INT(RND()*3) * 3

 IF MOD(si,3) = 0 THEN
  GR.COLOR 255,255,255,255
 ELSE if MOD(si,3) = 1 then
  GR.COLOR 255,255,0,0
 ELSE
  GR.COLOR 255,0,255,255
 ENDIF

 GR.POINT star[si],star_x[si],star_y[si]

NEXT
GR.RENDER

RETURN

STAR_BG: %星のスクロールサブルーチン

FOR sc=1 TO scount
 star_x[sc] = star_x[sc] - star_ix[sc]
 IF star_x[sc] < 0 THEN
  star_x[sc]=1000
  star_y[sc]=INT(RND()*500)
 ENDIF
 GR.MODIFY star[sc],"x",star_x[sc],"y",star_y[sc]
NEXT
GR.RENDER

RETURN

ONERROR:
!コンソール画面に切り替えてエラーメッセージを表示
GR.FRONT 0
PRINT GETERROR$()
END

50.キューブ避けゲームを作ろう その1

Androidで動く「BASIC!」で、まずはキャラクターをタッチパネルで操作できるプログラムを作ろうと考えた。

BASIC!では、命令の区切りにドット(.)が使われているが、いわゆるオブジェクト指向の参照ではなく、命令のジャンル分けのような使い方みたいだった(だったというのは、英語マニュアルを自動翻訳で読んだだけだからw)

グラフィック関係の命令はだいたい「GR.」で始まる。
まずは画像データをGR.BITMAP.LOADで読み込む。アルファチャンネル(透明色)が使えるのが楽なので、PNG画像を読み込ませた。
そして、その画像データのオブジェクトを、GR.BITMAP.DRAWで配置して表示する。

GR.BITMAP.LOAD p_bm_obj, "meta.png"
GR.BITMAP.DRAW p_bitmap, p_bm_obj, 100, 100
GR.RENDER

次は、タッチパネルの情報を取得する。
GR.TOUCH命令を使うと、1本指だけではなく、複数同時タッチした情報も取得できた。
でも、今回は1本指操作の値だけで十分なので、試しはしていない。

 GR.TOUCH touched,x,y

これで、タッチしたXY座標が変数に格納される。

そして作ったのがこれ。
グラディウス2のメタリオンをタッチパネルをなぞると動くように。
上下方向に移動すると、機体も傾いたグラフィックになるように小細工もしてみたw

www.youtube.com

GR.OPEN 255, 0, 0, 0
GR.SCREEN dis_x,dis_y

GR.SCALE 2,2 %画面比率1/2

!メタリオン画像読み込み
GR.BITMAP.LOAD p_bm_obj, "meta.png" %通常
GR.BITMAP.LOAD p_bm_obj2, "meta_u.png" %上方向
GR.BITMAP.LOAD p_bm_obj3, "meta_d.png" %下方向

DIM p_bitmap[3] %メタリオン画像オブジェクト配列

jx = 400:jy=200 %自機の初期座標
score=0

GR.BITMAP.DRAW p_bitmap[1], p_bm_obj, jx, jy
GR.BITMAP.DRAW p_bitmap[2], p_bm_obj2, jx, jy
GR.BITMAP.DRAW p_bitmap[3], p_bm_obj3, jx, jy

!画像を一旦隠す
GR.HIDE p_bitmap[1]
GR.HIDE p_bitmap[2]
GR.HIDE p_bitmap[3]

ar=1 %メタリオンの画像番号

!タッチパネルの座標
tx=0:ty=0

gr.text.size 25
gr.color 255,255,255,255
gr.text.draw scr,0,25,"SCORE:"+USING$("","%10d",int(score))

!ゲーム部
DO 

 !一旦すべて非表示
 FOR j=1 TO 3
  GR.HIDE p_bitmap[j]
 NEXT j

 !自機移動
 GR.TOUCH touched,x,y
 IF tx > 0 | ty > 0 THEN 
  ix = x - tx
  iy = y - ty
 ENDIF

 !自機グラフィック配列番号決定
 ar = 1+ (iy < 0) + (iy > 0)*2

 !自機座標設定
 IF ABS(ix) <= 70 & ABS(iy) <= 70 THEN % 大きな移動量は制限してワープするような動きを抑制
  jx = jx + ix
  IF jx < 0 THEN jx=0
  IF jx >=900 THEN jx=900 

  jy = jy + iy
  IF jy < 10 THEN jy=10
  IF jy >=500 THEN jy=500
 ENDIF

 !自機画像の1パターンだけ表示
 GR.SHOW p_bitmap[ar]

 !自機表示
 GR.MODIFY p_bitmap[ar],"x",jx,"y",jy
 GR.RENDER

 !タッチしたの座標更新
 tx = x:ty = y
 score += 10
 gr.modify scr,"text","SCORE:"+USING$("","%10d",int(score)) 
 gr.render
 
UNTIL 0

ONERROR:
!コンソール画面に切り替えてエラーメッセージを表示
GR.FRONT 0
PRINT GETERROR$()
END

これをベースに、某グラIIIの9面ラストをモチーフにしたキューブを避けるゲームを作ることにした。

<余談>
この「BASIC!」のグラフィックは、ポイントというか、癖があって、GRで始まる命令は、実行したら即画面に反映されない。GR.RENDERという命令を実行すると、それまでの変更箇所が一括で描画される。
これに気づくのに時間が掛かった。

初めてプログラムしたときは、バンバンGR.RENDERを実行したものだから、処理がめちゃくちゃ重かった。
AndroidのBASICだからこんなもんかと思ってたw

何度か試行錯誤して、まとめてオブジェクトを移動したあとで、GR.RENDERを実行すると処理速度も満足いくものになった。