개요

cocos2dx 자체의 버튼 클래스가 존재하는 것으로 알지만,

원하는 기능인 버튼이 눌린 상태를 체크할 방법과 여러 콜백함수 등록기능을 사용하기 위해 자체 클래스를 만들었습니다.

최대한 사용자가 버튼을 만들어 사용할 때 편리할 수 있도록 구현하였습니다.


코드 및 설명은 아래와 같습니다. 

(가독성을 위해 적당히 편집하였습니다.)


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*--------------------------CMyButton--------------------------
 * strategy pattern
 * texture 혹은 label로 초기화하여 사용하는 Button
 * Lambda혹은 함수포인터, 함수객체를 인자로 받는다.
 * 눌렀을때 체크
 * 누르고 있을 때 체크
 * 끝났을 때 체크 
 * ------------------------------------------------------------*/
 
 
 
 
/* 버튼의 상태 */
enum eMYBUTTON_STATE{
    BEGIN = 0,
    EXECUTE = 1,
    END = 2,
}
 
 
/* 텍스쳐와 함께 버튼 생성 
 * normalImg와 selectedImg를 인자로 전달*/
CMyButton* CMyButton::createWithTexture(
    std::string normalTextureName,                // 선택 전 버튼의 텍스쳐 이름
    std::string selectedTextureName,              // 선택 중 버튼의 텍스쳐 이름
    eMYBUTTON_STATE state,                        // 버튼 상태 (해당 상태일 때 함수 호출됨)
    const std::function<void(void)> &func)        // 람다 혹은 함수포인터 혹은 함수객체 전달(매개 변수는 void)
 
 
/* 문자열과 함께 버튼 생성 */
CMyButton* CMyButton::createWithString(
    std::string normalTextureName,                // 버튼의 텍스쳐 이름
    std::string labelString,                      // 버튼의 label 내용
    int fontSize,                                 // 폰트 사이즈
    eMYBUTTON_STATE state,                        // 버튼 상태 (해당 상태일 때 함수 호출됨)
    const std::function<void(void)> &func)        // 람다 혹은 함수포인터 혹은 함수객체 전달(매개 변수는 void)
 
 
/* 버튼의 함수 포인터 리스트에 함수 포인터 추가 */
void AddState(eMYBUTTON_STATE state,             // 상태 (해당 상태일 때 함수 호출됨)
    const std::function<void(void)> &func)        // 람다 혹은 함수포인터 혹은 함수객체 전달(매개 변수는 void)
 
 
/* 터치된 좌표를 버튼의 좌표로 변환 한 후에 버튼이 터치되었는지 검사 */
bool touchHits(Touch  *touch)
{
    const Rect area(00, m_pNormalTexture->getContentSize().width, 
        m_pNormalTexture->getContentSize().height);
 
    // world to nodespace 좌표 변환
    return area.containsPoint(m_pNormalTexture->convertToNodeSpace(touch->getLocation()));
}
 
 
/* 버튼이 눌렸을 때 BEGIN */
bool onTouchBegan(Touch  *touch, Event  *event)
{
    CC_UNUSED_PARAM(event);
    if (m_pNormalTexture){
 
        // 좌표 변환 후 터치 체크
        m_IsSelect = touchHits(touch);
 
        // 버튼의 좌표에서 진짜로 터치되었을 경우
        if (m_IsSelect){
 
            // BEGIN 상태일때 호출해야할 함수가 있다면 호출
            std::for_each(m_BeginFuncList.begin(), m_BeginFuncList.end(), 
                [](const std::function<void(void)> &func){
                func();
            });
            
            // 선택 시 이미지가 있다면 이미지 교체
            if (m_SelectedTextureName != ""){                    
                m_pNormalTexture->setTexture(m_SelectedTextureName);
            }
            else { // 교체될 이미지가 없다면 크기를 키움                                                
                m_pNormalTexture->setScale(1.1f);
            }
        }
    }
    return m_IsSelect;
}
 
 
/* 버튼에서 떨어졌을 때 END */
void CMyButton::onTouchEnded(Touch  *touch, Event  *event)
{
    CC_UNUSED_PARAM(event);
    if (m_pNormalTexture){
 
        // END 상태일때 호출해야할 함수가 있다면 호출
        std::for_each(m_EndFuncList.begin(), m_EndFuncList.end(), 
            [](const std::function<void(void)> &func){
            func();
        });
 
        // 이미지가 바뀌었었다면 다시 원래대로 바꿈
        if (m_SelectedTextureName != ""){                        
            m_pNormalTexture->setTexture(m_NormalTextureName);
        }
        else// 바뀔이미지가 없다면 크기를 원래대로 바꿈                                                    
            m_pNormalTexture->setScale(1.0f);
        }
 
        // 버튼 눌림 종료
        m_IsSelect = false;
    }
}
 
 
/* 버튼이 눌리고 있는 중일때 EXECUTE */
void Execute(float delta)
{
    // 버튼 눌림 상태이며, EXECUTE 상태일 때 실행되어야 할 함수가 1개 이상일 때
    if (m_IsSelect && m_ExecuteFuncList.size())
    {
        // EXECUTE 상태일때 호출해야할 함수가 있다면 호출
        std::for_each(m_ExecuteFuncList.begin(), m_ExecuteFuncList.end(), 
            [](const std::function<void(void)> &func){
            func();
        });
    }
}
 
 
/* lambda, 함수 포인터를 리스트로 저장
 * 각 상태일 때 호출할 함수를 저장하는 리스트 */
std::vector<std::function<void(void)>> m_BeginFuncList;
std::vector<std::function<void(void)>> m_ExecuteFuncList;
std::vector<std::function<void(void)>> m_EndFuncList;
cs

CMyButton 클래스의 주요 함수 및 동작원리 설명

1. 전략 디자인 패턴의 변형으로 버튼 생성시 함수 포인터를 전달합니다.

2. 때문에 사용자는 버튼을 눌렀을 때의 상황에 대해 부담없이 정의할 수 있습니다.

3. 각 버튼은 Begin, Execute, End의 상태를 가질 수 있습니다.

4. 각 상태에 맞는 함수를 생성시 초기화 혹은 AddState 함수로 추가할 수 있습니다. 


사용 예

1
2
3
4
5
auto leftButton = CMyButton::createWithTexture(
        "leftButton_1.png",
        "leftButton_2.png",
        EXECUTE,
        std::bind(&CObjectManager::RotationObject, CObjectManager::Instance(), -1)); 
cs

1. 눌리기 전엔 leftButton_1.png, 눌린 상태엔 leftButton_2.png의 이미지를 보이는 버튼을 생성합니다.

