“RUN” それは魔法の言葉

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

90.HSPでゲーム&ウォッチのヘルメットを作ろう その8

次はミスクリアの処理を作る

ミスクリアは200点と500点に達した時に、ミスがあればクリアされる。ミスしていなければ、そのままゲーム続行。

スコアが200(500)点に達するのは、工具の落下と、事務所に駆け込んだときの2パターンある。
なので、「オクトパス」のときに作った判定ロジックを流用した。
スコアを加算する前に、変数bscに待避しておき、bscと、現在のスコアを加味して判定する。

if (bsc>=195 & bsc<200 & score>=200) | (bsc>=495 & bsc<500 & score>=500) {
  if miss>0 and miss_clearflg=0 : miss_clearflg=1 : miss_clearcnt=300 : miss_flash=1 : bsc=score
}

ここまでは楽だった。問題は、ミスクリア時の演出だった。
ミスがあれば、ミスマークが点滅して、ミスがクリアされる。
単純にこれだけならよかったのだが、この間、ドアの開閉も発生するし、作業員も移動できる。
しかも、ドアが開いていると、入れるが、スコアは加算されない。そしてその場から移動できない。

これだけの演出をするために、フラグを作りまくりw
ミスクリア演出中であるフラグ、ミスマークが点滅するためのカウンタ、ミスマークが点灯しているか消灯しているかを表すフラグと、3つのフラグで強引に判定。
ただ、ミスマークを表示するサブルーチンや、移動のサブルーチンはなるべく使いまわすようにした。

*missmark ;ミスマークの表示ルーチン
  ;ミスクリア時の処理
  if miss_clearflg=1 {
    ;30フレーム単位でミスマークを点滅させる
    if (miss_clearcnt\30)=0 {
      miss_flash=miss_flash^1
    }
    miss_clearcnt-
    if miss_clearcnt<=0 : miss_clearflg=0 : miss=0
  }

  if miss_flash=1 or miss_clearflg=0 {
    repeat miss
      pos 530,178
      celput 6,cnt
    loop
  }
  return

89.HSPでゲーム&ウォッチのヘルメットを作ろう その7

次はGAME AとGAME Bの選択を作る。

今までの再現シリーズは、いきなりスタートしていたし、GAMEの違いは作っていない。
そこで今回は筐体画像の「GAME A」と「GAME B」のボタンの部分をタッチしてもらうようにした。

ただ、ボタンを押してもらうのがわからないと、バグかと思ってしまうので、画面に文字を表示することにした。
こちらも7セグフォントで作ろうとしたが、文字が読みづらいので、普通にmes命令で文字列を表示してみた。

タッチパネル処理は、本体のゲームと同じで、座標値の範囲だけ変更。
Windowsでは、キーボード選択できるように、getkey命令を使った。
stick命令では、拾えないキーの入力を拾える。
A、Bと直接キーを押せば、それぞれのゲームモードになるようにした。

ついでにハイスコアを5秒おきに切り替えることにした。
この時点では、ハイスコアの記録処理を作っていないので、ダミーの値をセット。
なかなかいい具合になってきた。

