'전체'에 해당되는 글 584건
- 2012/01/08 게임에서 자동 조작의 문제점
- 2012/01/05 온라인 게임에서 일회성 마케팅에 대한 생각 (2)
- 2011/12/04 게임 모드를 많이 만들 시간에 기본을 바꿀 것 (2)
- 2011/12/02 미니덤프만으로는 크래시 원인을 알아내기 어렵습니다.
- 2011/10/09 이진 파일 형상 관리 방법 (4)
- 2011/10/02 단지 컴파일 속도 향상을 위해서 Pimpl 패턴이나 인터페이스를 사용하진 말 것 (2)
- 2011/10/01 추가 근무는 자랑거리가 아닙니다. (2)
- 2011/09/13 게임 '야구 9단' 소감
- 2011/09/10 팀 플레이어를 채용할 것 (4)
- 2011/08/21 송강호 씨 인터뷰를 읽고...
- 2011/08/07 리팩토링할 때 주의할 점
- 2011/08/06 제가 자주 사용하는 구글 검색 방법
- 2011/08/01 한/영 전환 감지하기
- 2011/08/01 문자열 인코딩 바꾸기
- 2011/08/01 디스플레이 장치의 정보를 얻어내기
- 2011/07/25 전체 화면일 때에 마우스 커서가 옆 모니터로 나가지 않게 하기
- 2011/07/25 timeGetTime의 정밀도를 높이기
- 2011/07/21 한/영 입력 모드 알아내기
- 2011/07/18 함수의 이름을 얻어내는 매크로
- 2011/07/18 디버거에서 배열 값 조사하기
- 2011/07/18 지역화 작업용 문자열 처리 함수
- 2011/07/18 컴퓨터의 이름을 Windows API로 얻어내기
- 2011/07/18 강제로 예외 발생시키기
- 2011/07/18 C++에서 정수를 표현할 때 char를 쓰지 말 것
- 2011/07/14 함수 호출 대신에 메시지 방식이 필요한 때 (2)
- 2011/07/14 위키(Wiki)는 게임 개발에 적합하지 않습니다.
- 2011/07/12 디버그 힙 없이 디버깅하기
- 2011/07/07 자주 사용하는 netstat 옵션
- 2011/07/07 SEH로 처리할 수 없는 예외를 추가로 처리하기
- 2011/07/06 C++에서 문자열 내의 특정 문자열을 검색한 후 치환하기
게임 선택기준 1순위는 저사양보다 이것?
위 링크에서도 볼 수 있듯이, 요즘 사용자들이 게임에서 원하는 중요한 선택 기준 중 하나는, 편리한 조작입니다. 그런데 어떤 사람은 자동 조작이 조작을 편리하게 한다고 오해하는 것 같습니다. 하지만 자동 조작은 오히려 불편함을 주고 상호작용성을 떨어트릴 가능성이 높습니다.
의도하지 않은 자동 조작은 불편합니다. 차라리 사용자의 입력을 그대로 반영하는 것이, 사용자의 의도대로 움직이지 않는 자동 조작보다 낫습니다. 뇌의 생각을 그대로 전달하는 기술이 게임에 도입되지 않는 한, 자동 조작은 오히려 불편함을 더해 줍니다. 참고로, 보유 총알이 무한대인 슈팅 게임에서 자동으로 총알을 쏘는 것은, 불편함 개선에 효과가 있으므로 예외입니다.
또한 자동 조작은 게임의 가장 큰 특징 중 하나인 상호작용성을 떨어트립니다. 게임은 영화, 드라마, 만화, 그리고 소설 등과 달리 사용자가 직접 개입할 수 있기 때문에, 그런 볼거리에서는 느낄 수 없는 재미가 있습니다. 그런데 자동 조작은 사용자를 단지 구경만 하는 존재로 바꿔 버려서, 다른 놀거리와 차별화된 재미를 없애 버립니다.
게임을 편리하게 하는 것은 중요한 문제입니다. 그런데 자동 조작은 그 문제의 올바른 해법이 되기 어렵습니다.
마케팅은 제품에 맞춰 실행해야 합니다. 특히 온라인 게임은 일반적인 상품과 달리 구매 결정이 한 번으로 끝나지 않기 때문에, 다른 접근 방식을 취해야 합니다. 일회성 마케팅 전략은 온라인 게임에선 잘 통하지 않습니다.
온라인 게임은 영화보다는 드라마에 가깝습니다. 영화는 재미가 없더라도 개봉 초기에 마케팅을 통해 한 번 표를 팔기만 하면 됩니다. 따라서 영화는 패키지 게임과 비슷합니다. 반면에 드라마는 아무리 마케팅을 열심히 해도 재미가 없다면 시청률이 나오지 않습니다. 따라서 드라마는 온라인 게임과 비슷합니다.
이벤트 경품 같은 일회성 마케팅 전략은 패키지 게임엔 적합할 수 있지만, 온라인 게임에서는 밑 빠진 독에 물 붓기인 바보 같은 짓입니다. 차라리 다양한 경로를 통해 인지도를 높여서, 꾸준히 신규 사용자를 끌어 오는 전략이 낫습니다. 그리고 마케팅보다는 게임의 재미를 끌어 올려 사용자 수를 유지하는 것에 더 집중해야 합니다.
RTS 장르나 FPS 장르의 게임은 대개 여러 모드를 갖고 있습니다. 그리고 일부 기획자는 모드를 늘리는 것이 게임의 재미를 올리는 데 결정적인 역할을 한다고 생각하는 것 같습니다. 하지만 제 생각은 다릅니다. 기본이 갖춰지지 않은 상태에서 모드를 추가하는 것은, 비용 대비 효과가 작은 방법입니다. 굳이 뭔가를 추가해서 게임 컨텐츠를 늘리고 싶다면, 모드를 추가하기보다는 맵을 추가하는 것이 비용 대비 효과가 큽니다.
게임 모드 추가는 게임 컨텐츠를 새로운 방식으로 재사용할 수 있는 좋은 전략입니다. 컨텐츠 제작엔 노력이 많이 필요하다는 것을 생각해 보면, 적은 노력으로 새로운 재미를 줄 수 있다는 것에 혹할 수도 있습니다.
하지만, 컨텐츠를 완전히 새로운 형식으로 소비하게 만들면, 게임의 정체성이 모호해지고 사용자가 모드별로 분산되는 부작용이 있습니다. 예를 들어, 워크래프트 3는 원래 RTS 장르이지만, 실제로는 AOS 장르의 유즈맵이 더 많이 플레이됩니다. 그리고 처음부터 AOS 장르로 만들어진 League of Legends 게임에 당연히 밀려 버렸습니다. 결국 워크래프트 3는 이도 저도 아닌 게임이 되고 말았습니다.
뿐만 아니라 게임 모드 추가는 기존 모드의 규칙에 익숙해진 사용자에겐 학습 부담이 되기도 합니다. 모드가 새로우면 새로울수록 더 그렇습니다.
게임 모드 추가는 맵 추가에 비해 많은 노력도 필요합니다. 맵 추가는 단순히 레벨 디자인만으로도 가능한 일이지만, 게임 모드를 추가하게 되면 프로그래밍 작업이 필수적입니다. 프로그래밍 작업은 버그를 쉽게 찾기 어려우므로, 업데이트를 빨리 해야 할 때 상당히 위험합니다.
모드를 추가하기보다 게임 자체의 기본기를 튼튼하게 만드는 게 우선입니다. 유닛 간 밸런스 수정, 고수와의 플레이에 따르는 스트레스를 제거하기, UI 개선, 진행 속도 조절, 레벨 디자인 수정, 사기성 전략 봉쇄, 고착화된 플레이 패턴을 제거하기, 그래픽이나 사운드의 품질을 올리기, 그리고 심각한 버그를 수정하기 등 모드 추가보다 중요한 일은 많습니다.
미니덤프를 이용하면 크래시가 났을 때의 콜 스택, 지역 변수, 그리고 레지스터 등의 정보를 알 수 있습니다. 하지만 이런 정보만으로는 원인을 정확히 알기 어려울 때가 잦습니다.
이럴 때엔 크래시가 자주 발생하는 위치에서 필요한 정보를 지역 변수에 복사하는 방법이 어느 정도 도움이 됩니다. 하지만 좀 더 풍부한 정보를 얻으려면, 힙(heap)의 정보까지 갖고 있는 풀(full) 덤프가 필요합니다. QA 테스트나 사내 테스트처럼 큰 파일도 전송이 가능한 환경에서는 되도록 풀 덤프를 저장하는 게 좋습니다.
텍스트 파일이 아닌 이진 파일은, Subversion으로 형상 관리할 때 골칫거리입니다. 워드 문서는 그나마 diff라도 쉬운 편인데, 엑셀이나 파워 포인트로 만든 문서는 diff도 보기 어렵습니다. TortoiseSVN에서 지원하지 않는 형식의 다른 이진 파일은 말할 것도 없습니다. 그런데 게임 개발할 때엔 일반적인 소프트웨어 개발과 달리 이진 파일을 많이 사용하므로 더 문제입니다.
그러므로 이진 파일 사용을 최대한 줄여야 합니다. 형식이 꼭 필요한 문서는, HTML로 저장하는 게 최선인 듯합니다. 그런 문서에 위키를 사용하는 것은 사용법이 복잡하고 형상 관리 시스템과 동기화시키기 어렵기 때문에 좋지 않습니다. 내용만 필요한 각종 속성 정의 문서는 엑셀 대신에 .xml이나 .csv로 저장하면 될 것 같습니다.
텍스트 파일로 바꿀 수 없는 이진 파일엔 lock 기능을 사용해야 합니다. Lock 기능을 사용하는 방법은 Locking에 나와 있으니, 참고하면 될 것입니다.
하지만 lock을 사용하더라도, branch와 trunk 간의 병합 시 발생하는 충돌을 막을 수는 없습니다. 따라서 이진 파일 형상 관리의 궁극적인 해법은 형상 관리 시스템에 이진 파일을 넣는 일을 줄이는 것입니다.
프로그래머 중에 단지 컴파일 속도 향상을 위해서 Pimpl 패턴이나 인터페이스를 사용하는 사람이 많습니다. 하지만 그 방법은 예전엔 좋았을지는 몰라도, 지금은 별로 좋지 않습니다. 단점이 장점보다 더 많기 때문입니다. 그 이유를 나열해 보겠습니다.
첫째, Pimpl 패턴이나 인터페이스를 사용하려면, 레이어 역할을 하는 클래스를 하나 더 만들어야 합니다. 게다가 원래 클래스에서 public이나 protected인 멤버가 변경될 때마다 원래 클래스와 새로 만든 클래스를 모두 바꿔야 합니다. 이렇듯 유지 보수에 추가되는 노력이 꽤 큽니다.
둘째, 빌드할 때 시간을 많이 차지하는 건 컴파일이 아니라 링크입니다. 기능을 충분히 잘게 나눠서 구현하면, 기능 하나를 구현할 때 실제로 고쳐야 하는 소스 파일의 수는 많지 않기 때문입니다. 그 점을 감안하면, 컴파일 속도 향상은 큰 의미가 없습니다. 컴파일 시간은 프리컴파일드 헤더만 잘 활용해도 많이 줄일 수 있습니다. 그리고 인크레디빌드처럼 여러 파일을 분산 컴파일시켜 주는 도구를 활용해도 됩니다. 아니면 컴퓨터를 좋은 것으로 바꾸는 것도 컴파일 속도를 향상시키는 좋은 방법입니다.
셋째, 객체를 스택에 생성할 수 없어서, new와 delete에 들어가는 부하가 생깁니다. 더구나 이 부하는 프로그래머뿐만 아니라 사용자에게도 영향을 줍니다.
Pimpl 패턴이나 인터페이스는 컴파일 속도 향상보다는 구현과 인터페이스를 분리하는 용도로 사용하는 게 낫습니다. 즉 두 개 이상의 구현을 한 개의 인터페이스로 접근할 수 있게 하는 용도인 것입니다.
야근이나 주말 근무 같은 추가 근무를 하는 사람 중에 그걸 인정 받고 싶어하는 사람이 많습니다. 하지만 추가 근무는 자랑거리가 아닙니다. 추가 근무를 한다는 것은 뭔가 잘못됐다는 증거입니다.
추가 근무의 주된 원인은 다음과 같습니다.
첫째, 잘못된 일정 때문에 추가 근무를 하는 경우입니다. 이런 경우는 대개 관리자의 무리한 요구가 근본적인 원인입니다. 그런데 때론 작업자의 작업 기간 예상이 잘못돼서 발생하기도 합니다.
둘째, 불필요한 작업을 하느라고 필요한 작업을 못하는 경우입니다. 이런 경우는 작업의 목표를 잊고 자신의 욕심을 채우기 위해 불필요한 갈아엎기나 기술 연구에 시간을 소비하는 경우입니다.
셋째, 근무 시간에 다른 일을 하느라 시간을 소비한 경우입니다. 필수적인 휴식 외의 사적인 활동에 근무 시간을 상당히 소비하는 사람이 많습니다.
어떤 게 원인이든 추가 근무는 바람직하지 않습니다. 게임 개발은 최소 2년 이상 투자해야 하는 긴 작업이고, 추가 근무는 장기적으로 봤을 때 오히려 손해입니다. 그리고 적절한 보상도 이뤄지지 않는 추가 근무를 개인의 삶까지 희생해 가며 할 이유는 더더욱 없습니다.
요즘 '야구 9단'이라는 게임을 하고 있습니다. 그 게임을 하면서 몇 가지 느낀 점을 적어보겠습니다.
야구 9단은 비교적 완성도 있게 만든 게임이지만 재미 면에서는 많이 부족합니다. 가장 큰 이유는 자신이 할 수 있는 게 별로 없다는 것입니다. 이건 단순히 컨텐츠가 부족한 것뿐만 아니라, 낮은 자유도와 제한된 영향력과도 연관이 있습니다.
야구 9단을 처음 접하면 여러 메뉴가 있고 다양한 걸 할 수 있는 것처럼 보입니다. 하지만 실제로 해 보면, 매 시간마다 라인업을 정하고 컨디션 아이템을 적용하는 것 외에는 별로 할 게 없습니다. 작전 설정은 큰 도움이 안 된다는 것이 사용자들의 일반적인 평가입니다. 특수 훈련은 귀찮고 그 성과는 미미합니다. 휴양 또한 비싸고 손이 많이 갈 뿐, 전략적 선택의 즐거움을 느끼긴 어렵습니다. 스카우트는 노력 대비 효과가 적습니다. FA 영입과 FA 판매는 유료 등록권 때문에 매매 가격만 폭등했고 매매가 어렵습니다. 챌린지 모드는 뻔한 상대와 보상으로 동기 유발이 안 됩니다. 루키 리그 때 13개가 주어지는 퀘스트도 이후엔 전무합니다. 상위 리그로 승급하면 새로운 컨텐츠가 있는 것도 아니고 더 잘하는 상대들을 만나 패배할 확률만 커질 뿐이니, 굳이 열심히 할 이유를 찾기 어렵습니다.
이 게임의 불편한 유저 인터페이스도 게임에서 멀어지게 만드는 이유입니다. 라인업, 아이템, 작전, 훈련, 이적, 그리고 휴양을 한 화면에서 처리할 수도 있었을 텐데, 모든 메뉴가 분리돼 있으며 일관성도 없습니다. 그리고 각 선수의 기록과 능력 중에서 원하는 항목만 비교할 수도 없어서 선수 관리가 귀찮습니다. 심지어 아이템은 여러 선수에게 동시에 적용시킬 수 없어서, 각 선수마다 일일이 적용해야 합니다.
또 하나 큰 문제 중의 하나는 인공지능과 개입입니다. 인공지능이 있지만 수준이 떨어지다 보니, 개입하는 사람의 승률이 훨씬 좋습니다. 그런데 그 개입조차도 막상 할 수 있는 것은 많지 않습니다. 실제로는, 비정상적인 투수 라인업을 통해 레벨을 쥐어짜고 유료 아이템으로 자금을 동원해서 파워나 주력이 좋은 타자를 얼마나 확보하느냐가 경기의 승패를 좌우합니다.
미투데이나 네이버 블로그를 통하지 않고는 친구 맺기 어려운 점이나, 친구가 몇 명 이상 있어야만 일부 게임 기능을 사용할 수 있게 한 점은, 정말 '마이너스의 손' NHN스러운 운영답습니다.
최근엔 유료 FA 등록권이 도입됐는데, 제 생각엔 이것 때문에 이 게임은 망할 것 같습니다. 등록권 때문에 선수 가격은 10배 이상 폭등했는데 수입은 그대로니, 라이트 유저는 정상적으로 게임을 할 수가 없습니다. 이 FA 등록권은 잘못된 부분 유료화의 좋은 예입니다. NHN 측에서는 계정 도용 피해를 방지하기 위해 도입했다고 하는데, 그렇다면 다른 방법도 얼마든지 있을 텐데 엉뚱한 방법을 택한 것 같습니다.
야구 9단엔 장점도 많습니다. 특히 스마트폰과 연동이 돼서 언제 어디서나 게임을 즐길 수 있다는 것은 큰 장점입니다. 또 전체적으로 완성도가 높고 게임 플레이에 많은 시간이 필요하지 않다는 점도 훌륭합니다. 또 운보다는 실력이 많이 작용한다는 점, 그리고 라인업 레벨 제한 덕택에 자금이 부족한 무료 사용자도 어느 정도는 즐길 수 있다는 점도 좋습니다.
야구 9단은 꽤 괜찮은 매니지먼트 게임이 될 수 있었는데, 아쉽습니다.
게임 개발은 작업 중 대부분이 협업으로 이뤄집니다. 따라서, 반드시 협업 능력이 뛰어난 사람을 채용해야 합니다. 아무리 기술적인 능력이 뛰어나더라도 프로젝트의 성공을 위해 헌신하지 않고 개인적인 성취에만 관심이 있는 사람은, 팀에 도움이 되지 않으며 때로는 방해가 되기도 합니다.
야구나 축구에서 최고 기록을 기록 중인 스타 플레이어에게 기록에 대한 느낌을 물어 보면 대부분 같은 대답을 합니다. '내 기록엔 관심이 없고, 팀의 승리에 도움이 되고 싶다.' 그들이 그렇게 말하는 이유는, 이타적인 사람이라서가 아니라 팀의 승리가 행복을 가져다 주고 자신의 경력에도 도움이 된다는 걸 알기 때문일 것입니다.
참고로, 구글에서는 개인주의적 성향이 강한 사람도 채용한다고 합니다만, 그건 두 세 명의 프로그래머로도 작업이 가능한 경우일 것이고 게임 개발은 이제 최소 20명 이상이 협업해야 하는 큰 일입니다.
게임 개발을 할 때엔 능력이 다소 부족한 사람이더라도 팀 플레이어를 채용하는 게 좋습니다.
저는 송강호 씨의 연기를 참 좋아합니다. 자연스러우면서도 잘 전달되는 연기는 국내에서 최고인 것 같습니다. 한 분야에서 최고가 된 사람에게선 배울 것이 있기 마련인데, 송강호 씨 또한 예외는 아닌 듯 합니다. 오늘 아래 두 인터뷰를 읽어 보았습니다.
특히 첫 번째 인터뷰의 다음 대화는 배우가 아니더라도 한 번쯤 읽고 생각해 볼 필요가 있습니다. 송강호 씨가 연기를 할 때, 어떤 외형, 지식, 또는 방법론에 집착하는 게 아니라, 현장에서의 상황만을 토대로 제일 단순한 해법을 추구한다는 점은 제가 추구하는 소프트웨어 개발 방법과도 비슷합니다. 이런 가치관은 오래된 설계 격언인 "Keep it simple, stupid!"나, Kent Beck이 얘기한 "Do the simplest thing that could possibly work."와도 일맥상통합니다.
100: 이러지 마시죠. (웃음) 사실 송강호의 연기에 대한 방법론적 궁금증은 늘 있었던 것 같아요. 물론 그런 게 논리적으로 설명이 안 된다는 걸 알지만.송강호: 연극원에서 특강을 두 번쯤 한 적이 있는데, 거기서 어떻게 하면 연기를 잘하는지, 어떻게 하면 좋은 배우가 되는지 이런 질문을 많이 받는다고. 그런데 이게 답이 없는 거지. 어떻게 말로 논리적으로 설명을 하겠어요. 학생 본인도 이게 우문인줄 아는데요 선배님, 하면서 묻고. 그냥 현장에서 더 일해본 사람으로서 단지 조언을 해주고 싶은 게 있다면 너무 많은 생각을 하지 말자는 거예요. 이 인물이 어떤 사람일까? 대본에 밑줄 쭉쭉 그어가면서, 대본에도 없는 어디서 태어났고, 어떻게 자랐다는 백그라운드 성장환경 같은 것도 상상해서 만들어 놓잖아요. 그런 고민을 하지 말라고. 그런 많은 생각을 가지고 연기를 하면 오히려 생명력이 없어져요. 그냥 순순히 인물을 받아들여야죠.100: 그런데 머릿속으로 아무리 단순해지자고 생각 한다고 해서 현장에서 군더더기가 확, 없어지는 건 아니잖아요?송강호: 아니죠. 그러니까 어려운 거죠. 안 풀릴 때도 있죠. 그럴 땐 본능적으로 헤쳐 나가는 거죠. 그 본능이란 게 지난 경험도 있고 솔직히…. 타고난 재능을 숨길 수 없죠, 으하하하하. 현장에서도 어떻게 하면 연기를 잘하는 것처럼 보일까를 고민하지 말고 그냥 대본이 원하는 대로 따라가면서 툭툭, 연기를 하는 게 더 좋은 연기라고 생각해요. 예전에 <밀양> DVD 코멘터리에서 이창동 감독이 “연기는 단순해지려고 하는 거다”라고 했는데 그게 정답인 것 같아요. 연기를 하는 건 어떤 분명한 감정을 전달하는 건데 군더더기가 너무 많이 붙어있으면 그 엑기스가 전달이 안 되잖아요. 결국 배우는 가장 단순해져야 한다는 거죠. 그런 훈련을 하는 것이 배우에겐 오히려 이상한 배역연구를 하는 것 보다 훨씬 중요하다고 생각해요. 굳이 송강호의 연기에 대한 노하우를 말하자면 많은 생각하지 말라는 거, 가장 단순하게 하라는 거예요.
리팩토링에서 제일 중요한 것은 기존 코드의 기능을 100% 그대로 유지한 채로 코드만 다듬어야 한다는 것입니다. 즉, 사용자 입장에선 아무런 차이도 느낄 수 없어야 합니다.
자신만의 코드에 욕심이 많은 사람이 종종 저지르는 실수가, 기존 코드를 정리하면서 기능까지 바꿔 버리는 일입니다. 기능이 바뀌어 버리면 기존 테스트 케이스로는 테스트가 불가능하므로, 문제 없이 리팩토링됐는지 쉽게 판단할 수가 없습니다. 또 수정된 기능에 맞춰 연관된 다른 코드도 수정해야 합니다. 그래서 잘못된 리팩토링을 하고 나면, 연관된 코드를 수정하고 숨어 있는 테스트 케이스를 찾으며 버그를 잡느라고 고생하게 됩니다.
잘못된 리팩토링은 차라리 안 하는 게 낫습니다.
특별한 것은 없고, 그냥 제가 자주 사용하는 방법만 정리해 둡니다.
- 여러 개 중에 하나만 해당되도 보여 주기. OR를 이용. 예: windows OR linux
- 비슷한 단어도 검색하기. ~를 이용. 예: ~windows
- 구(phrase)를 검색하기. 한글 검색 시엔 단어에도 이걸 사용하는 게 좋습니다. ""를 이용. 예: "windows xp"
- 특정 사이트에서만 검색하기. site:를 이용. 예: windows site:www.microsoft.com
- 특정 단어를 제외하고 검색하기. -를 이용. 예: windows -linux
- 특정 웹 페이지와 비슷한 곳을 검색하기. related:를 이용. 예: related:www.microsoft.com
- google.co.kr보다 google.com을 활용. google.co.kr에선 한국어 위주로 보여 줘서, 영문 자료를 찾을 때엔 google.com을 사용하는 게 낫습니다.
- 고급 검색 활용. 한국어로만 된 결과를 보고 싶을 때 주로 사용합니다.
윈도우 메시지 처리 함수에서 다음처럼 처리하면 됩니다.
// 원래는 WM_INPUTLANGCHANGE로 한영 전환을 검출하는 게 맞는데, 일부 컴퓨터에선// WM_INPUTLANGCHANGE 메시지가 안 와서, IMN_SETCONVERSIONMODE 메시지로 검사해야 합니다.if (message == WM_INPUTLANGCHANGE || message == WM_IME_NOTIFY && wParam == IMN_SETCONVERSIONMODE){// 한/영 전환 처리}
// 인코딩을 직접 바꾸는 것은 어려우므로, UTF-16으로 바꾼 다음에 원하는 인코딩으로 다시 바꾸게 했습니다.// 참고로, MultiByteToWideChar 함수의 cbMultiByte 인자와 WideCharToMultiByte 함수의 cchWideChar 인자를 -1로 넘겨 주는 게 더// 편합니다. 하지만 그러면 공문자까지 변환이 되므로, outEncodingText에 공문자가 포함되서 const char*와 호환되지 않는 문제가// 생깁니다. 그래서 할 수 없이 입력 문자열의 크기도 넘겨 주게 했습니다.string convert_encoding(UINT in_encoding, UINT out_encoding, const string& in_encoding_text){// 인코딩을 in_encoding에서 UTF-16으로 변환합니다.int utf16_character_count = MultiByteToWideChar(in_encoding, 0, in_encoding_text.c_str(), in_encoding_text.size(), NULL, 0);if (utf16_character_count == 0)return "";wstring utf16_text(utf16_character_count, L'\0');MultiByteToWideChar(in_encoding, 0, in_encoding_text.c_str(), in_encoding_text.size(), &utf16_text[0], utf16_character_count);// 인코딩을 UTF-16에서 out_encoding으로 변환합니다.int utf8_character_count = WideCharToMultiByte(out_encoding, 0, &utf16_text[0], utf16_text.size(), NULL, 0, NULL, NULL);if (utf8_character_count == 0)return "";string out_encoding_text(utf8_character_count, '\0');WideCharToMultiByte(out_encoding, 0, &utf16_text[0], utf16_text.size(), &out_encoding_text[0], utf8_character_count, NULL, NULL);return out_encoding_text;}string convert_from_ansi_to_utf8(const string& text){return convert_encoding(CP_ACP, CP_UTF8, text);}string convert_from_utf8_to_ansi(const string& text){return convert_encoding(CP_UTF8, CP_ACP, text);}
다음처럼 하면 디스플레이 장치의 픽셀당 비트 수, 가로 크기, 세로 크기, 그리고 주파수 등을 얻어낼 수 있습니다.
DEVMODE dev_mode;dev_mode.dmSize = sizeof dev_mode;dev_mode.dmDriverExtra = 0;EnumDisplaySettings(장치 이름, ENUM_CURRENT_SETTINGS, &dev_mode);
이 방법이 좋은지는 확실하지 않은데, 대체로 잘 동작하는 것 같습니다. 다음 코드를 윈도우 메시지 처리 함수에 추가하면 됩니다.
case WM_ACTIVATEAPP:case WM_DISPLAYCHANGE:case WM_MOVE:case WM_PAINT:case WM_SIZE:case WM_WINDOWPOSCHANGED:RECT clip_rectangle;GetWindowRect(window_handle, &clip_rectangle);ClipCursor(&clip_rectangle);break;
timeGetTime의 정밀도는 5ms 이상일 수 있습니다. Obtaining and Setting Timer Resolution에 나와 있는 것처럼, 정밀도를 높이려면 다음처럼 timeBeginPeriod 함수를 호출해야 합니다.
// 애플리케이션을 시작할 때 한 번만 설정합니다.TIMECAPS time_caps;timeGetDevCaps(&time_caps, sizeof time_caps);UINT minimum_timer_resolution = time_caps.wPeriodMin;timeBeginPeriod(minimum_timer_resolution);// 여기에서 해야 할 일을 합니다.// 애플리케이션을 종료하기 전에 되돌려야 합니다.timeEndPeriod(minimum_timer_resolution);
bool is_native_language_input_mode(HWND window_handle){HIMC ime_context = ImmGetContext(window_handle);if (ime_context == NULL)return false;DWORD conversion;DWORD sentence;ImmGetConversionStatus(ime_context, &conversion, &sentence);ImmReleaseContext(window_handle, ime_context);return conversion & IME_CMODE_NATIVE;}
C++에서 흔히 사용하는 매크로로 __FILE__과 __LINE__이 있는데, 비쥬얼 C++에서는 __FUNCTION__이라는 매크로도 사용할 수 있습니다. 함수의 이름을 표시해야 할 때, 일일이 입력하지 않아도 돼서 유용합니다.
배열을 watch 창에 추가하면 배열 첫 번째 값만 표시되는데, 다른 부분을 보려면 다음처럼 하면 됩니다.
배열의 보려는 위치, 보려는 요소의 수
예:
array, 1
array + 2, 3
지역화 작업을 할 때엔 sprintf가 std::ostringstream보다 더 유연해서 낫습니다. 하지만 sprintf는 인자의 순서까지 지정할 수는 없어서, 다국어 지역화 작업에 문제가 생길 수 있습니다.
이럴 때엔 좀 더 유연한 boost::format이 좋습니다. boost::format은 %1%, %2%, ... 이렇게 인자의 순서까지 지정할 수 있어서 어순이 다른 언어에도 적합합니다. 윈도 API인 FormatMessage도 sprintf 대신에 사용할 수 있지만, boost::format이 좀 더 범용적이라 더 좋습니다.
컴퓨터의 이름을 얻어내는 기능은 디버깅할 때 유용하게 쓰일 때가 있습니다. GetComputerName이라는 API를 사용하면 됩니다.
강제로 예외를 발생시키는 방법 몇 가지를 적어 봅니다. 상황에 따라 알맞은 방법을 골라서 사용하면 됩니다.
*static_cast<int*>(NULL) = 0;RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);throw "";
정수를 표현할 때엔 보통 int나 short를 사용하게 되는데, 네트워크 패킷 등에 사용할 때 크기를 줄이려고 char 형을 사용할 때가 있습니다. 그런데 정수로 표현돼야 할 자료를 char 형으로 표현하면, 스트림 관련 함수들이 그 자료를 문자로 인식해서 표현이 제대로 안 되는 문제가 생깁니다. 예를 들면 다음과 같습니다.
char number = 0;cout << "number is: " << number << endl;
위와 같은 코드에서 number는 실제로 0이라고 출력돼야 하지만 제대로 출력되지 않습니다. number를 공문자 '\0'로 인식해 버리기 때문입니다. 형식을 강제로 지정하는 printf 스타일의 함수를 사용하면 위와 같은 문제를 피할 수는 있지만, 유지보수가 어려워져서 좋지 않습니다.
정수는 되도록 정수형으로 표현하는 게, 문제의 소지를 줄여 줍니다.
함수 호출 대신에 메시지 방식이 필요한 때는 드문데, 다음 경우엔 유용합니다.
- 프로세스 간 통신
- 스레드 간 동기화 (임계 영역 설정을 통한 동기화보다 복잡한 상황이 덜 생깁니다.)
- undo와 redo의 구현
- 비동기 처리
그 외의 대부분 상황에선 함수 호출 대신에 메시지 방식을 사용하는 것은 불필요한 복잡함만 가져 옵니다.
한때 위키에 매료돼서 직접 설치도 해 보고 꽤 오랫동안 사용해 봤지만, 위키는 게임 개발엔 적합하지 않습니다.
위키의 제일 큰 장점은 공동 편집이 쉽다는 것인데, 게임 개발할 때엔 어차피 형상 관리 소프트웨어를 사용해서 문서를 관리하게 되므로 위키의 장점이 사라집니다. 더구나 위키는 사용하기 어렵고 편집 기능도 부족합니다.
팀 내 의사소통 수단이 필요하다면, 위키보다는 블로그, 게시판, 트위터, 페이스북, 이메일, 또는 메신저 등의 도구가 훨씬 낫습니다. 다른 도구로도 위키의 기능 대부분을 훨씬 쉽게 대신할 수 있습니다.
디버거에서 프로그램을 실행시키면 릴리즈 빌드일지라도 디버그 힙(debug heap)을 사용하게 됩니다. 디버그 힙은 메모리를 디버깅하기 쉽게 힙을 특정 값으로 초기화시키는 등의 일을 합니다. 그래서 디버그 힙을 사용하면, 버그가 재현되지 않는 경우가 있습니다. 이럴 때엔 디버그 힙을 사용하지 않으면서 디버깅을 하면 되는데, 간단한 방법은 다음과 같습니다.
디버거에서 Start Debugging 대신에 Start Without Debugging으로 실행시킨 다음에 Attach to Process로 붙입니다. 디버그 힙을 사용할지 여부는 초기 실행 시에 결정되기 때문에 이 방법이 가능합니다.
netstat -ao
위의 옵션은 도메인 이름을 알고 싶을 때 유용합니다.
netstat -ano
위의 옵션은 IP를 숫자로 정확히 보고 싶을 때 유용합니다.
Structured Exception Handling(SEH)을 이용하면 대부분의 예외를 처리할 수 있는데, 몇몇 경우는 처리되지 않고 프로그램이 그냥 종료될 때가 있습니다. 다음은 그런 예외를 추가로 잡아내기 위한 코드입니다. 이것 말고도 예외 처리가 더 필요한지는 잘 모르겠습니다.
_set_purecall_handler(handle_pure_virtual_function_call);_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);signal(SIGABRT, handle_interrupt_signal);_set_invalid_parameter_handler(handle_invalid_parameter);
이런 일을 자주 하게 될 때가 많아서 정리해 둡니다. 아래는 a를 b로 바꾸는 예제입니다.
static const string search_string = "a";for (string::size_type position = 0; (position = a_string.find(search_string, position)) != string::npos; ++position)a_string.replace(position, search_string.size(), "b");

댓글을 달아 주세요