블로그 이미지
Every unexpected event is a path to learning for you.

카테고리

분류 전체보기 (2323)N
Unity3D (568)N
Programming (472)
Unreal (4)
Gamebryo (56)
Tip & Tech (184)
협업 (34)
3DS Max (3)
Game (12)
Utility (116)
Etc (92)
Link (31)
Portfolio (19)
Subject (90)
iOS,OSX (38)
Android (13)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (1)
Memories (19)
Interest (37)
Thinking (36)
한글 (26)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (19)
Exercise (3)
나만의 맛집 (2)
냥이 (9)
육아 (9)
Total1,343,990
Today28
Yesterday297
Statistics Graph

달력

« » 2019.12
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        

공지사항

태그목록

name1 라는 이름공간과, 이에 속해있는 이름공간 name2 가 있고,

각각에는 Parent라는 이름을 가진 클래스가 있다고 하자. 


즉, name1::Parent 와 name1::name2::Parent 클래스가 있는 상황이다.


이 때, Child라는 이름의 클래스를 하나 만들려고 한다.

name1::Parent 을 상속하며, 멤버 변수로는  name1::name2::Parent 클래스의 포인터를 가진다.


작업하는 코드의 상황은 이렇다.



이름은 Parent 로 같지만, 이름공간이 다른 이것들을 어떻게 표현해야할까?



본래, 자신이 있는 이름공간을 우선으로 검색되는 것이 기본 룰이지만, 이 경우는 예외다.




주목해야할 부분은 멤버 변수 선언 부분의 name2::Parent  이다.

자신이 name2 이름공간에 있음에도 불구하고, name2를 붙여주었다.


상속하는 클래스의 이름은 지금 있는 이름공간보다 우선시 되기 때문이다.


즉, name2를 명시하지 않고 단순히 Parent *p; 로 멤버 변수를 선언한다면,

p는 name2::Parent*형이 아니라 name1::Parent*형이 되어버린다.


출처 : http://blog.naver.com/doogie90/130102637818

Posted by blueasa
TAG namespace

댓글을 달아 주세요

카메라를 흔들려면 카메라를 흔드는 폭을 알아야 한다.

그런데 이 폭을 생각처럼 이쁘게 얻기 힘들다.

그래서 이쁜 공식에 대한 화두를 던저볼까 한다.

 

1. y = sin(x)

 

 

먼저 생각해 볼 수 있고 만만한 공식은 바로 sin 이다.

sin 특성상 파형을 나타낸다. 오르락 내리락 하는것이 단순하면서도

직선적이지 않아 카메라 흔들때 딱이라는 느낌이 온다.

하지만.. 너무 단조롭지 않은가? 점점 감쇄할수 있다면 좋을것 같다.

 

 

2. y = powf(0.5f, x) : 흔히 말하는 y=(1/2)^x 그래프 이다.

 

 

감쇄하는 모양이 아주 이쁘다. 특히나 5 정도를 지나면 0이 아니라 0에 수렴하는 곡선이다.

이 그래프의 감쇄 정도를 상수를 이용해서 수정한다면 아주 이쁜 상쇄 그래프가 나올것이다.

 

 

3. y = sin(x) * powf(0.5f, x)

 

그냥 단순히 곱해봤다. x 축의 값이 time이랑 맞지 않아 이런 결과가 나왔다.

만약 sin 파장을 줄인다면 뭔가 이쁜 그래프가 나올것 같다.

 

 

4. y = sin(x*10.0f) * powf(0.5f, x)

 
그래프가 아주 이뻐졌다.
두 함수의 속성을 이용해서만이라도 훌륭한 카메라 흔들기를 구현할수 있지 않을까?
여러가지 실험을 통해서 이쁜 상수들(일명 매직넘버)을 구하기 바란다.
또한 다른 그래프와의 간섭이 일어난다면 더욱 이쁜 그래프가 나올수도 있다.

[출처] 카메라 흔들기|작성자 시퍼렁새
Posted by blueasa

댓글을 달아 주세요

출처 - http://www.gamecode.org/bak/article.php3?no=3371&page=0&current=0&field=tip

 

작성자 : noerror
E 메일 : noerror@hitel.net
조회수 : 972
작성일 : 2006-07-22 16:53:55
목 : 손쉽게 카메라 흔들기

 

게임중에 충격등의 효과시 카메라를 흔들때 간단하게 sin 함수를 이용해서 그럴듯하게 처리하는 팁입니다. (약간은 감강적인 방식입니다만 - 이론적 근거가 별로 필요없다는 얘기죠. ^^ - 효과는 괜찮습니다.) 

결론부터 말씀드리자면 sin 함수를 여러개 합해서 사용하는 것입니다. 실제로 제어쪽에서는 충격(임펄스)를 가해서 그 응답 특성으로 시스템 특성이나 안정도등을 분석하기도 합니다. 보통 시간에 따른 주파수 특성을 분석하는데, 역으로 사인파로 어떤 특성을 가진 시스템을 만든다고 생각하면 될거 같네요. (구지 그럴듯하게 말을 붙이자면... ^^) 

기본적으로 사인파의 결과가 부드럽게 나타나기 때문에 자연스럽게 연속적인 모양으로 나옵니다. 충격에 따른 사인파의 특성 (진동하는 정도와 주파수, 충격이후 진폭이 서서이 줄어드는 특성)을 아래처럼 간단하게 사인함수로 구현할 수 있습니다. 

W = sin( 2 * 3.14159f * (t * freq) + phase ) * amp * (decaysec - t) / decaysec ; 

t 현재 시간, amp(const) 진폭, freq(const) 진동수, phase(const) 위상, decaysec(const) 선형감쇄시간 

사실상 특별한 이론이 있다기 보다는 상황에 따라 감각적으로 각 상수를 정해서 표현하게 됩니다.^^ 
* 진폭은 결과가 상하로 이동하는 정도가 되며 값이 클수록 더 크게 진동합니다. 
* 진동수는 초당 상하 운동하는 횟수로 값이 클수록 더 빨리 진동합니다. 
* 위상은 함수의 시작 포지션 정도로 실제로 값에 따른 느낌을 주는 것은 아니지만 함수들을 여러개 더할 때 좀 더 다양한 형태로 결과가 나옵니다. (사실상 주파수가 n 배로 할 경우 작은 주파수에서 항상 0가 되는 데, 위상을 주면 결과가 다르게 나옵니다. - 어차피 상수값을 감각적으로 넣는 데, 결과물에 다양함을 줄 수 있는 값 정도로 보면 될거 같습니다.) 
* 감쇄시간은 감쇄되는 함수모양에 따라 다른데, 어차피 모델링할 정확한 모양이 있는 게 아니므로 그냥 단순히 시간에 따라 감쇄한다고 설정하면 선형으로 하는 것도 별 문제는 없어 보입니다. (이 경우 t 가 decaysec 보다는 작거나 같아야 겠죠.) 

자 그럼 이걸로 어떻게 사용하냐 하면 

float t = (timeGetTime() - m_uWaveStartTime) * 0.001f; 

if (t < m_fDecayTime) 

    camera.pivot.y += (sin (2.0f * 3.14159f * t * 3) * 30.0f + 
        sin (2.0f * 3.14159f * t * 7 + 0.2f) * 10.1f + 
        sin (2.0f * 3.14159f * t * 15 + 0.5f) * 1.1f)) * ( m_fDecayTime - t) / m_fDecayTime; 


이런 식입니다. ^^ 

실제 주파수는 화면 갱신 타임에 영향을 주기 때문에 값이 크더라도 크게 의미가 없습니다. 
그리고 만약 60 hz로 갱신되는 모니터라면 화면이 흔들리지 않는 상황이 생깁니다. ^^ 
(여러번 해보면 주파수가 높은 사인파의 진폭에 대한 느낌이라던가, 파형수가 많을 때와 적을 때의 차이라던가하는 느낌이 옵니다.) 

그냥 적당히 감각적(!)으로 상수를 설정해서 사용해보고, 결과가 괜찮으면 OK!! 
(흔들기 뿐 아니라, 번쩍거리기 등 활용 예는 많겠죠 ?) 

이상 별 내용은 없지만, 너무 간단해서 별로 이슈화 안되는 내용을 함 적어봤습니다.

Posted by blueasa

댓글을 달아 주세요





Game Programming Gems 2권에 나와있는 데칼알고리즘을 이용해서 데칼을 만들어보았다.

흠... 만들어보긴했는데... 아직 실전에 응용을 하지않아 효용성이 얼마나

있는지는 모르겠다.

 

탄젠트 벡터를 구하는데... GPG에서 사용해보라고하는 (1.0f, 0.0f, 0.0f)를 넣으니까

데칼이 이상하게 박힌다.  해당 벡터가 법선과 수직이 아니라서 발생하는 문제인듯하다.

지형에서만 사용가능한건지는 모르겠지만..

 

