スクリプト(続き)

プロセシング

Godotのいくつかのアクションはコールバックまたは仮想関数によってトリガーされるため、常に実行されるようなコードを書く必要はありません。

ただし、すべてのフレームでスクリプトを処理する必要がある場合もあります。処理には、アイドル処理と物理処理の2種類があります。

メソッドNode._process()がスクリプトに存在すると、アイドル処理がアクティブになります。Node.set_process()関数を使用して、オンとオフを切り替えることができます。

このメソッドはフレームが描画されるたびに呼ばれます:

func _process(delta):
    # Do something...
    pass
public override void _Process(float delta)
{
    // Do something...
}

次の事を心に留めておく事は重要です。 _process() が呼び出される頻度は、アプリケーションが実行されている1秒あたりのフレーム数(FPS)に依存します。このレートは、時間とデバイスによって異なります。

この変動を管理しやすくするために delta パラメーターには _process() の前回の呼び出し以降の経過秒数が浮動小数点として含まれています。

このパラメータを使用すると、ゲームのFPSに関係なく、常に同じ時間がかかるようにすることができます。

たとえば、移動速度を一定にしフレームレートから独立させるために、移動量が時間デルタで乗算されることがよくあります。

_physics_process() での物理処理は似ていますが、これはキャラクタの制御など、各物理ステップの前に実行する必要がある処理に使用する必要があります。常に物理ステップの前に実行され、固定時間間隔(デフォルトでは毎秒60回)で呼び出されます。この間隔は、プロジェクト設定の Physics → Common → Physics Fps で変更できます。

一方、_process() 関数は物理処理と同期はされません。フレームレートは一定ではなく、ハードウェアとゲームの最適化に依存します。シングルスレッドのゲームでは、物理ステップの後に実行されます。

次のスクリプトを使用して、ひとつのLabelノードを持つシーンを作成すると、動作中の_process()関数を簡単に確認できます:

extends Label

var accum = 0

func _process(delta):
    accum += delta
    text = str(accum) # 'text' is a built-in label property.
public class CustomLabel : Label
{
    private float _accum;

    public override void _Process(float delta)
    {
        _accum += delta;
        Text = _accum.ToString(); // 'Text' is a built-in label property.
    }
}

これにより、各フレームが増加するカウンタが表示されます。

グループ

Godotのグループは、他のソフトウェアのタグのように機能します。ノードは、必要な数だけグループに追加できます。これは、大きなシーンを整理するのに便利な機能です。グループにノードを追加するには2つの方法があります。 1つ目はUIからで、[ノード] パネルの下の [グループ] ボタンを使います:

../../_images/groups_in_nodes.png

2番目の方法はコードからです。次のスクリプトは、シーンツリーに表示されるとすぐに現在のノードをenemiesグループに追加します。

func _ready():
    add_to_group("enemies")
public override void _Ready()
{
    base._Ready();

    AddToGroup("enemies");
}

このようにして、プレイヤーが秘密の基地に忍び込んでいることが発見された場合、SceneTree.call_group()を使用して、すべての敵にアラーム音を通知する事が出来ます:

func _on_discovered(): # This is a purely illustrative function.
    get_tree().call_group("enemies", "player_was_discovered")
public void _OnDiscovered() // This is a purely illustrative function.
{
    GetTree().CallGroup("enemies", "player_was_discovered");
}

上記のコードはグループ enemies のすべてのメンバーに対して関数 player_was_discovered を呼び出します。

またSceneTree.get_nodes_in_group()を呼び出して、 enemies ノードの完全なリストを取得することもできます:

var enemies = get_tree().get_nodes_in_group("enemies")
var enemies = GetTree().GetNodesInGroup("enemies");

SceneTreeクラスは、シーン、ノードの階層、ノードのグループとの対話など、多くの便利なメソッドを提供します。シーンを簡単に切り替えたり、リロード、ゲーム終了、一時停止、一時停止の解除もできます。そのうえ、興味深いシグナルもあります。時間があればチェックしてみてください!

通知

Godotには通知システムがあります。これらは低レベルであり、それらのほとんどに仮想関数が提供されているため、通常はスクリプトには必要ありません。それらが存在することを知っていれば良いのです。例えば、スクリプトにObject._notification() 関数を追加できます:

func _notification(what):
    match what:
        NOTIFICATION_READY:
            print("This is the same as overriding _ready()...")
        NOTIFICATION_PROCESS:
            print("This is the same as overriding _process()...")
public override void _Notification(int what)
{
    base._Notification(what);

    switch (what)
    {
        case NotificationReady:
            GD.Print("This is the same as overriding _Ready()...");
            break;
        case NotificationProcess:
            var delta = GetProcessDeltaTime();
            GD.Print("This is the same as overriding _Process()...");
            break;
    }
}

Class Referenceの各クラスのドキュメントには、受信可能な通知が記載されています。しかし、ほとんどの場合、GDScriptはより単純かつオーバーライドできる関数を提供します。

オーバーライド可能な関数

このようなオーバーライド可能な関数は、下記のような記述でノードに適用できます:

func _enter_tree():
    # When the node enters the Scene Tree, it becomes active
    # and  this function is called. Children nodes have not entered
    # the active scene yet. In general, it's better to use _ready()
    # for most cases.
    pass

