イベントリスト

目次

イベントリスト

 イベントリストを使用すると、オブジェクトノードにあらかじめ設定した動きをさせることができます。 メインループ内では何もする必要はありません。

 移動を伴わない姿勢やポーズの場合は、モーションを使用すると便利です。 それと同様に、移動を伴うような決まった動きをさせたい場合は、イベントを使用すると便利です。 モーション同様に事前に決まった動きを定義しておくだけなので、決まった順路をランダムに移動する敵や同じ動作を繰り返す背景のオブジェクトなどを手軽に配置することができます。 モーションと同様、メインループ内で毎ループ制御する必要がありません。

イベントの設定方法

 イベントリストの作成はneweventでイベントIDを宣言してから、event_系命令でイベント(処理)を登録すれば完成です。


	newevent イベントID
	event_… イベントID, オプション (動作内容を登録)
	…
	event_… イベントID, オプション (動作内容を登録)

 作ったイベントリストをオブジェクトノードに適用するには、seteventまたはgpcloneを使用します。


	setevent オブジェクトID, イベントID

 とりあえず動くやつを


;	イベントテスト
#include "hgimg4.as"
title "HGIMG4 Test"

  randomize
  gpreset
  setcls CLSMODE_SOLID, $808080		; 画面クリア設定
  setpos GPOBJ_CAMERA, 0, 4, 10		; カメラ位置を設定

  ; 箱ノードを追加
  gptexmat id_texmat, "res/qbox.png"
  gpbox   id_box, 1, -1, id_texmat
  setpos  id_box,	0, 4, 0

  ; 床ノードを追加
  gpfloor id_floor, 30,30, $404040

  ;	イベントリストを登録
  newevent   ev1
  event_pos  ev1, 60*2,  5, 0.5, 0
;	event_pos  ev1, 60*2,  5, 0.5, 0, 1
  event_wait ev1, 60*2
  event_pos  ev1, 60*2, -5, 0.5, 0
  event_wait ev1, 60*2
  event_jump ev1, 0

  ;	イベントを設定
  setevent id_box, ev1


repeat
  stick key,15
  if key&128 : end

  ; カメラを自機に向ける
  getpos id_box,dx,dy,dz
  gplookat GPOBJ_CAMERA, dx,dy,dz
  
  redraw 0			; 描画開始
  gpdraw				; シーンの描画
  redraw 1			; 描画終了
  await 1000/60		; 待ち時間

loop

 実行すると、箱が左右に動く動作を繰り返します。 メインループでは一切何もしていません。事前準備だけでこういうのが作れると、メインループ内を作る作業が楽になっていいですね。 うまく作ると弾幕系シューティングの弾幕みたいなのも作れそうです。

 まずは、event_posの動作を少し掘り下げておきます。 補間オプションを指定すると動きのつなぎ方が変わるようです。「スプライン補間(絶対値)」に書き換えてみます。


event_pos  ev1, 60*2,  5, 0.5, 0, 1

 なめらかな動き。いいですね。 そして、2,3は現在位置からの相対座標指定なので、左右に移動しながら徐々に上に上がっていきます。

 イベントスロットを使ってみます。 イベントリストは、1つのオブジェクトに0~3までの4つ存在するイベントスロットに登録して使用しています。 複数のイベントリストを登録すると、それらを同時に動かすことができるようです。

 seteventで登録する際に、何も指定しないと開いているイベントスロットIDに登録されます。 複数のイベントを切り替えて使いたい場合は、注意が必要ですね。


	;	イベントリストを登録
	newevent   ev1
	event_pos  ev1, 60*2,  5, 0.5, 0, 1
	event_wait ev1, 60*2
	event_pos  ev1, 60*2, -5, 0.5, 0, 1
	event_wait ev1, 60*2
	event_jump ev1, 0

	newevent   ev2
	event_ang  ev2, 65, 0, deg2rad(90), 0
	event_wait ev2, 65
	event_ang  ev2, 65, 0, 0, 0
	event_wait ev2, 65
	event_jump ev2, 0

	;	イベントを設定
	setevent id_box, ev1
	setevent id_box, ev2

 箱が左右に首振りしながら移動します。移動と首振りは、時間をずらしてあるので徐々にタイミングのズレが大きくなっていきます。

 ここでは注意しなければならない点があります。ev2でもposグループを書き換える命令を使うと、ev1で指定したposグループの値が上書きされてしまう点です。 hgimg4側は、イベントリストにしたがって適切なタイミングでオブジェクトノードが持っている変数(posやangなど)を書き換えているだけです。 ev1とev2でevent_posを使うと、event_posを実行した直後や、event_waitでの待機中に、オブジェクト1つにつき1つしか持っていないposグループの値が再度変更されてしまいます。これでは正常な動作は期待できません。 ev1とev2にevent_posを設定して、リサージュ曲線軌道を描くというような使用方法はできないというわけです。

 複数のイベントリストを重ねる場合は、posとangなど同時に指定しても問題ない組み合わせを使ったほうが安全なようです。

