遮蔽処理の不具合

Posted 2013.05.06 in 吉里吉里

吉里吉里では、かなり限定された状況で遮蔽処理が行われるが、
その遮蔽処理には、いくつか問題が潜んでいる。
遮蔽処理にまつわる問題は非常に複雑怪奇な現象を引き起こし
とにかく原因を特定しづらい。

不可思議な動作

まずは次のtjsスクリプトを実行して見て欲しい。


class TestWindow extends Window
{
  var layerGray;
  var layerBlack;
  var timer;

  function TestWindow()
  {
    super.Window();
    setInnerSize(800, 600);

    // プライマリレイヤー
    add(new Layer(this, null));
    primaryLayer.setImageSize(innerWidth, innerHeight);
    primaryLayer.setSizeToImageSize();
    primaryLayer.fillRect(0, 0, innerWidth, innerHeight, 0xFFFFFFFF); // 白

    // 灰色レイヤーを上に作る
    layerGray = new Layer(this, primaryLayer);
    initLayer(layerGray, 50, 50, 400, 400, 0xFF777777, 5, ltOpaque); // 灰
    
    // 黒レイヤーを下に作る
    layerBlack = new Layer(this, primaryLayer);
    initLayer(layerBlack, 100, 100, 400, 400, 0xFF000000, 0, ltOpaque); // 黒
    
    // 黒レイヤーに子レイヤーを作る
    var sub = new Layer(this, layerBlack);
    add(sub);
    initLayer(sub, 100, 100, 200, 200, 0xFFFF00FF, 0, ltAlpha); // 紫
    sub.opacity = 128; // 半透明
    
    // このタイミングで黒レイヤーを上に移動させると正しい動作結果になる
    // layerBlack.absolute = 10;
    
    // タイマーを起動
    timer = new Timer(onTimer, "");
    timer.interval = 1000;
    timer.enabled = true;
  }

  function finalize()
  {
    invalidate layerGray;
    invalidate layerBlack;
    invalidate timer;

    super.finalize();
  }

  function initLayer(layer, x, y, w, h, color, absolute, type)
  {
    layer.left = x;
    layer.top = y;
    layer.setImageSize(w, h);
    layer.setSizeToImageSize();
    layer.fillRect(0, 0, w, h, color);
    layer.absolute = absolute;
    layer.type = type;
    layer.visible = true;
  }

  var step = 0;
  function onTimer()
  {
    switch(step)
    {
    case 0:
      // 黒レイヤーを上に移動(不具合発生)
      layerBlack.absolute = 10;
      break;
    }
    step++;
  }
}

var win = new TestWindow();
win.visible = true;

これを実行すると次のような動作結果になる。(version2.32 rev2 で確認)

KiriKiri_Exclude1_1

おかしい動作結果なのだが、何がおかしいかわかるだろうか?

紫色が明るすぎる!

のである。
次の部分のコメントアウトを外すと、正しい動作結果になるので確認してみよう。


    // このタイミングで黒レイヤーを上に移動させると正しい動作結果になる
    // layerBlack.absolute = 10;

KiriKiri_Exclude1_2

紫色が暗くなった!
opcity=128なのだから、この程度の色合いが正しい。

さらに不可思議な動作

子レイヤーを作る部分を次のように書き換えると、さらに現象がわかりやすくなる。


    // 黒レイヤーに子レイヤーを作る
    for(var x=0; x<10; x++)
    {
      for(var y=0; y<10; y++)
      {
        var sub = new Layer(this, layerBlack);
        add(sub);
        initLayer(sub, 50+x*30, 50+y*30, 8, 8, 0xFFFF00FF, 0, ltAlpha); // 紫
        sub.opacity = 128; // 半透明
      }
    }

子レイヤーとして紫の四角をたくさん作成するようにしただけだ。
これを実行すると次のようになる。

KiriKiri_Exclude2_1

背後の灰色が透けてしまっている!

コメントアウトをはずすとやはり正しい動作になるので確認しておこう。

KiriKiri_Exclude2_2

何がおきているのか

この不可思議な現象は、遮蔽処理にまつわる問題によって引き起こされている。
遮蔽処理にミスが生じ、実際には遮蔽していないにもかかわらず、
遮蔽による最適化が行われてしまっているのである。

この現象の回避は、遮蔽処理の存在を知らないと非常に困難だろう。
詳しい原因の追求と回避方法についてはまた次回~


Leave a Reply

*