본문으로 바로가기

Box2d User Manual

category IT를보다/Apple 2011.08.29 11:14

http://box2d.org/manual.html
Box2d 사용자 매뉴얼


1장 Box2D 소개
1.1 About

1.2Prerequisites
Box2D는 c++로 만들어 졌다.

1.3 이 메뉴얼 문서에 관해

1.4 Feedback and Reproting Bugs
Box2D관련해 어떤 피드백 할게 있다면 포럼에 남겨라.

1.5 box2d 의 핵심 concept
shape
rigid body
fixture
constraint 
contact constraint 
joint
joint limit
joint motor
world

1.6 Moudles
box2d는 세가지 모들로 만들어진다.
Common
Collision
Dynamic

1.7 Units
box2d는 부동소수점으로 작동된다.

1.8 생성과 규정
메모리 관리는 box2d 의 메인 role 이기때문에  b2body나 b2joint 를 만들려고할때 절대 allocate 하지말고  factory 함수인  b2World 를 사용해라
 
 creation
b2body* b2World:CreateBody(const b2BodyDef* def)
b2joint* b2World:CreateJoint(cont b2JointDef* def)
 destruction
void b2World:DestoryBody(b2Body* body)
void b2World:DestoryJoint(b2Joint* joint)

1.9 User Data
GameActor* actor=GameCreateActor();
b2BodyDef bodyDef;
bodyDef.userData=actor;
actor->body=box2Dworld->CreateBody(&bodyDef);

Chapter2 Hello Box2D

2.1 World생성
모든 Box2D 프로그램은 World 를 생성하는것으로 시작한다. World 는 메모리관리,물리적 객체,시뮬레이션을 관리하는 물리적인 hub이다.
Box2D  world  를 생성하는것은 쉽다 .먼저 gravity vector를 정의하고  sleep 변수를 setting 하는것이다.

b2AABB worldAABB;
worldAABB.lowerBound.Set(-100.0f, -100.0f);
worldAABB.upperBound.Set(100.0f, 100.0f);

b2Vect2 gravity(0.0f,-10.0f);
bool doSleep=true;
b2World world(gravity,doSleep);
이제 우리의 물리세계가 만들어졋고, 이곳에 몇가지  재료들을 추가할 수 있게 되었다.

2.2 Ground Box 생성
1. 위치,진동의감폭 등의 변수를 가지는 body 를 정의
2. 이 body에 생성된   Word object를 사용한다.
3. 모양,마찰,밀도 등을 갖는 fixture를 정의
4.  body에 fixture를 생성

step1
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f, -10.0f);
step2
b2Body* groundBody=world.CreateBody(&groundBodyDef);
step3
b2PolygonShape groundBox;
groundBox.setAsBox(50.0f,10.0);
step4
groundBox->CreateFixture(&groundBox);

2.3 Dynamic Body 생성

 b2BodyDef bodyDef;
bodyDef.type=b2_dynamicBody;
bodyDef.position.Set(0.0f,4.0f);
b2Body* body=world.CreateBody($bodyDef);

다음으로  box를 생성하고
 b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(1.0f,1.0f);
그다음 box를 사용하여 fixture를 생성한다.
b2FixtureDef fixtureDef;
fixtureDef.shape=@dynamicBox;
fixtureDef.density=1.0f;
fixtureDef.friction=0.3f;

body->CreateFixture(@fixtureDef);
여기까지가 초기화이고. 
우리는 이제 시뮬레이팅 할 준비가 되었다.

2.4 Simulating the World(of Box2D)
Box2D는 전분자 라고하는 컴퓨터 알고리즘을 사용한다.

 float32 timeStep=1.0f/60.0f;

box2d에서 추천하는  iteration 숫자는 10이다.  iteration 은 보다 향상된 시뮬레이션을 보여주지만 성능에 문제가 있을수 있기때문에 적절하게 사용해야한다?
int 32 velocityIterations=6;
int32 positionIterations=2;

우리는 이제 시뮬레이션 루프를 시작할 준비가 되었다.  게임에서 시뮤레이션 루프는 게임루프와 함쳐질 것이다.

