관리 메뉴

도드넷

JAVASCRIPT#78 - offset 오프셋에 관하여 본문

창고/JS KING 포니 [중단]

JAVASCRIPT#78 - offset 오프셋에 관하여

도드! 2016. 8. 6. 05:30
반응형



if not now then when?, if not me then who?, if not her then whom? MARRY ME AJ!


JAVASCRIPT#78 - offset 오프셋에 관하여


gf.offset = function(div){

// 1. 해당 div의 데이터를 불러와서 options에 저장한다. 이후 x, y 값을 저장한다.

    var options = div.data("gf");
    var x = options.x;
    var y = options.y;
   

// 2. 해당 div의 부모 요소와 그 데이터를 불러와서 options에 저장한다.

    var parent = $(div.parent());

    options = parent.data("gf");


// 3. 부모요소의 x, y를 더해주고 그 부모의 부모를 찾아 저장해 이를 반복한다.

    while (!parent.is(gf.baseDiv) && options !== undefined){
        x = x + options.x;
        y = y + options.y;
        parent = $(parent.parent());
        options = parent.data("gf");
    }

// 4. 새로구한 x, y값을 반환한다.

    return {x: x, y: y};
}


위는 오프셋 함수다. 목적은 부모요소의 위치를 고려해 해당 요소의 직접적인 확실한 위치를 구하기 위해서 사용한다.


var offset = gf.offset(parent);

...

var offset = gf.offset(div);

...

var offset1 = gf.offset(sprite1);
var offset2 = gf.offset(sprite2);


위 오프셋 함수가 처음 쓰이는 구간은 다음 함수에서 였다.


gf.tilemapFragment = $("<div class='gf_tilemap' style='position: absolute'></div>");


gf.addTilemap = function(parent, divId, options){
    var options = $.extend({
        x: 0,
        y: 0,
        tileWidth: 64,
        tileHeight: 64,
        width: 0,
        height: 0,
        map: [],
        animations: []
    }, options);
   
    // find the visible part
    var offset = gf.offset(parent);
    var visible = gf.tilemapBox(options, {
        x:      - options.x - offset.x,
        y:      - options.y - offset.y,
        width:  gf.baseDiv.width(),
        height: gf.baseDiv.height()
    });
       options.visible = visible;
   
    //create line and row fragment:
    var tilemap = gf.tilemapFragment.clone().attr("id",divId).data("gf",options);
    for (var i=visible.y1; i < visible.y2; i++){
        for(var j=visible.x1; j < visible.x2; j++) {
            var animationIndex = options.map[i][j];
           
            if(animationIndex > 0){
                var tileOptions = {
                    x: options.x + j*options.tileWidth,
                    y: options.y + i*options.tileHeight,
                    width: options.tileWidth,
                    height: options.tileHeight
                }
                var tile = gf.spriteFragment.clone().css({
                    left:   tileOptions.x,
                    top:    tileOptions.y,
                    width:  tileOptions.width,
                    height: tileOptions.height}
                ).addClass("gf_line_"+i).addClass("gf_column_"+j).data("gf", tileOptions);
               
                gf.setAnimation(tile, options.animations[animationIndex-1]);
               
                tilemap.append(tile);
            }
        }
    }
    parent.append(tilemap);
    return tilemap;
}


여기서 gf.offset(parent); 안의 parent에 들어가는 것은 결국 "tiles"이다. 그려질 타일들을 담는 부모요소인데 상위 부모요소인 group의 위치를 더해준 위치를 반환하게 된다. 


아래 tilemapBox 함수에서는 두 상자의 충돌지점 인덱스를 구하고 있는데,