처음에는 픽킹된 폴리곤의 한정점과 픽킹된 점을 구해 탄젠트 벡터를 만들었다.

탄젠트 벡터가 테칼이 그려지는 방향에 영향을 주는 요소라.. 픽킹할때마다

그림의 방향이 바뀌게 된다.

 

그래서 탄젠트 벡터를 지정포지션과 법선벡터(0.0f, 1.0f, 0.0f)와 지정포지션을 정점으로

갖는 평면으로 지정포지션을 가지고있는 폴리곤을 분할해 충돌점을 구하고

지정포지션과 충돌점 사이의 벡터를 탄젠트 벡터로 주었다. 그러니까

나름대로 잘 나오는듯 한것 같다 ^^;

 

원래는 비공개로 할까 했는데... 어노인팅님이 저를 불사르시는군요..

ㅋ 그래서 공개자료로 전환합니다.

 

이소스는  NET2005 그리고 DX9.0c SDK를 필요로 합니다.

가져가실때 출처를 꼭 밝혀주세요.


출처 : http://blog.naver.com/swoosungi/90017269111

'Programming > C/C++' 카테고리의 다른 글

카메라 흔들기  (0) 2011.03.05
[게임코드]손쉽게 카메라 흔들기  (0) 2011.03.04
임의의 표면에 데칼 적용하기(소스포함)  (2) 2011.03.04
ifdef, if defined, ifndef, if !defined  (0) 2011.03.04
enum, 보다 나은 enum  (0) 2011.01.13
IME 소스 (한글)  (2) 2010.11.26
Posted by blueasa
TAG Decal

댓글을 달아 주세요

  1. 2011.07.13 14:23 ㅗㅁ  댓글주소  수정/삭제  댓글쓰기

    퍼가요 ㄳ합니다~~

#ifdef 는 정의되있는지를 테스트 하므로, 한번에 여러개를

사용할 수는 없다. 형식이 #ifdef name 처럼,

하나의 토큰이 되어야한다.

여러개가 정의되있는지 테스트 하기위해서는 
#if defined(MACRO1) || defined(MACRO2)처럼 사용.

단, #if는 ||로 중첩해서 사용할 수 있다.
형식이, #if expression이므로, C표현이 올수 있다.

#if MACRO1 || MACRO2 처럼 사용해도 된다.

 

 

#if !defined() 의 의미는 #ifndef 와 같다. (if not define)

()안의 내용이 정의되어 있지 않으면 그 아래부분 (#endif까지)을 유효한 소스코드로 인식하고 컴파일 시 적용하라는 의미다.

 

그런데 그 괄호안의 부분을 찾을 수가 없다는 것은

EXCLUDE_PGX_SUPPORT 가 아무데도 define되지 않았음을 의미

 

정의되어 있지 않으므로 해당 구문 이후의 소스코드는

유효한 코드가 된다.

 

#if !defined(...)

 

안의 내용은 유효코드

 

#endif

 

반대로, #ifdef (...)라면

해당 키워드가 정의되어 있지 않으므로

#endif  까지의 코드내용은 전부 무의미해진다.



출처 : http://blog.naver.com/ghostcbr954/110003736409

Posted by blueasa

댓글을 달아 주세요

#define 과 얼핏 비슷해 보이지만비슷한 유형별의 데이터를 표현하기 위해 반드시 필요한 C++의 필수 타입인 enum 의 몇 가지 재미있고 유용한 팁을 소개합니다.

 

1. namespace 와 결합하기

 

일반적으로 enum 을 이용하여 타입이름을 짓기란 쉬운 일이 아닙니다.

만약 리스트에 필요한 정렬 기준을 enum 으로 표현한다면 아래 정도가 됩니다.

 

enum SORT_LIST

{

           SORT_DATE,                 // 날짜순

           SORT_NAME,                // 이름

           SORT_CONTENT, // 내용

           S0RT_ETC,                    // 기타

};        

 

bool SortSomeData(SORT_LIST SortList)

{

           switch (SortList)

           {

           case SORT_DATE:

                     break;

           case S0RT_NAME:

                     break;

           // ....

           }

}

 

 

하지만이러한 enum 표현 방식은

  •  SORT_DATE 라는 이름은 너무 흔합니다.  프로그램이 조금만 커져도 이러한 이름들이 다른 곳에서도 필요하게 되어 이름 충돌을 피하기 힘들어 집니다.  이름 충돌을 피하기 위해서는 SORT_LIST를 변경해도 아무런 소용이 없고 SORT_DATE 를 완전히 다른 이름으로 지어야만 합니다.
  • enum 의 타입이름인 SORT_LIST와 실제 값인 SORT_DATE 를 서로 연관 짓기가 힘듭니다. switch 문 내에서도 타입이름(SORT_LIST)은 사용되지 않아서 SORT_DATE만 보고 이 값이 SORT_LIST 의 한 유형인지 구분 짓기 힘듭니다
  • enum 값에 항상 들어 있는 SORT 라는 prefix 도 부담스럽습니다. SORT_XXX 식으로 이름을 짓다 보니타입핑하기도 힘들구요

 

평소 이런 문제에 대해서 알고는 있었지만 그 해결책을 고민만 하던 차에 아주 좋은 해결책을 발견했습니다바로 아래 글에서요. (늘 해결책은 제가 아닌 책이나 다른 글에서 구합니다.)

 

Stupid C++ Tricks #2: Better Enums

 

이 글에서 제안하는 방식은 enum  namespace과 결합하여 가독성을 증가 시키고이름충돌도 최소화시키는 방식입니다. 위에 든 예를 namespace 식으로 표현하면 다음과 같습니다.

 

namespace SortList

{

           enum Enum

           {

                     BytDate, // 다가올날기준

                     ByName,                     // 이름

                     ByContent,        // 내용

                     Etc,

           };        

}

 

bool SortSomeData(SortList::Enum SortList)

{

           switch (SortList)

           {

           case SortList::ByDate:

                     break;

           case SortList::ByName:

                     break;

           // ....

           }

}

 

namespace 와 결합함으로써, enum 의 각 항목에는 항상 SortList 라는 namespace가 따라 붙게 되어 가독성이 증가함을 알 수 있습니다SORT_DATE  보다는 SortList::ByDate 가 읽기 쉽고요즘 웬만한 IDE 에서는 namespace SortList::만 입력하면 자동으로 이하 소속되는 타입이름들이 나열되기 때문에 미스타이핑 할 가능성도 줄여주네요.


사용자 삽입 이미지


게다가 namespace 를 사용하기 때문에 다른 모듈과 이름이 충돌할 가능성도 줄어들었습니다.

이름 충돌이 없기 때문에 아래와 같이 NameEdit 라는 enum 값을 여러 namespace 에 걸쳐 사용하면서 각각의 의미에 맞는 값들을 줄 수가 있게 됩니다.

 

namespace ControlWidth

{

           enum Enum

           {

                     NameEdit = 20,

                     AddressCombo = 60,

                     OkBtn = 30;

                     // ....

           };

};

 

namespace ControlID

{

           enum Enum

           {

                     NameEdit = 100,

                     AddressCombo = 101,

                     OkBtn = 102;

                     // ....

           };

}

 

이와 같이 enum 을 표시하는 방식은 실제 꽤 유용하게 써 먹을 수 있어서 지금 새로 만들고 있는 프로젝트에서도 재작성하는 코드에서 enum 타입을 위 방식으로 변경하고 있는 중입니다.

괜찮은 방법 아닌가요 ? ^^;

 

 

2.  enum 에 문자열 결합하기


2번째 팁은 숫자로만 되어 있는 enum 을 문자열로 치환하는 팁입니다.

C++ 에서 enum 을 정수형으로만 치환가능하기 때문에 사용자가 읽을 수 있는 텍스트로 변경하기 위해서는 enum 값에 1:1 매칭되는 텍스트가 필요한 경우가 많습니다.

이를 해결하는 방법으로 아래 포스팅에서는 enum  #define 매크로를 이용해서 문자열을 매칭시키는 방법을 소개합니다.

 

Enums, Macros, Unicode and Token-Pasting

 

Visual C++ Team Blog 에 소개된 이 방법은 숫자로이뤄진 enum 값과

 

enum Animal { dog, cat, fish, bird };

 

문자로이뤄진char값을매칭시키는방안에대한내용입니다.

 

wchar_t* AnimalDiscription[] = { L"dog", L"cat", L"fish", L"bird" };

 

이를 위해 animal.inc 파일에 아래 라인들을 추가하고

 

MYENUM(dog)

MYENUM(cat)

MYENUM(fish)

MYENUM(bird)

 

아래와 같이 enum 을 선언합니다.

 

enum Animal {

#define MYENUM(e) _##e,

#include "animal.inc"

#undef MYENUM

};

 

wchar_t* AnimalDescription[] = {

#define MYENUM(e) L"_" L#e,

#include "animal.inc"

#undef MYENUM

};

 

#define 으로 조금 복잡해 보여서 그렇지, enum 값과 wchar_t* 배열의 선언을 MYENUM 매크로 정의를 통해 확장하는 식입니다.  조금 특이한 것은 MYENUM  enum  wchar_t* 배열 속에서 조금씩 다르게 선언하고데이터를 추가한 후( #include "animal.inc") 에는 바로 undefined 시켜버리는 정도네요

 

이 방법 마음에 드시나요?

해당 링크에 달린 댓글들을 보면 많은 분들이 마음에 들지 않나 봅니다.

가장 크게 문제 삼은 내용은 역시 아래 글과 같은 #define 매크로에 대한 조금 헤묵은 논쟁들입니다.

 

Macros is something C++ (especially the Standards Committee) has always tried to avoid. Such use of macros like this will make code unreadable and cause maintainance problems. I think it's not a good idea to play with such thing.

 

개인적으로는 #define 을 활용한 매크로 활용을 좋게 보고 있습니다.

제가 #define을 좋아하는 이유는

  • 코딩량을 줄여 줍니다코딩하는 량이 줄어든다는 것은 그만큼 유지관리할 코드가 줄어 들고미스타이핑에 의한 작은 실수들을 걸러주기 때문입니다.
  • 자주 해야 하지만 귀찮은 작업들 - Get/Set 함수 같은 - 을 쉽게 처리해 줍니다저 같은 경우 클래스 하나를 만들면 대부분은 private 멤버변수들이어서 늘 public 으로 Get/Set 함수를 구현하는 게 고역 중에 고역인데요.  #define 매크로를 활용하면 늘 심플하고 코드 보기도 편하더군요.


Visual C++ 에 있는 _countof 매크로도 정말 없어서는 안될 매크로 중 하나입니다.

 

extern "C++"

{

template <typename _CountofType, size_t _SizeOfArray>

char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];

#define _countof(_Array) sizeof(*__countof_helper(_Array))

}

 