*selectgame ;ゲーム開始前の処理
  
  redraw 0
  pos 0,0
  gmode 2
  celput 1 ;筐体表示

  gosub *showdoor ;ドアの表示

  pos 400,300
  font "",24,1
  mes "Select \nGAME A or GAME B"

  gamecnt++
  if gamecnt>=100000 : gamecnt=0 ;際限なくカウントが増えてオーバーフローしないように

  ;ハイスコアを表示するゲームモードの切り替え
  if gamecnt\300=0 {
    togle=togle^1
  }

  ;ハイスコア表示
  pos 331+(283*togle),424
  celput 9,togle ;ゲームモードの表示
  score=hiscore(togle)
  gosub *showscore

  ;キーボードの操作
  getkey key,'A' : if key : gamemode=0
  getkey key,'B' : if key : gamemode=1

  ;タッチパネル操作の判定
  mtlist touchid
  num=stat
  if num>0 { ;一本以上指がタッチしていて、かつタッチした直後なら
    id = touchid(0) ;一本目のタッチは0番目
    mtinfo touch,id
    if touch(0)=1 { ;タッチがオンならば
      ;タッチされた座標を変数に代入、タッチしたフラグを付ける
      tx=touch(1):ty=touch(2)

      ;ボタンを押したか座標で判定
      if ty>=485 & ty<=638 {
        if tx>=476 & tx<=584 {
          gamemode=0
        } else : if tx>=585 & tx<=694 {
          gamemode=1
        }
      }
    }
  }
  
  await 1000/60
  redraw 1

  ;ゲームモードが選択されたらゲーム開始
  if gamemode>=0 : touched=1:score=0: goto *main
  
  goto *selectgame

ゲームモードによる違いを、3つ再現した。

  1. 工具の落下スピード
  2. 落ちてくる工具の総数
  3. ドアの開閉間隔

開閉間隔は、GAME Aが10秒、GAME Bが5秒と固定なので、配列変数に代入して初期値を設定した。

工具の落下スピードは、何フレームごとに工具を落とすかを早めることで変えられる。
工具の総数も、ゲームモードを絡めた判定を入れたらコントロールできる。
「ファイア」の時と同じように、論理式を使って調整している。

;扉開閉の間隔(GAME A:10秒、GAME B:5秒)
dim doorinterval,2
doorinterval=600,300

;ゲームモード
gamemode=-1 ;-1:選択前、0:GAME A、1:GAME B
		
;ゲームスピードの初期値
g_speed_def=12-gamemode*3
g_speed=g_speed_def

;スコアアップによる調整
;最大工具数
maxtools = 4+(score\100>10)+(score\100>50)+((gamemode=1)*(score\100>80))
;ゲームスピード
g_speed = g_speed_def-(score\100>10)-(score\100>30)-(score\100>50)-(score\100>80)

そして、できたのがこれ。完成にだんだん近づいてきた。

youtu.be

88.HSPでゲーム&ウォッチのヘルメットを作ろう その6

次は作業員と工具の当たり判定と、事務所に駆け込んだ時の処理を作る。

作業員の移動処理は、どのフレームでも受け付けている。
そのため、工具が落ちた時だけ判定すると、作業員の移動が勝って、すり抜けられてしまう。
これを防ぐ為に、今回は作業員の移動処理ごとに、工具との当たり判定を付けるようにした。

移動した位置pxと同じ場所に落ちてくる工具の管理変数toolline()で、最下段に工具がある=16になっていたら、ミスにすることにした。
ミスしたら、その工具は消さなくてはならないので、その処理も追加。管理変数toolline()の値から、-16すればいい。

そして、今回は再現になるべくこだわってみたいと思ったので、ミス時の他の処理についても似せて行くようにした。

まずは、ドアの開閉。
ミス時の作業員が表示されている短い間でも、カウンタは進んでおり、開閉する。
これを再現するには、ミス時の作業員表示をしているときも、ドアの開閉判定をしなくてはならない。
その間は工具の落下処理も止めなくてはならない。
ミス時は、missflgを1にすることで、各処理の前にmissflgの状態判定を追加した。

そして、ミスしたときは、中央にこけた作業員を数フレーム表示しなくてはならない。
この間もドアの開閉カウンタは動かし続ける。

if px>0 and px <6  {
  if (toolline(px-1)&16)=16 { ;一番下まで落下しているときにいたらミス
    ;ミス処理系
    missflg=1
    miss+
    missf=0
    startcnt=0

    ;工具を消す
    toolline(px-1)=toolline(px-1)-16
    falltools-

    ;スタート地点に戻す
    px=0
    return
  }
}