func _ready():
    # This function is called after _enter_tree, but it ensures
    # that all children nodes have also entered the Scene Tree,
    # and became active.
    pass

func _exit_tree():
    # When the node exits the Scene Tree, this function is called.
    # Children nodes have all exited the Scene Tree at this point
    # and all became inactive.
    pass

func _process(delta):
    # This function is called every frame.
    pass

func _physics_process(delta):
    # This is called every physics frame.
    pass
public override void _EnterTree()
{
    // When the node enters the Scene Tree, it becomes active
    // and  this function is called. Children nodes have not entered
    // the active scene yet. In general, it's better to use _ready()
    // for most cases.
    base._EnterTree();
}

public override void _Ready()
{
    // This function is called after _enter_tree, but it ensures
    // that all children nodes have also entered the Scene Tree,
    // and became active.
    base._Ready();
}

public override void _ExitTree()
{
    // When the node exits the Scene Tree, this function is called.
    // Children nodes have all exited the Scene Tree at this point
    // and all became inactive.
    base._ExitTree();
}

public override void _Process(float delta)
{
    // This function is called every frame.
    base._Process(delta);
}

public override void _PhysicsProcess(float delta)
{
    // This is called every physics frame.
    base._PhysicsProcess(delta);
}

前述したように、通知システムの代わりにこれらの機能を使用することをお勧めします。

ノード作成

コードからノードを作成するには、他のクラスベースのデータ型と同様に、.new()メソッドを呼び出します。例えば:

var s
func _ready():
    s = Sprite.new() # Create a new sprite!
    add_child(s) # Add it as a child of this node.
private Sprite _sprite;

public override void _Ready()
{
    base._Ready();

    _sprite = new Sprite(); // Create a new sprite!
    AddChild(_sprite); // Add it as a child of this node.
}

シーンの内部または外部にあるノードを削除するには、 free() を使用する必要があります:

func _someaction():
    s.free() # Immediately removes the node from the scene and frees it.
public void _SomeAction()
{
    _sprite.Free(); // Immediately removes the node from the scene and frees it.
}

ノードが解放されると、そのすべての子ノードも解放されます。このため、ノードを手動で削除するのは見た目よりずっと簡単です。ベースノードを解放すると、サブツリー内のその他すべてのものがなくなります。

シグナルを発信している、または関数を呼び出しているノードは「ブロック状態 (blocked)」になります。こうなったノードを削除したい場合があるかもしれませんが、そうするとゲームをクラッシュさせてしまいます。デバッガ有効でGodotを実行すると、この問題をたびたび検出して警告を表示します。

ノードを削除する最も安全な方法は、Node.queue_free()を使用することです。これにより、アイドル中にノードが安全に消去されます。

func _someaction():
    s.queue_free() # Removes the node from the scene and frees it when it becomes safe to do so.
public void _SomeAction()
{
    _sprite.QueueFree(); // Removes the node from the scene and frees it when it becomes safe to do so.
}

シーンのインスタンス化

コードからシーンをインスタンス化するには、2つの手順があります。 まず最初にハードドライブからシーンをロードします:

var scene = load("res://myscene.tscn") # Will load when the script is instanced.
var scene = GD.Load<PackedScene>("res://myscene.tscn"); // Will load when the script is instanced.

プリロードは解析時に動作するため便利です(GDScriptのみ):

var scene = preload("res://myscene.tscn") # Will load when parsing the script.

しかし、 scene はまだノードではありません。PackedSceneと呼ばれる特別なリソースにパックされています。実際のノードを作成するには、PackedScene.instance()関数を呼び出す必要があります。これにより、アクティブシーンに追加できるノードのツリーが返されます:

var node = scene.instance()
add_child(node)
var node = scene.Instance();
AddChild(node);

この2段階のプロセスの利点は、パックされたシーンをロードして使用できる状態に保ち、必要なだけインスタンスを作成できることです。 これは、アクティブシーン内の複数の敵、弾丸、その他の存在をすばやくインスタンス化するのに特に便利です。

スクリプトをクラスとして登録する

Godotには、エディタに個々のスクリプトを登録するための「スクリプトクラス」機能があります。デフォルトでは、ファイルを直接ロードすることでのみ名前のないスクリプトにアクセスできます。

class_name キーワードの後にクラスの名前を付けた後に、スクリプトに名前を付け、エディタにタイプとして登録できます。カンマを追加した後にアイコンとして使用する画像のパスを追加できます。これで、ノードまたはリソース作成ダイアログで新しいタイプを見つける事ができます。

extends Node

# Declare the class name here
class_name ScriptName, "res://path/to/optional/icon.svg"

func _ready():
    var this = ScriptName           # reference to the script
    var cppNode = MyCppNode.new()   # new instance of a class named MyCppNode

    cppNode.queue_free()
../../_images/script_class_nativescript_example.png

警告

Godot 3.1の場合:

  • スクリプトを登録できるのは、GDScriptおよびNativeScript、つまりC++およびその他のGDNativeを使用した言語のみです。
  • GDScriptのみが、名前付きスクリプトごとにグローバル変数を作成します。