for(int32 i=0;i <60;++i)
{
world.Step(timeStep,velocityIterations,positonIterations);
world.ClearForces();
b2Vec2 position=body->GetPosition(); 
float32 angle=body->GetAngle();
printf(%4.2f  %4.2f %4.2f\n",positonx.postion.y,angle); 


2.5 Cleanup
월드가 scpe를 나가거나 포인터 삭제가 될때, 바디,픽스쳐,조인트 에 예약되었던 모든 메모리는 해제 된다.

2.6 The Testbed
testbed 는 유닛 테스 프레임웍이자 데모 환경이다.
 teestbed의 몇가지 특징들
.이동과 확대 기능의 카메라
.  다이내믹 바디들이 연결된   mouse picking
.Extensible set of test.
.GUI for selecting tests, parameter tuning,and debug drawing options.
.Pause and single step simulation.
.Text reundering.


testbed 는 Box2D 관련된 많은 예제를 가지고 있다. Box2D를 배우는데  testbedrk 많이 도움을 줄것이다.


Chapter 3 Common

3.1 About
Common 모듈은 메모리관리,벡터 수학 ,설정을 포함하고 있다.

3.2 Settings
 b2Settings.h 헤더파일에 포함된 내용
.int32 와  float32 와 같은 형
.상수
.Allocation wrappers
.The version number;
.Friction and restitution mixing functions

Types
Box2D  는  float32,int8 같은 다양한 타입을 정의하고 있다. 
Constnts
Box2D는 몇몇 상수를 b2Settings.h 헤더파일에 전부 정의 하고있으며. 상수를  조정할 필요는 없다. Box2D는 시뮬레이션과 충돌에 있어 부동소수점을 사용한다.
Allocation wrappers
b2Alloc 과 b2Free 파일에 셋팅되어있다.
Version
b2Version 구조는 런타임시에 질의를 하면 현재 버젼을 알ㄹ려준다.
Friction and Restitution Mixing
당신의 어플이 커스터마이징 할 필요가 있는경우에 파일셋팅 된다.

3.3 Memory Management


3.4  Math

Chapter4 Collision Module
4.1 About
Collision moudle 은 shape 와 이것을 조절할수 있는 함수를 포함한다.

4.2 Shapes
Box2D shapes 는 b2Shapes 클래스를 구현한다.  이 b2Shapes  클래스는 다음 기능을 정의한다.
. Test a point for overlap with the shape
. Perfome a ray cast against the shape
.Compute the shape's AABB.
.Compte the mass properties of the shape.

4.3  Circle Shapes
b2CircleShape circle;
circle.m_p.Set(1.0f,2.0f,3.0f);
circle.m_radius=0.5f;

4.4 Polygon Shapes




 CCW(counter clockwise winding)으로 폴리건을 만들어야한다. 



각 모서리의 좌표 배열을 줌으로서 폴리곤 shape 를  만들수 있다.

b2Vec2 vertices[3];
vertices[0].Set(0,0f,0.0f); 

vertices[1].Set(1,0f,0.0f);
vertices[2].Set(0,0f,0.0f);
int32 count=3;

b2PolygonShape polygon;
polygon.Set(vertices,count);

The polygon shape has some custion initialization functions to create boxes and edges (line segments);
void SetAsBox(float32 hx,float32 hy);
void SetAsBox(float32 hx,float32 hy, const b2Vec2& center,float32 angle);
void SetAsEdge(const b2Vec2&v1, const b2Vec2& v2);
폴리곤은  b2Shape 클래스의 radius를 상속받는다. 

4.5 Shape Point Test

b2Transfrom transform;
transform.SetIdentity();
b2Vec2 point(5.0f,2.0f);
bool hit=shape->TestPoint(transform,point);

4.6 Shape Ray Cast 
b2Transform transform;
transform.SetIdentity();
b2RayCastInput input;
input.p1.Set(0.0f,0.0f,0.0f);
input.p2.Set(1.0f,0.0f,0.0f);
input.maxFractrion=1.0f;
b2RayCastOutput output;
bool hit=shape->RayCast(&output,input,transform);
if(hit)
{
b2Vect2 hitPoint=input.p1+output.fraction*(input.p2-input.p1);
..

}

 


4.7 Bilateral Functons 
Collision 모듈은 대칭적인 함수들을 가지고 있다.
.Contact mainfolds
.Distance
.Time of impact


4.8 Contact Manifolds
Box2D는 겹쳐진 Shape 객체의 겹친좌표를 계산할수 있는 함수를 가지고 있다.


b2Worldmanifold worldManifold;
worldManifold.Initialize(&manifold,transformA,shapeA.m_raidus,tranformB,shapeB.m_radius);;
dius);
for(int32 i=0;i<manifold.pointCount;++i)
{
b2Vec2 point=worldManifold.points[i];
}

b2PointState stat1[2],state2[2];
b2GetPointStates(stat1,state2,&manifold1,&manifold2);
if(state1[0]==b2_removeState)
{
//process event
}

4.9 Distance
b2Distance 함수는 두shape간의 거리를 계산할수 있다.


4.10 Time of Impact



b2TimeOfImpact 는 두개의 움직이는 shape가 충돌시 각 시간을 결정할때 사용된다. 이것이 TOI (Tie of impact)라고 한다. 




이함수는 두개의shape와 두개 의 b2Sweep 구조체를 필요료한다.

4.11 Dynamic Tree





일반적으로 당신은 직접적으로 dynamic  트리를 사용하지는 않을것이다. b2World class를 통하여 사용할 것이다.

4.12 Board-phase 
 


Chapter 5 Dynamics Module
5.1 Overview
Dynamic module은  Box2D  부품중에서 가장 복잡하고 intracte 한 부분이다. 
Dynamic module 이 포함하고 있는것
.shape fixture class
.rigid body class
.contact class
.joint class
.world class
.listener class


6.fixture

6.1 About
Box2D는 b2Fixture class shapes to bodies 를 제공한다.
. a single shape
.density, fricton, and restitution
.collision filtering flag
.back pointer to parent body
.user data
.sensor flag

6.2 Fixture Creation
b2FixtureDef fixtureDef;
fixtureDef.shape=&myShape;
fixtureDef.denstity-1.0f;
b2Fixture* myFixture=myBody->CreateFixture(&fixtureDef);

하나의 바디에 다수의 fixtures를 생성할 수 있다. 
부모 바디의  fixture를 파괴할 수도 있다.
myBody->DestoryFixture(myFixture);

Denstiy
fixture의 density 는 부모 body의 질량 속성을 계산하는데 사용되어진다.
fixture->SetDensity(5.0f);
body0>ResetMassData();

Friction
마찰력의 값은 0 에서 1 사이의 값을 갖고 음수는 가질수 없다. 0은 마찰력을 없애는것이고 1은 가장 강한 마찰력을 시뮬레이트한다. 마찰력이 두 shape 간에 작용될때 box2d는 두개 부모fixture들의 마찰력값을 합친다.
이것은 좌표적인 의미를 가지고 있다.

float32 friction;
friction=sqrtf(shape1->friction * shape2->friction);

Restitution(복원력,원상회복)
Restitution  은 객체의 탄성을 만들때 사용되어진다. 값은 주로  0 에서 1사이 값이 셋팅된다. 테이블에서 공이 떨어진것을 상상해보라. 0값은 셋팅된다면 그공은 튀지않는다.  이걸 비탄성 충돌 이라고한다. 1 의 값은 볼의 초기 속도대로 정확하게 반사 된다는것을 의미하며 이것을 완전 탄성 충돌 이라고 한다. 복원력은 방정식을 사용함으로써 구현된다.
float32 restitution;
restitution = b2Max(shape1->restitution,shape2->restitution);

Filtering
충돌 필터 는 두개의 shape 사이의 충돌을 보호하는 시스템이다.  예를 들어 당신이 자전거를 타고는 캐릭터를 만든다고하면 당신은 자전거와 지면의 충돌,캐릭터와의 충돌을 생각할 수 데 자전과와 지면의 충돌만 원할 수 도 있다.   Box2D는 이렇게 카테고리나 그룹을 사용해서 충돌 필터를 지원하고 있다.
-카테고리 규정
playerShapeDef.filter.categoryBit=0x0002;
monsterShapeDef.filter.categoryBits=0x0004;
playerShape.filter.maskBit=0x0004;
monsterShapeDef.filter.maksBits=0x0002;
정수 그룹 index번호를 써서 충돌그룹을 셋팅할 수 있다.  정수는 항상 같은 충돌 그룹에 있고 음수는 충돌 그룹에 있지않다? shap1,shap2은 항상 충돌 처리가 되지만 shape3,shape4 는 절대 충돌되지 않는다.
-그룹 규정
shape1Def.filter.groupIndex=2;
shape2Def.filter.groupIndex=2;
shape3Def.filter.groupIndex=-8;
shape4def.filter.groupIndex=-8;

.A shape on a static body never collides with a shape on another static body.
.Shape on the same body never collide with each other.
.You can optionally enable/disalbe collision betwwen shapes on bodies connected by a joint 

Sensors
myShapeDef.isSensor=true;

Circle Definition
b2CircleDef def;
def.radius=1.5f;
def.localPosition.Set(1.0f,0.0f);

Polygon Definitions


b2PolygonDef triangleDef;
triangleDef.vertexCount=3;
triangleDef.vertices[0].Set(-1.0f, 0.0f);
triangleDef.vertices[1].Set(1.0f,0.0f);
triangleDef.vertices[2].Set(0.0f,2.0f);

b2PolygonDef alignedBoxDef;
float32 hx=1.0f;
float32 hy=2.0f;
alignedBodyDef.SetAsBox(hx,hy);

b2PolygonDef orientedBoxDef;
b2Vec2 center(-1.5f,0.0f);
float32 angle=0.5f*b2_pi;
orientedBoxDef.SetAsBox(hx,hy,center,angle);

6.3 Shape Factory 
b2CircleDef circleDef;
circleDef.radius=3.0f;
circleDef.density=2.5f;
b2Shape* myShape=myBody->CreateShape(&circleDef);

이렇게 body 에 붙어서 shape가 생성된다. 부모바디가   destroy 될때 자동으로 같이 destroy 되기때문에 따로  shape pointer를 저장해둘 필요는 없다.
myBody->SetMassFromShapes(); 

myBody->DestoryShape(myShape);

6.4 Using a Shape
b2Shape.h 자세한 사항은 참고해라, 여기에 다 말하기엔 지면이 충분치 않다. 

Chapter 7. Joints 

7.1. About

7.2 The Joint Definition
각 joint 타입은  b2JointDef 를 얻음으로서 정의된다. 모든 joint들은 각기 두개의 다른 body 사이에 연결된다.
collideConnected Boolean 값이 정해져야 연결된 두개의 body사이에 충돌 여부를 허락한다.
좌표data를 초기화하는 작업이 짜증날 수 도 있는데 대부분의 joints들은 초기화 함수를 가지고 있다.

7.2.1 Distance Joint

가장 간단한 jont중 하나가 두개의 body의 두개 좌표 사이에에 연관된 거리joint 이다.  world 자표계에 기반한 anchor point 를 정하고나면 각각의  body 들은 거리적 제한을 갖게된다.



b2DistanceJointDef jointDef;
jointDef.Initialize(myBody1,myBody2,worldAnchorOnBody1,worldAnchorOnBody2);
jointDef.collideConnected=true;

7.2.2. Revolute Joint
 

b2RevoluteJointDef jointDef;
jointDef.Initialize(myBody1,myBody2,myBody1->GetWorldCenter());


b2RevoluteJointDef jointDef;
jointDef.Initialize(body1, body2,myBody1->GetWorldCenter());
jointDef.lowerAngle=-0.5f*b2_pi;
jointDef.upppwrAngle=0.25*b2_pi;
jointDef.eanbelLimit=true;
jointDef.maxMotoTorque=0.0f;
jointDef.enableMotor=true;

7.2.3 Prismatic Joint


7.2.4 Pulley Joint



7.2.5 Gear Joint
 




7.2.6 Mouse joint


7.3 Joint Factory
Joints 는 world 팩토리 메소드 를 사용함으로써 생성되고 파괴된다. 


b2RevoluteJointDef jointDef;
jointDef.body1=myBody1;
jointDef.body2=myBody2;
jointDef.anchorPoint=myBody1->GetCenterPosition)_;
b2RevoluteJoint* joint=myWorld->CreateJoint(&joinDef);

