【プチコン4講座】イベントシステム構築:メッセージウィンドウ後編
こんにちは。継続の錬金術士なおキーヌです。
ブログ毎日更新は176日目になります。
前回「【プチコン4講座】イベントシステム構築:メッセージウィンドウ前編」でウィンドウを表示して文字を載せることが出来ました。
このままではウィンドウが出っぱなしなので仕舞えるようにしましょう。
まずはボタンによる開閉を実装してから
イベントによるメッセージウィンドウ呼び出しを目指します!
これが出来れば宝箱イベントは完成したも同然です。
それではプチコン4でRPG作りその12回目始めましょう。
Xボタンでウィンドウ開閉をしてみよう
ウィンドウを閉じたり開いたりできるようにしてみましょう。
Xボタンはそのうちステータス画面を表示する時に使うので今のうちにウィンドウ開閉処理をマスターしておきます。
' ABXYボタン処理
def D_RightButtonProcess A_B, A_Type
~~~ 省略 ~~~
if (A_B AND BtnPush ) > O then
D_BtnPressCount A_Type
if G_BtnPressCount[A_Type] == 1 then
case A_Type
when #B_RUP
D_WindowDraw
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_WindowDraw
GFILL 80, 150, 320, 208 RGB(0, 0, 0)
GBOX 79, 149, 321, 209, RGB(128,126,129)
GBOX 78, 148, 322, 210, RGB(128,126,129)
GBOX 77, 147, 323, 211, RGB(128,126,129)
GPSET 78, 148, RGB(255,255,255)
GPSET 78, 210, RGB(255,255,255)
GPSET 322, 148, RGB(255,255,255)
GPSET 322, 210, RGB(255,255,255)
GLINE 77, 149, 77, 209, RGB(255,255,255)
GLINE 76, 149, 76, 209, RGB(128,126,129)
GLINE 323, 149, 323, 209, RGB(255,255,255)
GLINE 324, 149, 324, 209, RGB(128,126,129)
GLINE 79, 147, 321, 147, RGB(255,255,255)
GLINE 79, 146, 321, 146, RGB(128,126,129)
GLINE 79, 211, 321, 211, RGB(255,255,255)
GLINE 79, 212, 321, 212, RGB(128,126,129)
GPUTCHR 85, 155, "プレイヤー は たからばこ を あけた!",8,RGB(255,255,255),1
GPUTCHR 85, 165, "たからばこ には ライフポーション が はいっていた!",8,RGB(255,255,255),1
end
Xボタンを押したら「D_WindowDraw」を呼び出すようにして
前回初期化の下に直接書いていた処理を関数にそのまま移し替えました。
一度起動してXボタンを押してウィンドウが現れるか確かめてください。
しかしこのままでは閉じることが出来ないのでもう一度Xを押したら閉じるようにしてみましょう。
ウィンドウを閉じる
G_OpenStatusFlag = #FALSE
def D_WindowDraw
if G_OpenStatusFlag == #FALSE then
GFILL 80, 150, 320, 208 RGB(0, 0, 0)
GBOX 79, 149, 321, 209, RGB(128,126,129)
GBOX 78, 148, 322, 210, RGB(128,126,129)
GBOX 77, 147, 323, 211, RGB(128,126,129)
GPSET 78, 148, RGB(255,255,255)
GPSET 78, 210, RGB(255,255,255)
GPSET 322, 148, RGB(255,255,255)
GPSET 322, 210, RGB(255,255,255)
GLINE 77, 149, 77, 209, RGB(255,255,255)
GLINE 76, 149, 76, 209, RGB(128,126,129)
GLINE 323, 149, 323, 209, RGB(255,255,255)
GLINE 324, 149, 324, 209, RGB(128,126,129)
GLINE 79, 147, 321, 147, RGB(255,255,255)
GLINE 79, 146, 321, 146, RGB(128,126,129)
GLINE 79, 211, 321, 211, RGB(255,255,255)
GLINE 79, 212, 321, 212, RGB(128,126,129)
GPUTCHR 85, 155, "プレイヤー は たからばこ を あけた!",8,RGB(255,255,255),1
GPUTCHR 85, 165, "たからばこ には ライフポーション が はいっていた!",8,RGB(255,255,255),1
G_OpenStatusFlag = #TRUE
else
GCLS
G_OpenStatusFlag = #FALSE
endif
end
これでXボタンを押すたびにフラグが切り替わるのでウィンドウを閉じたり開いたりできるようになりました。
ここで重要なのはGCLSでウィンドウを消しているということです。
スプライトであればIDを指定して非表示に出来るのですが、
グラフィックの場合は透明に塗りつぶさないといけないのが難点です。
メッセージイベントが呼ばれたらウィンドウを開いてみよう
これでメッセージイベントを実装する準備が整いました。
メッセージイベントが呼び出されたらウィンドウを生成して、
内容の値によって文字を表示するようにしてみましょう。
Xボタンによる呼び出しは一旦削除してもらっても大丈夫です。
G_OpenMsgFlag = #FALSE
' イベントキュー処理
'──────────────────────────
def D_ShiftEvData
var type = SHIFT(G_EvQueue)
var event = SHIFT(G_EvQueue)
var reserve = SHIFT(G_EvQueue)
print "[イベント かいし]"
print "===================="
case type
when 1
if G_OpenMsgFlag == #FALSE then
D_WindowDraw
endif
D_MessageDraw event, 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_WindowDraw
GFILL 80, 150, 320, 208 RGB(0, 0, 0)
GBOX 79, 149, 321, 209, RGB(128,126,129)
GBOX 78, 148, 322, 210, RGB(128,126,129)
GBOX 77, 147, 323, 211, RGB(128,126,129)
GPSET 78, 148, RGB(255,255,255)
GPSET 78, 210, RGB(255,255,255)
GPSET 322, 148, RGB(255,255,255)
GPSET 322, 210, RGB(255,255,255)
GLINE 77, 149, 77, 209, RGB(255,255,255)
GLINE 76, 149, 76, 209, RGB(128,126,129)
GLINE 323, 149, 323, 209, RGB(255,255,255)
GLINE 324, 149, 324, 209, RGB(128,126,129)
GLINE 79, 147, 321, 147, RGB(255,255,255)
GLINE 79, 146, 321, 146, RGB(128,126,129)
GLINE 79, 211, 321, 211, RGB(255,255,255)
GLINE 79, 212, 321, 212, RGB(128,126,129)
G_OpenMsgFlag = #TRUE
end
' メッセージ描写
'──────────────────────────
def D_MessageDraw A_Ev, A_Re
case A_Ev
when 1
GPUTCHR 85, 155, "プレイヤー は たからばこ を あけた!",8,RGB(255,255,255),1
when 2
var itemName$ = ""
if A_Re == 0 then
itemName = "ライフポーション"
elseif A_Re == 1 then
itemName = "せいすい"
endif
GPUTCHR 85, 165, "たからばこ には "+ itemName +" が はいっていた!",8,RGB(255,255,255),1
endcase
end
ウィンドウは閉じられなくなりましたが、宝箱を調べたらメッセージが出るはずです。
ウィンドウ描写とメッセージ描写を分けましたがメッセージ送りを実装していないので、
一気に表示されちゃう状態になっています。
理想は最初のメッセージが出て決定を押して次のメッセージが出て、
もう一度決定を押したらウィンドウが閉じてイベントが終了するといった感じです。
あと、ウィンドウ描写も連続して描写すると前の文字が上書きされてしまうので、
1回開いたらオープン状態としてフラグを立てておき続けてメッセージが表示される場合はウィンドウ描写をしないようにしましょう。
──次回はメッセージ送りを実装します。
メッセージ送りさえ完成すれば宝箱イベントはほぼ完成です。
最後に今回の完成版ソースコードを置いておきます。
それでは
'──────────────────────────
' ▼ 動作モード設定:変数宣言は必須
'──────────────────────────
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]
'──────────────────────────
' ┠─ ウィンドウ用変数
'━━━━━━━━━━━━━━━━━━━━━━━━━━
G_OpenStatusFlag = #FALSE
G_OpenMsgFlag = #FALSE
'──────────────────────────
' ┠─ イベント用変数
'━━━━━━━━━━━━━━━━━━━━━━━━━━
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
' レイヤーのセッティング
D_ScreenLayerSettings
end
' ▼ スクリーンレイヤー変更
'──────────────────────────
def D_ScreenLayerSettings
TLAYER 0, 4
for G_I = 0 to LEN(G_SpAnimNo)-1
SPLAYER G_I, 3
next
GTARGET 0
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
D_WindowDraw
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
if G_OpenMsgFlag == #FALSE then
D_WindowDraw
endif
D_MessageDraw event, 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
'──────────────────────────
' ┠─ ▼ メッセージ処理関連
'━━━━━━━━━━━━━━━━━━━━━━━━━━
' ウィンドウ生成
'──────────────────────────
def D_WindowDraw
GFILL 80, 150, 320, 208 RGB(0, 0, 0)
GBOX 79, 149, 321, 209, RGB(128,126,129)
GBOX 78, 148, 322, 210, RGB(128,126,129)
GBOX 77, 147, 323, 211, RGB(128,126,129)
GPSET 78, 148, RGB(255,255,255)
GPSET 78, 210, RGB(255,255,255)
GPSET 322, 148, RGB(255,255,255)
GPSET 322, 210, RGB(255,255,255)
GLINE 77, 149, 77, 209, RGB(255,255,255)
GLINE 76, 149, 76, 209, RGB(128,126,129)
GLINE 323, 149, 323, 209, RGB(255,255,255)
GLINE 324, 149, 324, 209, RGB(128,126,129)
GLINE 79, 147, 321, 147, RGB(255,255,255)
GLINE 79, 146, 321, 146, RGB(128,126,129)
GLINE 79, 211, 321, 211, RGB(255,255,255)
GLINE 79, 212, 321, 212, RGB(128,126,129)
G_OpenMsgFlag = #TRUE
end
' メッセージ描写
'──────────────────────────
def D_MessageDraw A_Ev, A_Re
case A_Ev
when 1
GPUTCHR 85, 155, "プレイヤー は たからばこ を あけた!",8,RGB(255,255,255),1
when 2
var itemName$ = ""
if A_Re == 0 then
itemName = "ライフポーション"
elseif A_Re == 1 then
itemName = "せいすい"
endif
GPUTCHR 85, 165, "たからばこ には "+ itemName +" が はいっていた!",8,RGB(255,255,255),1
endcase
end