2. 버튼이 눌린 상태(EXECUTE)일 때 CObjectManager::Instance()의 RotationObject(int dir) 함수를 호출합니다.

3. std::bind : 함수 호출 시 전달하는 매개변수를 -1로 고정하고, 호출하는 인스턴스는 CObjectManager::Instance()로써 bind합니다.



귀차니즘을 위한 2줄 요약

1. Button에는 Begin, Execute, End 3 가지의 상태가 존재한다.

2. Button을 create할때 각 상태에 호출될 함수를 넣을 수 있다.


'All > Project' 카테고리의 다른 글

매주 일요일 랭킹 초기화  (0) 2017.04.05
UI_ HealthBarUI  (0) 2016.04.12
별게임의 상태트리  (0) 2016.04.12
AI_ FSM / State  (0) 2016.04.12
Shooter / Bullet  (0) 2016.04.12

앞선 FSM 및 State를 이용한 별게임의 상태 트리는 다음과 같습니다.





'All > Project' 카테고리의 다른 글

UI_ HealthBarUI  (0) 2016.04.12
UI_ MyButton  (0) 2016.04.12
AI_ FSM / State  (0) 2016.04.12
Shooter / Bullet  (0) 2016.04.12
ObjectManager  (0) 2016.04.12

개요

별게임에 나오는 모든 오브젝트는 다음과 같은 상태를 가질 수 있습니다.

1. 자석 아이템을 먹었을때의 상태

2. 거인 아이템을 먹었을때의 상태

3. 코인으로 변화시키는 아이템을 먹었을 때의 상태

4. 별로 변화시키는 아이템을 먹었을 때의 상태

위 4개의 상태는 공존할 수 있습니다.(3번 4번은 서로 공존할 수 없다.)

즉, 플레이어가 거인인 상태에서 자석을 먹고 모든 bullet이 코인으로 변화되는 아이템을 먹을 수 있다는 것입니다.


위와 같은 복잡한 상황을 구현하기 위해 FSM과 State를 도입하였습니다.

먼저 FSM(stateMachine)의 코드는 다음과 같습니다.

(가독성을 위해 적당히 편집하였습니다.)


FSM 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*------------------------CStateMachine------------------------
 * Template 클래스
 * FSM사용을 원하는 클래스에서 정의하여 사용할 수 있다.
 * 사용자는 States를 가지고 있어야 한다.
 * Global, Current, Previous 상태를 가지고 있다.
--------------------------------------------------------------*/
 
 
 
 
/* template으로 정의됨 */
template <class T>
 
 
/* 생성 시 FSM을 사용하고자하는 객체의 This를 넘겨 받는다. */
CStateMachine(T* Owner)
 
 
/* 현재 상태를 전달받은 상태로 변경한다. 
 * 현재 상태는 이전상태에 저장한다. 
 * 현재상태의 Exit함수를 호출하며 새로운 생태의 Enter함수를 호출한다. */
template <class T>
void CStateMachine<T>::ChangeState(CState<T>* pNewState)
{
    if (m_pCurrentState != nullptr)
        m_pCurrentState->Exit(m_pOwner);
    m_pPreviousState = m_pCurrentState;
    m_pCurrentState = pNewState;
    m_pCurrentState->Enter(m_pOwner);
}
 
 
/* 현재 상태와 Global상태를 Execute */
template <class T>
void CStateMachine<T>::Execute(float delta) const
{
    if (m_pGlobalState != nullptr)
        m_pGlobalState->Execute(m_pOwner, delta);
    if (m_pCurrentState != nullptr)
        m_pCurrentState->Execute(m_pOwner, delta);
}
 
 
/* FSM을 사용하고자 하는 주체 */
T* m_pOwner;
 
 
/* Previous 상태는 직전의 상태를 보관한다. 
 * Current에 들어있는 상태를 실행한다.
 * Global은 어떠한 상황에서도 가장 먼저 반영된다. */
CState<T>*            m_pPreviousState;
CState<T>*            m_pCurrentState;
CState<T>*            m_pGlobalState;
cs

CStateMachine<T> 클래스의 주요 함수 및 동작원리 설명

1. 유한 상태 머신 디자인 패턴으로 구현되어 있습니다.

2. Generic Programming 을 위한 Template으로 정의 되어있습니다. 

3. ChangeState에서 이전 상태의 후 처리 함수를 호출하고 새로운 상태의 전처리 상태를 호출하면서 변경합니다.

4. 오브젝트의 AI를 상태머신으로 관리하고 싶을때 Component로 정의하면됩니다. 


FSM 사용 예 ( Player의 FSM ) 

1
CStateMachine<CPlayer>* m_FSM;
cs

1. 플레이어의 AI를 FSM으로 관리하기 위해 정의

2. Player::Execute에서 FSM의 Execute 실행

3. Player의 AI상태를 실행 및 변경 합니다.



아래는 위 FSM 이 관리하는 State 추상클래스입니다.


State 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*---------------------------CState----------------------------
 * Template 클래스
 * FSM을 소유하는 클래스의 상태를 정의하는 추상클래스
 * State의 인터페이스만을 제공한다.
 * 각 함수는 모두 FSM의 주체 인스턴스를 인자로 받는다.
--------------------------------------------------------------*/
 
 
 
 
/* template으로 정의됨 */
template <class T>
 
 
/* Enter()은 상태가 Execute를 실행하기 전에 한번만 호출된다. */
virtual void Enter(T* pOwner) = 0;
 
 
/* Execute()은 상태의 메인 루프이다. */
virtual void Execute(T* pOwner, float delta) = 0;
 
 
/* Exit()은 상태가 종료 혹은 변경될 때 한번만 호출된다. */
virtual void Exit(T* pOwner) = 0;
cs

1. 위의 FSM이 관리하는 State들의 인터페이스 클래스입니다.(추상클래스)

2. 어떤 오브젝트이든 상태를 가질 수 있도록 template으로 정의하였습니다.

3. 전처리를 위한 Enter, 반복 실행을 위한 Execute, 후처리를 위한 Exit으로 구성되어 있습니다.


사용 예(Player의 상태 중 1개)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
class CPlayerNormal : public CState<CPlayer>{
 
public:
    static CPlayerNormal* Instance();
    
    /* 상태 전 처리 */
    void Enter(CPlayer* player)
    {}    
 
