'게임 개발'에 해당되는 글 325건

  1. 2010/02/14 폴리곤 수를 줄이는 미들웨어(Middleware) (4)
  2. 2010/02/14 게임용 라이팅(Lighting) 미들웨어(Middleware) (4)
  3. 2010/02/14 가시성 최적화 미들웨어(Middleware) (6)
  4. 2010/01/27 게임 엔진은 게임 개발의 결과물이어야 합니다 (4)
  5. 2010/01/19 컴퓨터 그래픽스에서 앨비도우(Albedo)의 뜻 (2)
  6. 2010/01/17 즉흥적인 결정을 피할 것 (2)
  7. 2010/01/17 게임 재미에 대한 연구의 필요성 (2)
  8. 2010/01/17 C++에서 비트 연산자를 쓸 때엔 우선순위를 항상 생각할 것
  9. 2010/01/17 데이터 수동 입력을 줄이기 위해서 네이밍(Naming)을 활용할 것
  10. 2010/01/17 회의를 효율적으로 하는 방법
  11. 2010/01/17 Visual C++ (Visual C++) 창의 레이아웃을 원래대로 돌리기
  12. 2010/01/12 바둑이 뛰어난 전략 게임인 이유
  13. 2010/01/10 기획자와 현장 고객의 비교
  14. 2010/01/10 온라인 게임은 영화보다 드라마에 가깝습니다
  15. 2010/01/09 스크럼(Scrum) 프로세스에 대한 의문 (2)
  16. 2010/01/09 퍼포스(Perforce)에서 잘못된 체인지리스트(Changelist)를 지우는 방법
  17. 2010/01/09 마우스 휠 처리
  18. 2010/01/04 소수 구하기
  19. 2010/01/03 IME에서 조합 중인 글자를 강제로 완성시키기
  20. 2010/01/02 mbstowcs나 wcstombs가 올바르게 동작하게 하려면, setlocale 설정이 필수
  21. 2010/01/01 게임 밸런스는 최소한만 수정할 것
  22. 2009/12/24 윈도에서 디버거에 연결돼 있는지 확인하기 (2)
  23. 2009/12/05 게임 그래픽스에서 정적 조명과 동적 조명
  24. 2009/12/05 게임 그래픽스 프로그래밍에서 베이킹(Baking)의 뜻 (2)
  25. 2009/11/20 유용한 비교 및 병합 도구, Perforce Visual Merge Tool(P4Merge)
  26. 2009/11/05 파일 접근 실패 오류 추적하기
  27. 2009/11/01 Image-Based Lighting (IBL) (2)
  28. 2009/10/11 더블 버퍼링(Double Buffering)을 이용해 스레드 동기화 줄이기 (2)
  29. 2009/10/07 "프로시저 시작 지점 ...을(를) DLL ...에서 찾을 수 없습니다."라는 오류를 해결하는 방법 (6)
  30. 2009/10/07 윈도 GUI 프로그램에서 콘솔 창 띄우기
사용해 본 적은 없지만, 이런 것들이 있다기에 정리해 둡니다.


요즘은 폴리곤 수가 그다지 문제되진 않는 것으로 알고 있습니다만, 그래도 어느 정도 최적화는 필요하겠죠.
2010/02/14 21:33 2010/02/14 21:33
실제로 사용해 보진 않아서 잘 모르지만, 대략 다음과 같은 것이 있나 봅니다.



Lightsprint는 실시간 global illumination을 지원하는군요. 성능이 얼마나 나올지는 모르겠지만, 독특한 미들웨어네요. 조명 전처리 때문에 생기는 레벨 디자인 확인 지연을 없앨 수 있다는 점에서는 가치가 있겠습니다.

참고로, Image-Based Lighting도 좀 더 섬세한 정적 조명을 표현하는 방법입니다. 하지만, 그래픽 아티스트가 이미지를 일일이 뽑아 내야 한다는 단점이 있습니다. 맥스 스크립트 등을 활용하면 좀 더 쉬워질 수는 있지만, 그렇다 해도 자동화가 상당히 어렵습니다. 그래서 이런 미들웨어를 활용해 정적 조명 전처리를 완전히 자동화하는 게 생산성 측면에서 더 나은 것 같습니다.
2010/02/14 01:50 2010/02/14 01:50
Umbra Software라는 곳에서 가시성 최적화 미들웨어를 판매하고 있네요. 비슷한 미들웨어가 더 있을 것 같은데, 찾아 보진 않았습니다.

여유 자금이 있다면 이런 미들웨어를 구입해서 가시성 최적화 문제를 해결하는 게 좋은 것 같습니다. 레벨 디자인 단계에서부터 레벨 디자이너가 가시성 최적화를 고민하는 것도 한 가지 방법일 것입니다. 하지만, 게임 개발자는 사용자에게 즐거움을 줄 만한 창의적인 일을 해야 하는 것이고, 기술적인 문제에 대한 고민에 노력을 많이 투자하는 것은 좀 아깝습니다.
2010/02/14 01:44 2010/02/14 01:44
엔진을 만들 때, 많이 실수하는 부분이 있습니다. 그건 바로 엔진을 먼저 만들고, 엔진을 이용해 게임을 만드는 것입니다. 기본적인 것을 먼저 구현하고 그 위에 부가적인 요소를 올리는 게 처음엔 맞는 것처럼 보입니다. 하지만, 실제로 작업해 보면 반대라는 것을 알게 됩니다.

