“RUN” それは魔法の言葉

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

118.HSPでゲーム&ウォッチのマンホールを作り直そう その7

最後は音や、ミスクリアの処理追加、ハイスコアの記録など。
ミスクリア処理、ハイスコア処理は、ほぼヘルメットのプログラムのまま使えるので流用して完了。

音については、以前MIDIで作ってWAVに変換したファイルを読み込むと、なぜかアプリが落ちてしまった。
しょうがないので、HSPで音が鳴ることを確認した、適当なビープ音のWAVをもとに、音を編集できるアプリ「Audacity」でピッチを変更した。

マンホールは、通行人が進むごとに音がなるのだが、他のゲームウォッチと違って、同じ音が鳴るのではなく、半音ずれた音が交互になって心地よいBGMになっていた。
Audacity」を使えば、ピッチを変更できるので、ベース音、ベース音を半音上げたものを作り、交互に鳴らすようにした。

このゲームも「ファイア」や「パラシュート」と同じキャッチ系で、キャッチしたことがわかる必要があるので、キャッチ音にあわせて、スマホではバイブレーションさせるようにした。これで無音でも快適に遊べる。

そうして完成したのがこれ。
youtu.be

ほぼ満足のいく仕上がり♪
通行人のロジックを考える作業は、なかなか楽しかった。十分な頭の体操になってるね。

次はオクトパスを作り直そうかなと思っているけど、新しいチャレンジがあまりない…
そもそもGAME AとGAME Bの違いもイマイチ分かっていない。さてどうしますか。

今年のHSPプログラムコンテストに向けて、早々とネタを考えたので、その基礎技術を習得することにするのと並行しよう。

117.HSPでゲーム&ウォッチのマンホールを作り直そう その6

前回の続き。
通行人のパターンは、前回作ったパターンシートを参考にした。
これを10か所分ごとに区切って、バッファに足す数をパターンを考えた。
通行人の人数ごとにパターンをいくつか出して、その内の1つを選んで、バッファに足すようにしてみた。

パターンは上下段ワンセットにして、カンマで区切った文字列にした。
例えば、上下段ともに1人を追加するなら、"512,512"。これが2人の時のパターン。

上下段で似たような出現パターンになるので、追加するたびに、反転するようにした。
両方とも同じ数字の時は意味がないが、上に1人、下に2人の追加パターンの場合、同じパターンでも上に2人、下に1人の時と1つのデータで済む。

こうして、1人~9人までの上下に出現するパターンを考えて、テストプレイしたが、すぐに具合が悪いことに気づいた…

このゲーム、上下段ともに、進行方向に向かって、4か所目と7か所目で通行人が穴の上に来るのだが、同時に落ちるようになってしまうと、どちらか一方が必ずミスになるので、まずい。
また、連続して通行人を出すときは、1コマ以上空ける。ただし、2コマ空けるのはNG。
この条件に従って、問題が無いように各パターンを考えてデータにしてみたが、連続するパターンによっては、同時に落ちるような問題パターンが発生してしまうのだった。

f:id:CORO3:20220129002409p:plain

問題パターンの組み合わせを人力で見つけていくのは時間がかかりすぎるので、今回考えた通行人追加ロジック部分のプログラムを抜き出して、パターン検証プログラムを作った。
問題のパターンを見つけるために、instr命令を使った。
instr命令は、文字列の中に、指定した文字列があったら、その位置を数字で返す。
無ければ、-1が返ってくる。これを利用した。
問題パターンを検出したら、logmes命令でログにNGの文字を出力するようにした。

buf=str(addpasser)+","+str(addpasser2)+": "
han=""
repeat 2
 j=cnt
 buf=buf+str(j)+": "
 han=""
 repeat 20
  if passline(j)&((512<<10)>>cnt) {
   buf=buf+"1"
   han=han+"1"
  } else : buf=buf+"0" : han=han+"0"
  if cnt=9 : buf=buf+"|"
 loop
 if instr(han,0,"1001")>0 or instr(han,0,"11")>0 {
  buf=buf+" NG!/ "
 } else : buf=buf+"    / "
loop
logmes buf

ログ出力したところ
f:id:CORO3:20220127010452p:plain

こうしてパターンを修正して、ランダムにパターンを繋げても、ゲームとして成り立つようにした。

あとは、スコアに応じて通行人の上限を上げていき、難易度が上がるようにした。
GAME Bでは、これに加えてスコアが増えると、ゲームスピードが速くなる調整も行った。

;スコアアップによる調整
;最大通行人数
maxpasser = 1+(score>10)+(score>45)+(score>150)+(score>230)+(score>350)+(score>450)+(score>600)+((gamemode=1)*(score>750))

;ゲームスピード(GAME Bだけスピードアップ)
if gamemode=1 {
  g_speed = g_speed_def-(score\100>10)*2-(score\100>30)*2-(score\100>50)*2-(score\100>80)*2
}

116.HSPでゲーム&ウォッチのマンホールを作り直そう その5

