소년코딩

2016. 09 04(일) 게임 엔진 스터디 8차

 

게임 엔진 스터디 8주차 내용은 다음과 같습니다.

    1. Matrix(행렬)

    2. Event(옵저버 패턴)


1. MATRIX

Matrix 클래스는 2D 행렬 변환 클래스입니다.

인스턴스를 단 한개만 생성하며(싱글톤 패턴), 이 Matrix 인스턴스를 통해 enchant.js의 Node 하위 클래스들의

이동(x, y), 규모(scale), 회전(rotate)등을 연산합니다.

HTML5 canvas의 transform() 메서드와 아주 밀접한 관계에 있습니다.

(라이센스로 인해 uml은 생략합니다...ㅠ)

// #Matrix
(function() {
    'use strict';

    /**
     * Matrix
     * 2D 변환 행렬을 나타낸다.
     * HTML canvas transform() Method에 사용.
     * void ctx.transform(a, b, c, d, e, f)
     * [a c e]
     * [b d f]
     * [0 0 1]
     * - basic -
     * [1 0 tx]
     * [0 1 ty]
     * [0 0  1]
     *
     *
     * a (m11) => Horizonatal scaling => y축 scale
     * b (m12) => Horizonatal skewing => y축 rotate
     * c (m21) => Vertical skewing => x축 rotate
     * d (m22) => Vertical scaling => x축 scale
     * e (tx)  => Horizonatal moving => x축 이동
     * f (ty)  => Vertical moving => y축 이동
     *
     * 번외 행렬 변환
     *
     * [x  x]
     * [y  y]
     *
     * [k  0] [x]    =>[kx]
     * [0  k] [y]    =>[ky]    k배 확대 닮은 변환 행렬
     *
     *
     * [1  0][x]     =>[x]
     * [0 -1][y]     =>[-y]   x축 대칭 행렬
     *
     *
     * [-1 0][x]     =>[-x]
     * [0  1][y]     =>[y]    y축 대칭 행렬
     *
     *
     * [-1 0][x]     => [-x]
     * [0 -1][y]     => [-y]  원점 대칭 행렬
     *
     *
     * [0  1][x]     => [y]
     * [1  0][y]     => [x]   y=x 대칭 변환 행렬
     *
     *
     * [0 -1][x]     => [-y]
     * [-1 0][y]     => [-x]   y=-x 대칭 변환 행렬
     *
     *
     * [cosΘ  -sinΘ] [x]    =>  [x']
     * [sinΘ   cosΘ] [y]    =>  [y']   Θ만틈 반시계 방향으로 회전한 회전변환 행렬
     *
     * [cosΘ  sinΘ] [x]    =>  [x']
     * [-sinΘ cosΘ] [y]    =>  [y']   Θ만틈 시계 방향으로 회전한 회전변환 행렬
     *
     *
     *
     * [a, c, tx]
     * [b, d, ty]
     *
     * [1, 0, 0]
     * [0, 1, 1]
     */

    var Matrix = adun.Matrix = adun.Class({
        TYPE: 'Matrix',

        init: function() {
            this.reset();
        },

        reset: function() {
            // ctx.transform(a, b, c, d, e, f)
            /**
            * [a c e]
            * [b d f]
            * [0 0 1]
            **/
            this.stack = [];

            this.stack.push([1, 0, 0, 1, 0, 0]);
        },

        // 행렬 연산(이동, 크기, 회전)
        makeTransformMatrix: function(node) {
            var x, y, width, height, w, h, rotation, scaleX, scaleY, theta, tmpcos, tmpsin,
                a, b, c, d, dx, dy, mat = [];

            x = node._x;
            y = node._y;
            width = node.width || 0;
            height = node.height || 0;
            w = adun.isNumber(node._originX) ? node._originX : width / 2;
            h = adun.isNumber(node._originY) ? node._originY : height / 2;
            scaleX = adun.isNumber(node._scaleX) ? node._scaleX : 1;
            scaleY = adun.isNumber(node._scaleY) ? node._scaleY : 1;
            rotation = node._rotation || 0;
            theta = rotation * Math.PI / 180;
            tmpcos = Math.cos(theta);
            tmpsin = Math.sin(theta);

            a = scaleX * tmpcos;
            b = scaleX * tmpsin;
            c = scaleY * tmpsin;
            d = scaleY * tmpcos;
            dx = (-a * w + c * h + x + w);
            dy = (-b * w - d * h + y + h);

            mat[0] = a;
            mat[1] = b;
            mat[2] = -c;
            mat[3] = d;
            mat[4] = dx;
            mat[5] = dy;

            return mat;
        },
        
        // 행렬 * 행렬
        multiply: function(m1, m2) {
            var mat = [];

            var a11 = m1[0], a21 = m1[2], adx = m1[4];
            var a12 = m1[1], a22 = m1[3], ady = m1[5];

            var b11 = m2[0], b21 = m2[2], bdx = m2[4];
            var b12 = m2[1], b22 = m2[3], bdy = m2[5];

            mat[0] = a11 * b11 + a21 * b12;
            mat[1] = a12 * b11 + a22 * b12;
            mat[2] = a11 * b21 + a21 * b22;
            mat[3] = a12 * b21 + a22 * b22;
            mat[4] = a11 * bdx + a21 * bdy + adx;
            mat[5] = a12 * bdx + a22 * bdy + ady;

            return mat;
        },

        // 행렬 * 벡터
        multiplyVec: function(m, vec) {
            var mat, x = vec[0], y = vec[1];
            mat = [];

            var a = m[0], c = m[2], dx = m[4];
            var b = m[1], d = m[3], dy = m[5];

            mat[0] = a * x + c * y + dx;
            mat[1] = b * x + d * y + dy;

            return mat;
        }
    });

    Matrix.instance = new Matrix();
})();