    void Execute(CPlayer* player, float delta)
    {
        /* 현재 플레이어가 거인 아이템을 먹었다면 */
        if(eITEM_FLAG_giant & CItemManager::Instance()->getCurrentItem())
        {
            /* 거인 아이템을 먹은 상태로 상태 변경 */
            player->getFSM()->ChangeState(CPlayerGiant::Instance());    
        }
    }
 
    /* 상태 후 처리 */
    void Exit(CPlayer* player)
    {}
 
private:        
    CPlayerNormal(){}    
    virtual ~CPlayerNormal(){}
}
cs

1. CPlayerNormal은 플레이어 클래스의 상태 중 하나입니다.

2. 주체로써 Player의 인스턴스를 전달합니다 



귀차니즘을 위한 3줄 요약

1. 게임의 상태표현을 위해 FSM구현

2. Stage, Player, Bullet은 상태를 가진다.

3. 각각의 상태는 Ente, Execute, Exit의 상태를 가진다.

'All > Project' 카테고리의 다른 글

UI_ MyButton  (0) 2016.04.12
별게임의 상태트리  (0) 2016.04.12
Shooter / Bullet  (0) 2016.04.12
ObjectManager  (0) 2016.04.12
PoolingManager  (0) 2016.04.12

개요

별게임을 개발하면서 가장 많이 변경된 두 클래스라고 생각합니다.

그만큼 앞으로도 많이 변경될 것으로 보입니다.

먼저 코드 전에 간단하게 설명하자면 

Bullet은 모든 Bullet 파생클래스들의 Base클래스 입니다.

모든 Bullet 들 에게 공통적으로 필요한 AI 조종행동 이나 이펙트, action 등을 정의합니다.


Shooter 역시 모든 Shooter 파생클래스의 Base클래스 입니다.


두 클래스가 공통적으로 가지고 있는 함수가 있다면 바로 operator new 와 operator delete 함수 입니다.

operator new 함수는 메모리를 실제로 할당하지 않고 PoolingManager에서 받아오기 위해 정의하였고 

operator delete 는 operator new의 특성을 커버할 수 있도록 정의 되었습니다.

( operator new / delete 에 대한 자세한 설명은 아래 포스팅을 참조해주세요. )

[MemoryPooling_1]

[MemoryPooling_2]

[operator new / operator delete]



이제 코드를 보겠습니다.

(가독성을 위해 필요한 부분만 적절히 편집하였습니다.)



코드


operator new

1
2
3
4
5
6
/* poolingManager에서 FreeMemory Block을 하나 가져옴 */
void* CBullet::operator new(size_t sizeconst std::nothrow_t)
{
    // PoolingManager에서 메모리를 할당 받는다.
    return CPoolingManager::Instance()->BulletNew();
}
cs

PoolingManager의 관리를 받기 위한 operator new

create함수에서 새롭게 메모리 생성하는 것이 아니라 미리 생성해 두었던 메모리를 사용합니다.



ReturnToMemoryBlock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 오브젝트의 메모리를 FreeMemoryBlock으로 변환 == 오브젝트 삭제 */
void CBullet::ReturnToMemoryBlock()
{
    /*removeFromParent 의 이유 : 
    이유는 모든 CMover의 파생 객체들은 메모리 블럭에서 메모리를 할당 받는다.
    그로인해 실행 중 addChild시 같은 메모리를 여러번 addChild할 수 있다.
    때문에 메모리 블럭으로 되돌릴때에는 부모관계를 제거하여야한다.
    또 ReferenceCount를 1 낮춰야 하는 이유도 있다.*/
    this->removeFromParent();
    this->removeAllChildren();
    this->setVisible(false);
    this->setAlive(false);    
    CPoolingManager::Instance()->Bullet_ReturnToFreeMemory(this);
}
cs

사용이 끝난 오브젝트의 메모리를 PoolingManager로 돌려보냅니다.

돌려 보내기 전에 현재 오브젝트에 붙어있는 Children들을 해제하고 오브젝트 역시 부모로 부터 관계를 끊습니다.



Rotation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* 회전행렬을 이용하여 오브젝트 회전 및 이동 */
void CBullet::Rotation(float dir, float delta)
{
    // 회전 속도와 방향을 이용하여 각도를 구하고 라디안으로 변환
    float radian = CC_DEGREES_TO_RADIANS(dir * (m_fRotationSpeed * delta));
 
    // 현재의 Direction Vector를 저장한다.
    Vec2 beforeRotation = getPosition() - m_pPlanet->getPosition();
 
    // 거리도 저장
    float length = beforeRotation.length();
 
    /* 회전행렬을 구함 
     * rotate x = ((x_ * cos(angle)) - (y_ * sin(angle)))
     * rotate y = ((x_ * sin(angle)) + (y_ * cos(angle))) */
    m_RotationVec = Vec2(
    (float)((beforeRotation.x * cos(radian)) - (beforeRotation.y * sin(radian))),
    (float)((beforeRotation.x * sin(radian)) + (beforeRotation.y * cos(radian))));
 
    // 노말라이즈
    m_RotationVec.normalize();
    m_RotationVec *= length;
    
    // 기존의 좌표에 새로운 좌표를 더해준다.
    setPosition(m_pPlanet->getPosition() + m_RotationVec);
 
    // 오브젝트 자체도 회전
    setRotation(getRotation() - (dir *( m_fRotationSpeed * delta)));
}
cs

오브젝트의 회전을 정의 합니다. 

회전행렬을 이용하여 회전 후 좌표를 구하고 오브젝트 자체도 회전하여 줍니다.



R_BezierWithScale

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* 일정 위치로 베지어 곡선을 그리며 이동한 후 커지면서 FadeOut */
void CBullet::R_BezierWithScale(
Vec2 targetPos, 
Vec2 controlPoint_1,
Vec2 controlPoint_2, 
float time, float scale)
 
{
    // 더이상 Execute 하지 않는다.
    setAlive(false);
 
    // 베지어 곡선 생성
    ccBezierConfig bezier;
    bezier.controlPoint_1 = Vec2(controlPoint_1);
    bezier.controlPoint_2 = Vec2(controlPoint_2);
    bezier.endPosition = Vec2(targetPos);
 
    // 베지어 액션 및 다른 액션 순서대로 실행
    auto bezierTo1 = BezierTo::create(time, bezier);
    this->setZOrder(101);
    auto action = Sequence::create(
        bezierTo1,
        ScaleBy::create(0.5f, scale),
 
        // 두 액션이 끝난후 스케줄을 걸어 오브젝트 삭제
        CallFunc::create([&](){
        this->scheduleOnce([=](float dt){
            this->ReturnToMemoryBlock();
        }, 1.0f, "BezierWithScale");
 
    }), nullptr);
    this->runAction(action);
 
    // texture 페이드 아웃
    auto textureAction = FadeOut::create(2.0f);
    m_pTexture->runAction(textureAction);
}
 
