Imaginantia

思ったことを書きます

バグレポート / PassageTeleport

バグのせいです。

 

6/9 解決済み: 末尾に追記

こんばんはお疲れさまです。

この度多大なる迷惑を現在も継続しておかけしておりますあの入り口について、現状どのような感じかお伝えしようと思います。

仕組み

PassageTeleportというUdonがあの機構全体を管理しています。基本的にはdisableの状態です。

bool open; // 開こうとしてるときのみ true
float openTime; // 開き始めてからの時間
bool closing; // 閉じる遷移のときのみ true
float closeTime; // 閉じ始めてからの時間
bool sleep; // テレポート直後は一度区画から出るまで処理を貫通

public void Update() {
    if (player == null) return;
    Vector3 playerPos = player.GetPosition();
    if (open) {
        openTime += Time.deltaTime * 0.5f;
        if (side0) {
            // (ドア0のアニメーション)
            if (insideClose0.Contains(playerPos) && !sleep) {
                open = false;
                closing = true;
                closeTime = 0;
            } else {
                if ((openTime > 5.0f || sleep) && !outsideClose0.Contains(playerPos)) {
                    open = false;
                    closing = true;
                    closeTime = 0;
                }
            }
        } else {
            // (ドア1のアニメーション)
            if (insideClose1.Contains(playerPos) && !sleep) {
                open = false;
                closing = true;
                closeTime = 0;
            } else {
                if ((openTime > 5.0f || sleep) && !outsideClose1.Contains(playerPos)) {
                    open = false;
                    closing = true;
                    closeTime = 0;
                }
            }
        }
    }
    if (closing) {
        closeTime += Time.deltaTime * 0.5f;
        if (side0) {
            // (ドア0のアニメーション)
        } else {
            // (ドア1のアニメーション)
        }
        if (closeTime > 1f) {
            closing = false;
            enabled = false;
        }
    }
    if (!open && !closing && !sleep) {
        if (bounds0.Contains(playerPos)) {
            Teleport0to1(playerPos);
        } else if (bounds1.Contains(playerPos)) {
            Teleport1to0(playerPos);
        }
    } else {
        if (sleep && !open && !closing) {
            if (!bounds0.Contains(playerPos) && !bounds1.Contains(playerPos)) {
                sleep = false;
            }
        }
    }
}

概ねこんな感じになっています。最悪ですね。

ただまぁconsistencyとしてはいくらかわかりやすい部分があって。closing = true のとき、一定時間経つと closing = false になるはずなんですよ。

何故なら closeTime はだんだん増加していくはずで、closing = true に成るとき同時に closeTime = 0 になるから、そして closeTime > 1 のとき closing = false になるからです。

だから扉が途中で止まるはずないんですよ。

調査報告

enabled = false は怪しいよなぁ、とは思って、enabled への代入を Update 末尾に行うようにしたんですが、変わりませんでした。

仮説としては「Update内でenabledをfalseにすると、Udonの実行時間チェックとかのコンテキストスイッチによって以降不活性化する」みたいなことを考えていたんですが、これは「enabledじゃないのにメソッドが呼べる」ことに反するのできっと違います。

ちなみに enabled = false にして直後 10000 回のループを回すテストをしてみましたが、ちゃんとラグは発生するけど別に途中で止まったりはしませんでした。

 

不思議なのは、一度死ぬとあのボタンが一切動かなくなることです。

public void Open0() {
    if (open || closing) return;
    open = true;
    openTime = 0;
    side0 = true;
}

ボタンを押す方のUdonではPassageTeleportのenableをしているので、Open0 がちゃんと最後まで呼ばれていれば次の瞬間扉は初期位置に戻るはずです。

つまり open || closing が成立してそうな雰囲気がします。

そして「閉じかけの扉」については open は成立しません。つまり「閉じかけで固まる状態」は、 closing = true ではないかと推測されます。

しかしこれは先の話と矛盾します。何故進行しないのか。

そうすると残る不審箇所は Time.deltaTime しかないのです。はて。

 

あと強いて言うなら、上の記述では省略したんですが、何故か Open0Open1 の呼び出し時についでに player = Networking.LocalPlayer をやっているらしいです。

それが何故か null になって Update が走らなくなるのはありうるのかな…とは思いましたが、別にボタンを押してないのに急に扉が途中で止まるのはやっぱり変だと思うのです。

結論

ちなみにこの不具合、人数が多くないとなかなか発現しません。まぁ。

なんだろう。なんなんでしょうね。ほんとご迷惑おかけしております。

enabled が死んだときの対策として EmergencyButton を追加したんですが、でも普通に enabled = true っぽいので多分効果は出てないのです。これ。

player か、Time.deltaTime か…。前者説はありそうだけどじゃあなんで Networking.LocalPlayer が気まぐれで null になるんかっていう話なんだよな…

もうちょっと調査はしようと思います。そしてまぁ最悪ケースの為のセーフティは入れようと思います、よろしくおねがいします。

 

お急ぎのところ、ご迷惑をおかけしています。


 

追記。

RunEvent will clear incorrect event parameter names

これじゃん~~~~~~~~~~~~~~~~~~~~~~~~~~は~~~~~~~~~~~~~~~~~~~~~

以上です。ありがとうございました。