スキップしてメイン コンテンツに移動

GDScript

Godotの公式サイトにあるドキュメントのGDScriptのページを訳してみました。

紹介

GDScriptは、コンテンツを作成するために使用される高度に動的に型付けされたプログラミング言語です。 文法はPythonと似ています。(インデントによるブロックや多くのキーワードが同じです。 その目的は、Godotエンジンに最適化され、緊密に統合され、コンテンツの作成と統合に大きな柔軟性を提供することです。

歴史

初期の段階では、このエンジンは Lua スクリプト言語を使用していました。 Luaは高速ですが、フォールバックを使用してオブジェクト指向システムへバインドすることは複雑で遅く、膨大な量のコードを必要とします。 いくつかの実験の後、 Python も埋め込みが難しいことが判明しました。 出荷されたゲームに使用された最後のサードパーティスクリプト言語は Squirrel でしたが、それも脱落しました。 この時点で、Godotの特定のアーキテクチャをより最適に利用できるのは独自のスクリプト言語であることが明らかになりました。

  • Godotはスクリプトをノードに埋め込みます。 ほとんどの言語はこれを念頭に置いて設計されていません。
  • Godotは、2Dおよび3D計算にいくつかの組込みデータ型を使用します。他のスクリプト言語ではこれが提供されず、それらをバインドすることは非効率的です。
  • Godotは、ネットやディスクからデータを取り込んだり初期化するためにスレッドを頻繁に使いますが、一般的なスクリプト言語のインタプリタでは容易ではありません。
  • Godotはすでにリソースのためのメモリ管理モデルを持っており、ほとんどのスクリプト言語は独自のものを提供しているため、重複した作業やバグを招きます。
  • バインディングコードは常に乱雑で障害の温床となり、予期しないバグを招き、一般的にメンテナンス性が低くなります。

これらの考慮の末、GDScriptに落ち着きました。GDScriptの言語とインタプリタは、同等の機能を備えたLuaやSquirrelのバインディングコード自体よりも小さくなりました。 時間の経過とともに、組み込み言語を使うことは大きな利点であることが証明されました。

GDScriptの例

実際の使われ方を見た方が分かりやすいでしょうから簡単な例を示します。

# ファイルがクラス!

# 継承

extends BaseClass

# メンバ変数

var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key":"value", 2:3}

# 定数

const answer = 42
const thename = "Charly"

# 列挙型

enum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
enum Named {THING_1, THING_2, ANOTHER_THING = -1}

# 組み込みのベクトル型

var v2 = Vector2(1, 2)
var v3 = Vector3(1, 2, 3)

# 関数

func some_function(param1, param2):
    var local_var = 5

    if param1 < local_var:
        print(param1)
    elif param2 > 5:
        print(param2)
    else:
        print("失敗!")

    for i in range(20):
        print(i)

    while(param2 != 0):
        param2 -= 1

    var local_var2 = param1+3
    return local_var2


# 内部クラス

class Something:
    var a = 10

# コンストラクタ

func _init():
    print("生成されました!")
    var lv = Something.new()
    print(lv.a)

もし以前に、C、C++、C#といった静的型付け言語の経験はあるけど動的型付け言語の経験が無い場合は、この チュートリアル: GDScript : 動的言語の紹介 (未訳)をお読みください。

言語

以下に、GDScriptの概要を示します。どのメソッドが配列やその他のオブジェクトに利用できるか等の詳細は、クラス詳細のリンクから参照してください。

識別子

識別子となる文字列は、アルファベット文字(a〜z、A〜Z)、数字(0〜9)、_のみ使用できます。識別子は数字で始めることはできません。 識別子は大文字小文字を区別します(fooはFOOとは異なります)。

キーワード

言語でサポートされているキーワードのリストは次の通りです。キーワードは予約語(トークン)なので、識別子として使用することはできません。

キーワード 説明
if if/else/elif を参照。
elif if/else/elif を参照。
else if/else/elif を参照。
for for を参照。
do 将来の do … while ループの実装用に予約。
while while を参照。
match match を参照。
switch 将来の実装用に予約。
case 将来の実装用に予約。
break 現在の forwhile ループを抜ける。
continue forwhile ループの次の繰り返しに直ちにスキップ。
pass 構文的にステートメントが必要な場所でコードの実行が不要な場合に使用。例えば空の関数に。
return 関数から値を返す。
class クラスを定義。
extends クラスの継承先を指定する。
is 変数が、指定したクラスを継承したものか検査する。
tool エディタからスクリプトを実行。
signal シグナルを定義。
func 関数を定義。
static 静的関数を定義。静的メンバ変数は不可。
const 定数を定義。
enum 列挙型を定義
var 変数を定義
onready スクリプトがアタッチされたノードとその子がシーンツリーの一部となった時に変数を初期化。
export アタッチされたリソースと共に変数を保存し、エディタから参照したり変更したりできるようにする。
setget 変数のセッター、ゲッターを定義
breakpoint デバッガブレークポイントのエディタヘルパ。

演算子

サポートされている演算子と優先順位のリストは次の通りです。(TODO, これはPythonの演算子を反映するために作られたものなので変更が必要)

演算子 説明
x[index] 添字。最優先
x.attribute 属性を参照
is 型の検査
~ 否定ビット演算
-x 負数
* / % 乗算 / 除算 / 剰余
  注記:これらの演算結果は、オペランドの型によって異なります。 両方がIntegerの場合、結果はIntegerになります。 つまり、1/10は0.1ではなく0を返します。 オペランドの少なくとも1つが浮動小数点数の場合、float:float(1)/ 10または1.0 / 10は両方とも0.1を返します。
+ - 加算 / 減算
>> << ビットシフト
& ビット積
^ ビット排他論理和
| ビット和
< > = ! >= <= 比較
in コンテンツのテスト
! not ブール NOT
and && ブール AND
or || ブール OR
if x else 3項 if/else
= += -= *= /= %= &= |= 代入 最低優先度

リテラル

リテラル
45 10進数の整数
0x8F51 16進数の整数
3.14, 581.e-10 浮動小数点数
"Hello", "Hi" 文字列
"""Hello, Dude""" 複数行文字列
@"Node/Label" ノードパスと文字列名

コメント

から行末までは無視され、コメントとみなされます。

# これはコメントです。

組み込み型

  • 基本的な組み込み型 GDScriptの変数は、いくつかの組み込み型に割り当てることができます。
    null
    null は、情報を含まない空のデータ型で、他の値を代入することはできません。
    bool
    ブール型のデータ型には、 true 又は false しか含めることはできません。
    int
    整数データ型には、整数値(負数と正数の両方)のみを含めることができます。
    float
    浮動小数点値(実数)を格納するために使用されます。
    string
    Unicode形式 の文字列。 文字列には、標準のCエスケープシーケンス を含めることができます。 GDScriptは、いわゆる printf形式のフォーマット文字列 をサポートしています。
  • ベクトル組み込み型
    Vector2
    x および y フィールドを含む2Dベクトル型。 読みやすさのためにフィールドを width と height としてアクセスすることもできます。 また配列としてもアクセスできます。
    Rect2
    2つのベクトルフィールド positionsize を含む2D Rectangle型。 代わりに、 position + sizeend フィールドも含みます。
    Vector3
    xyz フィールドを含む3Dベクタトル型。 これは配列としてアクセスすることもできます。
    Transform2D
    2D変換に使用される3×2行列。
    Plane
    normal (法線)ベクトルフィールドと d スカラー距離を含む正規化された形式の3D平面タイプ。
    Quat
    クォータニオンは、3D回転を表現するために使用されるデータ型です。 回転を補間するのに便利です。
    AABB
    座標軸に平行な境界ボックス(または3Dボックス)には、2つのベクトルフィールド positionsize があります。 代わりに、 position + sizeend フィールドも含みます。 このタイプのエイリアスとして、Rect3は互換的に使用できます。
    Basis
    3D回転と拡縮に使用される3x3マトリックス。 3つのベクトルフィールド( xyz )を含み、3Dベクトルの配列としてアクセスすることもできます。
    Transform
    3D変換型には、Matrix3フィールドの basis と、Vector3フィールドの origin が含まれます。
  • エンジンの組み込み型
    Color
    カラーデータ型には、 rgb 、および a フィールドが含まれます。また、色相(hue) / 彩度(saturation) / 明度(value)の hsv としてアクセスすることもできます。
    NodePath
    主にシーンシステムで使用されるコンパイルされたノードのパス。文字列と容易に相互変換できます。
    RID
    リソースID(RID)。サーバーは、一般的なRIDを使用して不透明な(opaque)データを参照します。
    Object
    組み込み型ではないものすべての基本クラス。
  • コンテナの組み込み型
    配列
    他の配列やディクショナリ(下記参照)を含む任意のオブジェクト型の一般的なシーケンスです。配列は動的にサイズ変更できます。配列のインデックスは0から始まります。 Godot 2.1より、Pythonのように配列の最後から数える負のインデックスも使えます。
    var arr=[]
    arr=[1, 2, 3]
    var b = arr[1]            # 結果は2です。
    var c = arr[arr.size()-1] # 結果は3です。
    var d = arr[-1]           # 上の行と同じ結果を簡潔に書けます。
    arr[0] = "Hi"             # 1を"Hi"に変更します。
    arr.append(4)             # 配列は["Hi", 2, 3, 4]となりました。
    

    GDScript配列は、スピードのためにメモリ内でリニアに割り当てられます。しかし、非常に大きな配列(数万個以上の要素)はメモリの断片化を生じる可能性があります。これが問題であれば、特別なタイプの配列を利用できます。これらは単一のデータ型のみ受け入れます。メモリの断片化を避け、メモリも少なくてすみますが、アトミックであり、汎用配列よりも低速で実行される傾向があります。したがって、非常に大きなデータセットにのみ使用することをお勧めします。

    PoolByteArrayeArray
    バイト配列(0から255までの整数)
    PoolIntArray
    Integerの配列
    PoolRealArray
    floatの配列
    PoolStringArray
    Stringの配列
    PoolVector2Array
    Vector2 の配列
    PoolVector3Array
    Vector3 の配列
    PoolColorArray
    Color の配列
    ディクショナリ
    固有のキーによって参照される値を含む連想型コンテナ。
    var d={4:5, "a key":"a value", 28:[1,2,3]}
    d["Hi!"] = 0
    var d = {
        22         : "Value",
        "somekey"  : 2,
        "otherkey" : [2,3,4],
        "morekey"  : "Hello"
    }
    

    Luaスタイルのテーブル構文もサポートされています。Luaスタイルでは、 : の代わりに = を使用し、文字列のキーに引用符は不要です。(記述量が多少減ります)但し、GDScriptの記述子と同様に、この形式で書かれたキーは数字で始めることはできません。

    var d = {
        test22 = "Value",
        somekey = 2,
        otherkey = [2,3,4],
        morekey = "Hello"
    }
    

    既存のディクショナリにキーを追加するには、キーが存在する場合と同様にアクセスし値を割り当てます。

    var d = {} # 空のディクショナリを生成
    d.Waiting = 14 # 文字列の"Waiting"をキーとして追加し、それに14を割り当て
    d[4] = "hello" # 数値の4をキーとして追加し、それに文字列の"hello"を割り当て
    d["Godot"] = 3.01 # 文字列の"Godot"をキーとして追加し、それに3.01を割り当て
    

データ

変数

変数は、クラスメンバまたは関数のローカルとして存在できます。 var キーワードを使って作成し、必要なら初期値を割り当てることもできます。

var a  # データ型はデフォルトでnull
var b = 5
var c = 3.8
var d = b + c  # 変数は常に順番に初期化されます。

定数

constは変数と似ていますが、定数、または定数式でなければならず、初期化時に割り当てる必要があります。

const a = 5
const b = Vector2(20, 20)
const c = 10 + 20 # 定数式
const d = Vector2(20, 30).x  # 定数式: 20
const e = [1, 2, 3, 4][0]  # 定数式: 1
const f = sin(20)  # 定数式の中で sin() を使用可
const g = x + 20  # 無効、これは定数式ではない!

列挙型

enumは基本的には定数の短縮形であり、連続する数値を定数に割り当てたい場合に非常に便利です。 enumに名前を渡すと、その名前の定数ディクショナリ内に全ての値も格納されます。

enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
# これは下と同義
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3

enum State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT}
# これは下と同義
const STATE_IDLE = 0
const STATE_JUMP = 5
const STATE_SHOOT = 6
const State = {STATE_IDLE = 0, STATE_JUMP = 5, STATE_SHOOT = 6}