아무튼논란의 여지가 있긴 하지만이 팁의 주요 목적은 enum Type 으로 추가할 숫자값과문자열 값이 어느 하나만 추가되거나삭제되지 않고 늘 한번에 관리 될 수 있도록 하기 위한 방안 중에 하나라고 봅니다.  유용했다면 쓰면 될 것이고 그렇지 않으면 안 쓰면 되겠죠 :-)

댓글 중에는 하나의 .inc  include 할 필요 없이 하나의 파일에서 모두 해결하는 방안도 있으니 참고하세요

 

#define ANIMALS(FOO) \

FOO(dog) \

FOO(fish) \

FOO(cat)

 

#define DO_DESCRIPTION(e)  L"_" L#e,

#define DO_ENUM(e)  _##e,

 

wchar_t* AnimalDescription[] = {

ANIMALS(DO_DESCRIPTION)

};

 

enum Animal {

ANIMALS(DO_ENUM)

};

 

 

3.  enum 의 처음과 끝을 지정하자

 

1, 2번에 비하면 좀 약한 팁입니다만, enum 을 사용할 때 처음과 끝을 지정하자는 아이디어는 Code Complete 2 책에 p436에 나와있습니다.

아래와 같은 국가명을 열거할 필요가 있을 때처음과 끝에 해당하는 값을 XXX_FIRST 와 XXX_LAST로 지정하는 방법입니다.

 

enum Country

{

           Country_First = 0,

           Country_China = 0,

           Country_England = 1,

           Country_France = 2,

           Country_Germany = 3,

           Country_India = 4,

           Country_Korea = 5,

           Country_Japan = 6,

           Country_Usa = 7,

           Country_Last = 7,

};

 

XXX_FIRST 와 XXX_LAST 가 시작과 끝을 가르키기 때문에 이를 활용하여 열거형의 모든 항목을 조회하여 원하는 일을 할 때 유용합니다.

 

for (Country cty = Country_First; cty <= Country_Last; cty++)

{

           // 원하는로직수행

}

 

추후 새로운 국가 Country_Italy 를 추가한다면 아래와 같이 enum 만 수정하면 enum 을 사용하는 for loop 는 손대지 않아도 되는 장점이 생깁니다.

 

           Country_Usa = 7,

           Country_Italy = 8,

           Country_Last = 8,

}

 

이상 enum 을 활용하는 몇 가지 팁을 알아 봤습니다 혹시 나만의 좋은 팁이 있으시면 같이 공유해 주세요.


출처 : http://eslife.tistory.com/300

Posted by blueasa
TAG enum

댓글을 달아 주세요

IME 소스 (한글)

Programming/C/C++ / 2010. 11. 26. 21:58



ime 뭉탱이(module)  날읽어(README) ver 0.01

                     안노라군.
                 TEAM TRINITY.
             http://anoa.wo.to 
             kjm37@hanmail.net
//---------------------------------------------------------------------
적은 허접한 맨트와.. 버그있는 소스로 ime 강좌(같지도 않은)를 써서 
초보 IMER (ime + er)분 들을 나락의 길(?)로 빠트렸던 anoa 군입니다.;

아직도 몇몇 군데의 웹페이지들에 그 글들이 있길래. 너무도 죄송한(이라고 
쓰고 쪽팔린 이라고 읽는다.) 마음에.. 최종버전 소스를 정리해서 올립니다.


이번엔 강좌대신 상세한 주석(이라고 쓰고 게으름이라고 읽는다)으로 때웁니다.


          2003. 2. 16.
           class 청소부.  아노아군.

//---------------------------------------------------------------------
 소스
//---------------------------------------------------------------------
 프로젝트  : vc.net (vc 7.0) 
 마지막 업데이트 : 2003 년 2월 17일.
 소스의 기능  : IME 입력 및.. 특수문자(한자)의 목록 얻어내기.
      커서 기능 추가. (멀티라인은 지원하지 않습니다.)
 프로젝트구성 : ime
      +- BASE
       +- Error.h  // 에러 메세지용 헤더
       +- Main.cpp  // WinMain 및 프록시져.
       +- Main.h
       +- stdafx.cpp
       +- stdafx.h  // 프리컴파일헤더
      +- IME
       +- ImeBox.cpp // 각각의 ImeBox.
       +- ImeBox.h
       +- Ime.cpp  // ImeBase
       +- Ime.h
      +- Document
       +- ReadMe.txt // 지금 이문서.

//---------------------------------------------------------------------
 사용방법.
file://---------------------------------------------------------------------

 . 주석을 읽기가 귀찮으신분은 ImeBox.cpp ImeBox.h ime.cpp ime.h 를 그대로
가져다 쓰시고 main.cpp 부분을 참고하셔서 붙이시길 바랍니다.

 . 한국어외의 다른 ime 입력을 보장하지 않습니다.. (수고하세요)
 
 . ime 특유의.. 입력 오류시 띵띵~ 거리는 소리를 없에실 좋은 아이디어가 있
    으신 분은 연락을 주세요.

 . 소스를 편집 및 재배포 할시에는 연락을(홈페이지) 해주세용~♡ 재배포시
  에는 본문서에 하나의 파트를 만드시고.. 소스의 구성이나 편집한 부분.
  아직 해야할부분. 작성자 등을 남겨주세요.


//---------------------------------------------------------------------
 버전(버진 아님. -//-)
file://---------------------------------------------------------------------
v 0.1    kwonil 님의 도움으로.. 조합중이던 문자를 날렸음. - _-)V



출처 : http://blog.naver.com/cpp2angel?Redirect=Log&logNo=100007002652

[출처] IME 소스 (한글)|작성자 아발롱


'Programming > C/C++' 카테고리의 다른 글

ifdef, if defined, ifndef, if !defined  (0) 2011.03.04
enum, 보다 나은 enum  (0) 2011.01.13
IME 소스 (한글)  (2) 2010.11.26
[C++] 형변환 (Typecasting) - const_cast, reinterpret_cast, static_cast, dynamic_cast  (0) 2010.09.28
GameLoop (번역)  (0) 2010.09.16
IME 관련  (0) 2010.09.10
Posted by blueasa
TAG ime