制御系

event_jump ジャンプイベントを追加
event_wait 待ち時間イベントを追加

 event_pos以外の命令の説明をする前に、制御系のevent_系命令について触れておきます。

 まず説明しやすくするため、イベント番号の考え方について説明します。 event_系命令でイベントを追加すると、追加した順にイベント番号という行番号のような0から始まる番号が割り当てられます。 さきほどの例の行頭にイベント番号を書き込んでみました。こんな感じです。


	newevent   ev1
	0: event_pos  ev1, 60*2,  5, 0.5, 0, 1
	1: event_wait ev1, 60*2
	2: event_pos  ev1, 60*2, -5, 0.5, 0, 1
	3: event_wait ev1, 60*2
	4: event_jump ev1, 0

 hgimg4は、イベント番号を実行すると直ちに次のイベント番号を実行します。

event_wait
 この例では、イベント番号0で「変化までのフレーム数」に120指定していますが、変化が終わるまで120フレーム待機しません。直ちに次のイベント番号1を実行します。 イベント番号1では、何もせず待機する命令です。ここで120フレーム待機してから次のイベント番号を実行します。

 もしここで待機時間を半分にしてみると、event_posで設定された移動は中断されて、イベント番号2で更新された移動目標を使った移動が開始されます。

event_jump
 最後のイベント番号4でevent_jumpが実行されると、次はジャンプ先として指定されたイベント番号0が実行されます。 この例の場合は、同じ処理を無限に繰り返すループになります。この場合は、goto命令と同じ役割をしています。

 またevent_jump命令は、処理の流れを分岐する機能も持っています。分岐は確率を指定したランダムで行われます。 複雑な条件分岐はできませんが、複雑な動きをしているように見せかけることができます。

 複雑な条件分岐を行いたい場合は、メインループ内で判定して別のイベントリストに差し替える必要があります。 手間に感じるかもしれませんが、あらかじめ決めておいた動作に切り替えるだけなのでお手軽だと思います。このあたりはモーションも同じですね。

ang、angr、dir、pos、scale、work

分類命令概要
追加event_addang angグループ加算イベントを追加
追加event_addangr angグループ加算イベントを追加
追加event_adddir dirグループ加算イベントを追加
追加event_addpos posグループ加算イベントを追加
追加event_addscalescaleグループ加算イベントを追加
追加event_addwork workグループ加算イベントを追加
設定event_setang angグループ設定イベントを追加
設定event_setangr angグループ設定イベントを追加
設定event_setdir dirグループ設定イベントを追加
設定event_setpos posグループ設定イベントを追加
設定event_setscalescaleグループ設定イベントを追加
設定event_setwork workグループ設定イベントを追加
変化event_ang angグループ変化イベントを追加
変化event_angr angグループ変化イベントを追加
変化event_dir dirグループ変化イベントを追加
変化event_pos posグループ変化イベントを追加
変化event_scale scaleグループ変化イベントを追加
変化event_work workグループ変化イベントを追加

 event_系命令ってたくさんあって、よくわからないんですよね。まずは基本的なものを整理してみます。

 基本的には、命令名の後半で取り扱う種類が決まっています。 ここは名前のとおりです。