関数

関数は常にクラスに属します。変数を探すスコープの優先順位は、ローカル → クラスメンバー → グローバル です。*self* 変数が常に利用可能であり、クラスメンバにアクセスするためのオプションとして提供されますが、必ずしも必要というわけではありません。(Pythonとは異なり、関数の最初の引数にselfを渡さないでください。)

func myfunction(a, b):
    print(a)
    print(b)
    return a + b  # returnは省略可能。無い場合はnullが返される。

どの時点でも return が可能です。デフォルトの返り値は null です。

関数の参照

基本クラスの関数(つまり、現在のクラスが継承したクラスの関数)を呼び出すには、関数名の前に . を付けます。

.basefunc(args)

Pythonとは異なり、GDScriptの関数は第一級オブジェクトではありません。つまり、関数を変数に格納したり、別の関数に引数として渡したり、他の関数の返り値とすることはできません。これはパフォーマンス上の理由によるものです。 実行時に名前で関数を参照するには(たとえば、変数に格納するか、別の関数に引数として渡すなど)、 call または funcref ヘルパーを使用する必要があります。

# 1ステップで関数を名前で呼び出す。
mynode.call("myfunction", args)

# 関数リファレンスを格納する。
var myfunc = funcref(mynode, "myfunction")
# 格納された関数リファレンスを呼び出す。
myfunc.call_func(args)