cs

오브젝트의 특정 action을 정의합니다. R_은 ReturnToMemoryBlock을 호출한다는 약자입니다.


'All > Project' 카테고리의 다른 글

별게임의 상태트리  (0) 2016.04.12
AI_ FSM / State  (0) 2016.04.12
ObjectManager  (0) 2016.04.12
PoolingManager  (0) 2016.04.12
게임 전체 클래스 UML 및 간단설명  (0) 2016.04.12

개요

PoolingManager에서 생성한 MemorBlock을 게임 오브젝트로서 관리하기 위해 CObejctManager를 만들었습니다.

자세한 설명은 아래 코드와 함께하겠습니다.

(역시 가독성을 위해 적당히 편집하였습니다.)


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/*------------------------ObjectManager--------------------------
*
* singleton class
* CMover를 상속받는 모든 클래스를 Execute및 Remove하는 함수이다.
* RemoveAllBullet()함수는 Mover->Delete()함수를 호출한다.
* Delete()에서 removeFromParent()와 operator delete를 호출하여 소멸자를 호출하기 위함
*
*---------------------------- ----------------------------------*/
 
 
 
 
/* PoolingManager에서 CreateBulletList할 때  
생성된 메모리를 관리하기 위해 이곳에 넣는다. */
void AddBullet(void* bullet){
    m_BulletList.emplace_back(static_cast<CBullet*>(bullet));
}
 
 
/* Bullet을 create할 때 이 리스트에 넣는다. */
void Auto_ReturnToMemoryBlock(CBullet* bullet){
    m_ReturnToMemoryBlockList.emplace_back(bullet);
}
 
 
/* 게임 종료 시점에 호출된다. */
void RemoveAllObject(){
    for (auto bullet : m_BulletList)
    {
        if (bullet->HasPointer()) 
            bullet->Delete();
    }
}
 
 
/* Alive 상태의 오브젝트를 Execute한다. */
void Execute(float delta){
    // 지워진 오브젝트는 returnToMemoryBlock을 호출한다.
    ReturnToMemoryBlockEveryFrame();
 
    // 오브젝트의 Execute를 호
    for (auto bullet : m_BulletList)
    {
        if (bullet->IsAlive()) {
            bullet->Execute(delta);
        }
    }
}
 
 
//callback
/* LeftButton RightButton 누르면 호출된다. */
void RotationObject(int dir){
    for (auto bullet : m_BulletList)
    {
        if (bullet->IsAlive()) {
            bullet->Rotation(dir);
        }
    }
}
 
 
/* ReturnToMemoryBlockEveryFrame()
 * 지운 오브젝트들의 ReturnToMemoryBlock을 즉시 호출하면
 * 같은 프레임에서 생성한 오브젝트들이 앞에서 Free로 만든 메모리를 참조할 수 있으므로
 * 한프레임 늦게 메모리 블럭으로 되돌리기 위해서. */
void ReturnToMemoryBlockEveryFrame()
{
    for (auto bullet : m_ReturnToMemoryBlockList)
    {
        if (!bullet->IsAlive()) {
            CPoolingManager::Instance()->Bullet_ReturnToFreeMemory(bullet);
        }
    }
}    
 
 
/* 생성된 메모리를 Object로 캐스팅하여 담을 리스트 */
std::vector<CBullet*> m_BulletList;
 
 
/* Alive가 false인 오브젝트들을 MemoryBlock으로 변경*/
std::vector<CBullet*> m_ReturnToMemoryBlockList;
 
cs

CObjectManager 클래스의 주요 함수 및 동작원리 설명

1. AddBullet을 호출하여 메모리 블럭을 Bullet으로써 관리하도록 합니다.

2. Bullet의 Create()함수에서 Auto_ReturnToMemoryBlock()을 호출하여 m_ReturnToMemoryBlockList에 등록합니다.

3.  m_ReturnToMemoryBlockList에 등록된 오브젝트 중 Alive가 false인 것은 다음 프레임에서 메모리블럭으로 되돌아갑니다.



사용 예

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* 사용 예 Auto_ReturnToMemoryBlock()에 포인터 등록  */
 
/* operator new */
void* CNormalBullet::operator new(size_t sizeconst std::nothrow_t)
{
    // PoolingManager에서 메모리를 할당 받는다.
    return CPoolingManager::Instance()->BulletNew();
}
 
 
CNormalBullet* CNormalBullet::create(
    std::string textureName,        //bullet 이미지
    float boundingRadius,           //bullet 충돌 범위
    float angle,                    //bullet 초기 각도 
    float speed)                    //bullet 초기 속도
{
    // operator new 호출 후 CNormalBullet 생성자로 초기화
    CNormalBullet* pRet = 
        (CNormalBullet*)new(std::nothrow)CNormalBullet(
        textureName, boundingRadius, angle, speed);    
 
    // nothrow로 인해 초기화 되지 않으면 nullptr반환됨
    if (pRet && pRet->init())
    {
        // ObjectManager의 Auto_ReturnToMemoryBlock()에 등록
        pRet->Auto_ReturnToMemoryBlock();
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = NULL;
        return NULL;
    }
}
cs

1. pRet->Auto_ReturnToMemoryBlock()에서는 ObjectManager의 Auto_ReturnToMemoryBlock()를 this를 인자로 전달하며 호출합니다.

2. ObjectManager의 Auto_ReturnToMemoryBlock()은 m_ReturnToMemoryBlockList 에 전달 받은 포인터를 등록합니다.


사용 예

1
2
3
4
5
/* 사용 예 RemoveAllObject 호출 */
CGameScene::~CGameScene()
{
    CObjectManager::Instance()->RemoveAllObject();
}
cs

1. Scene변경 및 종료 시 RemoveAllObject()를 호출합니다.

'All > Project' 카테고리의 다른 글

AI_ FSM / State  (0) 2016.04.12
Shooter / Bullet  (0) 2016.04.12
PoolingManager  (0) 2016.04.12
게임 전체 클래스 UML 및 간단설명  (0) 2016.04.12
게임 개요  (0) 2016.04.11

개요.


먼저 제가 만든 poolingManger는 아래 두 책에 나오는 내용을 기반으로 만들어 졌다는 것을 미리 밝힙니다.

               


앞에서도 이야기 했듯이 

별 게임은 별, 총알, 아이템 등이 매우 많이 만들어 지고 삭제됩니다..