myWorld->DestoryJoint(joint);
joint=NULL;

죠인트는 연결된 바디가   destroy될때 joint 도   destory된다.

7.4 Using Joints 

b2Body* GetBody1();
b2Body* GetBody2();
b2Vec2 GetAnchor1();
v2Vec2 GetAnchor2();
void* GetUserData();

모든 joint들은 반작용 힘과 토크를 가지고 있다. 반작용은 두개의 바디이 anchor point 에 가해진다.  이 반작용을 break joint 나 trigger 등 다른 게임 이벤트로서 사용할 수 있다. 
b2Vec2 GetReationForce();
float32 GetReationTorque();

7.4.1 Using Distnace Joint
7.4.2 Using Revolute Joint
float32 GetJointAngle() const;
float32 GetJointSpeed() const;
float32 GetMotorTorque() const;

void SetMotorSpeed(float32 speed);
void setMaxMotorTorque(float32 torque);
조인트 모터는 흥미로은 기능을 가지고 있다. 매시간 조인트 속도를  변경 해서  joint 를 사인곡선이나 당신이 원하는 동작으로 운동을 만들수 있다.

myJoint->SetMotorSpeed(cosf(0.5f*time));

float32 angleError=myJoint->GetJointAngle()-angleTarget;
float32 gain=0.1f;
myJoint->SetMotorSpeed(-gain*angleError);