_init のようなデフォルト関数、 _enter_tree_exit_tree_process_fixed_process などのほとんどの通知は、全ての基本クラスで自動的に呼び出されることに注意してください。 従って、オーバーロードする必要がある時だけそれらの関数を明示的に呼び出してください。

静的関数

関数はスタティックとして宣言できます。スタティックな関数はインスタンス変数や self にはアクセスできません。主にライブラリやヘルパー関数を作成する時に便利です。

static func sum2(a, b):
    return a + b;

ステートメントと制御フロー

ステートメントは標準的なもので、代入、関数呼び出し、制御フロー構造等があります。(下記参照) ステートメントのセパレータである ; は必須ではありません。

if/else/elif
単純な条件は、 if / else / elif 構文を使用して作成されます。 条件はカッコで囲むこともできますが必須ではありません。 タブベースのインデントの性質を考慮して、インデントのレベルを維持するために else / if の代わりに elif を使用することができます。
if [expression]:
    statement(s)
elif [expression]:
    statement(s)
else:
    statement(s)

短いステートメントは条件と同じ行に書けます。

if 1 + 1 == 2: return 2 + 2
else:
    var x = 3 + 3
    return x

ブール式に基づいて異なる初期値を割り当てたい場合は三項式が便利です。

var x = [true-value] if [expression] else [false-value]
y += 3 if y < 10 else -1
while
単純なループは while で作成できます。 break でループを抜けるか、 continue でループを継続できます。
while [expression]:
    statement(s)