게임을 만들 때엔 다른 것은 생각하지 말고, 게임을 만드는 데에만 집중해야 합니다. 게임을 만들고 나면 자연스럽게 재활용 가능한 기본적인 부분이 보이고, 그걸 따로 정리하면 엔진이 되는 것입니다. 그런데 하나의 프로젝트만으로는 어디까지가 엔진이 돼야 할지가 명확하지 않습니다. 여러 게임을 개발해 보면 각 게임의 컨텐츠와 무관한 코드를 좀 더 명확히 알 수 있게 됩니다.

엔진을 개발하고 싶다면, 게임을 먼저 개발해야 합니다. 그게 훌륭한 엔진을 만드는 방법입니다.
2010/01/27 20:02 2010/01/27 20:02
앨비도우는 반사율인데, 일반적으로 분산광에 대한 반사율을 말합니다. 그래서 분산 텍스쳐 (diffuse texture)를 앨비도우라고도 부릅니다. 그런데 때로는 반사광에 대한 반사율도 specular albedo라고 부를 때가 있어서, 혼란스럽습니다. 용어는 되도록 하나로 통일되면 좋겠습니다.
2010/01/19 00:07 2010/01/19 00:07
결정을 서둘지 말라. 하룻밤을 자고 나면 좋은 지혜가 생긴다. - 푸시킨
게임 개발을 하다 보면, 어떤 일을 결정해야 할 때가 잦습니다. 그런데 이런 상황에서 주의 깊게 생각해 보지도 않고 결정을 내리는 것은 좋지 않습니다. 생각은 즉흥적으로 할 수 있지만, 그 생각을 실제 행동으로 옮기는 것엔 신중해야 합니다. 왜냐하면 한 번 행동으로 옮긴 것을 되돌리는 것은 생각을 바꾸는 것보다 훨씬 어렵기 때문입니다. 즉흥적인 결정 뒤엔 언제나 후회가 뒤따릅니다.
2010/01/17 21:20 2010/01/17 21:20
게임을 개발하다 보면, 사용할 기술에 대해 어느 정도 시간을 갖고 연구할 때가 있습니다. 그런데 신기하게도, 만들 게임의 재미에 대해 연구하는 사례는 거의 못 본 것 같습니다.

다른 게임을 참고하면서 필요한 시스템을 기획하는 것은 흔한 일입니다. 그런데 정작 그 시스템이 왜 재미있는지, 어떻게 해야 더 재미있을지, 그리고 자신의 게임에 접목했을 때 어떤 일이 일어날 것인지를 기획자뿐만 아니라 팀원 전체가 함께 고민하는 일은 드뭅니다. 더 나아가서, 다른 게임에 없는 재미있는 시스템에 대해 고민하고 함께 토론하는 문화는 정말 드뭅니다.

게임은 기술이 주가 아니라 재미가 주가 돼야 합니다. 최신 기술을 연구할 시간에 재미에 대해 더 연구하는 게 훨씬 이득입니다.
2010/01/17 21:08 2010/01/17 21:08
C++을 어렵게 느껴지게 하는 것 중 하나로 연산자 우선순위가 있습니다. C++ 연산자 우선순위는 어떤 자연스러운 규칙에 의해 이해할 수 있는 게 아닙니다. 언어 제작자가 자신이 좋아하는 방식대로 정해 놓았을 뿐이고, 언어를 배우는 사람은 이해가 안 돼도 따라야 하는 규칙일 뿐입니다.

특히 비트 연산자의 우선순위는 상당히 의문스럽습니다. <<나 >>와 같은 비트 연산자는 ==, <=, >=, 그리고 !=와 같은 비교 연산자보다도 우선순위가 낮습니다. 그래서 비트 연산자를 괄호로 묶어 주지 않으면, 예상과 전혀 다른 동작을 할 때가 잦습니다.

비트 연산자가 있는 코드가 제대로 동작하지 않는다면, 연산자 우선순위를 꼭 확인해 봐야 합니다.
2010/01/17 20:59 2010/01/17 20:59
네이밍이 무엇인지 아는 사람은 많지만, 네이밍을 어떻게 활용해야 좋을지 아는 사람은 드문 듯합니다. 데이터 관리에서 네이밍이 효과를 제일 발휘할 때는, 손으로 입력하는 것을 줄이는 것입니다.

예를 들어, 아이디가 A라는 무기가 있다고 하겠습니다. 이 무기의 이름은 big sword이고, 메쉬는 big_sword_mesh.mesh, 아이콘 이름은 big_sword_icon.tga입니다. 이 무기를 관리하려면 위의 값을 따로 입력해서 어딘가에 파일로 저장해 두어야 합니다. 무기가 한 두 개라면 상관 없겠지만, 무기 수가 늘어날수록 관리해야 할 데이터가 계속 늘어나게 됩니다. 더 큰 문제는 데이터가 늘어나면 그만큼 실수도 늘어난다는 것입니다.

위의 예에서 발생하는 문제를 쉽게 해결하려면 네이밍을 사용하면 됩니다. 메쉬는 .mesh를, 아이콘 이름은 .tga를 아이디에 붙여서 표현한다라고 정하면, 별도의 파일에 메쉬 이름과 아이콘 이름을 기록할 필요가 없습니다. 위의 예에선 각각 A.mesh, A.tga가 되겠습니다. 아이디 대신에 무기의 이름을 활용하면 이해하기 더 쉽겠지만, 파일 이름으로 표현하기 어려운 이름이 많으므로 논의를 통해 결정하는 게 좋습니다.