때문에 게임 개발을 하는 것에 있어서 가장 먼저 고려했던 것이.. 

1. 생성과 삭제의 리스크

2. 메모리 단편화

위 두 가지 이유로 인한 게임 성능 저하 였습니다.

게임이 렉이 걸리고 느리면 게임을 하는 입장에서는 매우 답답하며 화가나는 것은 당연한 사실이니까요.

그래서 고민을 하다가 PoolingManager를 만들게 된 것입니다.



아래는 PoolingManager의 코드 및 간단한 설명입니다. 


(그리고 MemoryPooling에 대해 자세한 설명을 보고싶으시면 아래 포스트들을 참조해 주세요. )

[MemoryPooling_1]

[MemoryPooling_2]

[operator new / operator delete]



먼저 완성된 코드는 아래와 같습니다.

(가독성을 위해 적당히 편집하였습니다.)


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*-----------------------CPoolingManager------------------------
 *
 * singleton pattern
 * 동작 사이클은 아래와 같다.
 *
 * 1. CreateBulletList() - 원하는 만큼의 메모리 블럭 생성 및 리스트에 추가
 * 2. BulletNew() - 생성하고자 하는 오브젝트로 초기화(operator new, 생성자 호출)
 * 3. 사용
 * 4. ReturnToFreeMemory() - 메모리 반환
 * 5. 사용할 때 마다 2번부터 반복 
 * 6. 게임 종료시 메모리 해제
 *
----------------------------------------------------------------*/
 
 
 