for
配列やテーブルなどの範囲を反復処理するには、forループが使用されます。配列を反復処理するとき、現在の配列要素はループ変数に格納されます。ディクショナリを反復するときは、インデックスがループ変数に格納されます。
for x in [5, 7, 11]:
    statement # 3回反復。xはまず5になり、次に7 最後に11となる。

var dict = {"a": 0, "b": 1, "c": 2}
for i in dict:
    print(dict[i])

for i in range(3):
    statement # [0, 1, 2] と同様だが配列は割り当てられない。

for i in range(1,3):
    statement # [1, 2] と同様だが配列は割り当てられない。

for i in range(2,8,2):
    statement # [2, 4, 6] と同様だが配列は割り当てられない。

for c in "Hello":
    print(c) # 文字列内の全ての文字を繰返し。各文字が行毎に出力される。
match
match 文は、プログラムの実行を分岐するために使用されます。これは他の多くの言語で見られる switch 文と同等ですが、いくつかの追加機能があります。

基本構文:

match [expression]:
    [pattern](s):
        [block]
    [pattern](s):
        [block]
    [pattern](s):
        [block]

switch文に慣れている人のための速習コース

  1. switchmatch に置き換えます。
  2. case を取り除きます。
  3. break を取り除きます。もしデフォルトで抜けたくない場合は、 continue で継続できます。
  4. default を単一のアンダースコアに変更します。

制御フロー:

パターンは上から下に検査されます。パターンが一致すると対応するブロックが実行されます。その後制御は、match文の次に移ります。下のパターンの検査へ進めたい場合は、continueを使います。

6種類のパターンがあります。

定数パターン
数字や文字列などの定数プリミティブ
match x:
    1:
        print("我々が一番!")
    2:
        print("1より2の方が良い!")
    "test":
        print("うわっ、文字列だ!")
変数パターン
変数/列挙型の内容と一致します。
match typeof(x):
    TYPE_FLOAT:
        print("float")
    TYPE_STRING:
        print("text")
    TYPE_ARRAY:
        print("array")
ワイルドカードパターン
このパターンはすべてのものと一致します。 単一のアンダースコアとして書かれます。 他の言語の switch 文で使われる default と同等です。
match x:
    1:
        print("1です。")
    2:
        print("1の2倍!")
    _:
        print("1でも2でもないけど気にしない。")
