[GameMaker: Studio] おさらい・イベントの処理順序(補足・5)

GameMaker: Studioはマルチスレッドではなく、1フレーム内でお行儀よく順番に処理が実行されるというのは過去にも何度か書いてあったと思います。

では、各イベントはどのような順番になっているのか、ここであらためて整理してみることにします。

まずはroomに初期化用のobject “obj_init”を1つだけ配置します。
この“obj_init”Createイベントで“obj_init create”というメッセージを出力して、調査対象の各objectinstanceを作成します。
ついでにroomCreation codeでも“room0 active”というメッセージを出力しておきます。

調査対象のobjectは、depth値とparent(親object)だけが異なり各イベントの処理は同一で、単に現在のイベント名を出力するだけです。

object0       = depth   0、親なし
object10      = depth  10、親なし
object_m10    = depth -10、親なし
object0_c_20  = depth  20、親はobject0
object0_c_m20 = depth -20、親はobject0
instance_create(50, 50, object0);
instance_create(55, 55, object10);
instance_create(45, 45, object_m10);
instance_create(45, 55, object0_c_20);
instance_create(55, 45, object0_c_m20);

gms_collision_1

すべてのinstanceは衝突判定が起きるように重ねています。
Alarmは#0はカウント0、#1はカウント1に設定しました。

これを実行した結果がこちらです。

Run_Start
StartGame()
obj_init create     ← roomに事前配置されたobjectのcreateイベント
room0 active        ← roomのcreation code
StartGame() - DONE
Total memory used = 424385(0x00499649) bytes
**********************************.
Entering main loop.
**********************************.
object0       :   0 : create
object10      :  10 : create
object_m10    : -10 : create
object0_c_20  :  20 : create
object0_c_m20 : -20 : create

object0_c_m20 : -20 : game start
object_m10    : -10 : game start
object0       :   0 : game start
object10      :  10 : game start
object0_c_20  :  20 : game start

object0_c_m20 : -20 : room start
object_m10    : -10 : room start
object0       :   0 : room start
object10      :  10 : room start
object0_c_20  :  20 : room start

object0_c_m20 : -20 : animation end
object_m10    : -10 : animation end
object0       :   0 : animation end
object10      :  10 : animation end
object0_c_20  :  20 : animation end

object0       :   0 : step begin
object10      :  10 : step begin
object_m10    : -10 : step begin
object0_c_20  :  20 : step begin
object0_c_m20 : -20 : step begin

object0       :   0 : alarm 1
object10      :  10 : alarm 1
object_m10    : -10 : alarm 1
object0_c_20  :  20 : alarm 1
object0_c_m20 : -20 : alarm 1

object0       :   0 : keyboard
object10      :  10 : keyboard
object_m10    : -10 : keyboard
object0_c_20  :  20 : keyboard
object0_c_m20 : -20 : keyboard

object0       :   0 : mouse
object10      :  10 : mouse
object_m10    : -10 : mouse
object0_c_20  :  20 : mouse
object0_c_m20 : -20 : mouse

object0       :   0 : step
object10      :  10 : step
object_m10    : -10 : step
object0_c_20  :  20 : step
object0_c_m20 : -20 : step

object0       :   0 : collision
object0_c_20  :  20 : collision
object0       :   0 : collision
object0_c_m20 : -20 : collision
object10      :  10 : collision
object10      :  10 : collision
object10      :  10 : collision
object_m10    : -10 : collision
object_m10    : -10 : collision
object_m10    : -10 : collision
object0_c_20  :  20 : collision
object0_c_m20 : -20 : collision

object0       :   0 : step end
object10      :  10 : step end
object_m10    : -10 : step end
object0_c_20  :  20 : step end
object0_c_m20 : -20 : step end

object0_c_20  :  20 : pre draw
object10      :  10 : pre draw
object0       :   0 : pre draw
object_m10    : -10 : pre draw
object0_c_m20 : -20 : pre draw

object0_c_20  :  20 : draw begin
object10      :  10 : draw begin
object0       :   0 : draw begin
object_m10    : -10 : draw begin
object0_c_m20 : -20 : draw begin

object0_c_20  :  20 : draw
object10      :  10 : draw
object0       :   0 : draw
object_m10    : -10 : draw
object0_c_m20 : -20 : draw

object0_c_20  :  20 : draww end
object10      :  10 : draww end
object0       :   0 : draww end
object_m10    : -10 : draww end
object0_c_m20 : -20 : draww end

object0_c_20  :  20 : post draw
object10      :  10 : post draw
object0       :   0 : post draw
object_m10    : -10 : post draw
object0_c_m20 : -20 : post draw

object0_c_20  :  20 : draw gui begin
object10      :  10 : draw gui begin
object0       :   0 : draw gui begin
object_m10    : -10 : draw gui begin
object0_c_m20 : -20 : draw gui begin

object0_c_20  :  20 : draw gui
object10      :  10 : draw gui
object0       :   0 : draw gui
object_m10    : -10 : draw gui
object0_c_m20 : -20 : draw gui

object0_c_20  :  20 : draw gui end
object10      :  10 : draw gui end
object0       :   0 : draw gui end
object_m10    : -10 : draw gui end
object0_c_m20 : -20 : draw gui end

まずは起動直後の処理ですが、以前書いたようにroomCreation codeよりも先に、事前配置されたobjectCreateイベントが呼び出されています。
roomCreation codeは使用せずに初期化用のobjectInstance orderで最上位にして初期化した方が良いというのはこのためです。

次に各instanceの順序を見てみます。