댓글을 달아 주세요

  1. 2012.02.20 10:00 Amelia  댓글주소  수정/삭제  댓글쓰기

    웹사이트 !처럼 우리는 이것이 정말 내 중 하나입니다 사실은 친절 에 !

 형변환 (Typecasting)  
 : 그동안 명시적인 형변환을 할때는 괄호를 사용했었는데 C++에서는 이를 대체 할 수 있는 4가지 종류의 형변환 연산자가 추가 되었다. 그럼 그동안에 사용했던 명시적 형변환을 계속 쓰지 않고 새로운 형변환 종류가 추가 되었을까? 일단 기존의 C 스타일 형변환은 두가지 문제점이 있다. 첫번째는 C 스타일의 형변환(컴파일 타입 형변환)은 눈에 잘 띄지도 않고 찾아내기 힘이 든다는 점이다. 사용자가 프로그램을 짜다 보면, 형변환 말고도 괄호를 사용하는 부분이 많기 때문이다. 뭐 눈에 잘 안띈다고 단점이될까? 
  명시적 형변환을 수행한다는 것은 암시적인 형변환이 불가능하다는 뜻이고, 암시적인 형변환이 불가능하다는 것은 컴퓨터가 생각하기에는 문제의 소지가 있다는 뜻이다. 다음으로 C 스타일의 형변환은 형변환의 의도를 구별해내기가 힘들다는 문제점도 있다. C++에서의 형변환 연산자는 그 용도에 따라서, 안전한 형변환, const 속성을 제거하는 형변환, 위험한 형변환, 클래스 타입간의 형변환 등으로 나뉘어져 있다. 그렇기 때문에 C++ 연산자를 사용해서 형변환을 하면 코드를 읽는 사람이 형변환의 의도를 쉽게 알아챌 수 있다. 컴퓨터 역시 코드를 작성한 사람의  의도를 파악 할 수 있기 때문에 컴퓨터가 개발자의 실수를 발견해서 경고해주는 것도 가능하다. 

 const_cast  
 : const_cast는 어떤 타입에서 const 속성이나 volatile 속성을 제거 할때 사용한다. 
* volatile속성 
 : 변수를 정의할때 volatile 키워드를 붙여줄 수 있는데 컴퓨터는 가끔씩 어떤 이유에서 변수를 상수로 만들어버리는 작업을 하게 되는데,  volatile로 지정한 변수는 그 작업에서 열외가 된다. 대부분의 경우 변수를 상수로 만드는 작업은 프로그램의 성능을 높이는데 도움이 되지만, 어떤 상황에서는 문제를 일으키기 때문이다. (지금은 쓸데가 없지만, volatile은 Multi threading과 관련된 곳에서 사용할 일이 생길 수 있다.) 다음은 const int 타입을 int 타입으로 형변환 하는 코드이다. 
  1. const int value = 100;   
  2. int i = const_cast<int> (value);  

 reinterpret_cast  
 : reinterpret_cast는 일반적으로 허용하지 않는 위험한 형변환을(무조건적인 형변환 ) 할때 사용한다. 즉, 그 안의 데이터가 어떤 객체이던 그저 비트열로만 보고 원하는 형으로 강제로 변환을 한다는 것이다. 예를 들어 포인터를 정수로 변환하는 작업 등이 이에 해당 되겠다.
  1. int a, b;   
  2. a = reinterpret_cast<int>(&b);  

 static_cast  
 : static_cast는 가장 일반적인 형태의 형변환을 할때 사용한다. 만약에 A타입에서 B타입으로의 암시적인 형변환이 가능하다면 static_cast를 사용해서 B타입에서 A타입으로 형변환 할 수 있다. 예를 들어 double 타입을 char 형으로 형변환하는데 사용할 수 있겠다.
  1. double d = 10.0;   
  2. char c;   
  3. c = static_cast<char>(d);  
 static_cast는 명시적인 형변환이기는 하지만 대체적으로 안전한 형변환이라고 볼 수 있다. 아마도 우리가 수행하는 대부분의 형변환은 여기에 속할 것이다. 

 dynamic_cast  
 : 유일하게 C 스타일의 형변환으로는 흉내낼 수 없는 것이 dynamic_cast이다. dynamic_cast는 서로 상속 관계에 있는 클래스간에 형변환을 할 때 사용한다. 더불어 형변환을 수행하는 동시에 이 형변환이 안전한 것인지까지 검사 해준다. 그래서 dynamic_cast는 실시간에 형검사를 하거나 형변환할 때 사용한다.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Parent   
  5. {   
  6. public:   
  7.     virtual void Print( ) {}   
  8. };   
  9. class Child1 : public Parent   
  10. {   
  11. public:   
  12.     void Print( ) { cout << "class Child1 no problem" << endl; }   
  13. };   
  14. class Child2 : public Parent   
  15. {   
  16. public:   
  17.     void Print( ) { cout << "class Child2 no problem" << endl; }   
  18. };   
  19. void main( )   
  20. {   
  21.     Parent *p = new Child1;   
  22.     Parent *p1 = new Parent;   
  23.   
  24.     Child2* faile_child = dynamic_cast<Child2*> (p1);   
  25.     if( NULL == faile_child )   
  26.         cout << "Child2 Null 반환." <<endl;   
  27.     else  
  28.         faile_child->Print();   
  29.   
  30.     Child1 *pChild1 = dynamic_cast<Child1*>(p);    
  31.     if( NULL == pChild1 )   
  32.         cout << "Child1 Null 반환." <<endl;   
  33.     else  
  34.         pChild1->Print();   
  35.   
  36.     Child2 *pChild2 = dynamic_cast<Child2*>(p);   
  37.     if( NULL == pChild2 )   
  38.         cout << "Child2 Null 반환." <<endl;   
  39.     else  
  40.         pChild2->Print();   
  41.   
  42.     try  
  43.     {   
  44.         Child2& C2 = dynamic_cast<Child2&> (*p);   
  45.     }   
  46.     catch (bad_cast& e)   
  47.     {   
  48.         cout<<"bad_cast currupt"<<endl;   
  49.     }   
  50. }  

 위 예제에서 본것처럼, dynamic_cast는 다운 캐스트, 즉 부모 클래스 타입에서 자식 클래스 타입으로 형변환 할때 유용하게 사용할 수 있다. 다운 캐스트는 포인터나 레퍼런스가 가리키고 있는 객체의 실제 타입이 무엇이냐에 따라서 안전할 수 있고 위험할 수도 있는데 dynamic_cast가 알아서 안전여부는 검사를 해준다. 
 만약에 형변환에 문제가 있는 경우라면 dynamic_cast 연산자는 NULL 값을 반환하거나 bad_cast 예외를 던지게 된다.(위 결과 처럼 말이다.) 포인터의 형변환이라면 NULL을 반환함으로써 문제 상황을 알릴 수 있지만, 레퍼런스의 형변환인 경우에는 어떤 특정한 값은 반환하는 것이 불가능하므로 bad_cast 예외를 던지게 된다. (bas_cast 에외 역시 C++의 다른 예외 클래스들처럼 exception 클래스를 상속받았다.)
 위에서는 의미도 없는 가상함수를 사용했는데, 가상함수가 하나도 없는 클래스는 dyynamic_cast를 사용할 수 없기 때문이다. 이는 RTTI의 내부 구현과 관련이 있다. 위의 예제는 나머지 자식 클래스들도 상속받아 물려받은 가상 함수가 있는 셈이므로 dynamic_cast를 사용 할 수 있는 것이다. 만약 클래스가 가상함수를 하나도 가지지 않는다면 해당 클래스는 타입으로는 RTTI 를 이용할 수 있지만 객체로는 RTTI 를 이용할 수 없다.

 그럼 검사도 해주고 형변환도 해주는 dynamic_cast가 만능이냐? 아니다. static_cast<Child1*>(p); 이와 같이 static_cast를 해줄 수도있다. 하지만 이는 아무런 검사도 하지 않고 형변환을 하기 때문에, 실제론 잘못동작할수 있는 코드가 아무런 경고나 에러없이 컴파일되게 되는 불안요소를 가지고 있다고 할 수 있죠. 
 dynamic_cast는 RTTI를 이용해서 런타임시에 형을 체크하기 때문에, 잦은 dynamic_cast는 눈에 띌 정도의 퍼포먼스 저하의 원인이 될수 있습니다. 그렇기 때문에 자신이 하는 형변환하는 것이 안전하고 확실하다고 생각할때만 static_cast를 이용하고, 확신할수 없을 경우는 dynamic_cast를 이용하여 널포인터를 체크하는 것이 바람직하다.

 RTTI (Runtime Type Information)  
 : RTTI (Runtime Type Information)실행시간에 객체의 타입에 대한 정보를 얻을 수 있는 기능을 말한다. C++은 클래스의 객체만 가지고선 어떤 클래스의 객체인지 알수 있는 방법이 원래 없기 때문이다. 형변환 중 dynamic_cast를 할려면 RTTI가 필요 한데, 우리가 많이 쓰는 Visual Studio는 기본적으로 RTTI 기능을 사용하지 않게 설정되어 있다. 왜냐하면 RTTI의 특성상 객체를 생성할때마다 그 객체 내부에 타입 정보와 상속 정보를 넣어두기 때문에 속도(퍼포먼스)의 저하가 일어나기 때문이다. 