結合パターン
結合パターンは新しい変数を導入します。 ワイルドカードパターンと同様にすべてのものと一致し、且つその値に名前を与えます。配列パターンやディクショナリパターンで特に有用です。
match x:
    1:
        print("1です。")
    2:
        print("1の2倍!")
    var new_var:
        print("1でも2でもなく、それは、", new_var)
配列パターン
配列と一致します。配列の個々の要素もパターンとなりネストが可能です。 配列の要素数が最初に比較され、パターンの要素数と同じでなければ一致しません。

拡張可能配列:最後のサブパターンに 「..」を指定することでパターンより大きな配列にマッチさせることができます。

サブパターンはカンマで区切られます。

match x:
    []:
        print("空の配列")
    [1, 3, "test", null]:
        print("特殊な配列")
    [var start, _, "test"]:
        print("最初の要素は、", start, ", そして最後の要素は、\"test\" です。")
    [42, ..]:
        print("拡張可能配列")
ディクショナリパターン
配列パターンと同様に機能します。各キーは定数パターンとなります。 ディクショナリの要素数が最初に比較され、パターンの要素数と同じでなければ一致しません。

拡張可能ディクショナリ:最後のサブパターンに 「..」を指定することでパターンより大きなディクショナリにマッチさせることができます。

サブパターンはカンマで区切られます。

値を指定しなければキーの存在のみがチェックされます。

キーパターンと値パターンは、「:」で区切られます。

match x:
    {}:
        print("空のディクショナリ")
    {"name": "デニス"}:
        print("名前は、デニスです。")
    {"name": "デニス", "age": var age}:
        print("デニスは、", age, " 歳です。")
    {"name", "age"}:
        print("名前と年齢がありますが、デニスではありません。:(")
    {"key": "godotisawesome", ..}:
        print("要素を1つだけチェックし、残りは無視します。")
マルチパターン
カンマで区切って複数のパターンを指定できます。これらを変数に割り当てることはできません。
match x:
    1, 2, 3:
        print("1〜3です")
    "sword", "splashpotion", "fist":  " 剣、降りかかるポーション、拳
        print("あぁ、ダメージを受けた")

クラス

デフォルトでは、スクリプトファイルの本体は無名のクラスであり、リソースまたはファイルとしてのみ外部から参照することができます。 クラス構文は非常にコンパクトであり、メンバ変数または関数のみを含むことができます。 スタティック関数は使用できますが、スタティックメンバは使用できません。(これは、スレッドの安全性の精神に基づいており、ユーザが知ることなしにスクリプトを別のスレッドで初期化できるためです)。 同様に、メンバ変数(配列や辞書を含む)は、インスタンスが作成されるたびに初期化されます。

次はクラスファイルの例です。

# ファイル名 myclass.gd として保存

var a = 5

func print_value_of_a():
    print(a)
継承
クラス(ファイルとして保存される)は、以下を継承できます。
  • グローバルクラス
  • 別のクラスファイル
  • 別のクラスファイルの内部クラス

多重継承はできません。

継承するには、 extends キーワードを使います。

# グローバルなクラスを継承
extends SomeClass

# クラスファイルを継承
extends "somefile.gd"

# 別のクラスファイルの内部クラスを継承
extends "somefile.gd".SomeInnerClass

インスタンスが、あるクラスを継承しているかチェックするには、 is キーワードを使います。

# enemy クラスの先読み
const enemy_class = preload("enemy.gd")

# [...]

# 'is' を使って継承を確認
if (entity is enemy_class):
     entity.apply_damage()
コンストラクタ
クラスの初期化時に呼ばれるコンストラクタは、 _init と名付けられています。継承したクラスの親クラスのコンストラクタは自動的に呼ばれます。明示的に、 ._init() を呼ぶ必要はありません。

親クラスのコンストラクタが引数をとる場合は、次のように渡されます。

func _init(args).(parent_args):
    pass
内部クラス
クラスファイルには内部クラスを含めることができます。内部クラスは、 class キーワードを使って定義できます。インスタンスを生成するには、 ClassName.new() 関数を使用します。
# クラスファイル内

# このクラスファイルの内部クラス
class SomeInnerClass:
    var a = 5
    func print_value_of_a():
        print(a)

# このクラスファイルのメインクラスのコンストラクタ
func _init():
    var c = SomeInnerClass.new()
    c.print_value_of_a()
リソースとしてのクラス
ファイルに保存されたクラスはリソースとして扱われます。 他のクラス内でアクセスするには、ディスクからロードする必要があります。 これは、 load または preload 関数を使用してできます。(下記参照) ロードされたクラスリソースのインスタンス化は、クラスオブジェクトに対して new 関数を呼び出すことでできます。
# load() を呼ぶとクラスリソースが読み込まれる。
var MyClass = load("myclass.gd")

