Game Maker Language リファレンス > GMLの概要
※以下の内容はVer6の日本語のヘルプファイルより抜粋しています。
Game Maker は、独自のプログラミング言語が組み込まれています。
これは、標準のアクションを使用するよりも柔軟にゲームを制御することが出来ます。
この言語のことを、GML(Game Maker Languageの略)と呼びます。
様々な部分にこの言語を記述することが出来ます。
一つ目に、スクリプトを定義するときに用います。 スクリプトは、GMLによるプログラムとなります。
二つ目に、イベントに対するアクションの中に記述することが出来ます。 コードアクションで、GMLを使用することになります。
3番目に、ルーム生成コードの中に記述します。
そして最後に、アクションのパラメータの中にGMLを使った式を利用することが出来ます。 この式は、完全な制御プログラムではなく、ある値を返す命令となります。
ここでは、GMLの基本的な構造について説明しています。
GMLを使ってプログラムを利用したい場合、二つ気をつけなければならないことがあります。
一つ目に、全てのリソース(スプライト、オブジェクト、サウンドなど)の名前に、 英字から始め、その後に英数字およびアンダーバー'_'を使用しなければなりません。 そうでない場合、プログラム側から正確に参照できなくなります。
また、全てのリソース名は重複してはいけません。
また、'self','other','global','all'といった名前は、GMLで特別な意味を持つので、リソース名に使用出来ません。 後述されるキーワードは、全て使用することは出来ません。
■目次
プログラムは、ステートメント(文)と呼ばれる命令の集まりです。 プログラムは、'{' 記号と'}'記号の間に記述されなければなりません。 はさまれた部分にステートメントを記述します。 ステートメントは、';'で区切られていなければなりません。 プログラムの構造は大体以下のようになります
{ <ステートメント>; <ステートメント>; ... }
ステートメントには、後述されているように様々な種類があります。
GMLでもそうですが、プログラミング言語は全て変数を使用することが出来ます。 変数は、メモリ上に値を記録するものです。変数を使用するには、名前をつけます。 GMLの変数は、実数と文字列どちらも扱えます。 他の言語で見られるような、あらかじめ変数を宣言する必要はありません。 また、あらかじめ組み込まれている変数が多数あります。 一部の変数は、現在のマウス位置を示す変数mouse_xとmouse_yのように、ゲーム全体に共通するもの(グローバル変数)もあれば、 インスタンスの位置を示す変数 xや yといった、オブジェクトのインスタンスごとに設定されているもの(ローカル変数)もあります。
変数の名前のつけ方には規則があります。変数名には英数字およびアンダーバー'_'を使用し、先頭文字は数字以外でなければなりません。(最大で64文字です) 新しい変数を使用する場合、そのインスタンスのローカル変数に設定され、それは他のインスタンス(同種オブジェクトの場合でも)に知らせてはいません。 ですが、他のインスタンスから参照し設定する方法があります。(後述)
代入とは、変数に数値を格納することを言います。 代入は以下のようにします
<変数> = <式(数値)>;
式には単純な数値から、複雑な数式を使用することが出来ます。 固定値を代入しないで、現在の数値に加算する場合は += を使用してください。 同様に、減算する場合は-=、乗算の場合は *=、除算の場合は /=を使用します。 |=, &=, ^= のようなビット演算も使用できます。(注:ビットの複合演算は無理みたいです)
式は、実数(例:3.4)や、クォートやダブルクォートで囲まれた文字列(例:'hello'や、“hello”)、 複雑に組み合わせた式を利用することが出来ます。 式で使用できる演算子は以下の通りです:(優先度順になっています)
また、以下の単項演算子があります:
: ビットを反転します
数値や変数、値を返す関数を式に使用できます。括弧を使用することも出来ます。 全ての演算は実数で行われます。文字列の場合、+で文字を連結します。 (他の言語とは違い、最初の項で決定されてしまう論理演算でも、全て演算が行われてしまうことに注意してください)
いくつか式のサンプルを示します。
{ x = 23; str = 'hello world'; y += 5; x *= y; x = y << 2; x = 23*((2+4) / sin(y)); str = 'hello' + " world"; b = (x < 5) && !(x==2 || x==4); }
数値を代入することで新しい変数を使用することが出来ます。(前もって宣言する必要はありません) 単純に変数名を使う場合、それはそのオブジェクトのインスタンスに格納されます。 したがって、そのほかのオブジェクト(もしくは、同種オブジェクトの異なるインスタンス)からはそれを見つけることは出来ません。 しかし、変数名の前にオブジェクト名とピリオドをつけると、そのオブジェクトで定義されている変数を参照したり代入することが出来ます。 グローバル変数を作るには、変数名の前にglobalとピリオドをつけてください。 グローバル変数は、全てのインスタンスで共通して使用することが出来ます。 例を挙げます。
{ if (global.doit) { // do something global.doit = false; } }
(グローバル変数を作るのには globalvar キーワードを使う書き方もありますが、名前の競合の問題を避けるために上述のglobalとピリオドの方を使うと良いでしょう。)
また、スクリプトやコード内のみ有効な変数を利用したい場合もあります。 これは、メモリの節約や、名前の競合を避けるために利用されます。 また、グローバル変数よりも早く処理されます。 この変数を利用するときは、コードで使用する前にvarキーワードによって、宣言をしなければなりません。 下記のように記述します。
var <varname1>,<varname2>,<varname3>, ...
例を挙げます。
{ var xx,yy; xx = x+10; yy = y+10; instance_create(xx,yy,ball); }
前述されているように、現在のインスタンスの変数に代入するには、以下のように記述します。
x = 3;
しかし、他のインスタンスの変数を参照したくなることがよくあります。 例えば、全てのボールをとめたり、メインキャラクターを指定位置に移動させたり、 衝突したとき、相手側のインスタンスのスプライトを変更したい、といったようにです。 変数名の前にオブジェクト名とピリオドをつけると、そのオブジェクトの変数という意味になります。 例えば、下記のように記述します。
ball.speed = 0;
これは、ballオブジェクトの全てのインスタンスの速度が変更されます。 次に、特別な"オブジェクト"があります。
例えば、下記のコードのように記述することが出来ます
other.sprite_index = sprite5; all.speed = 0; global.message = 'A good result'; global.x = ball.x;
複数のballオブジェクトがある場合、最後の代入では何が起こるのか疑問に思うかもしれません。 これは、最初のballオブジェクトのインスタンスを示し、その x座標がグローバル変数に代入されます。
全てのボールではなく、ある特定のボールの速度を変更したいときはどうすればいいのでしょうか。 これはすこし複雑になってきます。 どのインスタンスにも固有のID番号(インスタンスID)が割り当てられています。 ルームにインスタンスを配置するときに、インスタンスの上にマウスポインタを置くと、そのインスタンスIDを見ることが出来ます。 この番号は、100000以上の数字になるようになっています。 この番号は、先のオブジェクト名のように、変数名のピリオドの左側に使用することが出来ます。 しかし、そのまま数字を記述すると、ピリオドは小数点の意味になってしまうので注意してください。 これを避けるために、ID番号を括弧で囲んでください。 ボールオブジェクトのインスタンスIDが100032のものを変更したいときは、下記のように記述します:
(100032).speed = 0;
プログラム中でインスタンスを生成する場合は、このID番号が返されます。 下記のプログラムコードを見てください。
{ nnn = instance_create(100,100,ball); nnn.speed = 8; }
このプログラムは、ボールを生成して、生成されたボールの速度が設定されています。 インスタンスIDを変数に代入し、その変数名をピリオドの前に記述している点に注目してください。 これは全くもって正しいプログラムです。 ここで、より正確に説明しましょう。このピリオドは、実は演算子なのです。 左側に数値を持ち、右側に変数名を持ちます。 そしてこの演算子は、特定のインスタンスまたはオブジェクトにある変数のアドレスを返します(アドレッシングします)。 全てのオブジェクト名、また、前述された特別なオブジェクト名は、それぞれ数値を表します。(数値に名前がつけられていると思ってもよい) したがって、これらを数値のように扱うことが出来ます。 下記のようなコードの記述は正しいです:
{ obj[0] = ball; obj[1] = flag; obj[0].alarm[4] = 12; obj[1].id.x = 12; }
最後の行にあるコードは、次のように解釈されます。 obj[1]には、flagオブジェクトの最初のインスタンスが代入されています。 左から読んでいくと、obj[1].idとなっているので、ここまででobj[1]のインスタンスidという意味になります。 そして最後に.xが付け加えられているので、そのインスタンスidのx座標に12を代入している、ということになります。 (※ball,flagはオブジェクトを示します)
オブジェクト名、特別なオブジェクト名、インスタンスIDは、関数でも使うことが出来ます。 それらはプログラムで定数として扱われます。
GMLでは、1次元配列、2次元配列を使用することが出来ます。 変数名の後ろに[]をつけ加え、その間に添字(インデックス)を1個(1次元配列)または、カンマ,で区切って2個(2次元配列)入れてください。 この添字を指定すると配列が自動的に作成されます。 配列の先頭番号は0です。 大きい配列を使うとメモリを消費してしまうので気をつけてください。 添字にマイナスの値を使用してはいけません。 各配列で32,000個までのインデックスを使用でき、全体で1,000,000サイズの配列を管理できます。 下記のように記述することが出来ます
{ a[0] = 1; i = 1; while (i < 10) { a[i] = 2*a[i-1]; i += 1;} b[4,6] = 32; }
if ステートメントは以下のように記述します。
if (<式>) <ステートメント1>
または、
if (<式>) <ステートメント1> else <ステートメント2>
ステートメントではブロック化( { } の間に複数のコードを記述する)することが出来ます。 if の後の式が計算され、結果(を四捨五入し、整数化された値)が <=0 ならば偽(false)となり、 'else'以降のステートメント2が実行されます。 そうでない場合は真(true)となり、ステートメント1が実行されます。 通常、if ステートメントでは { } を使用して記述するとよいでしょう。 以下のように記述します。
if (<式>) { <ステートメント1> } else { <ステートメント2> }
以下のように記述すると、オブジェクトは画面の中の方へ向かいます。
{ if (x<200) {x += 4} else {x -= 4}; }
repeat ステートメントは以下のように記述します。
repeat (<式>) <ステートメント>
これは、式の結果を四捨五入して整数化し、その回数だけ処理を繰り返します。
以下のように記述すると、ボールを5個生成しランダムに配置します。
{ repeat (5) instance_create(random(400),random(400),ball); }
while ステートメントは以下のように記述します。
while (<式>) <ステートメント>
式の結果が真である限り、ステートメント(ブロック化も可能)が実行されます。 whileループを使用するときは、ゲームが止まってしまうような無限ループにならないよう注意してください。
下記のように記述すると、 現在のオブジェクトが他と衝突しないように配置されます。 (これは、オブジェクトをランダムに配置するアクションと同じです)
{ while (!place_free(x,y)) { x = random(room_width); y = random(room_height); } }
do ステートメントは以下のように記述します。
do <ステートメント> until(<式>)
式の結果が真になるまで、ステートメント(ブロック化も可能)が実行されます。 ステートメントは、少なくとも1回は実行されます。 while ステートメントと同じく、ゲームが止まってしまうような無限ループにならないよう注意してください。
下記のように記述すると、 現在のオブジェクトが他と衝突しないように配置されます。 (これは、オブジェクトをランダムに配置するアクションと同じです)
{ do { x = random(room_width); y = random(room_height); } until (place_free(x,y)) }
For ステートメントは以下のように記述します。
for (<ステートメント1> ; <式> ;<ステートメント2>) <ステートメント3>
これは以下のように動作します。 最初にステートメント1が実行されます。 次に式の結果が真の場合、ステートメント3が実行されます。 その後にステートメント2が実行され、再び式が計算されます。 式の結果が真である限り繰り返されます。
複雑に思うかもしれませんが、次のように理解してください。 <ステートメント1>で初期化し、 <式>には継続条件を記述します。 <ステートメント2>では、終了条件に近づくようにします。
カウンタを用いてループさせるような場合に最もよく使われます。
下記のように記述すると、10個の配列にそれぞれ1-10の数字を代入します。
{ for (i=0; i<=9; i+=1) list[i] = i+1; }
ある数値の値によってアクションを変えたくなる状況がよくあります。 数多くのifステートメントを使用して行うことも出来ますが、 switch ステートメントを使う方がより簡単になります。 switch ステートメントは以下のように記述します:
switch (<式>) { case <式1>: <ステートメント1>; ... ; break; case <式2>: <ステートメント2>; ... ; break; ... default: <ステートメント>; ... }
これは、最初の式の結果が、それぞれの式(式1、式2...)と比較し、同じであれば、それに対応する ステートメントが、break ステートメントまで実行されます。 どの式にも当てはまらなかった場合、defaultで指定されているステートメントが実行されます。 (defaultは、なくても構いません) 複数のcaseに対して同じステートメントを割り当てることが出来ます。 また、break はなくても構いません。その場合、順に次のcaseにあるステートメントが実行されていきます。
下記のように記述すると、押されたキーによって別の命令が実行されます。
switch (keyboard_key) { case vk_left: case vk_numpad4: x -= 4; break; case vk_right: case vk_numpad6: x += 4; break; }
break ステートメントは以下のように記述します。
break
for、while,repeat等のループや、switch、with ステートメントのブロック内で使われると直ちにブロックから抜けます。 これらのステートメントの外で使われた場合は、そのプログラムを直ちに終了します。(ゲームを終了するわけではありません)
continue ステートメントは下記のように記述します。
continue
for、while、repeat等のループするステートメントや、withステートメント内で使用すると、直ちに次のループに移ります。
exit ステートメントは下記のように記述します。
exit
これを記述すると、現在のスクリプトやコードが直ちに終了します。(ゲームが終了するわけではありません!終了させたい場合は関数game_end()を使用してください)
下記のように記述すると、関数を呼び出します。関数には、0個以上の引数を指定することが出来ます。
<function>(<arg1>,<arg2>,...)
関数には二つの種類があります。一つは、ゲームをコントロールすることの出来る、GMLに組み込まれている関数です。 二つ目は、自分で定義したスクリプトです。これは関数のように使用することが出来ます。
関数に引数が指定されていない場合でも、括弧を省略してはいけません。 関数に戻り値がある場合は、式の中に使用することが出来ます。 戻り値のない関数は、命令を単に実行します。
関数を演算子の左側に直接使用することが出来ないことに注意してください。 例えば、以下のようには記述できません。
instance_nearest(x,y,obj).speed = 0
代わりに、次のように記述してください。
(instance_nearest(x,y,obj)).speed = 0
入力された引数を利用することが出来ます。 引数は、スクリプトアクション内や、このスクリプトを(別のプログラムや、または同じスクリプト内から再帰的に)呼ぶ時に設定されています。 引数は、変数 argument0, argument1,〜,argument15に格納されています。 したがって引数は16個使用することが出来ます。(スクリプトアクションの場合は最初の5個までです) argument[0]のように、配列のようにも使用できます。 スクリプトには、計算結果を出すための戻り値も設定できます。 戻り値を設定するには、return ステートメントを使用します
return <expression>
このreturn ステートメントが実行されるとスクリプトは終了します。
二つの引数を乗算し結果を返すスクリプトを定義しています:
{ return (argument0*argument0); }
コードからスクリプトを呼ぶときには、関数と同じようにします。 スクリプト名と、パラメータとなる引数を記述してください。
前述したように、他のインスタンスの変数を参照または変更することは可能です。 しかし、複数のインスタンスを扱いたい場合がよくあります。 例えば、全てのボールを8ピクセル下に移動させたいとします。 以下のように記述するとうまくいくと思うかもしれません。
ball.y = ball.y + 8;
しかしこれは正しくはありません。代入される右側の値は、最初のballオブジェクトのy座標に8を加えるという意味であり、 全てのボールのy座標に、この値が代入されてしまいます。 したがって、全てのボールのy座標は同じになってしまいます。 一方、下記のようにしても結果は同じです。
ball.y += 8;
なぜなら、最初の命令を簡略した形だからです。 どうやって、実現すればいいのでしょうか? これを解決するために、with ステートメントがあります。 一般に以下のように記述します
with (<式>) <ステートメント>
<式> には、一つもしくはそれ以上のインスタンスを指定します。 ここにインスタンスID、オブジェクト名(オブジェクトの全てのインスタンスという意味になります)、特別なオブジェクト(all,self,other,noone) を指定します。 <statement> は、指定したインスタンスそれぞれに実行されるコードを、あたかも現在のインスタンスに対して行われるように記述します。 したがって、全てのボールそれぞれに8ピクセル下に移動させるには次のように記述します。
with (ball) y += 8;
複数のコードを記述する場合は、{ }で囲んでください。 例えば、全てのボールをランダムな位置に再配置するときは、以下のようにします。
with (ball) { x = random(room_width); y = random(room_height); }
このステートメント内部では、selfの意味はwithで指定したインスタンスということになる点に注意してください。 本来のインスタンス自身を指す場合は、otherを指定してください。 したがって、次のように記述すると、全てのボールは現在のインスタンスの位置に移動します。
with (ball) { x = other.x; y = other.y; }
withステートメントを使用すると、とても強力です。 以下にもう少し例を挙げておきましょう。 全てのボールを破棄するときは、以下のように記述します。
with (ball) instance_destroy();
爆弾を爆発して近くのインスタンス全てを破棄したい場合は、 以下のように記述します。
with (all) { if (distance_to_object(other) < 50) instance_destroy(); }
コード内にコメントを記入することが出来ます。 // 以降の文はコメントとして読み飛ばされます。 また、コメントを複数行にまたがって記述したいときは/*〜*/で囲ってください。 (色付けが正しくされない場合は、F12を押して再度色付けするようにしてください)
GMLには、数多く関数や変数が組み込まれています。 これらを使用することで、ゲームをコントロールすることが出来ます。 全てのアクションは、関数によって実装されています。 したがって、コードを使って記述していくと、アクションを使う必要がありません。 逆に、こうした関数や変数のほうが多いので、アクションだけではコントロールできない部分があります。 したがって、高度なゲームを作る場合には、以降の項目を読んで、できるだけ概要をつかんでおくことを強く推奨します。 アクションのパラメータにも、こういった関数や変数の数値が使用出来ることに注目してください。 スクリプトやコードを記述しないような場合でも、これらの情報は役に立ちます。