그래서 RTTI 기능 및 dynamic_cast를 사용하기 위해서는 비주얼 스튜디오의 프로젝트 설정을 변경 시켜 줘야 한다. 프로젝트의 Properties에 들어가 C++ -> Language -> Enable Run-Tie Type Information을 Yes(/GR)로 바꿔주면 된다.

  그럼 이 RTTI와 dynamic_cast는 어떻게 작동을 하는 것일까? 컴파일러는 RTTI 와 객체를 연결하기 위해서 가상함수 포인터 테이블을 이용을 한다. 원래 C++ 언어의 가상함수 포인터 테이블은 순수한 가상함수에 대한 함수 포인터 배열이다. RTTI 와 객체의 연결을 위해 C++ 언어는 가상함수 포인터 테이블 앞에 4 byte 를 만들고 이것을 RTTI 와의 연결 고리로 사용한다. 
 프로그램이 dynamic_cast 를 이용하여 캐스트를 한 경우 실행 코드는 dynamic_cast 의 표현식에 기술된 객체를 이용하여 RTTI 포인터 테이블을 검색하고, 만약 RTTI 포인터 테이블 상에 일치하는 RTTI 가 존재 한다면 표현식에 기술된 객체의 타입을 변환하여 반환하고, RTTI 포인터 테이블 상에 일치하는 RTTI 가 존재 하지 않는다면 dynamic_cast 는 NULL(0) 을 반환을 할 것이다. 

출처 : 

'Programming > C/C++' 카테고리의 다른 글

enum, 보다 나은 enum  (0) 2011.01.13
IME 소스 (한글)  (2) 2010.11.26
[C++] 형변환 (Typecasting) - const_cast, reinterpret_cast, static_cast, dynamic_cast  (0) 2010.09.28
GameLoop (번역)  (0) 2010.09.16
IME 관련  (0) 2010.09.10
TLS (Thread Local Strorage)  (0) 2010.08.30
Posted by blueasa
TAG cast

댓글을 달아 주세요

GameLoop (번역)

Programming/C/C++ / 2010. 9. 16. 19:43

소개

 게임 루프는 모든 게임의 핵심입니다. 어떤 게임도 게임 루프 없이는 실행될 수가 없습니다. 그러나 불행히도 새로운 게임 프로그래머에게 인터넷 상에서 이러한 내용이 적절하게 나와있는 좋은 곳을 찾을 수 없습니다. 그러나 두려워하지 마세요. 왜냐하면 게임루프에 주목해 볼만한 글을 방금 발견했기 때문에요.


 나의 직업이 게임 프로그래머인 것에 감사합니다. 소형 모바일 게임들을 위한 많은 코드를 접하게 되었습니다. 몇 몇의 게임 루프들이 나를 놀라게 만들었습니다. 아마 당신도 게임 루프들이 어떻게 다르게 쓰여지는 것에 대해 궁금할것입니다. 가장 인기있는 게임 루프 방법에 대해 찬반토론을 하겠습니다. 또한 게임루프 실행의 최고 방법을 알려 드리겠습니다.( 제 생각으로요.)


(브라질의 포루투칼어로도 이용하게 해줄수 있게 한  Kao Cardoso Felix 감사드립니다.)

 

 게임 루프
 모든 게임들은 사용자의 입력, 게임의 상태 수정, AI 조작, 배경음과 효과음들의 연속으로 이루어져 있다. 이 연속된 동작들은 게임 루프를 통해 처리된다. 소개에서 말한대로 게임 루프는 모든 게임의 핵심이다. 앞서 언급한 작업에 대해 자세하게 설명하지 않고, 게임루프 하나에 집중하도록 하겠다. 그래서 작업들을 두개의 기능으로 단순하게 했다 : 게임 업데이트와 디스플레이

 

최고로 단순한 게임 루프 예제 코드:

 

bool game_is_running = true;
while( game_is_running )  {
    update_game();
    display_game();
}


 이 단순한 게임 루프의 문제는 시간을 고려하지 않고 있다. 그저 게임을 실행할 뿐이다. 느린 하드웨어에서는 느려지고, 빠른 하드웨어에서는 빨라진다. 예전 하드웨어의 속도를 알고 있다면 상관이 없다. 그러나 오늘날 많은 하드웨어 플랫폼이 존재한다. 우리는 속도를 다른 어떤 방법으로 실행해야 한다. 여기에 많은 방법들이 있고, 이 방법들에 대해 이어지는 섹션에서 토의해 보자

 

 첫째, 이 글에서 사용하는 두 단어에 대해 설명하겠다 :

 

FPS
 FPS는 Frames Per Second의 약어이다. display_game()의 초당 호출 횟수이다.

게임 속도
 게임 속도는 초당 게임 상태의 갱신 속도이다. 다른 말로는 초당 update_game()의 호출 횟수이다.

 

 FPS가 게임속도에 의존적

구현
 타이밍 문제를 해결하기 위한 쉬운 방법은 단지 초당 25 프레임으로 게임을 실행하면 된다. 코드는 다음과 같다.

   

    const int FRAMES_PER_SECOND = 25;
    const int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;

 

    DWORD next_game_tick = GetTickCount();
    // GetTickCount() returns the current number of milliseconds
    // that have elapsed since the system was started

 

    int sleep_time = 0;
    bool game_is_running = true;


    while( game_is_running ) {
        update_game();
        display_game();
        next_game_tick += SKIP_TICKS;
        sleep_time = next_game_tick - GetTickCount();
        if( sleep_time >= 0 ) {
            Sleep( sleep_time );
        }
        else {
            // Shit, we are running behind!
        }
    }

 

 이 방법은 큰 혜택이 있다. 참 쉽죠! update_game()이 초당 25번 불려진다는 것은 알기에 게임 코드를 작성하기에 쉽다. 예를 들면 이러한 게임 루프는 응답 구현 기능의 구현이 쉽다. 만약 게임 안에서 랜덤 값을 사용하지 않는다면, 단지 유저의 입력 변화 혹은 재생들을 기록하기만 하면 된다.

테스트하는 하드웨어에 FRAMES_PER_SECOND를 이상적인 값으로 적응시킬 수 있다. 하지만 보다 빠르거나 느린 하드웨어서는 무슨 일이 벌어질것인가? 그럼 이제 알아보자.

 

느린 하드웨어

 하드웨어서 정해놓은 FPS를 다룰 수 있다면 문제가 없다. 그렇지만 하드웨어에서 FPS를 다룰 수 없을 때 문제가 발생한다. 게임은 느려질 것이다. 가장 나쁜 케이스는 게임이 무거운 부분에서는 느리게 움직이고 어떤 부분에서 정상적이게 게임이 돌아가는 것이다. 타이밍은 왜곡되어서 게임을 할 수 없게 만든다.

 

빠른 하드웨어

 빠른 하드웨어에서는 문제가 없을 것이다. 하지만 정말로 많은 귀중한 클럭 사이클을 낭비하는 것이다. 쉽게 300 FPS를 만들 수 있을 때 25 또는 30FPS를 돌리는 것은... 부끄러운 줄 알아라! 특히 빠르게 움직이는 객체에 대해 시각적인 효과를 낭비하는 것이다.

 다른 한편으로 모바일 장치에서 이것은 효과적일 수 있다. 게임을 계속 돌리는게 아니라서 배터리 시간을 유지할 수 있다.

 

결론

 일정한 게임 속도에 FPS를 의존적이게 만드는 것은 쉽게 구현할 수있고 코드를 심플하게 하지만 문제가 약간 있다. 높은 FPS는 느린 하드웨어에서 문제를 야기할 수 있다. 그리고 느린 FPS는 빠른 하드웨어에서는 시각적 효과의 낭비로 이어진다.

 

 게임속도가 가변FPS에 의존적

구현

 또 다른 게임 루프의 구현은 가능한 빠르게 게임 루프를 도는 것이다. 그리고 FPS가 게임 속도를 결정한다. 바로 전 프레임과 다른 시간으로 게임은 갱신된다.

   

    DWORD prev_frame_tick;
    DWORD curr_frame_tick = GetTickCount();

 

    bool game_is_running = true;
    while( game_is_running ) {
        prev_frame_tick = curr_frame_tick;
        curr_frame_tick = GetTickCount();

 

        update_game( curr_frame_tick - prev_frame_tick );
        display_game();
    }


 게임코드가 약간 더 어려워 지고 있다. 왜냐하면 우리는 update_game()의 기능에 대해 고려해야 하기 때문이다. 그러나 아직까지 어렵지는 않다. 첫눈에 봐서 우리의 문제를 해결하기에 이상적인 방법으로 보인다. 나는 많은 똑똑한 프로그래머들이 이런 종류의 게임루프를 구현하는 것을 봤다. 그들 중 일부는 아마도 이러한 루프를 구현하기 전에 이 글을 봤어야 했는데 할 것이다. 내가 즉시 느린 하드웨어나 빠른 하드웨어( 맞다! 빠른!)  둘 모두에 심각한 문제를 발생하는 것을 보여주겠다.

 

