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';
})();
by 소년코딩
추천은 글쓴이에게 큰 도움이 됩니다.
악플보다 무서운 무플, 댓글은 블로그 운영에 큰 힘이됩니다.