株式会社WARDOG(ウォードッグ)は、
web、デザイン、動画、ゲームなどを制作しています。
ホーム
INFORMATION
会社概要
お問合せ
GMLリファレンス
GML 簡易リファレンス
GameMakerLanguageの自分用リファレンスです。
基本的なものと、注意が必要なものをまとめています。
基本・その他
標準出力
show_debug_message( string )
var my_score = 12300 ;
show_debug_message( "スコアは" + string( my_score ) ) ;
関数名が長いので私はecho()というユーザ関数を作ってます。
引数はstringなので、数値などは文字列に型キャストしないと実行エラーになります。文字列と数値の+連結の前に判定されるみたいです。
でも配列はカンマ区切りで出してくれました。
型キャスト
int64() real() string() bool()
var string_val = "3" ;
var int_temp = int64( string_val ) ; // 小数をint64にキャストするとエラー
show_debug_message( string( int_temp ) ) ;
string_val = "3.1415" ;
var real_temp = real( string_val ) ; // 小数第2位で打ち切られる?
show_debug_message( string( real_temp ) ) ;
var bool_temp = bool( 1 ) ;
show_debug_message( string( bool_temp ) ) ;
なぜかstring()がGML公式マニュアルにない…けど普通に出てきます。
int64()では小数の文字列がエラー。小数を整数に変換したい場合はfloor()を使うことになりそうです。
real()ではなぜか小数第2位で打ち切り…精度が低くなりすぎるので実数の型キャストは変数使いまわしに注意。
bool()ではブール値のtrue/falseに変換されるとしても、標準出力の際ではstringにキャストしなおすので1/0として表示されます。
型判定
typeof( value )
var my_type = typeof( aaa ) ;
if( my_type == "string" ){
show_debug_message( "stringでした。" ) ;
} else if( my_type == "array" ){
show_debug_message( "arrayでした。" ) ;
} else if( my_type == "bool" ){
show_debug_message( "boolでした。" ) ;
} else if( my_type == "int64" ){
show_debug_message( "int64でした。" ) ;
} else if( my_type == "number" ){
show_debug_message( "numberでした。" ) ;
} else if( my_type == "undefined" ){
show_debug_message( "undefinedでした。" ) ;
} else if( my_type == "null" ){
show_debug_message( "nullでした。" ) ;
}
型チェックの関数です。他にも文字列かどうかの判定はis_string()という関数でもできます。
int64は整数、numberは実数の違いみたいです。
ユーザ関数定義
function name(){ }
function hit_check( shot_x , shot_y , enemy_x , enemy_y ){
var hit_flag = false ;
if( shot_x == enemy_x && shot_y == enemy_y ){
hit_flag = true ;
}
return hit_flag ;
}
PHPと同じように使えます。
基本的にスクリプトファイルにユーザ関数をまとめることになると思います。
定数
pi
var my_square = my_radius * my_radius * pi ;
すでに組み込まれている定数はいくつかありますが、気にしないといけないのは円周率のpiくらいでしょう。
piの精度はハードウェアに依存するようです。超細かい計算をするときには注意?
演算子
複合代入演算子
+= -=
var my_score += 1000 ;
my_score -= 500 ;
算術演算子
+ - * / %
var return_val = ( 5 + 4 - 1 ) * 4 ;
return_val = return_val / 3 ; // 10.67
return_val = return_val % 7 ; // 3.67
使い方はPHPと同じ。
比較演算子
> >= < <= !=
var my_life = 10 ;
if( my_life == 10 ){
echo( "==10" ) ;
}
if( my_life >= 10 ){
echo( ">=10" ) ;
}
if( my_life != 9 ){
echo( "!=10" ) ;
}
インクリメント デクリメント
++ --
var my_life = 10 ;
my_life++ ;
my_life-- ;
pythonではなぜか実装されなかったインクリメント、デクリメント。GMLはPHPに近いのでありました。
文字列
文字列代入
"文字列代入"
var my_message = "文字列はダブルクォートで挟みます" ;
my_message += "シングルクォートはなんと使えません!!" ;
なんと『シングルクォートは使えない』ようです!!
文字列の代入を行う際は、ダブルクォートが含まれる際にはエスケープをしないといけないということですね。
pythonみたいに『my_message[3] = "に" ;』というようにインデックスを使ってアクセスすることはできません。
文字列検索
string_pos( substr , string )
var my_input = "URAR1R2S" ;
if( string_pos( "U" , my_input ) != 0 ){
echo( string_pos( "U" , my_input ) ); // ヒット位置は1から!!
}
文字列内を検索します。PHPと使い方はほぼ一緒です。関数名がちょっと違うので注意。
戻り値は検索がヒットした位置ですが『1からスタート』することに注意してif文で使わないといけません。
ヒットしなければ0、0文字目にヒットしたら1(!!)が返ってくることになります。これはPHPのstr_pos()もそうらしいです…知らなかった…。
文字列置換
string_replace_all( string , search , replace )
now_month = string_format( current_month , 2 , 0 ) ;
now_month = string_replace_all( now_month , " " , "0" );
文字列置換の関数です。上記の例では半角スペースで桁揃えした月をゼロパディングにするために置換しています。
PHPと違うのは引数の順番。ややこしい…。
allがついてることから
文字列切り出し
string_copy( string , index , count )
var my_message = "all your base are belong to us." ;
var your_pos = string_pos( my_message , "your" ) ; // yourがヒットした位置を取得。1から始まることに注意。
if( your_pos > 0 ){
my_message = string_copy( my_message , your_pos , 4 ) ; // "your"だけが切り出される。
}
文字列を開始位置から何文字か指定して切り出します。
注意しないといけないのは『1文字目からカウント』することです。0じゃないので注意!!
文字列連結
+
var my_message = "あなたのスコアは" ;
var my_score = 12300 ;
my_message = my_message + string( my_score ) + "でした。" ;
+を使って文字列を連結できるのはjavascriptと同じ。しかし数値はちゃんと文字列に型キャストしないとエラー。
文字列長取得
string_length( string )
var my_message = "これは開発中のversionです。" ;
my_message_length = string_length( my_message ) ; // 17が格納される。
マルチバイト関係なく文字数を取得。
他にバイト長や画面上での1行の幅を取得する関数もあるようです。
ループ
比較演算子
> >= < <= !=
var my_life = 10 ;
if( my_life == 10 ){
echo( "==10" ) ;
}
if( my_life >= 10 ){
echo( ">=10" ) ;
}
if( my_life != 9 ){
echo( "!=10" ) ;
}
条件分岐
if( ){} else if( ){} else{}
var now_year = 2026 ;
if( now_year % 400 == 0 ){
show_debug_message( "超レアなうるう年です。" ) ;
} else if( now_year % 100 == 0 ){
show_debug_message( "レアな非うるう年です。" ) ;
} else if ( now_year % 4 == 0 ){
show_debug_message( "うるう年です。" ) ;
} else {
show_debug_message( "非うるう年です。" ) ;
}
GMLのif分では、『else if』が使えます。スペースなしの『elseif』や『elif』は使えないので注意。
forループ
for( ; ; ) {}
id_array = [ 1 , 58 , 12 , 12 , 6533 , 804 , 0 ] ;
for( var i = 0 ; i < array_length( id_array ) ; i++ ){
show_debug_message( string( id_array[i] ) ) ;
}
PHPと同じ使い方ができます。
whileループ
while( ){}
var i = 0 ;
while( i < array_length( id_array ) ){
show_debug_message( string( id_array[i] ) ) ;
i++ ;
}
PHPと同じ使い方ができます。
withループ
with( ){}
with( obj_shot ){
if( x == other.x && y == other.y ){
show_debug_message( "HIT!" );
}
}
withは指定したオブジェクトのインスタンス全部に対して処理をするループです。実際にゲームとして作り始めて初めて使い方が見えてきます。
上記の例では『obj_shot』というオブジェクトのインスタンス…画面上にあるショット全てに対してヒット処理を行っています。
このwithループを書くのはobj_shotのイベント上ではなく、例えばエネミーのステップイベントに記述したりします。エネミーが全ショットに対してチェックするのです。
重要な注意点として、withループ内は対象のインスタンスが基準になります。
上記の例では『x』はエネミー自身のx座標、『y』はエネミー自身のy座標としてGMLで最初からプロパティとして準備されています。エネミーオブジェクト内に記述したらself.xとは書かず、省略して単にx、yと記述できるわけですが…。
withループ内になると今度はobj_shot基準になるので、『xとだけ書くと、ショットのx座標』を意味します。エネミー自身のxは『other.x』と、今度は『other』の指定が必要になります。with内では立場が逆転したような記述になるので混乱に注意。私は何度も頭を抱えることになりました…。
ループ脱出
break
for( var i = 0 ; i < 100 ; i++ ){
if( i == 50 ) {
break ;
}
}
PHPと同じように使えます。
次順のループにジャンプ
continue
for( var i = 0 ; i < 100 ; i++ ){
if( i == 20 ){
continue ;
}
if( i % 20 == 0 ) {
break ; // iが40の時にbreak ;
}
}
PHPと同じように使えます。でもできるだけ使いたくないかも。
配列
配列の作成
[]
var id_array = [] ; // 空の配列を準備
id_array = [ 65465 , 458 ] ;
id_array = [ 1 , 58 , 12 , 12 , 6533 , 804 , 0 ] ; // 上書きになる。
show_debug_message( id_array[ 0 ] ) ; // 1を出力する。
配列は[]だけで作成する(PHPはarray()を使う)
値が0で初期化された指定長配列の宣言関数array_create()もあるようですが、使いどころはまだ分かりません。
GMLには『連想配列はない』ようです。同時にforeach()もないようです。ちょっと不便そう…。
多次元配列の作成
[ [] , [], [], [], ]
var member_array = [
[ "yamada" , 1 ] ,
[ "tanaka" , 58 ] ,
[ "saito" , 12 ] ,
[ "inoue" , 6533 ] ,
] ;
show_debug_message( member_array[3][0] ) ; // inoueを出力。
配列宣言で最後の要素の後にカンマが来ても大丈夫。ちょっと前のPHPではエラーになってましたが、GMLでもカンマありが大丈夫なのでコピペが楽です。
配列長の取得
array_length( array )
id_array = [ 1 , 58 , 12 , 12 , 6533 , 804 , 0 ] ;
var id_array_length = array_length( id_array ) ; // 7が格納される。
GMLにforeach()がない以上、for()でお世話になりまくると思います。
文字列を配列に変換
string_split_ext( string , [ "," , "." ] )
var score_line = "123456000,0,0,1,4,WARDOG,RED.GREEN.BLUE" ;
var score_line_array = string_split_ext( score_line , [ "," ] ) ;
PHPのexplode()の処理と同じです。
分割文字(分割文字列)は配列で指定することになっているので、上記の例ではカンマだけで区切るとしても、配列として指定しています。
なぜかGMLのマニュアルで日本語ページが存在しません…大事な関数なのに…。
配列要素追加
array_insert( array , index , value , value , value ... );
array_insert( score_array , 0 , "wardog" );
配列に要素を挿入します。
第2引数はindex、第3引数は値です。上記の例では配列の先頭に要素を1個追加しています。
要素は複数まとめて追加できるので、カンマ区切りで第4引数、第5引数…と記述できます。
配列要素削除
array_delete( score_array , index , num );
array_delete( score_array , 0, 1 );
配列の要素を削除します。
第2引数はindex。先頭は0になります。第3引数は削除する数。上記の例では配列の先頭を1個だけ削除していることになります。
配列末尾に要素を追加
array_push( array , value, value , value ... )
array_push( history_array , "wardog" ) ;
配列の末尾に要素を追加するpush関数です。
複数の要素を追加できるので、カンマ区切りで第3引数、第4引数…と続けて記述できます。
配列末尾の要素を取得、削除
array_pop( array )
var ranking_last = array_pop( ranking_array ) ;
配列の末尾の要素を取得、削除します。
末尾の要素を削除するだけではないので、戻り値があります。
末尾操作をpush/popってなんかスタックのイメージがあるのですが、どうやらプログラム言語的にはこれがpush/popらしいです。
配列ソート
array_sort( array , false )
array_sort( array , false ) ;
配列をソートする関数。第2引数はfalseで降順、trueで昇順。
もともとの配列変数が書き換わるので戻り値はないようです。使ってみないとちょっと勝手が不明。
時間
現在の年月日時分秒取得
current_year current_month current_day current_hour current_minute current_second
var now_year = current_year ;
var now_month = current_month ;
var now_day = current_day ;
var now_hour = current_hour ;
var now_minute = current_minute;
var now_second = current_second ;
(タイムゾーンを指定しない場合は)ローカルの年月日時分秒をそれぞれ取得します。
戻り値は実数なので2桁揃えにはなっていないことに注意します。
数学
絶対値
abs( real )
if( abs( x - shot.x ) < 16 && abs( y - shot.y ) < 16 ){
hit_flag = true ;
}
abs()関数で絶対値を取得できます。私はインスタンス同士の距離が近いかの判定に使っています。(いろんな計算方法がありますが、計算が楽でイメージしやすいので)
数字の桁揃え
string_format( real , maxdigit , 0 ) ;
now_month = string_format( current_month , 2 , 0 ) ;
数字の桁揃えを行います。ゼロパディングの前準備にもなります。
3番目の引数は0にしていますが、これは小数を第1引数にした時の挙動に影響します。整数を桁揃えする場合は0固定で。
桁揃えは半角スペースで埋められています。
バンカー丸め
round( integer )
var my_score = 3.4 ;
my_score = round( my_score ) ; // 3になる。
my_score = 3.5 ;
my_score = round( my_score ) ; // 4になる。
my_score = 2.5 ;
my_score = round( my_score ) ; // 2になる!!
GMLのround()関数は『四捨五入ではありません』!!バンカー丸め(銀行丸め)、偶数丸めと言う端数処理を採用しているようです…。
四捨六入まではしますが、0.5の扱いが違います。
上の例では『2.5』が『2』になってしまいます。これは『偶数になるほうに丸める』という手法のためです。
3.5をバンカー丸めすると、3か4のどちらかというと偶数の4に丸められます。
2.5をバンカー丸めすると、2か3の…偶数である2のほうに丸められるのです。
これは『丸めた数値を合計した際に誤差が少なくなる』というメリットがあるようです。プログラマとしては『合計してから丸めればいいじゃないか』と思いますが、GMLだけでなくほかにもpythonなど、採用している言語は多いようです。ただし、どちらが主流というわけでもないようなので、言語によって確認しないといけません。
PHPは四捨五入ですが、round()のオプションを指定することでバンカー丸め方式にも対応しています。
数値切り上げ、切り捨て
ceil( integer ) floor( integer )
var my_score = 3.4 ;
var my_score_ceil = ceil( my_score ) ; // 4
var my_score_floor = ceil( my_score ) ; // 3
切り上げはceil()、切り捨てはfloor()で行います。PHPと同様です。
実数をint64()で整数に型キャストして切り捨て処理をすることはできませんでした。GMLの型キャストの仕様です。ちゃんとfloor()を使うようにしましょう。
乱数生成
irandom( integer ) irandom_range( start , end )
randomise(); // ゲーム開始時に実行
var damage_random = irandom( 100 ) ; // 0から100までの整数で乱数取得
var hp_random = irandom_range()( 10 , 20 ) ; // 10から20までの整数で乱数取得
random()もありますが、整数で乱数を発生させるこちらのほうが使いやすそうと思います。
irandom()では0から引数までの乱数、irandom_range()では2つの引数の間で乱数を生成します。どちらも引数を含みます。
ただ乱数生成関係の関数は再現性があるので、『ゲーム開始時にrandomise()を実行』しておくといいでしょう。最初のRoomのオブジェクトなど?
ラジアン度数変換
degtorad( deg ) radtodeg( rad )
var rad_temp = degtorad( 60 ) ; // 約1ラジアン
var deg_temp = radtodeg( 1.04 ) ; // 約60度
// degtorad()を使わない場合、degからradへの変換は以下の式を使って計算する。piは定数として組み込まれている。
// var rad_temp = 60 * pi / 180 ;
ラジアンと度数の変換は難しいものじゃないですが一発変換してくれます。
piは定数として組み込みで宣言されているのでそのまま使えますが…なんと環境によって値がわずかに違う可能性があるとか!?(通常は気にしないでいいでしょうが)
三角関数
cos( rad ) sin( rad ) tan( rad )
var x_pos = cos( 1.04 ) ; // 1.04ラジアンは約60度
var y_pos = sin( 1.04 ) ;
var h_pos = tan( 1.04 ) ;
基本の三角関数は一般的な使い方通りです。ただし引数はラジアンです。
2点間の角度の算出
point_direction( x , y , target_x , target_y )
var shot_direction = point_direction( 0 , 0 , 128 , 128 ) ;
echo( shot_direction ) ; // 315度となる!!
2点間の角度を簡単に計算してくれます…が!!注意が必要です。
まずGMLの座標系は原点がウィンドウ左上、xは右方向に増加、yは下方向に増加します。これはウィンドウ座標系とかいうのでしょうか、多くみられます。
しかし、角度の測り方が反時計回り…つまり数学での角度の方向になっています。これはどこかで間違う恐れが…。
しかも戻り値はラジアンではなく度数なので注意。
上の例では画面左上が原点で、右下方向への角度を求めていますが、45度ではなく315度になるのです。
狙い撃ちのx,y移動量
lengthdir_x( speed , deg ) lengthdir_y( speed , deg )
var shot_direction = point_direction( 0 , 0 , 128 , 128 ) ;
enemyshot.movex = lengthdir_x( shotspeed , shot_direction + diffdeg ) ;
enemyshot.movey = lengthdir_y( shotspeed , shot_direction + diffdeg ) ;
敵キャラが自機に狙い撃ち弾を撃つ時を想定します。その際の敵ショットのx移動量、y移動量を計算してくれる便利な関数です。
敵キャラが自機に狙い撃ちするには、敵キャラと自機キャラの座標から『どの角度で撃つか』を計算します。そのあと『ショットの速度』を加味して、x方向の移動量をどのくらいにすればいいか、y方向の移動量をどのくらいにすればいいかを最終的に計算します。pygameでシューティングゲームを作ったときはarctan2などを使ってました。
しかしこの2つの関数は『ショットスピードと角度を教えてくれたら一発で計算してくれる』便利な関数です。ちょっと楽になっただけですけど。
難しいことを考えず、算出されたxの移動量、yの移動量を加算していけば、敵弾は自機に当たるのです。小数の扱いには注意(低解像度なら誤差が蓄積して結構ずれる可能性がある)
ファイル操作
ファイル存在チェック
file_exists( filename )
if( file_exists( "save.txt" ) ){
var save_file = file_text_open_read(working_directory + "\save.txt");
}
file_exists()でファイルの有無をチェックします。
Windowsでは『user > APPData > Local > プロジェクト名』に保存されるようです。
ゲームハードによって仕様が異なるようです。
ファイルオープン
file_text_open_read( filepath ) file_text_open_write( filepath )
if( file_exists( "save.txt" ) ){
var save_file = file_text_open_read(working_directory + "\save.txt");
file_text_close(score_file);
var save_file = file_text_open_write(working_directory + "\save.txt");
file_text_close(score_file);
}
ファイルをread、writeモードでオープンします。
詳しく調べてなくて申し訳ありませんが、working_directoryはデフォルトディレクトリの指定でしょうか。file_exists()では指定しなくても動作したので…あってもなくてもとりあえずは大丈夫?
ファイルクローズ
file_text_close( file_id )
if( file_exists( "save.txt" ) ){
var save_file = file_text_open_write(working_directory + "\save.txt");
file_text_close( score_file );
}
オープンしたらクローズしましょう。メモリ開放につながるらしいです。
ファイル1行取得
file_text_read_string( file_id )
if( file_exists( "save.txt" ) ){
var save_file = file_text_open_read(working_directory + "\save.txt");
for( var i = 0 ; i < 11 ; i++ ){
temp_score_array[i] = file_text_read_string( save_file ) ;
file_text_readln( save_file );
}
file_text_close( save_file );
}
file_text_read_string()でreadオープンしたテキストファイルの1行を文字列として読み込めます。
任意の行を読み込めるわけではなくて、0行目から順番に読み込んでいて、現在どの行を読み込めるかは明示されませんので、for()で回して何行目か気にしないといけないでしょう。
ファイル読込ポインタを次行へ
file_text_readln( file_id )
if( file_exists( "save.txt" ) ){
var save_file = file_text_open_read(working_directory + "\save.txt");
for( var i = 0 ; i < 11 ; i++ ){
temp_score_array[i] = file_text_read_string( save_file ) ;
file_text_readln( save_file );
}
file_text_close( save_file );
}
file_text_open_read()で1行読み込んだら、読み込みポインタを次行に移しておきます。
GMLマニュアルではなんか1行丸ごと戻り値に入ってくるみたいな説明ですが、単独で使って次行に移動する使い方でいいと思います。マニュアルもそうしてますし。
ファイル1行書込
file_text_write_string( string , file_id )
if( file_exists( "save.txt" ) ){
var save_file = file_text_open_read(working_directory + "\save.txt");
file_text_write_string( "これはセーフファイルです。" , save_file ) ;
file_text_writeln( save_file );
file_text_close( save_file );
}
1行書き込みです。
ファイル1行目から順次上書きになりますので、追記したい場合はfile_text_open_append()を使うことになるでしょう。
ファイル書込ポインタを次行へ
file_text_writeln( file_iid )
if( file_exists( "save.txt" ) ){
var save_file = file_text_open_read(working_directory + "\save.txt");
file_text_write_string( "これはセーフファイルです。" , save_file ) ;
file_text_writeln( save_file );
file_text_close( save_file );
}
1行writeしたら、改行をwriteして次行に進めます。
インスタンス・スプライト
インスタンス座標
x y
x += 4 ;
y += 4 ;
インスタンスの座標はx、yで指定します。
なんと実数で保持してくれるので、小数保持用の別の変数は不要みたいです。
インスタンスの直前の座標
xprevious yprevious
if( x == xprevious && y == yprevious ){
// このインスタンスは動いていません。
}
このインスタンス変数で直前の座標(おそらく1フレーム前の?)を取得できますので、現在の座標と比較することで動いているかどうかが分かります。
これらも実数で保持されています。
インスタンスの透明度
image_alpha
image_alpha = 0 ;
透明度、opacityを設定します。1で不透明、0で透明です。
他にvisibleという非表示切り替えをするインスタンス変数もありますが、image_alphaは半透明や値を段階的に指定してフェード表現することができます。基本的にimage_alphaを使えれば大丈夫なのかも?
インスタンスz値
depth
depth = -1 * y :
インスタンスの重なり具合を制御するz値です。
GMLでは-16000~16000の間で指定します。『小さいほうが手前』となり、描画優先されますので注意してください。
上の例では、自分のy座標をマイナスにした値をdepthに入れていますが、これは画面下にいるものほど重なり順が手前になります。これによりRPGなどでキャラクタの重なり順を楽に自然に設定することができます。
インスタンスのスプライト名
sprite_index
if( my_life <= 0 ){
sprite_index = spr_dead ;
}
スプライトインデックス…というかスプライト名を指定することで、スプライトを変更できます。
上記の例では自分のライフが尽きたら、spr_deadと自分で指定したスプライトに切り替えます。
スプライトにはループアニメーションを設定できるので、いろんな動作をするキャラクタを作る場合は、スプライトをガンガン切り替えていくと楽そうです…が、スプライト管理が莫大になりそうです…。
スプライトアニメのフレーム数
image_index
if( my_life <= 50 ){
image_index = 4 ;
}
スプライトアニメーションの現在のフレーム番号を取得、またはセットできます。
上記の例ではライフが減少したら4番目(0から数えるので5枚目)のフレームに切り替えています。
image_indexに代入した時点で、自動再生されていたスプライトアニメーションは停止します。
スプライトアニメの総フレーム数
image_number
if( my_timer % 4 == 0 ){
image_index = ( image_index + 1 ) % image_number ;
}
インスタンスに指定されているスプライトのアニメーションのフレーム数を取得します。
上記の例では4フレーム(これはゲームの時間のほう)ごとにimage_indexを1つずつ増やしていきアニメーションさせていますが、フレーム総数であるimage_numberの剰余をとることでフレーム総数をはみ出さないようにしています。
スプライトアニメーションの速度倍率
image_speed
if( my_status == "slow" ){
image_speed = 0.5 ;
}
スプライトアニメーションの再生速度の倍率を指定します。通常は1倍ですが、上記の例ではslow状態になると0.5倍、つまりゆっくりになります。逆に2にすると早くなります。
引数はstringなので、数値などは文字列に型キャストしないと実行エラーになります。文字列と数値の+連結の前に判定されるみたいです。
でも配列はカンマ区切りで出してくれました。