# コンパイル時に一度だけ事前読み込みする。
var MyClass2 = preload("myclass.gd")

func _init():
    var a = MyClass.new()
    a.somefunction()

エクスポート

クラスのメンバはエクスポートできます。これは、それがアタッチされているリソース(つまり シーン )と一緒に値が保存されるということを意味します。また、プロパティエディタで編集することもできます。エクスポートするには export キーワードを使用します。

extends Button

export var number = 5 # この値は保存されプロパティエディタで編集も可能

エクスポートする変数は定数式で初期化するか、 export キーワードへの引数の形でエクスポートヒントを持たせる必要があります。(下記参照) メンバ変数をエクスポートする基本的な利点の1つは、エディタで変数を表示したり編集できるようになることです。これにより、アーティストやゲームデザイナが後でプログラムの実行に影響を与える値を変更できます。そのために特殊なエクスポート構文が提供されています。

# エクスポートする変数に定数や定数式を割り当てると推論された型がエディタで使用される

export var number = 5

# 引数に基本データ型を与えるとそれがエディタで使用される

export(int) var number

# ヒントとしてリソースタイプを指定することもできる

export(Texture) var character_face
export(PackedScene) var scene_file

# 整数と文字列で列挙型のヒントを与える

# エディタは、0, 1 2を列挙する
export(int, "Warrior", "Magician", "Thief") var character_class
# エディタは文字列名を列挙する
export(String, "Rebecca", "Mary", "Leah") var character_name

# パス文字列

# 文字列はファイルへのパスとなる
export(String, FILE) var f
# 文字列はディレクトリへのパスとなる
export(String, DIR) var f
# ヒントとしてフィルタされた文字列がファイルへのパスとなる
export(String, FILE, "*.txt") var f

# グローバルファイルシステムへのパスも使用可能
# 但し、ツールスクリプトでのみ (後で説明)

# 文字列はグローバルファイルシステムのPNGファイルへのパスとなる
export(String, FILE, GLOBAL, "*.png") var tool_image
# 文字列はグローバルファイルシステムのディレクトリーへのパスとなる
export(String, DIR, GLOBAL) var tool_dir

# MULTILINE を指定すると、エディタに複数行を編集するための大きな入力フィールドが表示される
export(String, MULTILINE) var text

# 入力範囲を制限する

# 0から20までの整数を許可
export(int, 20) var i
# -10から20までの整数を許可
export(int, -10, 20) var j
# -10から20までの0.2刻みの浮動小数点数を許可
export(float, -10, 20, 0.2) var k
# yの値が100から1000まで20ずつ変化する y = exp(x) の浮動小数点数を許可
# エディタには入力に便利なスライダーが表示される
export(float, EXP, 100, 1000, 20) var l

# イージングのヒント付きの浮動小数点数

# 編集時に *eash()* 関数の視覚的表現を表示する
export(float, EASE) var transition_speed

# 色

# 赤-緑-青の値で色を指定
export(Color, RGB) var col # RGB形式
# 赤-緑-青-透明度の値で色を指定
export(Color, RGBA) var col # RGBA形式

# シーン内の別のノードもエクスポートできる


export(NodePath) var node

エディタでスクリプトが実行されていなくても、エクスポートされたプロパティは編集可能です。(以下の「ツール」を参照)

ビットフラグのエクスポート
ビットフラグとして使用される整数は、1つのプロパティに複数の真偽値を格納できます。 エクスポートヒントの int、FLAGS を使用すると、エディタからこれらを設定できます。
# 整数値のビットを個別に編集
export(int, FLAGS) var spell_elements = ELEMENT_WIND | ELEMENT_WATER

特定の名前付きフラグに制限することも可能です。書き方は列挙型構文によく似ています。

# 特定のフラグをエディタから指定
export(int, FLAGS, "Fire", "Water", "Earth", "Wind") var spell_elements = 0

この例では、 Fire の値は1で、 Water は2、 Earth は4、 Wind は8となります。通常、定数はそれに応じて定義する必要があります。(例: const ELEMENT_WIND = 8 など)

ビットフラグを使用するには、ビット単位の操作を理解する必要があります。不確かなら代わりにブーリアン変数をエクスポートした方が良いでしょう。