참고링크(한글)


2. Event

enchant.js에는 객체의 상태 변화를 관찰하는 옵저버 패턴을 구현한 EventTarget 클래스와 Event 클래스가 있다.

옵저버 패턴: http://www.dofactory.com/javascript/observer-design-pattern

 

EventTarget 클래스

// #EventTarget
(function() {
    'use strict';
    // 옵저버 패턴
    var EventTarget = adun.EventTarget = adun.Class({
        extend: null,
        TYPE: 'EventTarget',

        init: function() {
            // 리스너 목록 => 빈 객체
            this._listeners = {};
        },

        addEventListener: function(type, listener) {
            // 리스터[타입] => 리스너 목록(배열)
            var listeners = this._listeners[type];

            // 리스너 목록이 없다면 if 블록에 진입
            if( listeners == undefined ) {
                this._listeners[type] = [listener];
            } else if( listeners.indexOf(listener) === -1 ) {
                // 리스너 목록 맨 앞에 추가
                listeners.unshift(listener);
            }
        },

        // on => addEventListener
        on: function() {
            this.addEventListener.apply(this, arguments);
        },

        removeEventListener: function(type, listener) {
            var listeners, i;

            listeners = this._listeners[type];

            if( listeners != undefined ) {
                i = listeners.indexOf(listener);

                if( i !== -1 ) {
                    listeners.splice(i, 1);
                }
            }
        },

        clearEventListener: function(type) {
            // 인자가 있을 경우 해당 타입의 리스너 모두 삭제
            // 인자가 없을 경우 리스너 모두 삭제

            if( type != undefined ) {
                delete this._listeners[type];
            } else {
                this._listeners = {};
            }
        },

        dispatchEvent: function(e) {
            e.target = this;
            e.localX = e.x - this._offsetX;
            e.localY = e.y - this._offsetY;

            if( this['on' + e.type] != undefined ) {
                this['on' + e.type](e);
            }
            var listeners = this._listeners[e.type];

            if( listeners != undefined ) {
                listeners = listeners.slice();

                for( var i = 0, len = listeners.length; i < len; ++i ) {
                    listeners[i].call(this, e);
                }
            }
        },

        // emit => dispatchEvent
        emit: function(e) {
            this.dispatchEvent.call(this, e);
        }
    });
})();

 

Event 클래스

// #Event
(function() {
    'use strict';

    var Event = adun.Event = adun.Class({
        extend: null,
        TYPE: 'Event',

        init: function(type) {
            this.type = type;   // important!

            this.target = null;
            this.x = 0;
            this.y = 0;
            this.localX = 0;
            this.loacalY = 0;
        },

        _initPosition: function(pageX, pageY) {
            var heart = adun.Heart.instance;

            this.x = this.localX = (pageX - heart._pageX) / heart.scale;
            this.y = this.loaclY = (pageY - heart._pageY) / heart.scale;
        }
    });

    // 준비 완료되면 발생한다.
    // ex) 이미지 프리로딩
    Event.LOAD = 'load';

    // 에러가 발생하면 발생한다.
    Event.ERROR = 'error';

    // display 사이즈가 변경되면 발생한다.
    Event.HEART_RESIZE = 'heartresize';

    // 심장이 로딩중일때 발생한다.
    // ex) 각 이미지라 로딩중일때
    Event.PROGRESS = 'progress';

    // 새로운 프레임마다 발생한다. (fps_)
    Event.ENTER_FRAME = 'enterframe';

    // 프레임이 끝나면 발생난다.
    Event.EXIT_FRAME = 'exitframe';

    // 새로운 씬에  진입할때 발생한다.
    Event.ENTER = 'enter';

    // 씬이 끝날때 발생한다.
    Event.EXIT = 'exit';

    // child가 Node에 추가될때 발생한다.
    Event.CHILD_ADDED = 'childadded';

    // 노드가 그룹에 추가될때 발생한다.
    Event.ADDED = 'added';

    // 노드가 씬에 추가될때 발생한다.
    Event.ADDED_TO_SCENE = 'addedtoscene';

    // child가 Node에서 제거될때 발생한다.
    Event.CHILD_REMOVED = 'childremoved';

    // 노드가 그룹에서 제거될때 발생한다.
    Event.REMOVED = 'removed';

    // 노드가 씬에서 제거될때 발생한다.
    Event.REMOVED_FROM_SCENE = 'removedfromscene';

    // Entity가 렌더될때 발생한다.
    Event.RENDER = 'render';

    // 버튼 인풋이 눌려졌을때 발생한다.
    Event.INPUT_START = 'inputstart';

    // 버튼 인풋 상태가 바겻을때 발생한다.
    Event.INPUT_CHANGE = 'inputchange';

    // 버튼 인풋이 끝나면 발생한다.
    Event.INPUT_END = 'inputend';

    // 인풋 상태가 변경되면 발생한다.
    Event.INPUT_STATE_CHANGED = 'inputstatechanged';

    // 스프라이트에서 프레임에 null값이 할당되 애니메이션이 끝나면 발생한다.
    Event.ANIMATION_END = 'animationend';

    // 키가눌러졋을때 heart로 발생한다.
    Event.KEY_DOWN = 'keydown';

    // 마우스 이벤트
    Event.CLICK_START = 'clickstart';
    Event.CLICK_MOVE = 'clickmove';
    Event.CLICK_END = 'clickend';

})();


game

by 소년코딩

추천은 글쓴이에게 큰 도움이 됩니다.

악플보다 무서운 무플, 댓글은 블로그 운영에 큰 힘이됩니다.

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기