프로젝트 관리의 핵심은 덜 중요한 일에 자원을 적게 할당하고, 더 중요한 일에 자원을 많이 할당하는 것입니다. 네이밍은 그런 일을 가능하게 하는 방법 중 하나입니다.
2010/01/17 20:45 2010/01/17 20:45
어떤 조직이 효율적으로 운영되는지 판단하려면, 회의에 참가해 보면 쉽게 알 수 있습니다. 회의 과정이 비효율적인 조직은 업무도 비효율적으로 처리할 가능성이 높습니다. 최근에 회의를 자주 참석해 보면서, 어떻게 하면 회의를 효율적으로 할 수 있을지 생각해 봤습니다.

첫째, 의제를 명확하고 결과 지향적으로 정해야 합니다. 대부분 회의가 단지 지식 공유를 목적으로 소집되는데, 이것은 명백한 시간 낭비입니다. 회의에 참석하는 사람은 각자 지식에 대한 이해도가 다릅니다. 그런데, 이해가 덜 된 사람을 위해서 이미 내용을 이해하고 있는 사람까지도 계속 참석해 있어야 합니다. 지식을 공유하는 목적이라면, 회의가 아니라 문서를 통해 각자 필요한 부분을 읽어 보게 해야 합니다. 긴급하고 필수적인 내용을 공유해야 한다면 회의 외에는 좋은 방법이 없습니다. 하지만, 그렇지 않을 때가 대부분입니다. 회의는 결정을 내리는 데에만 주로 쓰여야 합니다.

둘째, 아무에게나 발언권을 주면 안 됩니다. 회의를 하다 보면 몇몇 사람만 계속 얘기를 하고, 다른 사람은 듣고만 있게 됩니다. 이건 전적으로 회의 진행자의 실수입니다. 회의에선 모든 사람이 공평한 발언 기회를 갖되, 필요 이상으로 얘기를 많이 하지 않도록 조정해야 합니다. 개인당 발언 시간 제한을 두는 방법이 제일 나을 것입니다.

셋째, 회의 내용에 대해서 사전에 문서 공유를 해야 합니다. 회의에 참석해서야 회의의 내용을 알게 되는 것은 참가자가 미리 준비를 하지 못하게 되는 단점이 있을 뿐만 아니라, 회의 시간이 회의 내용을 이해하느라 소모된다는 단점까지 있습니다. 사전에 모두가 알고 있어야 하는 내용은 미리 전달해 주고, 회의 도중에 생각나는 것은 회의 때에 이야기하는 식이 돼야 합니다.

넷째, 회의와 관련 있는 얘기만 하도록 해야 합니다. 회의를 하다 보면 의제와 전혀 무관하거나 당장은 필요 없는 얘기가 종종 나옵니다. 그런 얘기가 도움이 될 수는 있지만, 의제에 대한 집중도를 떨어트리고 회의에서 다뤄야 할 중요할 내용을 제대로 다루지 못하게 만드는 문제가 생깁니다.

다섯째, 자세한 얘기는 당사자끼리 얘기하도록 해야 합니다. 회의 시간에 모두가 들을 필요가 없는 얘기는 관련된 당사자끼리 따로 모여서 얘기하는 게, 모두의 시간을 조금이라도 덜 낭비하게 만듭니다.

여섯째, 회의 진행 도중의 기록을 반드시 남겨야 합니다. 회의를 하면 아무 기록도 남기지 않고 얘기만 하다가 끝나는 때가 잦습니다. 회의를 했다면 반드시 어떤 기록이 남아서 업무에 활용돼야 합니다.

관리자는 어떻게 하면 회의를 효과적으로 진행할 수 있을지 계속 고민해야 합니다.
2010/01/17 13:56 2010/01/17 13:56
비쥬얼 C++을 사용하다 보면, 의도하지 않게 창 레이아웃이 배치돼 버릴 때가 있습니다. 그리고 원래 상태로 복구하려고 해도 잘 안 돼서 고생하게 됩니다. 그럴 때엔 메뉴에서 Window의 Reset Window Layout을 선택하면 됩니다.
2010/01/17 13:38 2010/01/17 13:38
바둑은 지루하지만 뛰어난 전략 게임입니다. 왜 바둑이 전략 게임으로서 훌륭한지 이유를 적어 보았습니다.

첫째, 바둑에선 하나를 얻으려면 반드시 다른 하나를 포기해야 합니다. 한 사람이 한 턴에 하나의 돌만 놓을 수 있기 때문에, 어떤 수를 선택하면 다른 수는 포기해야 합니다. 이게 전략적 선택을 강요합니다. 전략의 기본은 선택입니다.

둘째, 변화가 다양합니다. 한 칸만 옆에 두어도, 차이가 크기 때문에 다양한 변화가 생깁니다. 심지어는 단 한 수로 전세가 완전히 역전될 수도 있습니다. 게다가 상대가 사람이기 때문에, 그 사람의 성향에 따라 상황이 다르게 흘러갑니다.