配列のエクスポート
配列をエクスポートすることもできますが、重要な注意点があります。通常の配列は全てのクラスインスタンスに対してローカルに作成されますが、エクスポートされた配列は全てのインスタンスで共有されます。つまり、1つのインスタンスでそれらを編集すると、他の全てのインスタンスでそれらを変更することになります。エクスポートする配列は初期化することができますが、定数式でなければなりません。
# エクスポートされた配列は全てのインスタンスで共有される。
# 初期化には定数式を使う

export var a=[1,2,3]

# 型付き配列も可能。空の初期値のみ可。

export var vector3s = PoolVector3Array()
export var strings = PoolStringArray()

# インスタンス毎にローカルに生成される通常の配列
# 実行時の値で初期化できるが、エクスポートはできない。

var b = [a,2,3]

セッター/ゲッター

何らかの理由でメンバ変数がいつ変化したかを知ることが役に立つことがよくあります。また、変数のアクセスをカプセル化することが望ましい場合もあります。

このため、 GDScript では、 setget を使ったセッター/ゲッター構文が提供されます。これは変数宣言の直後に使用されます。

var variable = value setget setterfunc, getterfunc

変数の値が外部ソースによって変更されると(つまり、クラスのローカルな使用ではなく)、setter関数(上記のsetterfunc)が呼び出されます。これは、値が変更される前に発生します。 セッターは新しい値をどうするかを決める必要があります。 逆に、変数にアクセスすると、getter関数(上記のgetterfunc)は望ましい値を返す必要があります。 以下はその例です:

var myvar setget myvar_set,myvar_get

func myvar_set(newvalue):
    myvar=newvalue

func myvar_get():
    return myvar # ゲッターは値を返す必要がある

   セッターもゲッターも省略が可能です。

# セッターのみ
var myvar = 5 setget myvar_set
# ゲッターのみ (カンマに注意)
var myvar = 5 setget ,myvar_get

ゲッター/セッターは、入力を検証するためにツールスクリプトやプラグインでエディターに変数をエクスポートするときに特に便利です。

既に述べたいように、ローカルアクセスではセッター、ゲッターは起動されません。 例を示します。

func _init():
    # セッター/ゲッターは呼ばれない
    myinteger = 5
    print(myinteger)

    # セッター/ゲッターが呼ばれる
    self.myinteger = 5
    print(self.myinteger)

ツールモード

デフォルトでは、スクリプトはエディタ内で実行されず、エクスポートされたプロパティだけを変更することができます。時には、それらがエディタの内部で実行されることが望ましい場合がありますます(それらがゲームコードを実行しないか手動で実行しない限り)。 このため、 tool キーワードがあり、ファイルの先頭に記述します。

tool
extends Button

func _ready():
    print("Hello")

メモリ管理

もしクラスが、 Reference を継承するなら、使用されなくなった時点で開放されます。ガベージコレクタは存在しません。単純な参照カウントです。 デフォルトでは、継承を定義しないすべてのクラスは、 Reference を継承します。 これが望ましくない場合、クラスは手動で Object を継承し、 instance.free() を呼び出さなければなりません。 開放されない循環参照を避けるため、弱参照を作成するための weakref関数 が用意されています。

シグナル

インスタンス内で何かが起こったということを通知したいことがしばしばあります。 GDScriptは組み込みのGodotシグナルの作成をサポートしています。 signal キーワードを使用すると、 GDScriptのシグナルを簡単に宣言できます。

# 引数無し
signal your_signal_name
# 引数有り
signal your_signal_name_with_args(a,b)

これらのシグナルは、通常のシグナルと同様に、エディタまたはコードで接続することができます。 シグナルが宣言されたクラスのインスタンスを取得し、それを別のインスタンスのメソッドに接続するだけです。

func _callback_no_args():
    print("コールバックが呼ばれた!")

func _callback_args(a,b):
    print("引数a: ", a, " と b: ", b, "と共にコールバックが呼ばれた!")

func _at_some_func():
    instance.connect("your_signal_name", self, "_callback_no_args")
    instance.connect("your_signal_name_with_args", self, "_callback_args")

引数の欠けているシグナルにカスタム値を与えることもできます。

func _at_some_func():
    instance.connect("your_signal_name", self, "_callback_args", [22, "hello"])

これは、多くのオブジェクトからのシグナルが1つのコールバックに接続され、送信者を識別する必要がある場合に非常に便利です。

func _button_pressed(which):
    print("ボタンが押された: ", which.get_name())

func _ready():
    for b in get_node("buttons").get_children():
        b.connect("pressed", self, "_button_pressed",[b])

最後に、独自のシグナルを送出するには、 Object.emit_signal メソッドを使用します。