ang 姿勢 [rad]
angr 姿勢(0~255=0~365度)
dir 移動速度
pos 表示座標
scale表示倍率
work ワーク値

 命令名の前半で、値をどの様にセットするのかを表しています。

event_add~加算今の値に指定の値を加算
add~系相当の命令。
event_set~設定指定範囲内から乱数で値を作成して設定
set~系相当の命令。
event_~ 変化指定された目標値まで徐々に変化させる
移動中の補間計算方法は、オプションで指定できる。
目標値にワーク値が使用できる。

 event_~系だけが少し特殊で、時間経過とともに設定値に近づいていきます。

パラメーター設定

event_prmset パラメーター設定イベントを追加
event_prmon パラメータービット設定イベントを追加
event_prmoff パラメータービット消去イベントを追加

 ノードオブジェクトが持つパラメーターを設定、消去できます。 パラメーターには、32bit整数値のものとビット管理するものがあるので、設定にはevent_prmsetevent_prmonの2つの命令が用意されています。 設定できる内容は、次の通り。

パラメーターID内容設定値
PRMSET_FLAG オブジェクト登録フラグ ビット(1~4)
PRMSET_MODE モード値 ビット(OBJ_~)
PRMSET_ID オブジェクトID 整数値(0~)
PRMSET_ALPHA 透明度(α値) 整数値(0~255)
PRMSET_TIMER タイマー値 整数値(0~)
PRMSET_MYGROUP 自身のコリジョングループ ビット(0~0xFFFF 16個)
PRMSET_COLGROUP衝突検出するコリジョングループ ビット(0~0xFFFF 16個)
PRMSET_SHAPE 形状ID 整数値?
PRMSET_USEGPMATマテリアルID 整数値?
PRMSET_COLILOG コリジョンログID 整数値?
PRMSET_FADE フェードパラメーター 整数値
PRMSET_SPRID ソースバッファID(スプライトのみ)整数値?
PRMSET_SPRCELIDソースのセルID(スプライトのみ) 整数値?
PRMSET_SPRGMODEコピーモード(スプライトのみ) 整数値?

 説明した通り整数とビットが混在していますね。改めて、各命令の機能はこんな感じです。

命令機能通常のノード向け命令
event_prmset値を上書き gpsetprm
event_prmon 指定ビットをON gpsetprmon
event_prmoff指定ビットをOFFgpsetprmoff

 通常のノードオブジェクトと同じような操作ができる様です。

設定できるパラメーターの内容

 設定できるパラメーターの内容を、主なものを書き出してみました。 基本的には、gpsetprm、gpsetprmon で使用するものと変わりません。

PRMSET_FLAG

フラグ値 内容
1オブジェクトが有効
2オブジェクトの表示が有効
4オブジェクトの自動移動が有効

PRMSET_MODE
 詳しくは、HGIMG4プログラミングガイド「オブジェクトのモード設定」を参照。

ラベル 内容
OBJ_HIDE 非表示(画面から消す)
OBJ_CLIP 3Dクリッピングを有効にする
OBJ_XFRONT正面属性(常に画面に正面を向く)
OBJ_WIRE ワイヤーフレームで描画する
OBJ_MOVE 自動移動を行なう(XYZ移動量を参照する)
OBJ_FLIP ボーダー領域で反転する
OBJ_BORDERボーダー領域を有効にする
OBJ_2D 2Dスプライト
OBJ_TIMER タイマーを有効にする
OBJ_LATE 後から描画される(半透明オブジェクト用)

 「OBJ_XFRONT」ビルボード…こんなところにいたのか。とおもったらYは固定されないのね。そしてこの名前なのに、カメラ方向を向いているのはZ軸。  → カメラのX回転軸に合わせているという意味。HGIMG3から引き続き使用しています。変更の予定はないようです。