通行人の出現フレーム数のデータを準備せずに、パターンを発生させるロジックを考えてみた。

今までは、どのタイミングで通行人を1人追加すればよいのかと考えていた。
結局、適切なロジックを思いつくことができず、出現するフレーム数のデータを用意して、ゲーム内でカウントしていたフレームと、同じフレーム数の時に、512(1人)を足していた。

ふと、「なぜ1人ずつ足してたのだろう?、そうだ!次の出現パターンを丸ごと足してしまえば!」と考えた。
ただ、単純に足すのは難しい。そこでバッファ方式を取ることにした。

今までは画面内の10か所だけをビットに見立てて10ビットで管理していたが、バッファとしてさらに10ビット分のエリアを設けて、そこに次の出現パターンを足していくことにした。
このバッファを含めた通行人を表す数値を、1サイクルごとに1回右にシフトして、通行人を歩かせる。

f:id:CORO3:20220126004120p:plain

通行人が一通り動くのは、10回進めたとき。上下段ともに10回ずつ右シフトしたタイミングで、次の10か所分の通行人をパターンで足すようにしてみた。
パターンは、通行人の最大数に合わせて、上下でパターンを用意して、ランダムで足すことにする。

20ビットの内、上位の10ビットをバッファにするので、次の出現パターンとして、一番左に1人出現させる場合、10進数で524288を足すことになる。
パターンを用意するときには、もっと単純化したいので、10ビット分の数値でパターンを準備して、バッファに足すときに、左に10ビットシフトすることにした。

f:id:CORO3:20220125231637p:plain

長くなってきたので続く。

115.HSPでゲーム&ウォッチのマンホールを作り直そう その4

通行人の出現パターンをどうやって作るかの前に、移動処理と判定処理を作ってしまう。
パターンは、とりあえずダミーにする。

今回も通行人の位置はビットで管理して、シフト演算で進める。
そして、消えた後の判定は不要なので、右シフトで消えてもらうようにした。
BASIC!では、タイマー割込みで、上下段を一度に動かしていたが、HSPでは一定カウンタを進めるごとに、上→下→上→…と一段ずつ動かすようにしてみた。

BASIC!でのプログラムをベースに作り始めると、判定処理で面倒なことが発生。
上段と下段は、基本同じ処理なので、配列変数に通行人の状態を持たせて、1ビットずつ進めるのだが、進行方向が違うのだ。

BASIC!の時は、動けばよいのでw上下それぞれで、同じような処理を2つ書いていた。
今回は、一定フレームごとに上段、下段と動かすので、できれば処理を同じにしたい。

移動処理は、通行人の座標値を逆順に配列に格納したので、問題ないのだが、ミスターマンホールの位置を表す番号が問題になった。

 上段   Lineno=0
 下段   Lineno=1
という位置番号にしていたのだが、こうすると、下段のポジション判定が面倒。

そこで、下段の位置番号を左右逆にした。
 上段   Lineno=0
 下段   Lineno=1

こうすることで、上下のラインを表す番号をうまく利用して、位置情報をどちらも同じ数字にすることができた。
ppos-(passlineno*2)
 ppos:ミスターマンホールの位置番号
 passlineno:上段は0、下段は1
上段の時は、pposの値のまま、下段の時は必ずpposから2が引かれ、0と1だけになる。

って、たいそうなことのように書いているが、ブログ主の知識や考え方は、ほぼ高校時のレベルで止まっているのでご勘弁を。
こんな感じで、昔BASICでプログラムを作っていたオッサンなのねと思っていただけると。

*passermove
 ;通行人の表示・移動ルーチン
 
 if gamecnt\g_speed=0 and missflg=0 { ;通行人の処理フレームなら
   ;上か下かどちらか一方のラインだけ、通行人を1つ進める
   passline(passlineno)>>1
   linecnt++
   
   ;落下orキャッチ?
   if ((passline(passlineno)&64)=64 or (passline(passlineno)&8)=8) {
    ;ミスしたときの処理
    ifpos=ppos-(passlineno*2) ;判定用のポジション

    if (passline(passlineno)&64)=64 {
     if ifpos=0 {
      score++
     } else {
      passline(passlineno)=passline(passlineno)-64
      miss++
      missflg=1
      misspos=0+(passlineno*2)
     }
    } 
    if (passline(passlineno)&8)=8 {
     if ifpos=1 {
      score++
     } else {
      passline(passlineno)=passline(passlineno)-8
      miss++
      missflg=1
      misspos=1+(passlineno*2)
     }
    }
   }
  ;動かす通行人のラインを切り替える
  passlineno = passlineno^1
 }

*showpasser
 ;通行人の表示
 repeat 2
  j=cnt
  repeat 10
   if passline(j)=0 : break
   if passline(j)&(512>>cnt) {
    pos passerx(cnt,j),passery(cnt,j)
    celput 10+j,cnt
   }
  loop
 loop
 return