Game Maker Language リファレンス > 組み込み関数 用途別 > 2D グラフィック機能 > Spineの制御と描画
GameMaker:Studioでは、Spineはスプライトとして扱われます。 Spineツール側でエクスポートで出力した *.json ファイルを スプライトエディタから import し、そのスプライトを オプジェクトに登録するだけで再生が可能です。
描画はスプライトと同じように扱われるため、draw_self() で描画が可能です。細かくパラメータを指定したい場合は、draw_skeleton()を使用します。
rootボーンの座標やスケール値などはGameMaker:Studio から直接指定するため、rootボーンに対するアニメーションは無効となります。 rootボーン に別のボーンをぶら下げて、rootボーン以外でアニメーションを実装する必要があります。
Spineスプライトを使うとGameMaker:Studio側のコリジョンマスクは無効となります。 そのため、コリジョンは Spine側で定義する必要があります。 もしくはボーンの座標を skeleton_bone_state_get() で取得し、それをもとにコリジョンを動的に作って判定します。
Spineスプライトのアニメーションデータを GameMaker:Studio 側から書き換えることはできません。 アニメーションの編集は Spine側で行う必要があります。
利用可能なSpineデータのテクスチャサイズは、環境によって制限がかかります
Spineスプライトは、GameMaker:Studio上のスプライトとほぼ同じものとして扱えるため、以下のインスタンス変数とスプライトプロパティで制御が可能です
「エクスポート > パック設定」の「乗算済みアルファ」のチェックを外すと回避できます。(もしくは実行時に乗算ブレンドモードで描画? ※未検証)
一通りは動いている印象です。以下の機能は問題なく動いていました。
GameMaker:Studio2 で動作する Spineのバージョンは最新より古い可能性があります。
(※2020/3/12 現在 [ v2.2.5.481 ] 動作するのは「Ver. 3.7.94」)
(※2021/1/14 現在 [ v2.3.1.542 ] 動作するのは「Ver. 3.7.94」)
(※2021/11/28 現在 [ v2.3.7 ] Spine v4.0に対応 )
最新の対応状況は以下のページから確認可能です
Spineはデータフォーマットのバージョンを上げると、それよりも下のバージョンにできなくなるので、GameMaker:Studio側の対応が完了しない限り、古いバージョンで固定する必要があります。
GameMaker:Studio2 における Spineの制御 (正確には Spineランタイムが提供している機能) では、Trackという概念を使用することができます。
Trackとは、複数のアニメーションを混在して動作させる機能です。
例えば下半身の足の動きと、上半身の銃を構える動き、この2つのアニメーションを Trackに登録することで、アニメーションを組み合わせて動作させることが可能となります。
Track は、skeleton_animation_set_ext() で登録を行い、skeleton_animation_clear() で消去します。
アタッチメントを使用すると、特定のボーンにスプライトを追従させることが可能です。例えば手に剣や盾を装備させたり、頭に帽子をかぶらせたりするなどです。 skeleton_attachment_create() でアタッチメントを生成し、skeleton_attachment_set() でアタッチを行います。
アタッチ後はスロットとして扱われるので、skeleton_slot_* の関数で情報の変更が可能です。
スキンを使うとテクスチャを入れ替えてキャラクターの色変えや見栄えの変更をすることができます。
アタッチメントは特定のボーンに対してスプライトの画像を追従させます。 例えば、剣や盾などの装備品を手に持たせることが可能となります。
Spineスプライトを使用している場合のみ以下の Objectイベント が発生します
This event is designed only for use with the Skeletal Animation Functions. It is a special event that is triggered every step in an instance that uses a skeletal animation sprite, and is designed to "intercept" the bone data after the orientation of the bones has been calculated for the current animation state but before this data is committed to use for drawing. This allows you to make modifications to the bone data using the appropriate functions.
このイベントは Spineスプライト でのみ使用するように設計されています。 Spineスプライトを使用するインスタンスのすべてのステップでトリガーされる特別なイベントであり、 現在のアニメーション状態に対してボーンの方向が計算された後、このデータを描画に使用するためにコミットされる前にボーンデータを「インターセプト」するように設計されています。 これにより、適切な関数 (skeleton_bone_state_set())を使用してボーンデータを変更できます。
Spine側のアニメーションで設定したイベントが発生した際に呼び出されるイベントです。
Spine側の設定方法については以下のページを参照。
これを使用することで、アニメーションの特定のタイミングでSEを再生したり、エフェクトを発生させる、といった制御が可能となります。
このイベントが発生した場合、"event_data" に ds_map データとして、以下のパラメータが設定されます。
例えば "Animation Event" イベントに以下のような記述をすると、イベント発生時に Output に設定したイベント情報が出力されます
show_debug_message("index: " + string(image_index)); // アニメーションフレーム数 show_debug_message("name: " + string(event_data[? "name"])); // イベント名 show_debug_message("track: " + string(event_data[? "track"])); // track数 show_debug_message("integer: " + string(event_data[? "integer"])); // 整数値 show_debug_message("float: " + string(event_data[? "float"])); // 浮動小数値 show_debug_message("string: " + string(event_data[? "string"])); // 文字列
GameMaker:StudioでのSpine再生は常にループ再生を行います。 そのため、例えば1回だけ再生を行いたい場合には、以下のような制御が必要です。
// 再生開始時の処理 image_index = 0; // 0フレーム目から再生する image_speed = 1; // 等速再生.
// 停止判定 if(image_index >= image_number-1) { // 最終フレームに達したのでアニメーション停止. image_speed = 0; }
image_indexはimage_numberと同じ値になることはないので、"image_number-1"として近似的に最終フレームかどうかの判定をしています。 上記処理は、正確には最後の1フレーム近くを省略していますので、もし気になる場合は 1フレーム多めにデータを作っておいた方が良いかもしれません。
Animation End イベントでアニメーションが終端に達したタイミングを取ることが可能です。
// アニメーション停止 image_speed = 0;
上記コードを Animation Endイベントに記述すると、1回再生が可能となります。 ただ、Animation Endイベントは Spineスプライトが登録されていないときにも有効となっているので、もし1つのオブジェクトで複数の Spineスプライトを切り替える場合は、Createイベントで "sprite_index = noone" と記述し、Animation Endイベントの記述を以下のようにします。
if(sprite_exists(sprite_index)) { // Spineスプライトが存在する場合のみ、ループ時に停止する image_speed = 0; }
sprite_index に Spineスプライトを指定すると、自動で読み込みが行われます。
sprite_index = spr_spine; // Spineスプライト "spr_spine" を割り当て
上記のように記述すると、1つのオブジェクトで、複数の Spineスプライトを扱うことができます。
実装方法はいくつかありますが、ここでは簡単に実装できるものを紹介します。
skeleton_find_slot() は指定したX/Y座標に存在するスロットのリストを取得できる関数です。これにより指定の位置にあるスロット名を取得して判定を行うことができます。ただし問題点として、この関数で取得されるスロットは画像のサイズとなり画像の透過部分も衝突範囲とみなされます。
より細かな判定をしたい場合は、skeleton_get_bounds() を使用します。この関数は Spine側で指定したバウンディングボックスの頂点情報を取得します。これにより取得した頂点情報を使って point_in_triangle() や rectangle_in_triangle() などで衝突判定を行うことができます。
ボーンの座標を書き換える場合は "Animation Update" イベントで座標を変更する必要があります。
以下はマウスの座標に向かってボーンを移動させる例です。
("xofs" と "yofs" は Createイベントであらかじめ 0 に初期化しておいたものです)
// "hand" ボーンの座標を移動させる var name = "hand"; // 基本ポーズの情報を取得する skeleton_bone_data_get(name, bone_map); var lx = bone_map[? "x"]; // ボーンのローカル座標(X)を取得 var ly = bone_map[? "y"]; // ボーンのローカル座標(Y)を取得 // アニメーションの現在の情報を取得 skeleton_bone_state_get(name, bone_map); var px = bone_map[? "worldX"]; // ワールド座標Xを取得 var py = bone_map[? "worldY"]; // ワールド座標Yを取得 var dx = mouse_x - px; // マウス座標Xへの距離を計算する var dy = mouse_y - py; // マウス座標Yへの距離を計算する if(mouse_check_button(mb_left)) { // 左クリックしていたら5%の距離を移動する xofs += dx * 0.05; // あらかじめ定義したオフセット座標Xに足し込む yofs += dy * 0.05; // あらかじめ定義したオフセット座標Yに足し込む } else { // 左クリックしていなかったら元の座標に戻る(0に近づける) xofs *= 0.97; yofs *= 0.97; } // ローカル座標を上書き. bone_map[? "x"] = lx + xofs; bone_map[? "y"] = ly - yofs; // Y軸は逆になることに注意! // アニメーションデータを上書き skeleton_bone_state_set(name, bone_map);
注意点は基本ポーズのローカル座標の情報を skeleton_bone_data_get() で取得しておき、その座標のオフセットを使ってskeleton_bone_state_set() で上書きを行うようにします。理由としてはアニメーションのローカル座標を書き換えると、ワールドの値も変化するため、基本ポーズを基準に座標を設定する必要があるからです。
また、ローカル座標Yはスクリーン座標と向きが逆になることにも注意します。