PRMSET_TIMER
 指定したフレーム数が経過すると、ノードオブジェクトが削除されます。 使用するときは、OBJ_TIMERでタイマーを有効にする必要があります。


	;	イベントリストを登録
	newevent   ev1
	event_prmon  ev1, PRMSET_MODE, OBJ_TIMER
	event_prmset ev1, PRMSET_TIMER, 1000	; フレーム数
	event_pos  ev1, 60*2,  5, 0.5, 0, 1
	event_wait ev1, 60*2
	event_pos  ev1, 60*2, -5, 0.5, 0, 1
	event_wait ev1, 60*2
	event_jump ev1, 2


	; カメラを自機に向ける
	objexist id_box
	if stat = 0 {
		getpos id_box,dx,dy,dz
		gplookat GPOBJ_CAMERA, dx,dy,dz
	}
	
	redraw 0			; 描画開始
	gpdraw				; シーンの描画

	pos 10, 10
	objexist id_box
	if stat = 0 {
		gpgetprm v, id_box, PRMSET_TIMER
		mes "PRMSET_TIMER値:" + v
	} else {
		mes "削除されました。"
	}

PRMSET_FADE
 フレームごとに指定した値を透明度(α値)に加算します。 透明度(α値)がゼロになると、ノードオブジェクトは破棄されます。 半透明になるので、OBJ_LATEの指定もセットで使用する必要があります。


	;	イベントリストを登録
	newevent   ev1
	event_prmon  ev1, PRMSET_MODE, OBJ_LATE
	event_prmset ev1, PRMSET_FADE, -1
	event_pos  ev1, 60*2,  5, 0.5, 0, 1
	event_wait ev1, 60*2
	event_pos  ev1, 60*2, -5, 0.5, 0, 1
	event_wait ev1, 60*2
	event_jump ev1, 2

 これもオブジェクトが消えてしまうので、objexistでチェックが必要です。

 パラメーターって色々できてすごいですね。しかし、ここではイベントリスト関連についてまとめたいので、これ以上は掘り下げません。

フェード

event_fade

event_prmset命令でPRMSET_FADEパラメーターに設定することと等価です。
出典:HSP3.6 HDL event_fade

 だったら無くてもいいのでは?って思いますよね。私もそう思います。 しかし使用頻度の高さと、HDLで詳しい説明が読める点を考えるとこれはこれで。

 さてこの命令は、フレームごとの透明度(α値)の変化量を設定する命令です。指定した値によって透明度の変化の仕方が変わってきます。

-255 非表示にして廃棄
マイナスフェードアウト(徐々に消える)
0 現状維持
プラス フェードイン(徐々に表示される)
255 表示(不透明)

 フェードイン、フェードアウト…自機がダメージ受けたときの点滅表現に使えそうですね。

 さて、忘れがちですが、α値が0になるとオブジェクトが破棄されます。event_fadeを使ったオブジェクトはいつの間にか消えることがある点に注意です。 この事実を頭に入れておかないと、イベントの設定値を間違えて「あれ?表示されない??」とか、消えたオブジェクトをgetposしようとして「なぜエラー?!」と混乱することがあります。 (サンプル作りながら混乱しました。)

削除

event_delobj オブジェクト削除イベントを追加
event_suicide オブジェクト破棄イベントを追加

 このイベントが実行されると、ノードオブジェクトは破棄されます。

 2つありますが、どちらも同じ機能を持った命令で、違いについての説明はありません。 event_suicideは、hgimg4から登場した命令ですが、HGIMG4プログラミングガイドには記述がありません。event_delobjの方だけ記述があります。 名前が良くなかったのかな…?Google検索すると検索結果に心配されてしまうし。 event_suicideは、今後段階的に廃止となる予定だそうです。

まとめ

 複雑な条件分岐はできませんが、通常のノードオブジェクト向けの命令とほとんど同じ機能が用意されています。 複雑な条件分岐がしたいなら、メインループ内で少し手を加えるだけでできそうです。

 動きはオブジェクトひとつひとつ個別に制御されるので、オブジェクトごとのイベント開始タイミングが異なると動きがバラバラになるので、見た目の動きが自然です。 同じことをメインループ内でやろうとするととても面倒くさい事になります。 全部同じタイミングで同じ動作なら簡単ですが、個別にタイミングを制御するのはおそらくすごく大変です。

 行動に思考を伴わないような敵や物などに複雑な動きをつけるには、とても良い機能だと思います。

関連記事

  1. パラメータ 目次 はじめに PRMSET_FLAG PRMSET_MOD...