7.4.3 Using Prismatic Joints
float32 GetJointTranslation() const;
float32 GetJointSpeed() const;
float32 GetMotorForce() const;
void SetMotorSpeed(float32 speed);
void SetMotorForce(float32 force);

7.4.4. Using Pulley Joints
Pully joint는 현재 길이를 제공한다.
float32 GetLength1() const;
float32 GetLength2() const;

7.4.5 Using Gear Joints
Gear joint 는  b2Joint 함수 말고는 어떤 정보도 주지않ㄴ다.

7.4.6 Using Mouse joints
Mouse joint 는 각 단게마다 타겟 좌표를 수정함으로서 연결된 바디를 조정할 수 있다.

Chapter 8. Contacts
8.1. About
Contacts 는 두개의  shapes 사이에 충돌을 관리하기위해 Box2D에 의해서 만들어진 객체이다.
다른종류의 shape 사이에 contact 를 관리를 위해서   b2Contact 로 부터 파생된 다른 종류의 contact 가 있다.
polygon-polygon collison 관리하는 contact class   circle-circle collision 을 관리하는 contact class 가 있다.
여기에 contact와 연관된 전문용어 들이 있다.

contact point
contact  point는 두개 shape가 만나는  좌표이다. 
contact normal
contact point 는  shap1 과 shape2 좌표들의  vector 단위이다.
contact separation