func _at_some_func():
    emit_signal("your_signal_name")
    emit_signal("your_signal_name_with_args", 55, 128)
    someinstance.emit_signal("somesignal")

コルーチン

GDScriptは、 yield 組み込み関数を介してコルーチンをサポートしています。 yield() を呼び出すと、関数のその時点の凍結した状態を返り値として直ぐにリターンします。この返り値の resume を呼ぶと、処理が続行され、関数の本来の返り値が戻されます。 一旦再開されるとオブジェクトの状態は無効となります。次に例を示します。

func myfunc():
   print("hello")
   yield()
   print("world")

func _ready():
    var y = myfunc()
    # 関数の状態は 'y' に保存された
    print("my dear")
    y.resume()
    # 'y' は再開され今は無効な状態

出力結果は

hello
my dear
world

yield() と resume() の間で値を渡すことも可能です。例えば、

func myfunc():
   print("hello")
   print(yield())
   return "cheers!"

func _ready():
    var y = myfunc()
    # 関数の状態は 'Y' に保存された
    print(y.resume("world"))
    # 'y' は再開され、今は無効な状態

出力結果は

hello
world
cheers!
コルーチンとシグナル
yield を使う真の強みはシグナルと組み合わせた時です。yieldは、オブジェクトとシグナルの2つのパラメータを受け取ることができます。シグナルが受信されると実行を再開します。幾つかの例を示します。
# 次のフレームから再開
yield(get_tree(), "idle_frame")

# アニメーションの終了時に再開
yield(get_node("AnimationPlayer"), "finished")

# 5秒間待機した後再開
yield(get_tree().create_timer(5.0), "timeout")
Onready キーワード
ノードを使用する時は、シーンの一部への参照を変数に保存することがとても一般的です。シーンはアクティブシーンツリーに組み込まれて初めて設定が可能となるので、そのサブノードは、Node._ready() が呼ばれて初めて取得可能となります。
var mylabel

func _ready():
    mylabel = get_node("MyLabel")

これは、特にノードと外部参照が積み重なったときには少し面倒です。 このため、GDScriptにはonreadyキーワードがあり、これは _ready が呼び出されるまでメンバー変数の初期化を遅延させます。 上記のコードを1行で置き換えることができます:

onready var mylabel = get_node("MyLabel")

コメント

このブログの人気の投稿

UIコントロールを日本語化する

GUIコントロールを日本語化する Godotには、ユーザインターフェースのための様々なUIコントロールが含まれていますが、これらは標準の状態では日本語に対応していません。 例えば、画面にテキストを表示するための、ラベルというコントロールがありますが、これの Text プロパティに日本語文字を設定しても表示されません。 日本語を扱えるようにするためには、日本語のフォントを使うように設定する必要があります。 日本語のフォントは、Godot 自体には含まれていないので自分で用意する必要があります。 ここでは、Google から提供されている、 Google Noto Fonts を使ってみます。 Noto Fonts のサイトの検索窓に、「cjk jp」と入力すると、日本語用のフォントが表示されるので、これを選んでダウンロードします。 今回は、「Noto Sans CJK JP」をダウンロードしました。 ダウンロードしたファイルを解凍すると、様々な太さの OpenTypeFont ファイル(拡張子が otf のファイル)が入っていますが、ここでは、NotoSansCJKjp-Regular.otf を使います。 選んだフォントファイルを Godot プロジェクトのフォルダにコピーしたら、次は Godot で設定します。 日本語化したいコントロールのインスペクタで、「Custome Fonts」をクリックし、「New DynamicFont」を選択し、次に「編集」を選択します。 次に、DynamicFont の編集画面で、「Font Data」の「Load」を選択し、先ほどフォルダーへコピーしたフォントファイルを選択します。 フォントが設定できたら、サイズも適当な大きさに変えておきましょう。サイズが 0 のままだと文字が見えません。 以上で日本語フォントが設定できたので、ラベルのテキストも日本語が表示されているはずです。 ラベルだけじゃなく、ボタンやテキストエディット等、他のUIコントロールも同じ手順で日本語化できます。

メッシュ変形アニメーション

Godotにメッシュ変形アニメーションが組み込まれるらしい。 https://godotengine.org/article/godot-gets-2d-skeletal-deform これまで簡単なボーンアニメーションは組み込まれていたんですが、メッシュ変形はしてくれませんでした。今回のはボーンの動きに合わせて2Dメッシュも変形させられます。 これで外部アニメーションツールとか使わないでも表現力を格段にアップできそう。 どんどん進化していくね。