【プチコン講座】Aボタンでイベントを調べられるようにしよう

プチコン

プチコン イベント 調べる

こんにちは。継続の錬金術士なおキーヌです。

ブログ毎日更新は140日目になります。

前回「【プチコン講座】イベントシステム用の衝突判定の実装」でイベントに衝突判定を付けました。

かなりRPGぽくなってきましたが、まだマップを歩けるだけなのでイベントに話しかけられるようにしましょう。

しかしまだメッセージシステムも組んでいないので宝箱を開けてもメッセージを出すことが出来ません。

とりあえずPRINT命令でちゃんと調べられているか確認するところから始めます。

それではプチコンでRPG作り第12回目を始めましょう。

  1. Aボタンを押せる機能をコントローラー関数に追加しよう
  2. Aボタンを押したときに前方のMAP配列の添え字にアクセスしてみよう
  3. イベントを発動させるためにはどうするかを考えてみよう
  4. シーンという概念を知ろう
  5. シーンの実装方法を解説

Aボタンを押せる機能をコントローラー関数に追加しよう

現在、十字キーの処理しか作っていません。

なのでコントローラー関数にAボタンを押したら何かを表示する処理を加えましょう。


# コントローラー関数
DEF D_CONTROLLER
  IF SPVAR(SP_PLAYER, 2) != 1 THEN
    VAR B = BUTTON()
    IF (B AND #UP) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #UP) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #UP
      ENDIF
    ELSEIF (B AND #DOWN) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #DOWN) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #DOWN
      ENDIF
    ELSEIF (B AND #LEFT) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #LEFT) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #LEFT
      ENDIF
    ELSEIF (B AND #RIGHT) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #RIGHT) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #RIGHT
      ENDIF
    ELSEIF (B AND #A) > 0 THEN
      PRINT "Aボタンが押されているよ!"
    ENDIF
  ELSE
    D_GRID_MOVE SP_PLAYER, (SPVAR SP_PLAYER, 3)
  ENDIF
END

条件を追加して「#A」を使えばAボタン判定が出来ます。

実際に動かしてみてAボタンを押してもらうとわかるのですが、1回押しただけでものすごく押されている状態になっています。

これはキーが押されている間条件が通過するので、毎フレーム呼び出されているわけです。

移動時の十字キーの場合はこれでも問題ないですが、
もし選択肢があった場合1回押して何回もうごいてしまってはまともに選択できませんね。

RPGであれば1回Aボタンを押したら「たたかう」コマンドを連打してしまうことになります。

これではストレスのたまるゲームになってしまうのでボタンを1回押したら1回だけ発動という風に切り替えましょう。


# ボタン分の配列を用意
DIM G_BTN_PRESS_COUNT[13]
# 一応定義したボタン配列を0で初期化しておく
FILL G_BTN_PRESS_COUNT,0


# コントローラー関数
DEF D_CONTROLLER
  IF SPVAR(SP_PLAYER, 2) != 1 THEN
    VAR B = BUTTON()
    IF (B AND #UP) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #UP) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #UP
      ENDIF
    ELSEIF (B AND #DOWN) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #DOWN) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #DOWN
      ENDIF
    ELSEIF (B AND #LEFT) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #LEFT) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #LEFT
      ENDIF
    ELSEIF (B AND #RIGHT) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #RIGHT) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #RIGHT
      ENDIF
    ENDIF
  ELSE
    D_GRID_MOVE SP_PLAYER, (SPVAR SP_PLAYER, 3)
  ENDIF

  # 移動中もボタンを押せるようにしておく
  IF G_BTN_PRESS_COUNT[4] == 1 THEN
    PRINT "Aボタンが押されているよ!"
  ENDIF

END

# ボタンカウント関数
DEF D_BTN_PRESS_COUNT
  VAR B = BUTTON()

  # Aボタンカウント
  IF (B AND #A) > 0 THEN
    INC G_BTN_PRESS_COUNT[4]
  ELSE
    # 1回でも離されたらカウントリセット
    G_BTN_PRESS_COUNT[4] = 0
  ENDIF
END

これで実行してみてください。

今度は長押しししても1回しか発動しないはずです。

それもそのはずで、ボタンを押されるたびにカウントが増えていきます。

カウントが1以外は発動しないようにしているので1回しか発動しませんし、
ボタンが離すまでカウントは終わらないしリセットされないので動きません。

しかしこのままでは押し続けるといずれオーバーフローを起こしてしまうので、
カウント数上限を決めておかなければいけません。

カウント関数を以下のように修正しましょう。


# ボタンカウント関数
DEF D_BTN_PRESS_COUNT
  VAR B = BUTTON()

  # Aボタンカウント
  IF (B AND #A) > 0 THEN
    IF G_BTN_PRESS_COUNT[4] < 255 THEN 
      INC G_BTN_PRESS_COUNT[4]
    ENDIF
  ELSE
    # 1回でも離されたらカウントリセット
    G_BTN_PRESS_COUNT[4] = 0
  ENDIF
END

ボタンが押されていたらカウントする条件になっていましたが、
ボタンが押されてからさらに自分自身の数が255未満であればカウントするという風に変えました。

カウントの数値はお好みで大丈夫です。

作るゲームに合わせて変更してください。

初心者的な話になるのですがこの条件の順番が結構重要で

「B AND #A && G_BTN_PRESS_COUNT[4] < 255」

楽をしようとしてという条件にしてしまうとどちらかが満たない場合は、
偽りであるボタンカウントリセットの処理が働いてしまうので、
255までいくと押し続けていてもまたAボタンが押されたことになってしまいます。

後はボタンの種類にを増やしたい場合はここにボタン分の処理を書いていけばOKです。

このボタンカウント処理は汎用性が高いので、アクションゲームなんかでも使えます。

例えばシューティングのチャージショットを実装する時にボタンカウントが一定まで溜まるとチャージ開始。

ボタンカウント数で強さを変えるとかにも考え方次第で色々使えますね。

ボタンを押したときに前方のMAP配列の添え字にアクセスしてみよう

ボタンを1回だけ処理する方法が実装できたので、今度はAボタンを押したときに
プレイヤーが向いている先のマップ配列にアクセスできるようにしてみましょう。

この時調べるのはイベントレイヤーだけでOKです。

やり方は衝突判定の時と殆ど同じです。

詳しく言うと、十字キーを押したときに進めるかどうかの判断をしたと思います。

その時、前方のグリッドにアクセスしたと思います。

その処理をそのまま流用して調べたイベントレイヤーの数値を見て分岐処理を作ります。

もし調べた値が1だった場合、イベントのデータベースからID1に該当するものをひっぱってくるだけです。

しかし今はイベントデータベースを作成していないので分岐しようがないですよね。

今回はやりませんが、画面に何に触れたかわかるようにしてみましょう。

現状プレイヤーが触れられるのはコウモリが道を防いでるせいでコウモリと宝箱しか調べられないと思います。

それではとりあえずこの2つを調べられるコードを書いていきましょう。

イベントレイヤー書き換え関数とほぼ同じ処理になりますがこの関数をコピーして、
イベントチェックをする新たな関数を作ってみましょう。


# 前方のイベントをチェックする
DEF D_EV_CHECK A_NO A_DIRECT

  VAR POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 3)
  VAR AFTER_POS = POS + D_GET_DIRECT_POINT(A_DIRECT)

  VAR EV_ID = G_MAP[AFTER_POS]

  IF EV_ID == 0 THEN
    PRINT "なにも ない よ!"
  ELSEIF EV_ID == 1 THEN
    PRINT "モンスター コウモリ が いるよ!"
  ELSEIF EV_ID == 6 THEN
    PRINT "たからばこ が あるよ!"
  ENDIF

END

イベントIDで分岐する処理は関数化できそうですね。

こうやって最初からいきなり関数化するのではなく、
実際にコードを組んでみて「これ何回も使いそうだし関数化したほうが便利だな」ってのが出てきます。

無駄に関数化してもプログラムが動いている中1回しか使わなかったり、
他に使えない汎用性が無さ過ぎるものは関数化しないほうがいいでしょう。

そして作った関数をAボタンを押したときに発動するようにしてみましょう。


# コントローラー関数
DEF D_CONTROLLER

  ~~ 省略 ~~

  # 移動中もボタンを押せるようにしておく
  IF G_BTN_PRESS_COUNT[4] == 1 THEN
    PRINT "Aボタンが押されたよ!"
    D_EV_CHECK G_SP_PLAYER SPVAR(G_SP_PLAYER, 3)
  ENDIF

END

それでは実行してみましょう。

まずは何もない所でAボタンを押すと「なにも ない よ!」と表示されます。

次に、宝箱の前に立ってAボタンを押すと「たからばこ が あるよ!」と表示されます。

そして、コウモリの前に立ってAボタンを押すと「モンスター コウモリ が いるよ!」と表示されます。

ここで色々操作を弄っているとある違和感に気付くと思います。

それは、コウモリの前で左右キーを押してからでもコウモリの方を向いていることになってしまっています。

試しにコウモリの前で左右を押してからAボタンを押してみると、コウモリを調べたことになります。

というのも、左右は木があって動けない状態ですね。

現状では動けるようになって初めてプレイヤーの向きが更新される状態になっています。

蟹歩きなので今どの方向を向いているのかもわかりませんし、改良する点はいっぱいあります。

ですがゲームをする上で今はそこまで重要じゃないので作り終えてから改良として考えましょう。

とはいっても動けなくても向きの変更はしておきたいので、次回に修正しようと思います。

イベントを発動させるためにはどうするかを考えてみよう

ここから考察のターンになりますので、今回のコーディングはこれにて終了です。 お疲れ様でした。

次回はイベントを調べたらイベントが発動するようにしていきます。

では、イベントを発動させるためにはどうしたらいいでしょうか?

現在は調べた時に取得したIDによってIFで分岐していますね。

PRINTで文字を出していますが、それだと面白くありません。

じゃあどうするかというと、イベントIDに紐づいたイベントリストが必要になります。

イベントリストとは

イベントリスとは、イベントの流れをまとめたものです。

なのでイベント1つ1つに何をするか記述するファイルを作らなければいけません。

RPGツクールで考えると、1つのイベントに対してIDも座標もイベントリストもすべて情報を持っていますよね。

あれを作ろうとするとかなり骨が折れるので、今回はイベント位置とイベントリストは切り分けました。

初回の講座はなるべく外部ファイルを使わずにいきたいところなので、
コードの最下部にイベント1つ1つのデータ配列を作っていきます。

イベント1つ1つにタイプを持たせて、取り出したときにどの処理を動かすかを決めます。

数日前にも同じようなことを話しましたがもう一度言っておきます。

たからばこのイベントを作ったとしましょう。

すると以下のようなフローチャートになります。

  • たからばこを調べる
  • たからばこのイベントIDを取得する
  • スプライト変数を参照する
  • イベントIDのイベントリストを取得する
  • イベントリスト1つ目はメッセージだった
  • メッセージ関数にメッセージを渡して画面に表示させる
  • メッセージ送り待機(Aボタン入力で次へ)
  • 宝箱の画像を開いたものに変更
  • メッセージ関数にメッセージを渡して画面に表示させる
  • メッセージ送り待機(Aボタン入力で次へ)
  • アイテムの増減処理
  • イベント終了
  • スプライト変数を変化させる

宝箱1つでもこんなに処理があります。

しかし1度組んでおけば宝箱関数に文字列と取得したいアイテムの番号を渡すだけでいいですね。

注目してほしいのが

  • スプライト変数を参照する
  • スプライト変数を変化させる

この二つです。

これは何をしているかというと、宝箱の場合最初は閉じているためアイテムを取得するイベントが発動します。

しかし、そのままだと開けてからもう一度調べるとまたアイテムを取得するイベントが発動してしまいます。

RPGツクールでいうスイッチをONにしていない状態なので無限にアイテムを手に入れられます。

幸い、プチコンはスプライトに独自変数を持つことができるようになっていましたね。

そこの一部をスイッチと見立てて変数の値を変更しておけば、
調べた時に変数が1だった場合既に宝箱は取得済みという判断ができるので、
「中身は空っぽだった」という処理に分岐させることが出来ます。

余談:2回目を調べさせたいとき

例えばですが、もし宝箱を2回目調べてみたら、まだ底にお宝が残っていた!
という状況を作りたい場合は別のイベントを呼び出してくるような感じで作らなければいけません。

なのでイベント1つに付きスペースをいくつか設けておかなければいけません。

楽をするなら配列の配列を作っておく感じでしょうか。

これをやるとかなり複雑なコードになってしまうので今回はやりません。

シーンという概念を知ろう

ゲームを作るときにはシーンという概念が必要になります。

簡単に言うとRPGでは以下のようなシーンがあります。

  • タイトルシーン
  • オープニングシーン
  • 会話シーン
  • マップ移動シーン
  • ステータス画面シーン
  • バトルシーン
  • ゲームオーバーシーン
  • エンディングシーン

現在は「マップ移動シーン」だけ作ってますね。

ちょっと話は戻りますが、宝箱を開けた時にプレイヤーが自由に動き回れるとしたら不自然ですよね。

開けた瞬間に移動してメッセージが出て動き続けながらアイテムを取得したメッセージが出るわけです。

FPSやシューティング系ならそれでもいいですが、RPGだと本来はその場で止めるべきです。

なのでイベントが動いている時はシーンを切り替えて移動できないようにしなければいけません。

シーンの実装方法

シーンの実装は簡単で、現在ゲームループのところでコントローラー関数とボタンカウント関数を呼び出しています。

毎フレーム呼び出されるのでプレイヤーを動かせるわけですよね。

シーン用のグローバル変数を作ってここで条件分岐を作ればシーンの切り替えが出来るようになります。

例えばシーン変数0だった場合はいつも通りマップを動かせるようにしておいて、
宝箱を開けたらシーン変数に1を代入してメッセージシーンに切り替えるような感じですね。

全てのイベントが終わったらシーン変数を0に戻してマップ移動シーンに戻します。

バトルの時なんかもまったく同じでシーン変数によって呼び出す変数を切り替えるだけです。

言葉にすると結構難しく感じられますが、コードにするとかなりシンプルになって分かりやすいと思います。

今回はコーディングしませんので、知識として覚えておいてください。

最後に恒例のここまでのソースコードを張り付けておきます。

それでは。

ACLS

# 変数定義
VAR G_STOP_FLAG = TRUE # ゲームループフラグ
VAR G_I # 汎用変数
VAR G_OX = 0, G_OY = 0 # BG読み込みのオフセット
VAR G_SZ=16, G_MW, G_MH # チップサイズ、マップ幅、マップ高さ
VAR G_BGW = CEIL(400/G_SZ), G_BGH = CEIL(240/G_SZ) # BG読み込み準備
DIM G_MAP[0] # マップレイヤー4枚+イベントレイヤー分

# 変数定義
VAR G_SP_PLAYER = 0 # PLAYERスプライトNo.
DIM G_SP_ANIM_NO[9] # スプライトアニメーション初期値変数 添え字=ID
DIM G_EV_ID[32] # イベント用情報の配列
VAR G_EV_STEP = 4 # 1つのイベントデータ数

# ボタン分の配列を用意
DIM G_BTN_PRESS_COUNT[13]
# 一応定義したボタン配列を0で初期化しておく
FILL G_BTN_PRESS_COUNT,0


# データ定義場所
DATA 500,1040,920,1000,980,1020,269,269,269
DATA 1,5,5,2,  2,4,11,2,  3,23,13,2,  4,13,3,2,  5,22,4,2
DATA 6,6,7,1,  7,3,11,1,  8,22,3,1

# アニメーション配列初期値代入
D_FIRST_DATA_READ G_SP_ANIM_NO
# イベントID,X座標,Y座標代入
D_FIRST_DATA_READ G_EV_ID

# マップデータ準備
LOAD "DAT:TEST", G_MAP, 0 # マップデータのロード
G_MW = SHIFT(G_MAP) # 読み込んだデータ配列の0個目を配列から切り離して取得(1638415という数字が入ってる)
G_MH = G_MW AND &HFFFF : G_MW = G_MW >> 16 AND &HFFFF # 取り出したデータからシフト演算やらビット演算をする

# マップデータ描写
FOR G_I=0 TO 3
  BGSCREEN G_I, G_BGW, G_BGH, G_SZ # 1画面分のBG
  BGLOAD I, -G_OX, ( -G_OY - ( G_I * G_MH )), G_MW, G_MH * ( G_I + 1 ), G_MAP # マップ描写
NEXT

# プレイヤー配置
D_SP_SETUP G_SP_PLAYER, 1, 0

# イベント配置
FOR G_I=0 TO LEN(G_EV_ID)-1 STEP G_EV_STEP
  D_SP_SETUP G_EV_ID[G_I], G_EV_ID[G_I+1], EV_ID[G_I+2]
NEXT

# ゲームループ
WHILE G_STOP_FLAG
  D_CONTROLLER # コントローラー関数呼び出し
  VSYNC 1 # 垂直同期
WEND

# コントローラー関数
DEF D_CONTROLLER
  IF SPVAR(SP_PLAYER, 2) != 1 THEN
    VAR B = BUTTON()
    IF (B AND #UP) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #UP) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #UP
      ENDIF
    ELSEIF (B AND #DOWN) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #DOWN) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #DOWN
      ENDIF
    ELSEIF (B AND #LEFT) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #LEFT) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #LEFT
      ENDIF
    ELSEIF (B AND #RIGHT) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #RIGHT) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #RIGHT
      ENDIF
    ENDIF
  ELSE
    D_GRID_MOVE SP_PLAYER, (SPVAR SP_PLAYER, 3)
  ENDIF

  # 移動中もボタンを押せるようにしておく
  IF G_BTN_PRESS_COUNT[4] == 1 THEN
    PRINT "Aボタンが押されたよ!"
    D_EV_CHECK G_SP_PLAYER SPVAR(G_SP_PLAYER, 3)
  ENDIF

END

# 向きと移動フラグを設定
DEF D_DIRECTION_MOVE A_SPRITE_NO, A_SPRITE_DIRECTION
  SPVAR A_SPRITE_NO, 2, 1
  SPVAR A_SPRITE_NO, 3, A_DIRECTION
END

# 向いている方向に止まるまで歩き続ける(グリッド移動)
DEF D_GRID_MOVE SPRITE_NO, SPRITE_DIRECTION
  # 渡されたスプライトと向きによって移動方向を決定
  IF SPRITE_DIRECTION == #UP THEN
    SPVAR SPRITE_NO, 1, (SPVAR SPRITE_NO, 1) - 1
  ELSEIF SPRITE_DIRECTION == #DOWN THEN
    SPVAR SPRITE_NO, 1, (SPVAR SPRITE_NO, 1) + 1
  ELSEIF SPRITE_DIRECTION == #LEFT THEN
    SPVAR SPRITE_NO, 0, (SPVAR SPRITE_NO, 0) - 1
  ELSEIF SPRITE_DIRECTION == #RIGHT THEN
    SPVAR SPRITE_NO, 0, (SPVAR SPRITE_NO, 0) + 1
  ENDIF

  # 移動させる
  SPOFS SPRITE_NO,(SPVAR SPRITE_NO, 0),(SPVAR SPRITE_NO, 1)

  # 割った余りが0になったら動きを止める
  IF (SPVAR SPRITE_NO, 0) MOD G_SZ == 0 && (SPVAR G_SPRITE_NO, 1) MOD G_SZ == 0 THEN
    SPVAR G_SPRITE_NO, 2, 0
  ENDIF

END

# ドット座標からグリッド座標に変換
DEF D_GET_GRID_XY(A_X_OR_Y)
  RETURN A_X_OR_Y / G_SZ
END

# MAP配列のどこにいるかチェック
DEF D_GET_MAP_POSITION(A_X, A_Y, A_LAYER)
  VAR RETURN_POS = 0
  VAR GRID_X = D_GET_GRID_XY(A_X)
  VAR GRID_Y = D_GET_GRID_XY(A_Y)

  VAR PULS_LAYER = (G_MW * G_MH) * A_LAYER

  IF A_Y <= 0 THEN
    RETURN_POS = GRID_X + PLUS_LAYER
  ELSE
    RETURN_POS = (GRID_X + (GRID_Y * G_MW)) + PLUS_LAYER
  ENDIF

  RETURN RETURN_POS
END

# 当たり判定チェック
DEF D_CHECK_COLLISION(A_NO, A_DIRECTION)
  VAR MP1_SUM_POS = 0
  VAR EV_SUM_POS = 0

  VAR DIRECT = D_GET_DIRECT_POINT(A_DIRECTION)

  VAR MAP_POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 1)
  VAR EVENT_POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 3)

  MP1_SUM_POS = MAP_POS + DIRECT
  EV_SUM_POS = EVENT_POS + DIRECT

  IF G_MAP[MP1_SUM_POS] == 0 THEN
    IF G_MAP[EV_SUM_POS] == 0 THEN
      D_EV_LAYER_REWRITE A_NO, A_DIRECTION, 1
      RETURN 1
    ELSE
      RETURN
    ENDIF
  ELSE
    RETURN 0
  ENDIF
END

# スプライトセットアップ
DEF D_SP_SETUP A_NO, A_X, A_Y
  VAR F = 20
  VAR ANIM = G_SP_ANIM_NO[A_NO]

  SPSET A_NO ANIM
  SPSET A_NO 0, G_SZ * A_X
  SPSET A_NO 1, G_SZ * A_Y
  SPSET A_NO SPVAR(A_NO,0), SPVAR(A_NO,1)

  IF A_NO == 0 || G_EV_ID[(G_EV_STEP * A_NO)-1] == 2 THEN
    SPANIM A_NO, "I", F,ANIM, F,ANIM+1, F,ANIM+2, F,ANIM+3, 0 
  ENDIF
END

# イベントレイヤー書き換え
DEF D_EV_LAYER_REWRITE A_NO, A_DIRECT, A_FLAG

  VAR POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 3)

  IF A_FLAG > 0 THEN
    IF A_DIRECT > 0 THEN
      VAR AFTER_POS = POS + D_GET_DIRECT_POINT(A_DIRECT)
      G_MAP[AFTER_POS] = A_NO
    ELSE
      G_MAP[POS] = A_NO
    ENDIF
  ELSE
    VAR BEFORE_POS = POS - D_GET_DIRECT_POINT(A_DIRECT)
    G_MAP[BEFORE_POS] = 0
  ENDIF

END

# 方向定数から1歩前のグリッド座標を得るための値を得る
DEF D_GET_DIRECT_POINT(A_DIRECTION)
  IF A_DIRECTION == #UP THEN
    RETURN DIRECT = -MW
  ELSEIF A_DIRECTION == #DOWN THEN
    RETURN DIRECT = MW
  ELSEIF A_DIRECTION == #LEFT THEN
    RETURN DIRECT = -1
  ELSEIF A_DIRECTION == #RIGHT THEN
    RETURN DIRECT = 1
  ENDIF
END

# DATA初期読み込み
DEF DD_FIRST_DATA_READ A_ARRAY
  FOR G_I=0 TO LEN(G_EV_ID)-1
    VAR N=0: READ N
    G_EV_ID[G_I] = N
  NEXT
END

# ボタンカウント関数
DEF D_BTN_PRESS_COUNT
  VAR B = BUTTON()

  # Aボタンカウント
  IF (B AND #A) > 0 THEN
    IF G_BTN_PRESS_COUNT[4] < 255 THEN 
      INC G_BTN_PRESS_COUNT[4]
    ENDIF
  ELSE
    # 1回でも離されたらカウントリセット
    G_BTN_PRESS_COUNT[4] = 0
  ENDIF
END

# 前方のイベントをチェックする
DEF D_EV_CHECK A_NO A_DIRECT

  VAR POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 3)
  VAR AFTER_POS = POS + D_GET_DIRECT_POINT(A_DIRECT)

  VAR EV_ID = G_MAP[AFTER_POS]

  IF EV_ID == 0 THEN
    PRINT "なにも ない よ!"
  ELSEIF EV_ID == 1 THEN
    PRINT "モンスター コウモリ が いるよ!"
  ELSEIF EV_ID == 6 THEN
    PRINT "たからばこ が あるよ!"
  ENDIF

END