normal force

tangent force

contact ids


8.2  Contact  Listener
b2ContactListener 를 구현함으로 contact data를 받을 수 있다.  이 리스너는 생성될때 contact point를 알려준다  두개의 shape 는 다수의 contact point 를 가진다는것을 주의 해라.

class MyContactListener : public b2ContactListener
{
public:
	void Add(const b2ContactPoint* point)
    {
        // handle add point
    }
    
	void Persist(const b2ContactPoint* point)
    {
        // handle persist point
    }
    
	void Remove(const b2ContactPoint* point)
    {
        // handle remove point
    }
    
    void Result(const b2ContactResult* point)
    {
        // handle results
    }
};


충돌처리를 하는 아래코드는 contact buffer 가 처리 될때 orphaned body를 다루는 방법을 보여준다. 
// We are going to destroy some bodies according to contact
// points. We must buffer the bodies that should be destroyed
// because they may belong to multiple contact points.
const int32 k_maxNuke = 6;
b2Body* nuke[k_maxNuke];
int32 nukeCount = 0;

// Traverse the contact buffer. Destroy bodies that
// are touching heavier bodies.
for (int32 i = 0; i < m_pointCount; ++i)
{
    ContactPoint* point = m_points + i;

    b2Body* body1 = point->shape1->GetBody();
    b2Body* body2 = point->shape2->GetBody();
    float32 mass1 = body1->GetMass();
    float32 mass2 = body2->GetMass();

    if (mass1 > 0.0f && mass2 > 0.0f)
    {
        if (mass2 > mass1)
        {
            nuke[nukeCount++] = body1;
        }
        else
        {
            nuke[nukeCount++] = body2;
        }

        if (nukeCount == k_maxNuke)
        {
            break;
        }
    }
}

// Sort the nuke array to group duplicates.
std::sort(nuke, nuke + nukeCount);