느린 하드웨어

 느린 하드웨어서 때때로 어떤 곳에서 확실하게 느려진다.  게임 내 무거운 곳에서. 이러한 현상은 3D게임에서 수많은 폴리곤들이 보여지는 일정한 시간동안 확실하게 느려진다. 이러한 프레임 하락은 입력 반응 시간에 영향을 미치게 되고 마찬지로 플레이어의 반응 시간에도 영양을 끼친다. 게임 업데이트에 딜레이를 느낄 것이고 게임 상태는 큰 시간뒤에 갱신될 것이다. 그 결과 플레이어 및 AI의 반응 시간 또한 느려진다. 그래서 단순한 이동 실패 심지어 이동 불가능이 될 것이다. 예를 들어 정상적인 FPS에서는 장애물을 피할 수 있는데 낮은 FPS에서는 불가능하게 된다. 좀 더 심각한 문제는 시뮬레이션이 폭발할 수도 있다는 것이다!

 

빠른 하드웨어
 위에 나와있는 게임코드가 어떻게 빠른 하드웨어에 문제인지 궁금할 것이다. 불행하게도 가능하다. 첫째로 컴퓨터 위에서의 계산에 대해 설명하겠다.
 float 또는 double형의 메모리 공간 한계 때문에 몇 몇 값들을 나타낼 수가 없다. 예를 들어 0.1은 이진수로 나타낼 수가 없어서 double에서 저장될때 어림수로 된다. pyton을 통해 보여주겠다. :


>>> 0.1
0.10000000000000001


 이것은 별로 극적이지 않다. 예를 들면 레이싱카가 밀리세컨드당 0.001의 속도로 움직이는데 10초 후에 움직인 거리는 10.0이다. 만약 게임공간 내에서 쪼개서 계산을 한다면 초당 프레임으로 입력을 받는 다음과 같은 함수를 사용할 것이다.

 

>>> def get_distance( fps ):
...     skip_ticks = 1000 / fps
...     total_ticks = 0
...     distance = 0.0
...     speed_per_tick = 0.001
...     while total_ticks < 10000:
...             distance += speed_per_tick * skip_ticks
...             total_ticks += skip_ticks
...     return distance

 

 이제 우리는 초당 40프레임 때의 거리를 계산 할 것이다. :


>>> get_distance( 40 )
10.000000000000075


 잠시만 기다려봐라... 10.0이 아니네?? 무슨일인가? 흠, 왜냐하면 우리는 400 추가를 나누어서 계산했고 작은 에러들은 커진 것이다. 초당 100 프레임에서 무슨 일이 일어나는가...


>>> get_distance( 100 )
9.9999999999998312


 머라고? 심지어 에러가 더 커졌다!! 흠, 왜냐하면 우리는 100fps에서 좀 더 더했기 때문이다. 라운딩 에러가 크게될 가능성이 있다. 그래서 게임에서 초당 40 이나 100프레임이 틀린것이다.

 

>>> get_distance( 40 ) - get_distance( 100 )
2.4336088699783431e-13


 게임에서 이러한 차이가 너무 작다고 느낄 것이다. 그러나 진짜 문제는 잘못된 값으로 무엇을 계산하려고 할 때 생긴다. 이 방법으로는 작은 에러가 크게 된다. 그리고 높은 프레임 레이트에서는 게임을 불능으로 만들어 버린다. 이러한 일이 생기냐고? 고려하기에 충분하다! 이러한 게임 루프를 사용하는 게임을 보아왔고 정말로 높은 프레임 레이트에서 문제를 일으켰다. 후에 프로그래머가 게임 코어에 숨어있는 문제를 발견했다. 결국 많은 약간의 코드들을 수정했다.

 

결론
 얼핏보면 이러한 게임 루프들은 좋다. 하지만 어리석게 굴지 마라. 느리거나 빠른 하드웨어 둘 모두 게임에 심각한 문제를 발생시킨다. 그리고 그거 외에도 고정 프레임 레이트를 사용할 때보다 구현이 더 어렵다. 그래서 굳이 이것을 사용 하겠는가?

 

 최대FPS에 대한 일정한 게임 속도
구현

 우리 첫번째 방법인 일정한 게임 속도에 의존적인 FPS의 경우 느린 하드웨에서 문제를 일으켰다. 그 경우 프레임레이트와 게임속도 둘 모두 저하됬다. 만일 그렇다면 가능한 방법은 게임 갱신은 그대로 두고 랜더링 프레임레이트를 줄이는 것이다. 이 것은 다음과 같은 게임루프를 사용한다 :

 

    const int TICKS_PER_SECOND = 50;
    const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
    const int MAX_FRAMESKIP = 10;

 

    DWORD next_game_tick = GetTickCount();
    int loops;

 

    bool game_is_running = true;
    while( game_is_running )
{

 

        loops = 0;
        while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP)
{
            update_game();

 

            next_game_tick += SKIP_TICKS;
            loops++;
        }

        display_game();
    }

 

 이 게임은 일정하게 초당 50번 갱신을 하고 랜더링은 가능한 빠르게 완료한다. 랜더링은 초당 50번보다 많게 이루어진다는 것을 알 수 있고 이어지는 몇몇 프레임도 비슷해진다. 그래서 실제 영상 프레임은 초당 50프레임의 최대로 표현되어 질것이다. 느린 하드웨어서 돌아갈 경우 프레임레이트는 게임 업데이트가 MAX_FRAMESKIP에 도착할 때까지 떨어질 것이다. 실제로 render FPS가 5까지 (= FRAMES_PER_SECOND / MAX_FRAMESKIP)까지 떨어진다는 것을 의미한다. 실제 게임은 느려질 것이다.


느린 하드웨어
 느린 하드웨어에서 초당 프레임은 떨어질것이다. 그러나 게임은 아마도 정상적인 속도로 돌아갈 것이다. 만약 하드웨어가 여전히 정상적인 속도를 따라가지 못한다면 게임은 느려지고 프레임레이트는 전혀 부드럽지 않을 것이다.

 

빠른 하드웨어
 빠른하드웨어에서 게임은 문제가 없을 것이다. 그러나 처음 해결책과 비슷하다. 높은 프레임 레이트로 쓸 수 있는 많은 귀중한 클럭 사이클을 낭비하고 있다. 빠른 갱신율과 느린 컴퓨터와의 균형을 발견하는게 중요하다.

 

결론
 최대 FPS를 쓴 일정한 게임속도는 쉽게 구현을 할 수 있고 코드가 심플하다. 그러나 여전히 약간의 문제를 가지고 있다 : 높은 FPS를 설정할 경우 여전히 느린 하드웨어에서의 위험성을 가지고 있다(그러나 첫번째 해결책만큼 심각하지 않다) 그리고 낮은 FPS를 설정할 경우 높은 하드웨어에서는 영상효과의 낭비이다.


 가변FPS에 비의존적인 일정한 게임 속도
구현

 느린하드웨어서는 한층 더 빠르고 빠른 하드웨어에서는 시각적으로 좀 더 매력적이게 향상시키는 게 가능한가? 그럼 우리에게 행운이다! 가능하다. 게임 상태는 초당 60번의 갱신이 필요하지 않다. 사용자의 입력이나 AI 그리고 게임 상태는 초당 25프레임으로 충분하다. 그래서 초당 update_game()를 더적게 더 많이도 말고 25번 부르도록 하자. 한편으로 랜더링은 하드웨어가 다룰 수 있으면 있을 만큼 빠르게 되도록하자. 그러나 느린 프레임 레이트는 게임의 갱신을 방해할 것이다. 이것을 해결하는 하는 방법은 이 게임루프를 따르는 것이다.

   
    const int TICKS_PER_SECOND = 25;
    const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
    const int MAX_FRAMESKIP = 5;

 

    DWORD next_game_tick = GetTickCount();
    int loops;


    float interpolation;

    bool game_is_running = true;


    while( game_is_running ) {

        loops = 0;
        while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) {
            update_game();

 

            next_game_tick += SKIP_TICKS;
            loops++;

        }

 

        interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
                        / float( SKIP_TICKS );
        display_game( interpolation );
    }

 

 이러한 게임 루프에서 update_game() 부분은 여전히 쉽다. 그러나 불행하게도 display_game() 함수는 좀 더 복잡하게 되었다. 보간 변수를 쓰는 예상함수를 구현해야 한다. 그러나 걱정하지 마라. 어렵지 않다. 단지 조금의 일을 더 하는 것 뿐이다. 보간과 예상하는 일을 어떻게 하는지에 대해 설명해 주겠다. 그러나 첫째 왜 이게 필요한지 보여주겠다.

 

보간의 필요성

 게임 상태는 초당 25번 업데이트를 한다. 그래서 만약 랜더링에 보간을 사용하지 않는다면 마찬가지의 속도로 보여질 것이다. 예를 들어 영화의 경우 초당 24 프레임으로 실행되기에 25FPS가 어떤사람들은 느리지 않다고 생각한다. 그래서 25FPS가 시각적으로 만족스럽다고 생각한다. 그러나 빠르게 움직이는 객체에 대해서는 여전히 좀더 빠른 FPS를 쓰는게 개선된다는 것을 알 수있다. 그러므로 프레임 사이 마다의 빠른 움직임을 좀 더 부드럽게 하려면 무엇을 해야 한다. 그래서 보간과 예상 함수는 해결책을 제시해 쥔다.

 