・”create”
instanceが生成されたときに一度だけ呼び出されます。
Depth値に関わらず生成順(room配置の場合はInstance order順)に呼び出されています。
instance_createで作られた場合は即時呼び出されます。
たとえば、obj1Createイベントでstr = “ABC”;とした場合、n = instance_create(0, 0, obj1);すると、その時点でn.strには“ABC”が格納されています。
※Physicsを使用する場合、Createイベントの時点ではphy_*系変数はまだ初期化されていません。Physicsオブジェクトであっても、Createイベントで座標を操作する場合はphy_position_x/yではなくx/yを使用してください。Createイベントを抜けた後は参照してもエラーにはならないので、Createイベント終了直後に初期化されているようです。
↑別プロジェクトで試すとCreateイベントでphy_*を参照してもエラーにはなりませんでした。条件調査中です。

・”game start”
ゲームスタートより先に配置されたobjectで、一度だけ発生します。
これは先ほどの「roomのCreation codeよりも先に、事前配置されたobjectのCreateイベントが呼び出される」に関連します。
事前配置されたobjectCreateイベントで生成されたinstanceは、その時点ではまだゲームスタートしていないということになります。
これはDepth値の小さい(優先順位が高い)順に呼び出されています。

・”room start”
こちらも“game start”と似ていますが、roomが切り替わった際にも発生します。
こちらもDepth値の小さい順に呼び出されています。


ここからフレーム毎の処理として繰り返されます。


・”animation end”
spriteが一周した際に発生します。ここではまだimage_indexへの加算は行われません。
image_numberが1(sub imageが1枚だけ)の場合は初回を含め毎フレームで発生します。
このイベントはDepth値の小さい順に処理されます。
※Animation updateイベントは、spriteではなくSkeletal Animationの状態変更で発生します。image_indexの変化で発生するわけではないので注意してください。

・”step begin”
事実上の1フレームの起点になります。
Depth値に関わらず生成順に呼び出されています。

・”alarm 1″
alarm[n] – 1 = 0の時に発生します。-1ではAlarmが無効となり発生しません。
alarm#0が発生していないのは、設定値を0にしていたので、0 – 1 = -1で無効になったためです。
Depth値に関わらず生成順に呼び出されています。

・”keyboard”、”mouse”
キーボードやマウスの操作により発生します。
Depth値に関わらず生成順に呼び出されています。

・”step”
毎フレームで確実に呼び出される、object処理のメインとなる部分です。
通常はここでほとんどの処理を行うことになります。
Depth値に関わらず生成順に呼び出されています。


Physicsを使用している場合は、このタイミング(Stepイベント終了後)Physicsの計算が行われます。
Physicsオブジェクトに何らかの作用を与えたい場合はStepイベント(またはそれ以前)で行い、Physicsの計算結果を得たい場合はStep Endイベント(またはそれ以降)を使用するということになります。


・”collision”
今回は衝突対象はすべて“object0”としていますので、本来はCollisionイベントは4回しか発生しないはずです。
しかし“object0”を親に持つ子objectが2つあるため、それぞれに衝突判定が行われているため、このように発生回数が多くなっています。
これはDepth値に関わらず生成順+衝突順に呼び出されています。
※Physicsを使用したroomでは、Physicsを有効にしたオブジェクトのみCollisionイベントが発生します。Physics無効のオブジェクトでは発生しないので注意してください。

・”step end”
1フレームの終点です。
Depth値に関わらず生成順に呼び出されています。


ここでDepth値によるソートが行われます。
Depth値を動的に変化させて描画順序を変化させる場合は、ここまでに確定させてください。これ以降にDepth値を変更しても処理順序は変りません。


・”pre draw”
ここから描画処理に変ります。
これ以降のイベントはすべてDepth値の大きい方(奥にあって隠れる方)から順に処理されます。
自前surfaceを使用する場合はここでsurface_existsによるチェックを行ってください。
※Pre drawイベント内の描画ですが、環境依存かはわかりませんが、描ける場合と描けない場合があるようです。
GM:SマニュアルのApplication Surfaceの解説では「Pre drawの後にApplication surfaceが有効となる」といった記述がありますが、draw_clear_alpha命令の説明では「たとえばPre drawイベントでこの命令を使用する」というような解説になっています。
現状では、Pre drawイベント内では描画はしない方が良さそうです。

・”draw begin”
ここでは優先度の低い描画を行います。
例えば遥か彼方に見える雲や星といった、確実に他よりも奥に存在しなければならないものを描画します。

・”draw”
通常はここで描画を行います。Draw系イベントが設定されていなければ、自動的にこのタイミングで描画されます。
Drawイベントを使用する場合はdraw_self()命令で同等の描画が可能です。

・”draw end”
UIやゲージ、エフェクト等の、他よりも手前に描画したい場合はこのイベントを使用します。

・”post draw”
この段階でApplication surfaceは完成しています。
これ以降はsurface内の画像は失われる可能性があるので、スクリーンショット等で画面の保存が必要な場合はここで行いましょう。
ここでゲーム画面の描画処理は終了です。


・”draw gui begin”、”draw gui”、”draw gui end”
GUIはゲーム画面とは別で、いわゆるオンスクリーンコントローラー(OSC)等の描画に関連するイベントです。
通常はVirtual key系命令が自動的に処理してくれます。
GUIもDepth値の大きい方から順に処理されます。


GUIの描画が終わると1フレームの処理は終了となり、ここでimage_indeximage_speedが加算されます。
image_index >= image_numberになると、image_indexからimage_numberが引かれます。image_speedには小数値の指定も可能なので、必ずしもimage_index = 0にならないことに注意してください。
たとえばimage_speed = 1.3と設定して、if(image_index == 0)という判定を行うと、sub imageの枚数によっては正常に動作しません。


以上がGM:Sのイベント処理順序になります。

ゲームのメインループとなるinstanceを1つだけ作成し、その中でBegin系イベントとEnd系イベントを使うことで、安全にゲーム全体をコントロールできるようになります。