【プチコン4講座】イベントシステム構築:イベント駆動処理
こんにちは。継続の錬金術士なおキーヌです。
ブログ毎日更新は174日目になります。
前回「【プチコン4講座】シーンという概念を学ぶ」でシーンの概念を学んでマップとイベントを切り替えることをおしました。
今回はイベントを処理するために、キュー構造を利用します。
前回の最後でも説明しましたが、キューとは先に入れたものから取り出す方式です。
運動会のプログラム表をイメージしてもらうとわかりやすいかもしれません。
ようは配列の0個めのデータを順に取り出して、なくなればイベントシーンを終わりマップシーンに戻すわけです。
それではプチコン4でRPG作りその10回目始めましょう。
イベントキュー用の配列を作ろう
DIM G_EvQueue[]
イベントキューの数は予想がつきませんので、配列の数を明示的に指定しません。
C言語とかだと要素数を指定しないとエラーで怒られちゃうので、
配列数を指定しないでもいける言語以外ではココからが難易度が上がってしまいます。
対策としては多めに配列数を確保しておいて最大イベント数がわかれば調整するという感じでしょうか。
いつかはC++でゲームを完成させてみたいですが、当分はプチコン4とC#でしょうか。
話がズレたので戻すと、このイベントキュー配列に入ったイベントを1つずつ取り出して
その内容に応じて処理していくといった感じです。
イベントキューの配列数が0より大きければイベントシーンに遷移
イベントキュー配列を作ったのでイベントを調べたら、
各自が持っているイベントリストをキューに入れていきます。
マップで移動している時はイベントキューが空の状態です。
イベントを調べてキューにリストを突っ込んだら強制的にイベントシーンにうつるようにします。
そのためにはマップシーン側でイベントキュー配列の要素数を監視する必要があります。
'──────────────────────────
' シーン管理
'──────────────────────────
def D_GameSceneManager
var direct = D_Controller()
case G_SceneFlag
when 0
' マップシーン
D_SpriteGridMove 0, direct
' イベントキュー監視
if LEN(G_EvQueue) > 0 then
G_SceneFlag = 1
endif
when 1
' イベントシーン
D_EventScene
endcase
end
このように配列数を監視してキューが入ったらシーンの切り替えを行います。
イベント構造を考えよう
イベントキューにイベントをいれていくにはそれぞれがイベントリストを持たなければいけません。
幸いイベントにはユニークなIDが振られているので、
データをひとまとめにしていてもイベントIDによって取得位置を変更すればいいですね。
イベントリストを作ってみよう
まずは宝箱のイベントリストを作ってみましょう。
宝箱はミミックとかを除けば調べるとアイテムが手に入るのが基本ですね。
その際に動くイベントといえばなんでしょうか?
RPGツクールをしている人はすぐにわかると思いますが、
宝箱を開けるだけでアイテム取得以外にも複数のイベントが必要になります。
まず調べると「ガチャッ(SE)」「アイテム入手」「宝箱を開いたグラフィックに変更」そして、
「宝箱を開けた」というメッセージが表示されます。
調べただけで4つのイベントが処理されていますね。
その後、決定ボタンが押されるまでメッセージを表示したまま待機します。
これがメッセージ送り待機です。
決定ボタンを押してメッセージ送りをされたら次は「ピロリン(SE)」「ポーションが入っていた」と表示されます。
これで5つのイベントが発動しましたね。
その後、処理をしないと無限にポーションを手に入れられてしまうので
イベントの最後にイベントを作動したというイベントを入れます。
RPGツクールで言えばスイッチをONにすると同じことをやります。
イベントについてはプチコンBIGの「【プチコン講座】イベントを動かすためのシーンを作ってみよう」方でも解説したので良ければ見てください。
それでは宝箱のイベントを組んでみましょう。
@Event007
DATA 5, 10,10,0, 3,0,1, 2,1,0, 1,1,0, 1,2,1
プチコン4になって配列を扱いやすくなりましたが、
初期値を入れておくより必要な時に値をコピーして配列に挿入する方が何かと都合がよいです。
例えば村人のイベントを作るとき、イベントが終わっても再び話しかけたら同じことを喋ると思います。
その場合最初から配列に値を入れていたら処理が面倒くさいのでこの手法で行きます。
「DATA」について少しだけ説明しておくと、READ命令を使えば1つずつ読み込めるものです。
forで回して配列に代入すれば1つのデータセットが出来上がりますね。
そして手前に@~でラベルを付けておきます。
こうするとRESTORE @~ でそこに飛ぶことが出来るのでREADを使えばまたここから読み込んでくれるという感じです。
それではDATAの中身を見てみましょう。
データの構造を詳しく見てみる
まずDATA0個めは5となっていますね。
この0個目の値はイベントの数を表しています。
RESTOREして1回目のREADをしたときに何回forで回せばいいのかが分かります。
しかしDATAは5つ以上ありますね。
よく見てもらうと3つずつで空白でわかるように区切っていると思います。
これは1つのイベントで3つの値を使うという感じです。
内訳を書くと
- 1つめ > イベントタイプ
- 2つめ > イベント内容
- 3つめ > イベント詳細
となります。
1つ目のイベントタイプは「メッセージ」「音を鳴らす」「バトル開始」などのイベントのタイプを決めるものです。
2つ目はイベントタイプに合わせて何を呼び出すのかですね。
メッセージであれば用意されたメッセージ配列のどこから取り出すのか。という風に使います。
最後に3つ目のイベント詳細はそのイベントの中でもさらに分岐させるためにといった感じに使います。
例えば「アイテム取得イベント」だった場合「ポーション」という内容で詳細は「2つ」手に入れる。
「バトルイベント」の場合「コウモリ」という内容で詳細は「負けイベント」みたいな感じに使えますね。
ゲームが大掛かりになってくると3つでは足りないでしょうから、その時に拡張すると良いでしょう。
データの中身を見てみよう
それでは0個め以降の数値を見てみましょう。
最初以外は3つずつといったので「10,10,0」が順番に取り出されますね。
1つ目はイベントタイプでしたね
10は「音を鳴らす」という風に決めました。
ようはBEEP命令を呼び出す準備です。
2つ目はイベントの内容です。
10ということは「BEEP 10」という風に書き替えることも出来ます。
そして最後はイベント詳細でしたね。
BEEPは引数2つ目はピッチになっていましたので詳細はピッチの変更ということになります。
0なのでピッチの変更は無しです。
残りの「3,0,1 2,1,0 1,1,0 1,2,1」を1つずつ紹介していたら無駄に記事が長くなってしまうので
イベントタイプだけ解説します。
イベントタイプ3はアイテムの増減です。
データ上でライフポーションを1つ手に入れます。
イベントタイプ2はグラフィックの変更です。
宝箱を調べた瞬間にグラフィックを開けたものに変えたいので呼び出しています。
イベントタイプ1はメッセージ表示です。
「宝箱を開けた」
「ポーションを1つ手に入れた」
とそれぞれ表示されます。
イベントを処理してみよう
イベントデータの準備も内容も分かったところで、これをイベントキューに入れて処理してみましょう。
前回の記事「【プチコン4講座】シーンという概念を学ぶ」で作った空の関数「def D_EventScene」があったと思います。
イベントシーンに切り替えた時に呼び出してた関数です。
イベント処理はこの中に書いていきます。
まずはシーン関数の中で使うイベントキューを取り出し関数を作成します。
def D_ShiftEvData
var type = SHIFT(G_EvQueue)
var event = SHIFT(G_EvQueue)
var reserve = SHIFT(G_EvQueue)
print "[イベント かいし]"
print "===================="
case type
when 1
print "メッセージイベント:" + STR$(event) +", " + "しょうさい:" + STR$(reserve)
when 2
print "スプライトへんこうイベント:" + STR$(event) +", " + "詳細" + STR$(reserve)
when 3
print "アイテムぞうげんイベント:" + STR$(event) +", " + "詳細" + STR$(reserve)
when 10
print "SEえんそうイベント:" + STR$(event) +", " + "詳細" + STR$(reserve)
endcase
print "===================="
print "-------------------"
if LEN(G_EvQueue) <= 0 then
G_SceneFlag = 0
endif
end
SHIFTは破壊的な配列の値取り出し命令です。
破壊的とは元の変数や配列の内容を変更してしまう処理のことです。
通常なら値のコピーになったりアドレスを入れるだけだったりしますが、
破壊的な命令は元の変数や配列に影響を与えます。
SHIFTは配列の0番目を値を取り出す、というか切り出す命令です。
頭を切り取られた配列は頭を埋めるために内容がズレます。(0番目がなくなったので1番目が0番目にズレる)
こうすることで先に入れた値を取り出すキューを実現できます。
一度のループで3つ取り出しているのがわかるでしょうか。
後はそれぞれの内容によってcaseで分岐させています。
今はイベント内容をなにかするということはしていないのでPRINTで中身を表示しているだけです。
最後に取り出した配列の長さを監視して0以下であればシーンをマップに戻すようにしています。
こうすることでイベントが終わったらマップシーンに戻るようになっているわけですね。
イベントをチェックして
イベント取り出して処理する関数はループで処理していきますので、
扱う前にデータの1つ目のイベントの数をSHIFTで切り出してその切り出した値を元にループさせます。
イベントを調べたらそのイベントIDによって準備するイベントデータ配列を変更し、
イベントデータをキューに入れてみましょう。
'──────────────────────────
' ┠─ ▼ イベント判定関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' 前方のイベントをチェックする
def D_EvCheck A_Id, A_Direct
var pos = D_GetMapPosition(SPVAR(A_Id,"X"), SPVAR(A_Id,"Y"), #LAYER_EV)
var afterPos = pos + D_GetGridDirectPoint(A_Direct)
var evId = G_MapData[afterPos]
D_GetEvData evId
end
' イベントデータ取り出してキューに格納
'──────────────────────────
def D_GetEvData A_Id
var evCount = 0
var evData = 0
case A_Id
when 7
RESTORE @Event007
READ evCount
for G_I=0 to (evCount * 3)-1
READ evData
PUSH G_EvQueue, evData
next
endcase
if LEN(G_EvQueue) > 0 then
G_SceneFlag = 1
endif
end
これでイベントを調べたらIDを元にデータを参照して、
イベントキュー配列にデータを挿入することが出来るようになりました。
それではこれらの関数を動かすためにシーンに組み込みましょう。
def D_EventScene
D_ShiftEvData
end
前回でD_EventScene関数の引数に方向を渡してましたが、特にいりませんでしたので消しました。
これでマップシーンの時にイベントを調べるとイベントキューが作成され、
イベントキューが0個より大きい場合にイベントシーンに切り替えるので、イベント処理が始まります。
そしてイベント処理が完全に終わったらマップシーンに戻ります。
最後にボタンでシーン切り替えを実装していたと思いますが、
もういらないので決定ボタン以外の処理は消しておいてください。
──イベントを種類で分岐できるようになったので次回はメッセージを表示するためのウィンドウを作ります。
ウィンドウはスプライトで作った方がいいのですが、用意するのが面倒なので線や矩形を描写する命令で作ります。
その際に表示の優先度を変更しなければいけないのでちょっと面倒ですが頑張りましょう。
最後に今回の完成版ソースコードを置いておきます。
それでは
'──────────────────────────
' ▼ 動作モード設定:変数宣言は必須
'──────────────────────────
OPTION STRICT
'──────────────────────────
' ▼ 画面のクリア
'──────────────────────────
ACLS
'──────────────────────────
' ▼ 定数・構造体の定義
'──────────────────────────
CONST #CSZ = 16
ENUM #LAYER_MAP1=0,#LAYER_MAP2,#LAYER_EV
'──────────────────────────
' ▼ 変数・配列の定義
'──────────────────────────
' ┠─ 汎用変数
'━━━━━━━━━━━━━━━━━━━━━━━━━━
var G_I = 0, G_J = 0, ¥
G_BGW = 0, G_BGH = 0
' ゲームループを制御するフラグ
var G_GameLoopFlag = #TRUE
'──────────────────────────
' ┠─ マップシーン用配列
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' 空配列宣言
DIM G_MapData[]
'──────────────────────────
' ┠─ スプライト用配列
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' アニメーション開始スプライト定義No配列
var G_SpEvStep = 4
DIM G_SpAnimNo[] = [500,1040,920,1000,980,1080,1020,269,269,269,269]
'──────────────────────────
' ┠─ イベント用配列
'━━━━━━━━━━━━━━━━━━━━━━━━━━
DIM G_EvId[] = [0,1,0,0,\
1,5,5,0,\
2,4,11,0,\
3,23,13,0,\
4,13,3,0,\
5,8,2,0,\
6,22,4,0,\
7,6,7,1,\
8,21,9,1,\
9,3,11,1,\
10,22,2,1\
]
' イベントキュー
'──────────────────────────
DIM G_EvQueue[]
' 各イベントデータ(ラベル管理
'──────────────────────────
@Event007
DATA 5, 10,10,0 3,0,1 2,1,0 1,1,0 1,2,1
'──────────────────────────
' ┠─ コントローラー用配列
'━━━━━━━━━━━━━━━━━━━━━━━━━━
’ ボタンカウント配列
G_BtnPressCount[4]
'──────────────────────────
' ▼ プログラム初期化処理
'──────────────────────────
D_GAME_INITIALIZE
'──────────────────────────
' ゲームループ開始
'──────────────────────────
loop
'シーン管理呼び出し
D_GameSceneManager
' ゲームループフラグ監視
if !G_GameLoopFlag then BREAK
' フレームレート安定
VSYNC 1
endloop
'──────────────────────────
' シーン管理
'──────────────────────────
def D_GameSceneManager
var direct = D_Controller()
case G_SceneFlag
when 0
' マップシーン
D_SpriteGridMove 0, direct
' イベントキュー監視
if LEN(G_EvQueue) > 0 then
G_SceneFlag = 1
when 1
' イベントシーン
D_EventScene direct
print "イベントシーン"
endcase
end
'──────────────────────────
' ▼ 関数の定義
'──────────────────────────
' ┠─ ▼ 初期化関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
def D_GameInitialize
' マップデータのロード
D_MapInitialize "MAP001.DAT"
' マップデータの描写
'D_MapDraw
' スプライトの初期化
for G_I=0 to LEN(G_EvId)-1 step G_SpEvStep
D_SpInitialize G_EvId[G_I]
next
END
'──────────────────────────
' ┗━┓ ▼ マップ関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
def D_MapInitialize A_FILENAME$
' マップデータファイル読み込み
G_MAPDATA = LOADV(A_FILENAME$)
' マップデータ配列の先頭2つを取り出す
G_BGW = SHIFT(G_MapData)
G_BGH = SHIFT(G_MapData)
end
'──────────────────────────
' ┗━┳━ ▼ マップデータ描写
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' マップデータの描写関数
def D_MapDraw
' マップチップ配列を描写
for G_J = 0 to G_BGH-1
for G_I = 0 to G_BGW-1
' ローカル変数CPに計算した値を入れる
var cp = G_I + ( G_J * G_BGW )
' CASE文でMAPDATA[CP]の内容に応じて処理を切り替える
case G_MapDat[cp]
' 0のとき
when 0:
' 地面を表示
TPUT 0, G_I, G_J, CHR(&HE8C9)
' 1のとき
when 1:
' 木を表示
TPUT 0, G_I, G_J, CHR(&HE8CA)
endcase
next
next
end
'──────────────────────────
' ┠─ コントローラー関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' コントローラー関数
def D_Controller()
var B = BUTTON(0)
var direct = -1
' 十字キー監視
direct = D_LeftButtonProcess(B, #B_LUP, direct)
direct = D_LeftButtonProcess(B, #B_LDOWN, direct)
direct = D_LeftButtonProcess(B, #B_LLEFT, direct)
direct = D_LeftButtonProcess(B, #B_LRIGHT, direct)
' ABXY監視
D_RightButtonProcess B, #B_RUP
D_RightButtonProcess B, #B_RDOWN
D_RightButtonProcess B, #B_RLEFT
D_RightButtonProcess B, #B_RRIGHT
return direct
end
' 十字キー処理
def D_LeftButtonProcess(A_B, A_Type, A_NowDirect)
if A_NowDirect > -1 then
return A_NowDirect
endif
var BtnPush = 0
var direct = -1
' 方向を保持
case A_Type
when #B_LUP
BtnPush = 1 << #B_LUP
direct = #B_RUP
when #B_LDOWN
BtnPush = 1 << #B_LDOWN
direct = #B_RDOWN
when #B_LLEFT
BtnPush = 1 << #B_LLEFT
direct = #B_RLEFT
when #B_LRIGHT
BtnPush = 1 << #B_LRIGHT
direct = #B_RRIGHT
endcase
' 押されていたら方向を返し押されていなければ-1を返す
if (B and BtnPush) > O then
return direct
else
direct = -1
return direct
endif
end
' ABXYボタン処理
def D_RightButtonProcess A_B, A_Type
var BtnPush = 0
case A_Type
when #B_RUP
BtnPush = 1 << #B_RUP
when #B_RDOWN
BtnPush = 1 << #B_RDOWN
when #B_RLEFT
BtnPush = 1 << #B_RLEFT
when #B_RRIGHT
BtnPush = 1 << #B_RRIGHT
endcase
if (A_B AND BtnPush ) > O then
D_BtnPressCount A_Type
if G_BtnPressCount[A_Type] == 1 then
case A_Type
when #B_RUP
when #B_RDOWN
when #B_RLEFT
when #B_RRIGHT
D_EvCheck 0, SPVAR(0, "Direct")
endcase
endif
else
if G_BtnPressCount[A_Type] >= 1 then
G_BtnPressCount[A_Type] = 0
endif
endif
end
' ボタンカウント関数
def D_BtnPressCount A_Button
if G_BtnPressCount[A_Button] < 256 then
INC G_BtnPressCount[A_Button]
endif
end
'──────────────────────────
' ┠─ ▼ スプライト関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' スプライト初期化
def D_SpInitialize A_Id
SPSET A_Id, 500
SPVAR A_Id, "X", G_EvId[G_SpEvStep * A_Id + 1] * #CSZ
SPVAR A_Id, "Y", G_EvId[G_SpEvStep * A_Id + 2] * #CSZ
SPVAR A_Id, "Direct", -1
SPVAR A_Id, "MoveFlag", 0
SPOFS A_Id, SPVAR(A_Id,"X"), SPVAR(A_Id,"Y")
D_MapArrayReWrite A_Id, SPVAR(A_Id, "Direct"), 1, #LAYER_EV
if G_EvId[G_SpEvStep * A_Id + 3] != -1 then
SPANIM A_Id, "I",\
20,spAnimNo[A_Id],\
20,spAnimNo[A_Id]+1,\
20,spAnimNo[A_Id]+2,\
20,spAnimNo[A_Id]+3,\
G_EvId[G_SpEvStep * A_Id + 3]
endif
end
' スプライトの移動
def D_SpriteGridMove A_Id, A_Direct
if SPVAR(A_Id,"MoveFlag") == 1 then
case SPVAR(A_Id,"Direct")
when #B_RUP
SPVAR A_Id, "Y", SPVAR(A_Id, "Y") - 1
when #B_RDOWN
SPVAR A_Id, "Y", SPVAR(A_Id, "Y") + 1
when #B_RLEFT
SPVAR A_Id, "X", SPVAR(A_Id, "X") - 1
when #B_RRIGHT
SPVAR A_Id, "X", SPVAR(A_Id, "X") + 1
endcase
' キャラクターの移動
SPOFS A_Id, SPVAR(A_Id,"X"), SPVAR(A_Id,"Y")
' 割った余りが0になったら動きを止める
if SPVAR(A_Id, "X") MOD #CSZ == 0 && SPVAR(A_Id, "Y") MOD #CSZ == 0 then
SPVAR A_Id, "MoveFlag", 0
endif
else
if A_Direct >= 0 then
' 現在のグリッド座標にアクセスするマップ配列添え字を取得
var pos = D_GetMapPosition(SPVAR(A_Id, "X"), SPVAR(A_Id, "Y"), 0)
' 移動の可否関係なく向きは変更しておく
SPVAR 0, "Direct", A_ Direct
' 向かう先が移動可能かどうかを調べる(MAPレイヤー&イベントレイヤー)
if D_CheckCollision(pos, SPVAR(A_Id, "Direct")) == #TRUE then
SPVAR A_Id, "MoveFlag", 1
endif
endif
endif
end
'──────────────────────────
' ┠─ ▼ 衝突判定関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' ドット座標をグリッド座標に変換
def D_GetGridXY(A_XY)
return A_XY / #CSZ
end
' グリッド座標とレイヤーを与えてマップ配列の値を取得する
def D_GetMapPosition(A_X, A_Y, A_LAYER)
var gridX = D_GetGridXY(A_X)
var gridX = D_GetGridXY(A_Y)
VAR layerNo = (G_BGW * G_BGH) * A_LAYER
if A_Y <= 0 then
return gridX + layerNo
else
return gridX + (GRIX_Y * G_BGW)) + layerNo
endif
end
' 進む先のグリッドが移動可能かどうかを調べる
def D_CheckCollision(A_Pos, A_Direct)
var sumPos = 0
var evPos = 0
sumPos = D_GetMapArrayLayerNo(A_Pos, #LAYER_MAP1) + D_GetGridDirectPoint(A_Direct)
evPos = D_GetMapArrayLayerNo(A_Pos, #LAYER_EV) + D_GetGridDirectPoint(A_Direct)
if G_MapData[sumPos] == 0 then
if G_MapData[evPos] == 0 then
return #TRUE
endif
endif
return #FALSE
end
' 方向定数から1歩前のグリッド座標を得るための値を得る
def D_GetGridDirectPoint(A_Direct)
var direct = 0
case A_Direct
when #B_RUP
direct = -G_BGW
when #B_RDOWN
direct = G_BGW
when #B_RLEFT
direct = -1
when #B_RRIGHT
direct = 1
endcase
return direct
end
' マップ配列添字生成
def D_GetMapArrayLayerNo(A_Pos,A_Layer)
' 与えられた位置に対してレイヤーを考慮した配列添字を返す
return A_Pos + ((G_BGW * G_BGH) * A_Layer)
end
' マップ配列書き換え(レイヤー指定
def D_MapArrayReWrite A_Id, A_Direct, A_Flag, A_Layer
var pos = D_GetMapPosition(SPVAR(A_Id,"X"), SPVAR(A_Id,"Y), A_Layer)
if A_Flag > 0 then
if A_Direct >= 0 then
var afterPos = pos + D_GetGridDirectPoint(A_Direct)
G_MapData[afterPos] = A_Id
else
G_MapData[pos] = A_Id
endif
else
var beforePos = pos - D_GetGridDirectPoint(A_Direct)
G_MapData[beforePos] = 0
endif
end
'──────────────────────────
' ┠─ ▼ イベント判定関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' 前方のイベントをチェックする
def D_EvCheck A_Id, A_Direct
var pos = D_GetMapPosition(SPVAR(A_Id,"X"), SPVAR(A_Id,"Y"), #LAYER_EV)
var afterPos = pos + D_GetGridDirectPoint(A_Direct)
var evId = G_MapData[afterPos]
D_GetEvData evId
end
'──────────────────────────
' ┠─ ▼ イベント処理関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
def D_EventScene A_Direct
D_ShiftEvData
end
' イベントキュー処理
'──────────────────────────
def D_ShiftEvData
var type = SHIFT(G_EvQueue)
var event = SHIFT(G_EvQueue)
var reserve = SHIFT(G_EvQueue)
print "[イベント かいし]"
print "===================="
case type
when 1
print "メッセージイベント:" + STR$(event) +", " + "詳細" + STR$(reserve)
when 2
print "スプライトへんこうイベント:" + STR$(event) +", " + "詳細" + STR$(reserve)
when 3
print "アイテムぞうげんイベント:" + STR$(event) +", " + "詳細" + STR$(reserve)
when 10
print "SEえんそうイベント:" + STR$(event) +", " + "詳細" + STR$(reserve)
endcase
print "-------------------"
if LEN(G_EvQueue) <= 0 then
G_SceneFlag = 0
endif
end
' イベントデータ取り出してキューに格納
'──────────────────────────
def D_GetEvData A_Id
var evCount = 0
var evData = 0
case A_Id
when 7
RESTORE @Event007
READ evCount
for G_I=1 to (evCount * 3)-1
READ evData
PUSH G_EvQueue, evData
next
endcase
if LEN(G_EvQueue) > 0 then
G_SceneFlag = 1
endif
end