보간과 예상

 게임 코드는 초당의 현재 프레임으로 돌고 있다고 말했다. 그래서 프레임들 마다 draw/render할 때 2개의 게임시간이 가능하다. 예를 들어 게임상태를 10번 업데이트하고 난 후 장면을 나타내려고 한다. 랜더링은 10번째와 11번째 게임업데이트 사이에 될 것이다. 그래서 10.3 쯤에 랜더링이 가능 할 것이다. 보간 값은 그 때 0.3을 유지하게 된다. 예들 들자면 : 차가 다음과 같은 게임 시간으로 움직인다 :

 

    position = position + speed;

 

만약 10번째 게임시간에 500이라는 위치에 있을때 속도는 100이다. 그 후 11번째 게임 시간에 위치는 600일 것이다. 그렇다면 차는 랜더링 시에 어느 위치에 있겠는가? 마지막 게임시간의 위치에 있게 할것이다(여기서는 500). 하지만 더 좋은 방법은 정확한 10.3 위치를 예상하는 것이다. 이와 같은 것이다 :

 

    view_positon = position + (speed * interpolation))


 차는 530 위치에 그려질 것이다. 그래서 기본적으로 보간 값은 이전 게임시간과 다음(previous = 0.0, next = 1.0) 사이에 값을 포함하고 있다.  랜더링 시간에 차/카메라/…가 배치될 때 "예상"함수를 어디에 만들어야 하는가? 예상함수를 객체의 속도 또는 조정, 회전 속도에 기초를 둬야 한다. 이것은 복잡함을 필요로 하지 않다. 왜냐하면 우리는 단지 프레임사이를 부드럽게 하려고 할 뿐이기 때문이다. 충돌을 감기 되기 전에 객체가 다른 객체로 표현되어지는 것이 실제로 가능하다. 그러나 이전에 봐왔듯이 게임은 초당 25프레임으로 업데이트 되고 있다. 그러므로 이와 같이 일이 일어날때 에러는 순식간에 보여진다. 사람 눈으로는 거의 알아차리기 어렵다.

 

느린 하드웨어

 대부분의 경우 update_game()은 display_game()보다 훨씬 적은 시작을 필요로 한다. 사실상 우리는 심지어 느린 하드웨어 위에서도 update_game() 함수가 초당 25번 실행된다고 예상한다. 비록 게임이 초당 15프레임으로 화면을 보여줄지라도 많은 문제없이 플레이어의 입력을 다루고 게임상태를 업데이트할 것이다.

 

빠른 하드웨어

 빠른 하드웨어 위에서 게임은 일정하게 초당 25프레임을 유지 할 것이다. 그러나 화면에 업데이트는 되는 것은 이것보다 빠를 것이다. 보간/예상 함수는 높은 프레임레이트에서 돌아가는 멋진 효과를 만들어 낼 것이다. FPS를 속이는 좋은 방법이다. 왜냐하면 매 프레임 레이트마다 게임 상태를 업데이트 하지 않는다. 내가 설명한 두번째 방법보다 높은 FPS를 가지고 있다.

 

결론

 FPS에 독립적인 게임상태를 만드는 것은 가장 이상적인 게임루프 구현으로 보인다. 하지만 disply_game() 함수를 예상하는 것을 구현해야 한다. 그러나 어렵지 않다.

 

종합 결론

 게임루프는 생각한 것보다 좀 더 많은 것을 가지고 있다. 우리는 4가지 가능한 구현을 살펴 보았다. 그 중 하나는 확실하게 피해야 할 것이다. 그 하나는 가변FPS에 따르는 게임속도이다. 모바일 장치에게 일정한 프레임레이트는 좋고 간단한 해결책이다. 그러나 모든 하드웨어에 쓰이기에는 별로이다. 가장 좋은 게임루프는 높은 프레임레이트를 위해 예상을 사용하는 게임속도에 독립적인 FPS이다.

만약 일부로 예상 함수를 사용하지 않는다면 최대 프레임레이트에서 작업할 수 있다. 하지만 적당한 게임 업데이트 레이트가 느리거나 빠른 하드웨어 모두 고장을 내는 것을 발견 할 수 있을 것이다. 이제 멋진 게임을 위한 코드를 생각해 보자!

 

Koen Witters

------------------------------------------------------------------------------------------

출처 : http://dewitters.koonsolo.com/gameloop.html

 

발번역이 올려도 될지 모르겠습니다. 이제 겨우 AI 공부를 끝내고 스프라이트 툴을 만지고 있습니다 ^^

말없이 많이 도움을 받아가서 부끄럽지만 올립니다 ^^;;;;;

 

의역 및 오역이 많으니 이상한 부분은 원문을... 번역이 더 머리 아플지도...


출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=30&MAEULNO=12&no=235&page=1

'Programming > C/C++' 카테고리의 다른 글

IME 소스 (한글)  (2) 2010.11.26
[C++] 형변환 (Typecasting) - const_cast, reinterpret_cast, static_cast, dynamic_cast  (0) 2010.09.28
GameLoop (번역)  (0) 2010.09.16
IME 관련  (0) 2010.09.10
TLS (Thread Local Strorage)  (0) 2010.08.30
TCHAR을쓰면 바뀌는 함수들  (0) 2010.07.27
Posted by blueasa
TAG GameLoop

댓글을 달아 주세요

IME 관련

Programming/C/C++ / 2010. 9. 10. 17:14

이준곤(LeeChen)님이 작성하신 IME 관련 내용 풀 소스로 만들어본것입니다.

뭐 큰 변경은 없지만 잘잘한 것 조금 변경했습니다.

 

 

#pragma comment(lib,"imm32.lib")
#include <windows.h>


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
HWND hWndMain;

LPCTSTR lpszClass=TEXT("Ime1");

void putstring(HWND hWnd);
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
       ,LPSTR lpszCmdParam,int nCmdShow)
{
     HWND hWnd;
     MSG Message;
     WNDCLASS WndClass;
     g_hInst=hInstance;
    

     WndClass.cbClsExtra=0;
     WndClass.cbWndExtra=0;
     WndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
     WndClass.hCursor=LoadCursor(NULL,IDC_IBEAM);
     WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
     WndClass.hInstance=hInstance;
     WndClass.lpfnWndProc=(WNDPROC)WndProc;
     WndClass.lpszClassName=lpszClass;
     WndClass.lpszMenuName=NULL;
     WndClass.style=CS_HREDRAW | CS_VREDRAW;
     RegisterClass(&WndClass);

 

     hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
          CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
          NULL,(HMENU)NULL,hInstance,NULL);

     ShowWindow(hWnd,nCmdShow);
     hWndMain=hWnd;
    

     while(GetMessage(&Message,0,0,0)) {
          TranslateMessage(&Message);
          DispatchMessage(&Message);
    putstring(hWnd);
     }
     return (int)Message.wParam;
}

 


#include <imm.h>

char Text[255];     // 텍스트를 저장하기위한 변수
char Cstr[10];      // 조합중인 문자!!
char CanText[200];   // 특수문자를 위한 변수
int CNumber=0;        // 현제의 특수문자 위치
int CanMax=0;         // 현제 특수문자 목록의 최대 겟수