첫번째 파라미터 options 타일맵 전체를 의미하고 그다음 {#*#$} 안의 내용은 보이는 스크린을 의미한다.


{
        x:      - options.x - offset.x,
        y:      - options.y - offset.y,
        width:  gf.baseDiv.width(),
        height: gf.baseDiv.height()
}


스크린은 실제로 존재하지는 않는 어떤 임시 상자객체이다.

- options.x 은 전체 타일맵의 시작위치를 음수를 취한것이고 뒤에 - offset.x 은 tiles의 상위 요소인 group의 위치를 고려해서 tiles의 위치를 반환, 빼주고 있다. 음수를 쓰는 이유는 최상위 부모요소 group이 배경의 역할로 플레이어가 이동하면 반대방향으로 움직여 항상 위치값이 음수가 되기 때문이다.


이 임시상자 객체의 x, y값은 타일맵내에서의 카메라의 위치와 같다.


offset이 쓰인 두번째 함수를 살펴보자.


gf.updateVisibility = function(div){
    var options = div.data("gf");
    var oldVisibility = options.visible;
   
    var parent = div.parent();
   
    var offset = gf.offset(div);
    var newVisibility = gf.tilemapBox(options, {
        x:      -offset.x,
        y:      -offset.y,
        width:  gf.baseDiv.width(),
        height: gf.baseDiv.height()
    });
   
    if( oldVisibility.x1 !== newVisibility.x1 ||
        oldVisibility.x2 !== newVisibility.x2 ||
        oldVisibility.y1 !== newVisibility.y1 ||
        oldVisibility.y2 !== newVisibility.y2){
           
        div.detach();
       
        // remove old tiles
        for(var i = oldVisibility.y1; i < newVisibility.y1; i++){
            for (var j = oldVisibility.x1; j < oldVisibility.x2; j++){
                div.find(".gf_line_"+i+".gf_column_"+j).remove();
            }
        }
        for(var i = newVisibility.y2; i < oldVisibility.y2; i++){
            for (var j = oldVisibility.x1; j < oldVisibility.x2; j++){
                div.find(".gf_line_"+i+".gf_column_"+j).remove();
            }
        }
        for(var j = oldVisibility.x1; j < newVisibility.x1; j++){
            for(var i = oldVisibility.y1; i < oldVisibility.y2; i++){
                div.find(".gf_line_"+i+".gf_column_"+j).remove();
            }
        }
        for(var j = newVisibility.x2; j < oldVisibility.x2; j++){
            for(var i = oldVisibility.y1; i < oldVisibility.y2; i++){
                div.find(".gf_line_"+i+".gf_column_"+j).remove();
            }
        }
        // add new tiles
       
        for(var i = oldVisibility.y2; i < newVisibility.y2; i++){
            for (var j = oldVisibility.x1; j < oldVisibility.x2; j++){
                createTile(div,i,j,options);
            }
        }
        for(var i = newVisibility.y1; i < oldVisibility.y1; i++){
            for (var j = oldVisibility.x1; j < oldVisibility.x2; j++){
                createTile(div,i,j,options);
            }
        }
        for(var j = oldVisibility.x2; j < newVisibility.x2; j++){
            for(var i = oldVisibility.y1; i < oldVisibility.y2; i++){
                createTile(div,i,j,options);
            }
        }
        for(var j = newVisibility.x1; j < oldVisibility.x1; j++){
            for(var i = oldVisibility.y1; i < oldVisibility.y2; i++){
                createTile(div,i,j,options);
            }
        }
        div.appendTo(parent);
       
    }
    // update visibility
    options.visible = newVisibility;
}


함수 분석을 좀 해보자.


var options = div.data("gf");


옵션에 해당 div의 데이터를 저장하고있다.

여기서 div는 타일맵이다.


var oldVisibility = options.visible;


저장한 데이터 options에 접근해서 visible이라는 프로퍼티를 oldVisibility에 저장한다.

visible이라는 변수는 이전에 addTilemap에서 만들어서 저장한것으로 스크린과 타일맵전체가 충돌하는 구간의 인덱스정보를 담고있다.


var parent = div.parent();
   
    var offset = gf.offset(div);
    var newVisibility = gf.tilemapBox(options, {
        x:      -offset.x,
        y:      -offset.y,
        width:  gf.baseDiv.width(),
        height: gf.baseDiv.height()
    });


그 다음은 새로운 스크린 ~ 타일맵의 충돌 구간인덱스를 구하고 있는 모습이다. 전이랑 다른점은 offset 파라미터에 tiles가 아닌 div 개별 타일맵을 넣고있다는데 어짜피 offset.x 와 offset.y의 의미는 위와 동일하게, 타일맵의 부모위치에 의한 개별 타일맵의 위치를 구하고 있다.


if( oldVisibility.x1 !== newVisibility.x1 ||
   oldVisibility.x2 !== newVisibility.x2 ||
   oldVisibility.y1 !== newVisibility.y1 ||
   oldVisibility.y2 !== newVisibility.y2){

...


그 다음 엄청나게 많은 양의 if구문이 나오는데, 구 visibility 인덱스와 현재 새로 갱신한 visibility 인덱스를 비교해서 하나라도 다를 경우 구인덱스에 해당하는 타일을 삭제하고 새 타일을 만들도록하고 있다.



실제로 보면, div에는 레이어별 타일맵이 들어가기때문에 여러 레이어의 타일맵이 현재 visibility에 의거해서 갱신되는것을 볼수있다.


보면 detach 메소드와 appendto 메소드를 번갈아써주면서 해당 타일맵을 땟다 붙였다 하고있는데, 굳이 쓸필요는 없어 보인다 둘다 제거해도 정상작동한다. 아마도 예기치못한 버그를 줄이기위함이 아닐까?


gf.spriteCollide = function(sprite1, sprite2){
    var option1 = sprite1.data("gf");
    var option2 = sprite2.data("gf");
   
    var offset1 = gf.offset(sprite1);
    var offset2 = gf.offset(sprite2);
   
    var x = gf.intersect(
        offset1.x,
        offset1.x + option1.width,
        offset2.x,
        offset2.x + option2.width);


    var y = gf.intersect(
        offset1.y,
        offset1.y + option1.height,
        offset2.y,
        offset2.y + option2.height);
   
    if (x[0] == x[1] || y[0] == y[1]){
        return false;
    } else {
        return true;
    }
}


마지막으로 offset을 이용한 함수는 spriteCollide로 스프라이트간의 충돌을 검출해서 참또는 거짓을 반환해주는 함수다.


이렇게 offset을 쓰는 이유는 어떤 객체의 위치가 부모의 위치에 따라 달라져서 (당연히 붙어서 움직이니까) 실제위치가 달라지기 때문이다.



예를들어 어떤 캐릭터에 달려있는 칼의 위치는 캐릭터로부터 가까운 4px 0px이라고 하자 그런데 칼의 위치를 말하라고했을때 4px 0px라는 위치값은 전체게임을 기준으로 했을때 말도안되는 위치이다. 칼의 전체기준의 정확한 위치를 구하고자하면 부모요소들의 위치를 더해줌으로 구한다.


결론적으로 오프셋 OFFSET이란 무엇인가?

- 사전적 정의는 "상쇄하다"이다. 쉬운 말로는 "차이를 매꾸다." 즉, offset 함수는 부모와 자식 요소사이의 차이를 매꿔 진정한 위치를 반환하는 그런 함수가 된다.




 

반응형
Comments