Unixtime

そういえば今までしっかり意識していなかった話……

任意の日の0時0分0秒(UTC)のunixtimeを1日の長さ(86400秒)で割ると、必ず整数になる。

2024-12-15 09:00:00(日本時間) = 2024-12-15 00:00:00(UTC) → 1734220800 / 86400 = 20072

何を当たり前のことを、と思うかもしれないが、うるう秒というものを思い出すとこれは奇妙だ。うるう秒を考えると、0時0分0秒が86400の倍数とは限らない。 この謎の答えはシンプルで、unixtime はうるう秒を無視している。
UNIX時間 - Wikipedia

おかげで00:00:00からの経過秒数や、毎時0分からの経過秒数を求めるような処理は簡単に書ける(逆に言うと、うるう秒が影響するようなシステムにunixtimeを使うときはよくよく考えないといけない)

実数値比較のユーティリティ

(Godot 3.x)

浮動小数点数を==で比較してはいけない(基本)

ということで、abs(a-b) < eps みたいなコードを書くことになるのだけど、GDScriptにはそれが用意されている。

print(is_equal_approx(0.000011, 0.000012)) # True

ベクトルやクォータニオン、行列(Transform2D)にも用意されていて気がきいている。

print( Vector2(0, 0).is_equal_approx(Vector2(0, 0.000001)) ) # True

しかしあることに気がつく。イプシロン(閾値)はいくつなの?ということ。

https://github.com/godotengine/godot/blob/607b230ffe120b2757c56bd3d52a7a0d4e502cfe/core/math/math_funcs.h#L596

 static _ALWAYS_INLINE_ bool is_equal_approx(double a, double b) {
        // Check for exact equality first, required to handle "infinity" values.
        if (a == b) {
            return true;
        }
        // Then check for approximate equality.
        double tolerance = CMP_EPSILON * abs(a);
        if (tolerance < CMP_EPSILON) {
            tolerance = CMP_EPSILON;
        }
        return abs(a - b) < tolerance;
    }

CMP_EPSILONは0.00001と定義されているので、

  • ε = a*0.00001
  • ただしε < 0.00001の場合はε = 0.00001とする

と、ε = 0.00001を基本としつつ、巨大な値を扱う場合は閾値を緩めて適応するようになっている。ただ、aの値しか見てない点には注意が必要かもしれない。

ベースクラスのメソッド呼び出し

Ver 3.xの話。

GDScriptでは、ピリオドを先頭に付けることでベースクラスのメソッドを呼び出すことができる。これはオブジェクト指向言語ではよくある話。

例: .base_method("foo")

ところがこれには例外があって、マニュアルによると

_init などのデフォルト関数と、 _enter_tree 、 _exit_tree 、 _process 、 _physics_process などのほとんどの通知は、すべての親クラスで自動的に呼び出されます。それらの関数をオーバーロードしたときに明示的に呼び出す必要はありません。

ということで、これらのケースで明示的にベースクラスのメソッドを呼んでしまうと、処理が二重に実行されるので呼んではいけない。

メソッドにより挙動が違うのは気持ち悪いな……と思っていたら4.xでは変更されたらしい。ただ4.xはまだ触っていないので詳細は機会があれば……

スプライトの縁がチカチカする

複数のスプライトを繋いだシートを使っていると、隣のチップがチラチラ見切れることがある。

Rendering → 2D → Use GPU Pixel Snap をオンにするだけで解決。 2Dゲームを作る場合は無条件オンでよさそう

shape_owner_get_shape の shape_id

(3.xの話)

疑問

Owner ID の一覧は get_shape_owners で取れる。問題はShape IDである。これはどこから取ってくるのか。0番から連番になってると見ていいのか。

↓

答え

内部のC++部分ではvectorで管理されているので、連番と見てよさそうである。Shape IDとは誤解を招く命名で、実態はShape Indexである。ひとつしかshapeがない場合は0決め打ちでいい。

https://github.com/godotengine/godot/blob/a0943acda1232ee9efd2fae9a6f3138603b95979/scene/2d/physics/collision_object_2d.h#L71

一方、Owner IDの方はMapキーなので要注意。