// Destroy the bodies, skipping duplicates.
int32 i = 0;
while (i < nukeCount)
{
    b2Body* b = nuke[i++];
    while (i < nukeCount && nuke[i] == b)
    {
        ++i;
    }

    m_world->DestroyBody(b);
}

8.3. Contact Filtering
가끔 당신은 게임에서 모든 충돌하는객체를 원치않는다.  예를들어 오로지 주인공 캐릭터만 통과할 수 있는 문을 만들때 그렇다. 이것을 contact filtering 이라고 부른다. 왜냐하면 interations들이 filtered out 되기 대문이다. 
Box2D는   b2ContactFilter class를 구현함으로서 사용자 정의  custion filtering 을 만들 수 있다.
이 클래스는 두개의  b2Shapes 포인터를 받아 ShouldCollide 함수를 구현해야한다.  만약 shape들이  충돌한다면 당신의 함수는 true를 리턴 해야한다. 

bool b2ContactFilter::ShouldCollide(b2Shape* shape1, b2Shape* shape2)
{
	const b2FilterData& filter1 = shape1->GetFilterData();
	const b2FilterData& filter2 = shape2->GetFilterData();

	if (filter1.groupIndex == filter2.groupIndex && filter1.groupIndex != 0)
	{
		return filter1.groupIndex > 0;
	}

	bool collide = (filter1.maskBits & filter2.categoryBits) != 0 && (filter1.categoryBits & filter2.maskBits) != 0;
	return collide;
}



Chapter 9. Loose Ends

9.1 World Boundary

class MyBoundaryListener : public b2BoundaryListener
{
    void Violation(b2Body* body)
    {
        MyActor* myActor = (MyActor*)body->GetUserData();
        myActor->MarkForErrorHandling();
    }
};
 당신의 world 객체에 boundary listener의 instance 를 이렇게 등록할 수 있다.

       myWorld->SetListener(myBoundaryListener);

9.2. 암묵적 파괴 Implicit Desturction 
Box2D는 레퍼런스 카운팅을 사용하지않는다 . 그래서 바디가 파괴되면 완전히 파괴된다. 파괴된 body의 포인터 접근이 예기치 않은 동작을 가져온다.  이런 문제를 해결하기 위해서  FDFDFD 로 파괴된 속성들 정무를 메모리 관리자에 최어서 빌드해라.
이것이 문제를  좀더를 쉽게 해결할 수 있는 방법이다.


Box2D 는 암묵적 파괴가 일어날때 당신의 어플 정보로 콜백 미케니즘을 제공한다.  이것은 당신의 어플리케이션에겨 완전히 pointer해제하는 기회를 제공한다. 

class MyDestructionListener : public b2DestructionListener { void SayGoodbye(b2Joint* joint) { // remove all references to joint. } };
myWorld->SetListener(myDestructionListener); 

Chapter 10.Settings
10.1 About
10.2. Tolerances
10.3 Memory Allocation
모든 Box2D'의 메모리 할당은 b2Alloc 과   b2Free를 통해서 일생을 하고 다음 의 경우를 제외하고는
.b2World 는 스택으로 만들어질수 있다.
.어떤 Box2D 캘래스는  팩토리 메소드 없이 생성될 수 있는데  이것들은 콜백 클래스나 포인터 버퍼 를 접근할수 잇는 것을 제공한다.

Chapter 11. Debug Drawing 
물리적 세계에 세부적인 그리기를 위해서는 b2DebugDraw클래스를 구현할 수 있다. 여게에 사용가능한 속성들이 있다.
.shape outline
.joint connectivity
.core shapes (for continuous collsion)
.broad-phase axis-aligned bouding boxes(AABBs), including the world AABB
.polygon oriented bounding boxes(OBBs)
.broad-phase pairs(potential contacts)
.center of mass






 
저작자 표시
신고

'IT를보다 > Apple' 카테고리의 다른 글

iTunes Connect Mobile 링크  (0) 2011.09.18
iPhone 앱 기획단계에서 필요할 만한 이미지  (0) 2011.09.18
iOS 개발자 프로그램 등록  (0) 2011.09.17
자신의 앱에 iAD,AdMob 달기  (5) 2011.09.17
Box2d User Manual  (0) 2011.08.29
CCScene 전환시 callback 되는 함수  (0) 2011.08.27

댓글을 달아 주세요