셋째, 바둑은 완전히 순간적인 머리 싸움입니다. 물론 정석이나 포석을 많이 외우고 있으면 유리하지만, 그걸 많이 안다고 항상 이기진 않습니다. 게다가 손에 의한 컨트롤은 게임 승패에 전혀 영향을 미치지 않습니다. 운에 의해 승패가 갈리는 경우도 거의 없습니다.

넷째, 바둑엔 급수가 있습니다. 그래서 자신과 비슷한 실력의 상대를 쉽게 찾을 수 있습니다. 그리고 실력 차이가 나더라도, 접바둑을 통해 고수에게 핸디캡을 줄 수 있습니다. 그래서 고수나 하수나 서로 긴장하면서 게임을 하게 됩니다.
2010/01/12 06:30 2010/01/12 06:30
익스트림 프로그래밍(Extreme Programming)의 현장 고객에서 생각을 따와서, 기획자를 현장 고객으로 생각할 수 있습니다. 하지만, 기획자는 일반 고객과 약간 다른 성격을 갖고 있으므로, 그대로 적용하는 것은 어려워 보입니다.

일반적으로 고객은 돈을 지불하는 역할을 합니다. 고객이 생산자에게 원하는 것을 말하면, 생산자는 그것을 생산하는 데에 필요한 비용과 시간을 답해 줍니다. 고객은 생산자의 답변을 참고해서, 그에 맞는 돈을 지불합니다. 그런데 여기서 돈에 관련된 문제는 전적으로 고객의 책임입니다. 예를 들어, 위험 요소가 많다는 답변을 듣고도 돈을 지불했다면, 그 돈을 날리는 것은 고객이 감수한다는 것입니다. 고객 자신이 원하는 것을 충분히 설명하지 않았거나 잘못 설명해서 엉뚱한 물건이 나온다면, 그 또한 고객의 책임입니다.

그런데, 일반적으로 게임 기획자는 결과에 대해 책임을 많이 지지 않습니다. 위험 요소가 많다는 답변을 듣고도 강행한 후에 결과가 잘못돼도, 기획자는 책임을 떠안지 않습니다. 그리고 자신이 원하는 기획을 정확하고 구체적으로 표현하지 않아서 생기는 문제에 대해서도 책임을 지지 않습니다. 실제로 프로젝트에 문제가 생겼을 때 영향을 받는 것은 개발 팀 전체입니다. 이렇다 보니, 기획자는 기획에 대해 책임감을 강하게 느끼기 어렵고, 신중한 결정을 하질 않게 되는 것입니다. 이건 기획자 개인의 문제라기보다는 게임 개발 체계의 문제입니다.

하지만 프로그래머가 기획을 간섭하려 하는 것은 좋지 않다고 봅니다. 기획자의 요구에 대해 예상되는 문제점과 필요한 자원을 정확히 알려 주는 것에 중점을 두어야 할 것입니다. 기획자에게 충분한 정보를 주었음에도 불구하고 일이 잘못된 방향으로 전개된다면, 관리자의 협조를 얻어 함께 조율하는 게 나을 것입니다. 만약 관리자마저도 잘못된 결정을 한다면, 다른 팀이나 회사를 알아보는 게 나을 것입니다.
2010/01/10 13:56 2010/01/10 13:56
흔히 게임을 영화와 비교하곤 합니다. 다양한 분야의 많은 인력이 여러 최신 기술을 이용해 하나의 작품을 단기간에 완성하고, 주로 화려한 볼거리와 광고에 치중하며, 그 작품이 주로 공개 초기에 일반 대중에 의해 소비되기 때문일 것입니다. 이렇듯 패키지 게임은 영화와 비슷한 점이 많습니다. 그런데, 온라인 게임은 영화보다 드라마에 더 가까운 것 같습니다.

온라인 게임은 끊임없이 변화되는 특징을 가집니다. 계속 연장 방영하는 드라마처럼, 온라인 게임은 패치를 계속 하면서 서비스의 수명을 늘려 나갑니다. 시청자의 의견을 반영해서 엔딩을 수정하는 한국 드라마처럼, 온라인 게임도 게이머의 의견을 토대로 패치를 하곤 합니다.

또, 드라마에선 유명 배우나 화려한 볼거리도 중요하지만, 이야기가 잘 짜여지지 않으면 드라마를 계속 보게 만들 수 없습니다. 제가 최고의 미국 드라마 중 하나로 생각하는 프리즌 브레이크 (Prison Break) 시즌 1을 보면, 다음 회를 꼭 보게 만드는 스릴이 있었습니다. 온라인 게임 또한 마찬가지입니다. 다음 이야기에 해당하는 컨텐츠를 지속적으로 만들어 주지 못하면, 온라인 게임을 더 이상 하고 싶어지지 않게 됩니다.

온라인 게임을 제작하는 사람이라면, 영화를 흉내내려고 애쓰기보다는 드라마를 보고 연구하는 게 나을 것입니다.
2010/01/10 07:13 2010/01/10 07:13
제가 있는 팀에서는 현재 어떤 정형화된 소프트웨어 개발 프로세스도 도입해서 쓰고 있지 않습니다. 전에 다른 회사에서 스크럼 프로세스를 경험해 본 적이 있는데, 장점도 있지만 단점도 많았습니다. 다른 분야로부터 도입된 여러 소프트웨어 방법론이 대부분 그렇듯이, 스크럼 프로세스도 소프트웨어 개발, 특히 게임 개발엔 왠지 부적합해 보입니다.