*missprocess
  missf+
  pos 467,413:celput 5
    if missf>=60 {
    missf=0:missflg=0
  }
  return

次は、事務所に駆け込んだ時の処理。
スコアを5点加算することはもちろんだけど、そのままスタート地点に戻す。
ただスタート地点に戻してしまうと、処理が速すぎて、ワープしたみたいになる。
ここでも専用のカウンタを作って、5フレームは、そのままドアの中に入ったポーズを表示するように工夫した。

;ドアに入った状態なら
if px=6 {
  if goalcnt<=5 {
    goalcnt+:goto *showplayer
  } else : goalcnt=0 :px=0
}

;右ボタンを押したときに、ドアが開いていれば得点
if px=6 {
  if doorflg=1 {
      score=score+5
  } else : px=5
}

これでひとまずゲームの形にはなった。

87.HSPでゲーム&ウォッチのヘルメットを作ろう その5

次は工具の落下処理を作っていく。
工具は5種類で、それぞれ5か所落ちると地面になる。
「マンホール」の時に使った、ビットシフトでの管理を行ってみる。今回は5列あるので、配列変数で管理する。

はじめは「マンホール」と同じように、上から下に落ちていく方向にビットが減るようにシフト(右シフト)を行うつもりだった。
右シフトし続ければ、最後のビットを追い出してしまえるので、画面外に出たという処理が不要になる。
そして、シフトした後、各ビットが1になっているときに、その位置の工具を表示すればいい。

「マンホール」の通行人は、画面外にいなくなればいいので、これでよかったが、ヘルメットでは、工具が地面に3つ落ちたら1点と、落ちた数をカウントしなくてはならない。
そうすると、右シフトで追い出されたビットが1なのかを判定しなくてはならない。これだと処理が面倒。
そこで、逆順にして左シフトすることにした。

5ビットしかないから、事実上の桁あふれは気にせずに、単に6ビット目の32が立ったら、カウントして、-32すればいい。
このとき、工具が落ちたという判定になり、カウンタを+1して、3になったらスコアを足す。

f:id:CORO3:20210530222532j:plain

あとは、1ゲームサイクルで、1ラインずつ処理する。
HSPのビット演算は、>><<で書く。

工具の落下処理とともに、落下個数とゲームスピードの調整も作成した。

  if gamecnt\g_speed=0 { ;工具の落下処理フレームなら
    ;1ライン分だけ、工具を落とす
    toolline(toollinecnt)<<1

    ;地面に落下した判定
    if (toolline(toollinecnt)&32)=32 {
      falltoolcnt+
      toolline(toollinecnt)=toolline(toollinecnt)-32
      falltools-
      if falltoolcnt=3 { ;工具3つが地面に落ちたら1点
        score+
        falltoolcnt=0
      }
    }

    ;工具を追加する?
    if falltools < maxtools {
      if rnd(10)>3 and (toolline(toollinecnt)&3)=0 { ;必ず1つ以上あける
        toolline(toollinecnt)=toolline(toollinecnt)+1
        falltools+
      }
    }

    ;動かす工具のラインを進める
    toollinecnt+
    mmplay toollinecnt+1
    if toollinecnt >=5 :  toollinecnt=0
  }
  
*showtools
  ;工具の表示
  repeat 5
    j=cnt
    repeat 5
      if toolline(j)&(1<<cnt) {
        pos toolx(j),tooly(cnt,j)
        celput 10+j,cnt
      }
    loop
  loop

  ;スコアアップによる調整
  ;最大工具数
  maxtools = 4+(score\100>10)+(score\100>50)+((gamemode=1)*(score\100>80))
  ;ゲームスピード
  g_speed = g_speed_def-(score\100>10)-(score\100>30)-(score\100>50)-(score\100>80)
  
  return

そうして、できたのがこれ。
まだ、作業員の当たり判定も、事務所に入った判定もないけど、それっぽくなってきた。

youtu.be