/* 메모리 블럭 타입 정의    */
typedef char* MEMORYBLOCK;
 
 
/* size만큼의 메모리 블럭을 생성한다. */
MEMORYBLOCK NewMemoryBlock(size_t sizeconst
{
    /* memoryAlive flag를 위한 1바이트 추가 생성 */
    MEMORYBLOCK block = new char[size + 1];
 
    /* memory 초기화 및 memoryAlive = false */
    memset(block, 0size + 1);                
    return block;
}
 
 
/* size만큼의 char형 포인터를 count만큼 Bullet리스트에 add */
void CreateBulletList(size_t count, size_t size)
{
    m_BulletSize = size;
    m_BulletList.reserve(m_BulletSize);
    while (count--)
    {
        /* 하나의 크기가 size만큼의 메모리 블럭을 count만큼 생성한다. */
        MEMORYBLOCK memBlock = NewMemoryBlock(m_BulletSize);    
        m_BulletList.emplace_back(memBlock);
 
        /* 오브젝트 매니저 리스트에 추가한다. */
        CObjectManager::Instance()->AddBullet(memBlock);
    }
}
 
 
/* pool이 보유한 메모리 리스트가 생성하려는 것보다 적으면 새로 생성 */
void* BulletNew()
{
    for (auto bullet : m_BulletList)
    {
        /* 메모리가 Free(false)상태면 메모리를 사용 중 상태(true)로 전환 후 반환 */
        if (false == bullet[m_BulletSize]) {    
            bullet[m_BulletSize] = true;        
            return bullet;
        }
    }
 
    /* 모든 메모리가 사용 중 상태라면 새롭게 하나 생성 */
    CCLOG("BULLET LIST OVERFLOWED");
    MEMORYBLOCK memBlock = NewMemoryBlock(m_BulletSize);
    m_BulletList.emplace_back(memBlock);
 
    /* 오브젝트 매니저 리스트에 추가한다. */
    CObjectManager::Instance()->AddBullet(memBlock);
 
    /* 메모리를 사용 중 상태로 전환 */
    memBlock[m_BulletSize] = true;    
 
    return memBlock;
}        
 
    
/* Bullet을 메모리블럭으로 전환 (alive off) */
void Bullet_ReturnToFreeMemory(void* bullet)
{
    static_cast<char*>(bullet)[m_BulletSize] = false;
}        
 
 
/* 모든 메모리를 해제한다. (게임이 종료되거나 Scene이 변경될때 호출) */
void DeleteAllMemory()
{
    for (auto bullet : m_BulletList)
    {
        delete[] bullet;
        bullet = nullptr;
    }
    m_BulletList.clear();
}                            
 
 
/* 메모리 블럭 리스트 */
std::vector<MEMORYBLOCK> m_BulletList;
 
 
/* 메모리 블럭 하나 당 크기 */
int m_BulletSize;
cs

CPoolingManager 클래스의 주요 함수 및 동작원리 설명


1. memoryPooling기능을 수행하는 singleton 클래스 입니다.

2. 게임을 시작할 때 원하는 사이즈의 메모리 블럭을 원하는 개수만큼 미리 생성해 놓습니다.

3. 사용자는 필요할 때 해당 메모리들을 사용할 수 있습니다. 

4. MemoryPooling의 관리를 받고 싶다면 해당 클래스의 operator new에서 PoolingManager의 New를 호출해주면 됩니다. 

6. 그러면 미리 생성된 메모리 중 Free상태의 메모리를 할당 받을 수 있습니다. 



사용 예

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* 사용 예 1 객체 초기화 */
 
/* operator new */
void* CNormalBullet::operator new(size_t sizeconst std::nothrow_t)
{
    // PoolingManager에서 메모리를 할당 받는다.
    return CPoolingManager::Instance()->BulletNew();
}
 
 
CNormalBullet* CNormalBullet::create(
    std::string textureName,        //bullet 이미지
    float boundingRadius,           //bullet 충돌 범위
    float angle,                    //bullet 초기 각도 
    float speed)                    //bullet 초기 속도
{
    // operator new 호출 후 CNormalBullet 생성자로 초기화
    CNormalBullet* pRet = 
        (CNormalBullet*)new(std::nothrow)CNormalBullet(
        textureName, boundingRadius, angle, speed);    
 
    // nothrow로 인해 초기화 되지 않으면 nullptr반환됨
    if (pRet && pRet->init())
    {
        // ObjectManager의 Auto_ReturnToMemoryBlock()에 등록
        pRet->Auto_ReturnToMemoryBlock();
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = NULL;
        return NULL;
    }
}
cs

사용 예 (객체 초기화)

1. NormalBullet의 create에서 호출하는 new는 operator new 로 poolingManager의 New함수를 호출합니다.

2. PoolingManager의 New함수는 미리 생성된 Free메모리를 반환합니다.

3. 반환된 MemoryBlock은 CNormalBullet 생성자로 초기화 되어 사용합니다. 


1
2
3
4
5
6
7
8
9
10
11
/* 사용 예 2 메모리 반환 */
    
void CNormalBullet::ReturnToMemoryBlock()
{
    this->removeFromParent();
    this->removeAllChildren();
    this->setVisible(false);
    this->setAlive(false);    
}
 
cs

사용 예 (메모리 반환)

1. 사용한 메모리를 Free메모리로 반환하려면 setAlive(false) 하면 됩니다.

2. alive가 false인 오브젝트는 다음 프레임에서 Free메모리로 전환됩니다.

3. this->removeFromParent();

    this->removeAllChildren();

    this->setVisible(false);

    this->setAlive(false);

4. 위 일련의 작업들은 현재 오브젝트에 AddChild되어 있는 메모리들을 해제해주는 것과 부모로부터 현재 메모리를 단절시키는 작업을 진행합니다. 



EXTRA : 

처음엔 addChild되어야만 object로써 제대로된 구실을 하는 cocos2dx의 특성을 고려하여 

createBulletList할 때 모든 NormalBullet 등의 최상위 클래스인 CBullet으로 생성한 후 addChild까지 했었습니다.

게임 중 addChild하는 비용까지 없애자는 야심찬 계획이었지만 다음과 같은 이유로 뻘짓이 되었습니다.


1. bullet으로 생성자를 호출하여 초기화 하면 이후 normalBullet으로써 사용하지 못한다.

    (normalBullet의 생성자를 호출한 것이 아니기 때문에 다형성 불가.)


2. 그래서 처음에 Bullet으로 초기화 후 사용할 때 NormalBullet으로 다시 초기화 해보았는데 

    이때는 Ref생성자 호출 과정에서 RefCount가 다시 1이 되기 때문에 이전의 addChild 한 것이 소용없어졌습니다.

    (그리고 생성자를 두 번 호출하는 말도 안되는 문제가 있었죠.)


3.  Bullet으로 생성한 메모리를 NormalBullet으로 사용하려면 두 클래스의 크기가 같아야 했습니다.


위와 같은 일이 있었기에 지금의 모습인 Char* 형으로 모든 Bullet보다 적당히 큰 메모리 블럭을 만들어 놓고 

사용할 때 operator new를 이용하여 NormalBullet이든 SpecificBullet이든 초기화하여 사용할 수 있도록 변경하였습니다.

다형성을 이용할 수 있게된 것이죠.


현재 구조를 보면 사용할 때마다 addChild및 RemoveFromParent를 하고 있습니다.

같은 메모리를 반복해서 사용하니까 당연하게 해주어야 할 일이긴 한데.. 뭔가찜찜한 상태라 차후에 리펙토링을 고려하고 있습니다.



귀차니즘을 위한 3줄 요약

1. PoolingManager구현 

2. 별게임에 적용

3. 게임성능 UP



'All > Project' 카테고리의 다른 글

Shooter / Bullet  (0) 2016.04.12
ObjectManager  (0) 2016.04.12
게임 전체 클래스 UML 및 간단설명  (0) 2016.04.12
게임 개요  (0) 2016.04.11
MemoryPooling_2  (0) 2016.03.15

게임 전체 클래스 UML을 보기 편하게 쪼개어 보았습니다.

(사정상 UML프로그램이 아닌 마인드맵 프로그램을 사용하였습니다.)

관계는 상속에 관련해서만 표현하였습니다.

각각의 클래스에 자세한 설명은 링크를 걸어 놓았습니다.



GameObject

- GameObject

   게임상에 등장하는 모든 오브젝트의 최상위 클래스 입니다. 

   BondingRadius, Execute 등 모든 오브젝트에 꼭 필요한 인터페이스를 제공합니다.


- Mover (자세히)

  게임 상에서 빈번히 생성되고, 삭제되는 오브젝트들의 베이스 클래스입니다.

   PoolingManager의 메모리 관리를 받는 파생클래스들의 베이스로서 필요한 함수들의 인터페이스를 제공합니다. 


- Player

  플레이어 클래스입니다. 캐릭터의 생명이나 FSM 등을 소유합니다.


- Planet

  행성 클래스입니다. 


- HealthUI (자세히)

- ScoreUI

- MyButton (자세히)

- BonusUI

  UI 클래스들입니다. 




State

- State (자세히)

  게임상의 오브젝트들은 AI상태를 구현하기 위해 State가 제공하는 인터페이스를 상속받아 상태를 정의합니다.

   게임의 AI는 Stage -> (Player, Bullet)의 계층 구조를 가집니다. (Stage의 상태에 따라 Player, Bullet의 상태가 변화합니다.)


- BulletState (자세히)

  Bullet들의 AI 상태입니다. 표기는 BulletState로 했지만 NormalState, BonusTimeState, MagnetItemState 가 존재합니다.

 

- PlayerState (자세히)

  Player의 AI 상태입니다. 역시 표기는 PlayerState로 했지만 NormalState, GiantState 가 존재합니다.


- StageState (자세히)

  현 Stage의 AI 상태입니다. 역시 표기는 StageState로 했지만 NormalStageState, BonusTimeStageState 가 존재합니다.




& Singleton


- UIManager 

  UI를 key값으로 저장하고 관리합니다.


- AudioManager

  기존의 AudioEngine을 랩핑한 클래스입니다. Audio의 동시 플레이 카운트를 제어하기위해 정의하였습니다.


- ItemManager 

  현재 플레이어가 획득한 아이템이 무엇인지 저장하고 타이머를 관리하도록 정의 되어 있습니다.


- ObjectManager (자세히)

  게임상의 모든 오브젝트의 Execute 및 안전한 Remove를 보장하기 위해 정의 되었습니다.


- PoolingManager (자세히)

  게임상에서 오브젝트의 생성과 삭제가 빈번하게 일어나기 때문에 그 리스크를 줄이고 메모리관리를 효율적으로 하기 위해 정의 하였습니다.




'All > Project' 카테고리의 다른 글

ObjectManager  (0) 2016.04.12
PoolingManager  (0) 2016.04.12
게임 개요  (0) 2016.04.11
MemoryPooling_2  (0) 2016.03.15
MemoryPooling_1  (0) 2016.03.15

지루한 개요


간단한 설명

사방에서 날아오는 총알을 피하고 별과 코인을 모아 점수를 겨루는 게임


게임 이름 

StarStarStar 입니다.


제작 기간  

6주 정도지만 아직 완성단계는 아닙니다.


개발 인원

프로그래밍, 기획, 그래픽 모두 혼자 작업했습니다. 그래픽은 파워포인트(ppt)와 웹 포토샾(pixlr)으로 작업했습니다.


개발 엔진 

cocos2d-x 3.4 ver


사용 언어

c++

 

개발 환경

visual studio 2013 , eclipse


외부 library

jsoncpp 

- 맵데이터나 패턴 등을 저장하는 용도로 사용했습니다.

- cocos2d-x에 내장 되어 있는 rapidJson을 사용하지 않은 것은 파싱하는 데 불안정했기 때문입니다.

(시행착오 끝에 많은 데이터를 사용하는 것에 적합하지 않다고 판단했습니다.)


보기 불편한 스크린샷 



보기편한 영상 




화질이 구지네요.. 화질구지.. 화질구지..
동영상이 렉이 걸리는데요?
내가 해도 이거 보다는 잘하겠다!! 는 분들을 위해




플레이시 주의 사항

- 캐릭터가 죽는게 슬퍼서 죽지 않도록 설정해 놓았습니다.
더 이상 지겨워서 못하겠다 싶을때 직접 종료버튼을 눌러주세요.
종료버튼은 최대한 작게 만들었습니다.

- 버그를 확인 하셨다면 당황하지 마시고 가까운 이클립스에서 로그를 획득하실 수 있습니다.
디버깅이 취미라면 찾아서 제게 알려주세요.
더이상 로그를 획득하실 수 없습니다.

- 삑삑 거리는 사운드가 징그러울 수 있습니다.

- 렉이 걸리는 것은 조만간 업데이트 될 예정이니 너무 당황하지 마세요. 

- 아직까지 1스테이지만 구성되어 있습니다. (보너스타임 1번)


 

앞으로의 기획적 업데이트 예정 사항

- 베리어 아이템 구현,

- 보너스 타임이 너무 무난하여 더 강렬하게 변경

- 펫, 버프아이템, 아티펙트 등

- 스테이지 재 구성 (밸런스 조정)

- 여러가지 캐릭터 및 능력

- 능력( 블랙홀, 코인 폭죽, 자이언트, 달리기, 일정 시간 무적 등)

- 종합점수 및 결과



앞으로의 프로그래밍적 업데이트 예정 사항

- 캐릭터에 붙어있는 FSM을 매번 생성하지 않고 PoolingManager에 위탁

- 게임팝업

- 유저 정보 저장 

- 구글 로그인 및 페이스북 로그인 SDK 부착

- 한번에 큰 패턴을 생성하면 렉걸리는 현상 수정

- 전체 적인 리펙토링 및 아직 계획되지 않은 추가 작업



앞으로의 그래픽적 업데이트 예정 사항

- 라이프 바가 잘안 보이는 듯 해 교체 예정

- 캐릭터를 고양이로 변경하여 카와이함을 추구할 예정

- 행성도 카와이~

- 모든 것이 카와이~



이후의 포스팅은 전부 게임을 개발할때  프로그래밍적인 내용입니다.


'All > Project' 카테고리의 다른 글

ObjectManager  (0) 2016.04.12
PoolingManager  (0) 2016.04.12
게임 전체 클래스 UML 및 간단설명  (0) 2016.04.12
MemoryPooling_2  (0) 2016.03.15
MemoryPooling_1  (0) 2016.03.15

코드설명은 부분적으로 설명하고 전체를 살펴보겠다.

동작 원리나 개념은 앞의 [MemoryPooling_1] 포스트를 참고하면 된다.



header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <vector>
 
class CPoolingManager
{
    typedef char* MEMORYBLOCK;
    
public:
    static CPoolingManager* Instance();
    
    void CreateObjectList(size_t count, size_t size);     // size만큼의 char형 포인터를 count만큼 Object리스트에 add
    void* ObjectNew();                                    // pool이 보유한 메모리 리스트가 생성하려는 것보다 적으면 새로 생성
    void ReturnToFreeMemory(void* object);               // Object을 메모리블럭으로 전환 (alive off)
    void ReturnToFreeMemoryAll();                        // 모든 Bullet을 메모리 블럭으로 전환 (alive off)
    void DeleteAllMemory();                               // 모든 메모리를 해제한다. (게임이 종료되거나 Scene이 변경될때 호출)
    
private:
    MEMORYBLOCK NewMemoryBlock(size_t sizeconst;        // size만큼의 메모리 블럭을 생성한다.
    CPoolingManager();
    ~CPoolingManager();                                   // 메모리 블럭을 해제한다.
    
private:
    std::vector<MEMORYBLOCK> m_ObjectList;
    int m_ObjectSize;
};
cs

 음.. 뭔가 무지 간단해 보인다.

주석을 달아 놓긴했지만 일단 하나씩 보자.



CPP

가장 먼저 


MEMORYBLOCK

이 MemoryPooling클래스는  MEMORYBLOC이라는 타입을 사용한다. 단순하게 char*이고 

여기에 원하는 만큼의 메모리를 동적할당해서 블럭형태로 만든다.

바로 NewMemoryBlock(size_t size)함수가 size만큼의 메모리 블럭을 만드는 역할을 한다.

내부는 아래와 같다.

1
2
3
4
5
6
CPoolingManager::MEMORYBLOCK CPoolingManager::NewMemoryBlock(size_t sizeconst
{
    MEMORYBLOCK block = new char[size + 1];         // memory alive를 위한 1바이트 추가 생성
    memset(block, 0size + 1);                       // memory 초기화 및 memory alive = false
    return block;             
}
cs

메모리 블럭을 사용 중인지 알아내기 위해 블럭의 원래 사이즈보다 1크게 할당한다.

또 memset을 이용하여 전체를 초기화한다. 이때 마지막 부분은 false로 되어 사용중이 아니라는 것을 체크한다.


이렇게 만든 메모리블럭들은 CreateObjectList(size_t count, size_t size)함수 안에서 리스트에 등록된다. 



CreateObjectList

1
2
3
4
5
6
7
8
9
10
11
void CPoolingManager::CreateObjectList(size_t count, size_t size)
{
    m_ObjectSize = size;
    m_ObjectList.reserve(count);
    while (count--)
    {
        MEMORYBLOCK memBlock = NewMemoryBlock(m_ObejctSize);        // 하나의 크기가 size만큼의 메모리 블럭을
        m_ObejctList.emplace_back(memBlock);                     // count만큼 생성한다.
        CObjectManager::Instance()->AddObject(memBlock);
    }
}
cs

다른 건 신경쓰지말고 중간에 ObjectManager를 보자.

메모리를 어딘가에 등록하고 있다. 

클래스 이름처럼 싱글톤으로 구현된 오브젝트 매니저 클래스이다.

ObjectManager에서는 각각의 블럭포인터를 리스트에 가지고 있다가 모든 object의 Execute를 하기 위해 사용한다.



ObjectNew

사실 제일 할말이 많은 부분이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void* CPoolingManager::ObjectNew()
{
    for (auto object : m_ObejctList)
    {
        if (false == object[m_ObjectSize]) {     // 메모리가 Free상태면
            object[m_ObjectSize] = true;         // 메모리를 사용 상태로 전환 후 반환
            return object;
        }
    }
 
    CCLOG("OBJECT LIST OVERFLOWED");
    MEMORYBLOCK memBlock = NewMemoryBlock(m_ObjectSize);
    m_ObejctList.emplace_back(memBlock);
    CObjectManager::Instance()->AddObject(memBlock);
    memBlock[m_ObjectSize] = true;               // 메모리를 사용 중 상태로 전환
    
    return memBlock;
}
cs

이 함수는 object가 생성될 때,  다시 말해 new 할때 object의 operator new에서 호출한다. 

(operator new에 대해서는 [operator new / operator delete] 포스트를 참고하자.)

상식적으로 이 함수가 수행해야 할 일은 원래의 new가 수행해야할 메모리 할당 이다.

함수의 첫부분을 보면 m_ObjectList를 순회하면서 각각의 object중 현재 사용중이지 않은 메모리를 찾아서 반환해준다. 

반환할 때에는 어떤 포인터가 들어있을지 모르기 때문에 void*로 반환하며 사용중 플래그를 true로 바꾼다.

만약 Object리스트의 모든 object들이 사용중 상태라면 메모리 블록을 하나 만들어서 기존 리스트에 추가한다.

( 이런일은 보통 생각했던것보다 많은 object를 생성하게되면 일어나는 상황이다. )



ReturnToFreeMemory

원래라면 메모리를 사용하고 나서 릭을 보지 않으려면 delete를 수행해야 할 것이다. 

하지만 PoolManager를 사용하고 있는 프로그램에서는 delete를 하지 않고 사용가능 상태로만 만들어준다.

이유는 앞 포스트에서도 설명했듯이 메모리풀링을 하기 위해서이다.

1
2
3
4
void CPoolingManager::ReturnToFreeMemory(void* object)
{
    memset(object, 0, m_ObjectSize + 1);                 // memory 초기화 및 memory alive = false
}
cs



DeleteAllMemory

모든 메모리를 실제로 해제하는 함수이다. 게임을 종료하거나 Scene이 바뀌어서 더이상 메모리를 사용하지 않을때 릭을 방지하기 위해 호출한다.

게임을 종료하기전에는 무조건 호출해야한다. 현재는 Scene이 하나이기 때문에 PoolManager클래스의 소멸자에서 호출한다.

1
2
3
4
5
6
7
8
void CPoolingManager::DeleteAllMemory()
{
    for (auto obejct : m_ObjectList)
    {
        delete []obejct;
    }
    m_ObjectList.clear();
}
cs


이상 포스트를 마친다.

'All > Project' 카테고리의 다른 글

ObjectManager  (0) 2016.04.12
PoolingManager  (0) 2016.04.12
게임 전체 클래스 UML 및 간단설명  (0) 2016.04.12
게임 개요  (0) 2016.04.11
MemoryPooling_1  (0) 2016.03.15

개인적으로 프로젝트를 진행하면서 새롭게 알게된 내용을 정리하려고 한다.

일단은 프로젝트 탭에서 작성하긴 하는데.. 어디에 들어갈지 애매하긴하다. 

언젠가 쓸 operator new,  operator delete에 대한 내용은 어느 탭에 들어가야 하지..

뭐 아무튼 그때가서 생각하기로 하겠다.


MemoryPooling

게임상에서는 많은 오브젝트들이 생성 혹은 삭제된다. 

많은 몬스터가 나오는 게임에서 

몬스터가 만들어질때 new, 죽을때 delete를 하게되면 게임성능 및 속도에 많은 영향을 미치게된다.

특히 탄막슈팅 게임의 경우, 각 총알마다 생성, 삭제를 하게된다면 끔찍한 결과를 보게될 것이다. 

이런경우 MemoryPooling 개념을 도입하면 성능 향상에 큰 도움이 될 것이다.


개념은 간단하다.

1. 게임을 시작하면 메모리 컨테이너에 필요한 만큼의 메모리를 미리 할당 해 놓는다. 초기의 모든 메모리는 Free상태이다.

2. 게임 플레이 중 오브젝트를 생성할 상황이 오면 메모리를 새로 할당하는 것이 아니라 

미리 생성해 놓은 메모리 중 Free메모리를 이용하여 오브젝트를 초기화한다.

4. 사용중인 메모리는 non_Free상태가 된다.

3. 사용 후 오브젝트를 삭제할 때 메모리를 해제하는 것이 아니라 Free메모리로 전환 시킨다.

즉, 게임 플레이중 메모리를 동적할당 하지 않는것이 핵심이다.

좀더 쉽게 그림으로 표현하면 아래와 같다.



장점

1. 위에서 언급했던 것처럼 플레이 중 생성과 삭제의 오버헤드에서 자유로울 수 있다.

2. 메모리 단편화를 막을 수 있다. (메모리 단편화에 대한 포스트는 따로 진행하겠다.)


단점

단순히 new와 delete를 했던 구조보다는 조금 복잡해질 수 있다. 

(물론 ObjectPooling사용자입장에서는 느끼지 못하도록 캡슐화를 해야한다.)


결과 

MemoryPooling을 이용하여 구현한 탄막과 그렇지 않은 탄막의 성능 및 속도 차이 


동영상

1. MemoryPooling사용안함

https://drive.google.com/file/d/0Bzy8i8T-Oo5kSTB3TDFhZU5rT3c/view?usp=sharing


2. MemoryPooling사용

https://drive.google.com/file/d/0Bzy8i8T-Oo5kcktSMjdockNISkk/view?usp=sharing


두 영상은 가운데 존재하는 원과 충돌하면 없어지는 탄막들이다. 

영상을 찍기 시작한 처음부분과 끝나는 부분을 제외하더라도

MemoryPooing개념을 도입한 이후 눈에 띄게 부드러워진 것을 확인 할 수 있었다.


코드에 관한 내용은 [MemoryPooling_2] 포스트에서 계속 이어나가겠다.

'All > Project' 카테고리의 다른 글

ObjectManager  (0) 2016.04.12
PoolingManager  (0) 2016.04.12
게임 전체 클래스 UML 및 간단설명  (0) 2016.04.12
게임 개요  (0) 2016.04.11
MemoryPooling_2  (0) 2016.03.15

+ Recent posts