스크럼 프로세스에선 일정 기간 단위로 스프린트라는 것을 정해서 계획을 미리 잡아 두라고 합니다. 그런데 게임 개발에서 30일 단위로 할 일을 정하고 일정을 정하는 게 과연 의미가 있을까요? 일정을 고정시키면 변화하는 상황에 빠르게 대처할 수 없고, 반대로 일정을 바꾸면 계획의 의미가 퇴색됩니다. 개발을 진행하다 보면 무수한 변수가 발생해서, 일정을 언제나 수정해야 할 때가 대부분입니다.

스프린트 첫날에 일정을 잡는 게 과연 정확할까요? 충분한 사전 정보 없이 일정을 잡는 게 큰 의미가 있을까요? 구현하다 보면 예상치 못한 문제가 생기기 마련이고, 그 문제를 해결하는 데 얼마나 시간이 걸릴지는 아무도 모릅니다. 따라서 스프린트 첫날에 비교적 정확한 일정을 잡는다는 것은 불가능에 가깝습니다.

스크럼 프로세스에선 회의를 매일 하기를 권장합니다. 그런데 그게 과연 큰 의미가 있을까요? 필요할 때마다 참석해야 하는 사람끼리 모여서 회의를 하는 게 낫지 않을까요? 특별한 의제 없이 회의를 한다는 것은 시간 낭비입니다.

스크럼 프로세스에선 스프린트 단위로 결과를 공개하고 회고하기를 주장합니다. 이것도 역시 필요할 때마다 공개하는 게 나아 보입니다. 외부의 요청이 있을 때 즉시 공개할 준비는 언제나 돼 있어야 하는 것이고, 반면에 회고는 필요할 때마다 하면 충분해 보입니다.

스크럼에선 스프린트 단위로 한 가지 목표를 정하라고 하는데, 별로 의미가 없습니다. 한 달 단위로 어떤 목표를 세우기가 애매합니다. 왜냐하면, 한 달 동안 한 가지 큰 일조차 못할 수도 있고, 많은 중요한 일을 할 수도 있기 때문입니다.

스크럼 프로세스에 대해 전체적으로 생각해 보면, 고정 생산 분야에나 적합한 방식을 연구 개발 위주의 소프트웨어 분야에 무리하게 적용시킨 것 같습니다.
2010/01/09 21:09 2010/01/09 21:09
퍼포스를 사용하다 보면, 대기 중인 체인지리스트가 불필요하게 남아 있을 때가 가끔 있습니다. 이걸 지우려면 콘솔 명령 창(cmd.exe)을 열어서 다음처럼 하면 됩니다.

p4 change -d [번호]
예: p4 change -d 1000

참고: p4 change
2010/01/09 20:48 2010/01/09 20:48
윈도 메시지 처리 함수에서 다음처럼 처리하면 됩니다.

case WM_MOUSEWHEEL:
POINT window_position_in_screen = {};
ClientToScreen(window_handle, &window_position_in_screen);
UINT wheel_scroll_line;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_scroll_line, 0);
POINT cursor_position_in_client =
{
GET_X_LPARAM(l_parameter) - window_position_in_screen.x,
GET_Y_LPARAM(l_parameter) - window_position_in_screen.y
};
int wheel_delta = GET_WHEEL_DELTA_WPARAM(w_parameter) / WHEEL_DELTA * wheel_scroll_line;
...
break;
2010/01/09 17:41 2010/01/09 17:41
소수를 실무에서 구해야 할 일은 별로 없습니다. 하지만, 면접 볼 때 관련된 질문을 받을 때가 있습니다. 참고로, 저는 이렇게 실전엔 별로 유용하지 않은 이론적인 질문을 하는 면접을 상당히 싫어합니다.

소수를 구하려면 소수의 성질을 이용하면 됩니다. 어떤 수 n이 소수인지 판별하려면 다음처럼 하면 됩니다. n을 2부터 n - 1까지 하나씩 나누어서 나머지가 0일 때가 한 번도 없으면, n은 소수가 됩니다. 소스 코드는 아래와 같습니다.

bool is_prime_number(unsigned int number)
{
if (number < 2)
return false;
for (unsigned int divisor = 2; divisor < number; ++divisor)
if (number % divisor == 0)
return false;
return true;
}
2010/01/04 19:17 2010/01/04 19:17
IME를 처리하다 보면, 조합 중인 글자를 강제로 완성시켜야 할 때가 있습니다. 예를 들면, 글자 조합 도중에 버튼을 누르는데 조합이 완료된 텍스트를 일반적인 방법으로는 도저히 얻어 낼 수 없을 때입니다. 이럴 때엔 다음처럼 조합 중인 글자를 강제로 완성시키게 하면 됩니다. 하지만 그다지 권장할 만한 방법은 아니므로, 되도록 이런 처리가 필요없도록 하는 게 더 좋습니다.