//WinAPI창에 출력
void putstring(HWND hWnd){
 //더블버퍼링 준비(백버퍼생성)
 HDC hDC = GetDC(hWnd);
 
 HDC hDCMem;
 RECT rect;
 HBITMAP BitMem, OldBitMap;

 hDCMem = CreateCompatibleDC(hDC);
 GetClientRect(hWnd, &rect);
 BitMem = CreateCompatibleBitmap(hDC, rect.right-rect.left, rect.bottom - rect.top);
 OldBitMap = (HBITMAP)SelectObject(hDCMem, BitMem);
 FillRect(hDCMem, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); // 전체를 하얗게

 //문자열 작성
 char Text1[255]; 
 memset(Text1,0,255);
 
 TextOut(hDCMem,0,60,CanText,strlen(CanText)); // 특수문자가 있음 찍어주기;.
 strcpy(Text1,Text);
 if(Cstr[0] != 0 ){
   strcpy(Text1 + strlen(Text), Cstr);
 }
 strcpy(Text1 + strlen(Text1), "_");
 TextOut(hDCMem,0,0,Text1,strlen(Text1));
 //ReleaseDC(hWnd,hDCMem);

 //백버퍼를 메인버퍼로 옮김
 BitBlt(hDC, rect.left,rect.top,rect.right - rect.left,rect.bottom - rect.top,hDCMem,0,0,SRCCOPY);
    BitMem = (HBITMAP)SelectObject(hDCMem, OldBitMap);
 //사용한 핸들 반환
 DeleteDC(hDCMem);
    ReleaseDC(hWnd, hDC);
    DeleteObject(BitMem);


}
//특수문자 처리
void GetCandi(HWND hWnd, WPARAM wParam, LPARAM lParam,int Number)
{
 DWORD       dwBufLen;    // 버퍼길이
 LPCANDIDATELIST lpCandList;   // 특수문자 리스트
 char aa[255];
 int i;
 LPCANDIDATELIST m_CandList;
 HIMC m_hIMC = ImmGetContext(hWnd);

    switch (wParam) {
        case IMN_OPENCANDIDATE:   // 처음 열렸을때.;;;;
   memset(CanText,0,100); //  특수문자가 들어갈 변수 초기화
   if(!(dwBufLen = ImmGetCandidateList(m_hIMC, 0, NULL,0)))break;//lpCandList, 0)))break;
            // 리스트의 길이 받기..
   m_CandList = (LPCANDIDATELIST)new char[dwBufLen];  // 길이만큼 메모리 얻기
   lpCandList = m_CandList;
   ImmGetCandidateList(m_hIMC, 0, lpCandList, dwBufLen);
            // 할당받은 데로 리스트를 받는다.
            // 아까도 그랬지만 인자에 따라서 같은 함수가 용도가 달라짐;;...
   CanMax=lpCandList->dwCount;
             // 특수문자 목록의 겟수를 넣는다!
   for(i=Number;i<Number+9; i++)                               // 한번에 9개만 보이니까 9개만받는다.
   {   
    if(i>=lpCandList->dwCount) break;
     // 모두 알다시피 더크면 뽀로롱~ 나간다!
     LPSTR lpStr = (LPSTR)lpCandList + lpCandList->dwOffset[i];
     // 문자얻기;.
    strcpy(CanText + strlen(CanText), lpStr);
     // 특수문자 문자열에 넣는다;.
     // 근데 왜 두번이나 지나서 넣는거지? -_-;.
   }
     delete m_CandList;     // 만든걸 날린다!
     m_CandList=NULL;       // 널을 넣는다;.(안넣어도 무방하다;.)
     CNumber = 0;           // 현제의 특수문자 번호를 0으로 만든다;.
    break;
   case IMN_CHANGECANDIDATE:  // 딴 키를 눌러서 새로운 목록으로 바꾸기라면
    memset(CanText,0,100); // 또 다 날린다!
    if(!(dwBufLen = ImmGetCandidateList(m_hIMC, 0, NULL, 0)))break;// 길이를 얻는다
    m_CandList = (LPCANDIDATELIST)new char[dwBufLen]; // 또 메모리 할당한다;.
    lpCandList = m_CandList;// 역시 위와 마찬가지로 어느날 이렇게;;.
    ImmGetCandidateList(m_hIMC, 0, lpCandList, dwBufLen);
        // lpCandList에 목록을 얻는다;.
    for(i=Number;i<Number+9; i++)                               // 한번에 9개만 보이니까 9개만받는다.
    {
     if(i>=lpCandList->dwCount) break;   // 역시 더크면 뾰로롱 나간다~
        LPSTR lpStr = (LPSTR)lpCandList + lpCandList->dwOffset[i];  // 위처럼 lpStr에 문자를 넣고..
     strcpy(CanText + strlen(CanText), lpStr);

          }
    delete m_CandList;           // 역시 할당한것을 날리고
    m_CandList=NULL;             // 리스트에 널을 넣는다.
    break;                       // 빠져나온다..
      case IMN_CLOSECANDIDATE:         // 목록 리스트가 닫힐경우.
    CanText[0]=0;                // CanText젤 앞에 0을 넣어버린다;;.
    break;
 }
 ImmReleaseContext(hWnd, m_hIMC);
}

int GetText(HWND hWnd,UINT msg,WPARAM wparam, LPARAM lparam)
{
 int len;     // 그냥 여기저기서 써먹을 변수!
 HIMC m_hIMC=NULL;   // IME 핸들

 switch (msg){
  case WM_IME_COMPOSITION:   // 글씨조합중
  m_hIMC = ImmGetContext(hWnd); //현재윈도우핸들이 ime핸들을 얻는다.
  if(lparam & GCS_RESULTSTR) //조합이 완료되면
  {
   len = ImmGetCompositionString(m_hIMC,GCS_RESULTSTR,NULL,0);//현재 IME의(조합중인) 스트링 길이를 얻는다.
   if(len >0)
   {
    ImmGetCompositionString(m_hIMC,GCS_RESULTSTR,Cstr, len);//Cstr에 조합중인 문자열을 받아낸다,.
    Cstr[len] = 0; //젤 끝에 0을 붙여서 끝을 표시한다.
    //조합중인 문자는 완료된 상태이기때문에 최종 문자저장소에 넣는다.
    //strcpy(Text+strlen(Text),Cstr); //전체 내용(실제 보일 텍스트) 뒤에 붙여준다.
    strcpy(Text+strlen(Text),Cstr);
    memset(Cstr,0,10); //지움
   }
  }
  else if (lparam & GCS_COMPSTR){  // 조합중이면;.
   len = ImmGetCompositionString(m_hIMC, GCS_COMPSTR, NULL, 0);// 조합중인 길이를 얻는다.
   ImmGetCompositionString(m_hIMC, GCS_COMPSTR, Cstr, len);   // str에  조합중인 문자를 얻는다.
   Cstr[len] = 0;// 뒤에 0을 붙인다.
   // 이렇게 해두시고 글씨 찍을때 Text + Cstr 하셔서 찍어주시면 된다
   // 이건 쫌있다가 찍는 예제를 하나 보여드리죠!
  }
  ImmReleaseContext(hWnd, m_hIMC);// IME 핸들 반환!!
  return 0;
    case WM_CHAR:              // 한글 이외의 나머지 글은 이리로 들어옵니다. ^^;.
  if(wparam == 8 ){      // 만약 8번(빽스페이스)라면..
   if(strlen(Text) > 0){   // 길이가 0보다 길면
    //isDBCSLeadByte  //한글의 첫번째 바이트라면 true 두번째 바이트나 영어라면 false;로 체크
    if(strlen(Text)>1 && IsDBCSLeadByteEx(949,Text[strlen(Text)-1])) // 949 means Hangul code page
    {
     //if(!IsDBCSLeadByteEx(0,Text[strlen(Text)-1])) //만약 특수키체크(특수키 '\' '?'등등);
     //{
     // Text[strlen(Text)-1] = '\0';   // 한자를 지운
     //}else
     {
      Text[strlen(Text)-2] = '\0';   // 한자를 지운다;.
     }
    }
    else //영어
    {
     Text[strlen(Text)-1] = '\0';   // 한자를 지운다;.
    }
    memset(Cstr,0,10);         // 조합중문자를 초기화..(왜했지? 기억이.)
    // 만약 글자 조합중 일때 빽스페이스가 들어오면 이리 메세지로 안들어
    // 오고 조합중이란 메세지로 들어오니 염려 놓으시길;;..
   }
  }else{  // 빽스페이스가 아니면..
   len = strlen(Text);
   Text[len] = wparam & 0xff;   //  넘어온 문자를 문자열에 넣기..
   //Text[len+1] = 0;               //  뒤에 0붙이깃!!
  }
  return 0;
    case WM_IME_NOTIFY:  // 한자입력...
   GetCandi(hWnd,wparam,lparam,CNumber);
   return 0;
     case WM_PAINT:
   {
    PAINTSTRUCT ps;
       HDC hdc=BeginPaint(hWnd,&ps);
     EndPaint(hWnd,&ps);
    return 0;
   }
    case WM_KEYDOWN:    // 키다운..
    if(CanText[0]==0) break; 
        // 특수문자에 글씨가 없뜨면;;..
        // 그러니까 창이 안열여뜨면!
    if(wparam == VK_PROCESSKEY){
        // 프로세스키로 들어가는거면..
     // 특수문자 창이 열렸을경우 이렇게 // 들어가요;;..
     if((lparam & 0xff0000) == 4915200){ // 왼쪽이면
       if(CNumber >0)CNumber-=9;  
   break;
     }
     if((lparam & 0xff0000) == 5046272){ // 오른쪽이면;;..
     if(CNumber+9 < CanMax)CNumber+=9;// 구더하기..
   break;
     }
   }
   break;
 }
  return 1;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 if(GetText(hWnd, iMessage,wParam, lParam) == 0)return 0;

     switch(iMessage) {

      case WM_DESTROY:
          PostQuitMessage(0);

          return 0;
     }

     return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

[출처] IME 관련|작성자 찐남자

'Programming > C/C++' 카테고리의 다른 글

[C++] 형변환 (Typecasting) - const_cast, reinterpret_cast, static_cast, dynamic_cast  (0) 2010.09.28
GameLoop (번역)  (0) 2010.09.16
IME 관련  (0) 2010.09.10
TLS (Thread Local Strorage)  (0) 2010.08.30
TCHAR을쓰면 바뀌는 함수들  (0) 2010.07.27
TCHAR printf  (0) 2010.07.12
Posted by blueasa
TAG ime

댓글을 달아 주세요