case WM_LBUTTONDOWN:
HIMC ime_context = ImmGetContext(window_handle);
if (ime_context != NULL)
{
if (ImmGetCompositionString(ime_context, GCS_COMPSTR, NULL, 0) > 0)
ImmNotifyIME(ime_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
ImmReleaseContext(window_handle, ime_context);
}
break;
2010/01/03 18:57 2010/01/03 18:57
문자열 코드 변환에 사용되는 함수 중에서 코드 페이지를 인자로 받는 MultiByteToWideChar 함수나 WideCharToMultiByte 함수와 달리, mbstowcs 함수나 wcstombs 함수는 코드 페이지를 인자로 받지 않습니다. 그래서, 미리 코드 페이지 설정을 해 두어야 합니다. 실제로 mbstowcs 함수나 wcstombs 함수의 MSDN 도움말에도 지역 설정이 돼 있어야 한다고 명시돼 있지만, 구체적으로 setlocale 함수를 호출해야 한다고는 명시돼 있지 않아서 좀 혼란스럽습니다.

그런데, 외국에서 만든 일부 소프트웨어는 이런 처리를 하지 않습니다. 그래서 영어가 아닌 문자열을 제대로 인식하지 못할 때가 있는데, 이럴 때엔 당황하지 말고 코드 변환 함수 호출 전에 setlocale(LC_ALL, "")라고 해 주면, 현재 운영체제의 지역 설정에 따라 정상적으로 동작합니다.
2010/01/02 17:31 2010/01/02 17:31
게임 밸런스를 수정할 때 실수하기 쉬운 것이 있습니다. 그건 바로, 밸런스를 조금만 수정해도 게임 플레이에 미치는 영향이 상당히 클 수 있다는 사실입니다.

많은 기획자가 자신의 직관을 믿고 밸런스를 한 번에 목표 값까지 변경하려고 합니다. 하지만, 실제로 플레이해 보면 밸런스 조정이 지나쳐서, 또 다른 부분에서 밸런스를 붕괴시킬 때가 잦습니다. 게임에선 어떤 값을 바꾸면, 그것과 연관된 모든 것의 상대적인 값 또한 바뀌는 셈입니다. 어떤 값 하나를 수정했을 때, 그게 이렇게 보이지 않는 연관 관계에 얼마나 큰 영향을 미치는지 정확히 파악하기는 어렵습니다. 그래서 밸런스 수정은 최소한으로 주의 깊게 해야 합니다.

밸런스는 어디까지나 전체적으로 상대적인 균형을 맞추는 것이며, 부분만 보아서는 안 됩니다. 밸런스를 한 번에 맞춘다는 것은 어려운 일이므로, 최소한으로 수정하고 피드백을 얻어서 이상적인 값으로 조금씩 바꾸는 게 안전합니다. 프로그래밍뿐만 아니라, 밸런스 조정에도 점진적이고 반복적인 접근 방법을 취해야 합니다.
2010/01/01 13:13 2010/01/01 13:13
IsDebuggerPresent 함수를 이용하면 됩니다. 하지만, 특별히 사용할 일은 거의 없는 듯합니다.
2009/12/24 10:00 2009/12/24 10:00
게임 그래픽스에선 조명이 크게 두 가지 종류로 나뉘어집니다. 하나는 정적 조명이고, 다른 하나는 동적 조명입니다. 정적 조명은 실시간으로 속성 변경이 불가능하고 동적 객체로부터 영향을 받지 못합니다. 그래서 사실감이 떨어지므로, 최근엔 정적 조명보다 동적 조명을 지향하는 추세입니다.

정적 조명은 속성이 정적인 조명입니다. 여기서 속성이라 하면 조명의 종류, 위치, 방향, 색깔, 그리고 강도 등이 됩니다. 정적 조명은 동적인 객체(대표적으로 캐릭터)에 독립적(동적인 객체로부터 영향을 받지 않는)입니다. 정적 조명은 동적인 객체로부터 영향을 받지 않지만, 동적인 객체에 영향을 줄 수는 있습니다. 정적 조명은 실시간 계산이 필요없습니다. 그래서 대개 결과를 미리 구해 놓으므로, 복잡한 조명을 구현하더라도 게임 성능엔 거의 영향을 주지 않습니다. 정적 조명은 라이트 그리드(light grid), 라이트맵(lightmap), vertex color, ambient cube, irradiance volume, spherical harmonics environment light probes, 그리고 이미지-베이스드 라이팅(image-based lighting) 등의 기법으로 활용됩니다.

동적 조명은 속성이 동적인 조명입니다. 동적 조명은 동적인 객체의 영향을 받기도 해서, 동적인 객체의 형태가 변하면 그것의 영향을 받는 동적 조명의 결과도 달라질 수 있습니다. 동적 조명은 실시간으로 계산되므로, 조명 구현이 복잡하거나 많은 수의 동적 조명을 사용하면 게임 성능을 크게 떨어트립니다. 그런데 최근엔 deferred rendering과 그 유사한 기법의 등장으로, 상당히 많은 수의 동적 조명을 활용하는 것도 가능해졌습니다. 동적 조명은 크게 두 가지 종류로 나뉘어집니다. 첫 번째는 동적 객체에만 영향을 주는 것입니다. 두 번째는 정적 객체에도 영향을 주는 것인데, 이것은 부하가 당연히 더 큽니다. 대표적인 예로 태양광이 있습니다.

참고:
2009/12/05 14:25 2009/12/05 14:25
게임 그래픽스에 등장하는 용어 중에 베이킹이라는 게 있습니다. 일반적으로 베이킹은 빵 같은 걸 굽는 것을 말하는데, 그래픽스에선 여러 구성 요소에 어떤 처리를 해서 하나의 완성품을 만드는 것을 뜻하는 듯합니다. 특히 그 작업은 실시간으로 진행되지 않고 미리 해 두는 때가 잦습니다.

대표적으로, 복잡한 연산을 수행한 여러 결과를 조합해서 텍스쳐로 미리 만들어 두거나, 소프트웨어 인스턴싱을 구현하기 위해서 메쉬 여러 개를 미리 하나의 메쉬로 조합하는 것이 베이킹에 해당합니다.
2009/12/05 13:45 2009/12/05 13:45
문서 파일의 비교(diff) 및 병합(merge)에 사용할 수 있는 도구는 많습니다. TortoiseSVN 사용자라면 기본적으로 TortoiseMerge를 사용하게 돼 있는데, 그 도구도 괜찮지만 찾아 보면 자신에게 더 잘 맞는 도구가 있습니다. 유명한 비교 및 병합 도구로는 Beyond Compare, Araxis Merge, WinMerge,그리고 KDiff3 등이 있습니다.

P4MergePerforce에 포함된 도구입니다. P4Merge는 다른 비교 및 병합 도구에 비해 많이 사용되는 것 같진 않지만, 4개의 문서를 동시에 비교할 수 있는 기능을 제공하고 깔끔한 사용자 인터페이스까지 갖춰서 좋습니다. 게다가 Perforce와 달리, 무료로 사용할 수 있습니다.

P4Merge의 큰 단점은 편집이 쉽지 않다는 것입니다. 병합 도중에 충돌이 나면 TortoiseMerge에서는 클릭 한 번으로 두 파일 중 어느 파일의 것을 선택할지 정할 수 있어서 편합니다. 하지만, P4Merge에서는 각 파일의 내용을 순서대로 합쳐 버리기 때문에, 필요 없는 부분을 사용자가 일일이 제거해야 합니다.

TortoiseSVN에 P4Merge를 연결하려면, P4Merge를 설치한 후에 TortoiseSVN's Settings를 참고해서 다음처럼 하면 됩니다.

TortoiseSVN Settings의 External Programs의 Diff Viewer의 Configure the program used for comparing different revisions of files에서 TortoiseMerge 대신에 External을 선택하고, '...\P4Merge.exe %base %mine'라고 입력합니다. ('...'엔 P4Merge의 경로를 넣으면 됩니다.)

TortoiseSVN Settings의 External Programs의 Merge Tool의 Configure the program used to resolve conflicted files에서 TortoiseMerge 대신에 External을 선택하고, '...\P4Merge.exe %base %theirs %mine %merged'라고 입력합니다. ('...'엔 P4Merge의 경로를 넣으면 됩니다.)
2009/11/20 22:28 2009/11/20 22:28
얼마 전에 게임 엔진을 새 버전으로 교체하는 작업을 했습니다. 그런데, D3DXAssembleShader 함수가 HRESULT 0x8007007e 오류를 되돌리면서 실행되지 않는 것이었습니다. 디버거 창에는 '지정된 모듈을 찾을 수 없습니다.'라고만 나오니까 어떻게 해야 하는지 알 수 없었습니다. D3DXAssembleShader 함수 인자를 설정하면 추가적인 메시지를 얻을 수 있다고 돼 있지만, 소스 코드를 수정하기는 어려운 상황이었습니다.

지푸라기라도 찾자는 마음으로 구글을 열심히 검색하다가, 어떤 파일을 접근하다가 실패하는지 찾으려면 Process Explorer를 활용하면 된다는 글을 우연히 발견했습니다. 그래서 그 프로그램을 띄워 놓고 에러 날 때의 파일 접근을 조사해 보았습니다. 그랬더니 d3dcompiler_42.dll이라는 파일을 찾다가 실패하는 것임을 알았습니다. 그래서 DirectX 2009년 8월 버전을 설치하니까, 그 DLL에 접근이 잘 되네요.

아... 역시 디버깅은 어렵습니다.
2009/11/05 02:45 2009/11/05 02:45
이미지-베이스드 라이팅을 요즘에 좀 조사했는데, 아직도 잘 모르겠습니다.

IBL로 반사광(specular)을 얻는 방식은 cube-mapped reflection과 차이가 없는 듯합니다. 카메라에서 물체 표면을 향하는 벡터, 물체 표면 법선 벡터, 그리고 반사 벡터를 이용해서 큐브 맵 텍셀을 얻어 오면 됩니다. Cube-mapped reflection을 쓰면 해당 큐브 맵이 텍스쳐로 사용되고, IBL로 반사광을 얻으면 해당 큐브 맵이 반사광 조명으로 사용된다는 차이뿐인 듯합니다. 하지만 정확한 차이점을 모르겠네요.

재미있는 것은 IBL을 이용해서 분산광(diffuse)을 실시간으로 얻을 수 있다는 것입니다. 굉장히 많은 수의 정적 조명이나 global illumination 효과를 움직이는 객체에 적용하고 싶은 게임에서는 쓸모가 있을 수도 있겠습니다. 예를 들어, 밝게 빛나는 파란 건물 앞에 캐릭터를 세우면 캐릭터 앞이 살짝 파래지는 효과를 낼 수 있습니다. 하지만 저는 굳이 이렇게까지 할 필요가 있을까란 생각이 듭니다. 많은 수의 분산광 조명을 활용하려면 deferred shading을 활용하는 게 나을 듯하고, global illumination은 별로 티도 안 나니 큰 이득이 없어 보입니다.

IBL로 분산광을 실시간으로 얻는 과정을 제가 이해하고 생각한 대로 정리하면 다음과 같습니다.

우선 전처리 과정이 필요합니다. 월드의 몇몇 지점에서 큐브 맵을 만듭니다. 그 다음, 각 큐브의 중앙으로부터 모든 방향에 대한 분산광을 계산해서 저장합니다. 각 방향의 분산광은 그 방향과 큐브 맵이 이루는 각도에 따라 큐브 맵 색상에 가중치를 곱한 것을 모두 더해서 평균을 구하면 됩니다. 좀 더 빠르게 하려면 HDRI로 큐브 맵의 빛 강도를 미리 추출해서 강도가 센 텍셀 위주로만 계산할 수도 있고, 특정 방향의 큐브 맵 텍셀 몇 개만 추출해서 계산할 수도 있습니다. 만약 global illumination을 적용해서 렌더링할 수 있으면, 렌더링한 것을 그대로 큐브 맵으로 써도 됩니다.

전처리가 끝났으면 전처리된 텍스쳐의 정보, 그리고 정점이나 픽셀의 법선을 이용해서 해당 방향에 맞는 텍셀을 얻으면 됩니다. 객체가 큐브 안에 있을 때엔 그 큐브의 맵을 이용하면 되고, 그렇지 않으면 가장 가까운 큐브의 맵을 이용하면 될 것입니다. 또는 주변 큐브와의 거리에 따라 가중치를 둬서 여러 큐브의 맵을 혼합해서 사용하면 좀 더 정확하겠습니다.

추가적으로, 큐브 맵 안에서의 좌표에 따라 좀 더 정확한 큐브 맵 텍스쳐의 좌표를 얻어 오도록 개량할 수도 있습니다.

다음 문서를 읽어 보면 도움이 될 것입니다.


적어 놓고 보니, 그림이 없어서 이해가 쉽지 않겠네요. 나중에 그림을 추가해야겠습니다.
2009/11/01 03:27 2009/11/01 03:27
Designing & Optimizing Software for N Cores을 보다 보니까, 더블 버퍼링을 이용해서 스레드 동기화를 줄이는 것이 소개돼 있었습니다. 더블 버퍼링의 개념은 그래픽스 프로그래머에겐 익숙한 개념인데, 이걸 스레드 동기화에도 사용할 수 있다는 게 신선하게 느껴졌습니다. 그 문서엔 구현이 구체적으로 나와 있지 않았는데, 어떻게 구현하면 될 것인지 그냥 제 나름대로 생각해 보았습니다.

더블 버퍼링엔 두 개의 버퍼가 필요합니다. 하나는 읽기용이고, 다른 하나는 쓰기용입니다. 외부로부터 요청을 받아 어떤 일을 하는 예제를 만들어 보겠습니다.
class work_type
{
};
class worker_type
{
public:
worker_type()
:
my_current_work(&my_work[0]),
my_next_work(&my_work[1])
{
}
void add_work(const work_type& work)
{
my_next_work->push(work);
}
private:
typedef std::queue<work_type> work_queue_type;
void do_work()
{
for (work_queue_type::iterator work_iterator = my_current_work->begin(); work_iterator != my_current_work->end(); ++work_iterator)
{
// 각 작업을 합니다.
}
my_current_work.clear();
std::swap(my_current_work, my_next_work);
}
work_queue_type my_work[2];
work_queue_type* my_current_work;
work_queue_type* my_next_work;
};
대략 위와 같은 형태가 될 것 같습니다. 외부에선 항상 add_work로 다음 버퍼에만 접근해서 작업을 추가하므로, 이 객체가 do_work로 현재 버퍼를 이용해 자신의 일을 처리하고 있는 데엔 영향을 주지 않습니다. 따라서 위와 같이 하면, 큐에 작업을 추가할 때마다 크리티컬 섹션 등으로 스레드 동기화하던 부하를 없앨 수 있습니다. 그런데 참고로, 버퍼 교환엔 동기화가 필요할 수도 있습니다.
2009/10/11 11:19 2009/10/11 11:19
DLL을 사용하는 프로그램을 실행시켰는데, "프로시저 시작 지점 ...을(를) DLL ...에서 찾을 수 없습니다."라고 표시하는 메시지 박스가 뜨면서 실행이 안 될 때가 있습니다. 이럴 때 원인은 대개 해당 DLL의 문제입니다. 하지만 가끔, 해당 DLL에 접근하는 다른 DLL이나 EXE가 잘못된 것이어서 그런 오류가 나는 때도 있습니다. 따라서 모든 DLL이 정상적인 것인지 확인해야 합니다.
2009/10/07 22:37 2009/10/07 22:37
콘솔 프로그램이 아니지만 콘솔 창을 띄워서 실시간으로 어떤 값을 확인하고 싶을 때가 있습니다. 이럴 때엔 다음처럼 처리하면 됩니다.

// 콘솔 창을 생성합니다.
AllocConsole();

// 콘솔 창에 텍스트를 출력합니다.
DWORD number_of_characters_written;
string output_text;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), output_text.c_str(), output_text.size(), &number_of_characters_written, NULL);

// 콘솔 창을 해제합니다.
FreeConsole();
2009/10/07 22:28 2009/10/07 22:28