Win32 - Game
[TOC]
버퍼
버퍼는 메모리 혹은 임시 메모리
더블 버퍼링(Double Buffering)
- 모니터는 기계적이고 메모리는 전자적이다.
- 전자적인 메모리가 기계적인 모니터보다 훨씬 빠르다.
- primary buffer
- 모니터에 전송될 이미지가 저장되는 곳
- 모니터에 그림을 다 그리기 전에 새로운 그림을 그리면 화면이 깨지는 티어링 현상이 발생한다.
- 모니터에서 한 장의 화면을 그리는 것은 primary buffer에 그림을 그리는 것보다 훨씬 느리기 때문에 속도 차이에 의해 모니터 그림이 깨진다.
- back buffer
- primary buffer에 그림을 그리기 전에 미리 그림을 완전히 그려놓고 primary buffer에 복사하기 위한 메모리 공간
- DirectX를 사용한다면 알아서 처리해준다.
- 그리기 버퍼는 HDC와 Bitmap 중 어떤걸 전역변수로 유지해야하나?
- 둘다 유지할 필요는 없어보이고 생성에 더 큰 비용이 필요한걸 한번만 생성해놓고 계속 사용하면 될듯한데 어떤게 더 큰 비용이 필요한지 잘 모르겠다.
- 실제 그려지는 곳은 비트맵이니 더 많은 메모리가 필요하지 않을까?
- 둘다 유지할 필요는 없어보이고 생성에 더 큰 비용이 필요한걸 한번만 생성해놓고 계속 사용하면 될듯한데 어떤게 더 큰 비용이 필요한지 잘 모르겠다.
- 윈도우 사이즈가 변경되면 비트맵(혹은 DC)를 다시 생성해야한다.
페이지 전환(Page Flipping)
- 비디오 메모리의 출력 시작 주소를 전환
- 더블 버퍼링의 버퍼 복사 시간을 줄이고자 Primary buffer와 Back buffer를 번갈아가며 화면에 출력하는 기법
- 현재 게임 대부분은 페이지 전환을 사용한다.
프레임워크 구조
- 게임 세팅 부분
- 게임 코드 부분
프레임워크 클래스
- abstract GameFrame class
- Init(HWND, int w, int h, isFullScreen)
- 프레임워크가 제공
- 게임 세팅, 메모리와 객체 생성, 셋업 관련
- 백버퍼 생성
- Memory DC
- Bitmap
- ChangeScreenMode()
- 전체화면과 윈도우화면을 전환하는 역활
- SetWindowLong(hWnd, GWL_STYLE, WS_POPUP)
- 윈도우의 스타일을 다시 설정
- WS_POPUP
- 상단 타이틀바 등이 없는 가장 기본적인 형태
- SetWindowPos()
- 윈도우 위치 설정
- FullScreenModeChange() 제작
- DEVMODE sDevMode // device mode
- sDevMode.dmSize
- sDevMode.dmPelsWidth
- sDevMode.dmPelsHeight
- sDevMode.dmFields
- ChangeDisplaySetting(&sDevMode, )
- virtual SceneInit()
- 프레임워크가 화면설정까지 한 후 호출하는 메서드
- 실제 사용자 코드는 여기에 들어간다.
- 프로그램 시작 시간 저장
- GetTickCount()
- Update()
- 데이터 갱신 -> 값 변경
- 입력 체크, 이동, 충돌 체크
- ShowFPS()
- FPS를 화면에 출력
- Update() 호출 시 마다 FPSCount를 +1
- 1초에 한번만 FPSCount를 글로벌 g_nPrevFPSCount에 갱신
- g_nPrevFPSCount를 사용자 코드에서 출력
- 이전 시간과 지금의 시간차이로 1을 나눈값을 출력해도 될듯.
- virtual SceneUpdate()
- Render()
- 출력 부분
- 이중 버퍼링 구현
- 백 버퍼 초기화
- SceneRender() 호출
- FPS 출력
- 백 버퍼를 프라이머리 버퍼에 복사
- virtual SceneRender()
- Release()
- 객체 또는 메모리 해제
- 프레임워크가 만든 객체 및 DC 해제
- DeleteDC
- SceneRelease() 사용자 해제 코드 호출
- virtual SceneRelease()
- Init(HWND, int w, int h, isFullScreen)
- GameWork class
- GameFrame을 상속받아 구현
- SceneInit()
- SceneUpdate()
- SceneRender()
- SceneRelease()
프레임워크 적용
- Win32 프로그래밍에서 GameFrame가 호출되는 시점
- InitInstance 시점에 GameFrame::Init() 호출
- 메시지 루프에서 GameFrame::Update(), GameFrame::Render() 호출
- WM_DESTROY 메시지 발생 시 GameFrame::Release() 호출
프로젝트 디렉터리
포함 디렉터리
- include 파일
라이브러리 디렉터리
- .lib 파일
패턴툴
패턴
- 일정한 형태나 양식 또는 유형
- 반복되는 형식이나 형태
- 인공지능을 흉내내는 효과
- 적 캐릭터에 적용
패턴툴 기능
- 이동 경로 편집
- 이동방향
- 이동속도
- 이동거리
- 대기시간
- 최종 도착 좌표
- 패턴 데이터 저장 및 읽기
- 패턴 출력 및 테스트
패턴툴 설계
유닛 상태 타입
- UnitStateType Enum
- Idle
- Walk
유닛 상태
- UnitState Class
- UnitStateType
- XY
- Time
상태 패턴 클래스
- UnitStatePattern Class
- UnitStates
- UnitStateIndex
- 파일 구조
- .usp
- count
- UnitStateType x y time
유닛
- 캐릭터 등 행동을 하는 주체
- Unit Class
- xy
- UnitStatePattern
- Render()
- 현재 유닛상태에 따라 그림
- 파일구조
- .unit
- .ani 파일 경로
- .usp 파일 경로
- SpeedPerSecond
Interface
- UnitState 등록
- 렌더링 화면 가운데를 기준으로 마우스 클릭으로 지정
- 클릭 시 자동으로 등록됨
- 현재 마우스의 좌표를 좌하단에 출력
- Play/Stop
- Unit : 작은 원
- Load/Save
- .usp
- Speed per frame or Speed per second
유닛 툴
- 유닛 이름
- 유닛 상태 타입 별 애니메이션 파일 할당
유닛
- 이름
- 스프라이트
- 상태 별 애니메이션
- 패턴
적 유닛
플레이어 유닛
공 유닛
사운드
FMOD
https://www.fmod.com
FMOD Studio API 다운로드(로그인 필요)
설치
C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows
헤더파일 C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\api\core\inc
라이브러리 C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\api\core\lib\x86 or x64
예제파일 C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\api\core\examples
설명서 C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\doc
프래임 당 한번만 변경되어야하는 변수는 한 프레임에 두 번 변경되지 않도록 주의한다.
- 애니메이션 인덱스와 같이 한 프레임에 한번만 증가해야하는 변수를 한 프레임에 증가시키고 조건에 따라 초기화 등을 하면 애니메이션 프레임이 한 프레임에 두 번 증가할 수 있다.
- Update에서 행동이 완료됐다고 다음 행동으로 옮기면서 애니메이션 인덱스를 증가시키면 한 프레임에 두 번 Update와 애니메이션 프레임 이동이 일어난다.
- 이럴 때는 상태 갱신됐다는 표시를 하고 Update 최상단에서 상태 갱신이 있다면 다음 상태로 넘기고 진행하면 된다.
- 한 프레임에서 완료까지되면 같은 액션을 계속 줘도 완료 후 다시 받기 때문에 iAniIndex가 계속 초기화 된다.
화면에 텍스트 출력
- TextOut, DrawText 등을 사용한다.
- 텍스트의 배경이 뒤 이미지를 덮는게 싫다면 배경 모드를 투명으로 설정한다.
- SetBkMode(_hdcMem, TRANSPARENT);
vector, deque을 멤버변수로 사용한 경우
- 클래스에서 복사 대입 연산자 함수를 정의해야하며 vector, deque의 복사는 std::copy를 이용한다.
- mempcy 등 다른 방법을 사용하면 복사가 되지 않아서 오작동하거나 종료된다.
vector에 push_back()은 매개변수를 복사한다
- vector.push_back(Class());
- 클래스를 생성과 동시에 push_back()에 매개변수로 전달하면 매개변수가 이동 생성자가인 push_back이 호출된다.
- 이 때 vector의 크기가 작다면 새로운 메모리를 할당하고 기존의 메모리를 해제한다.
- 기존 메모리 해제 시 모든 클래스의 소멸자가 호출된다.
- 따라서 하나씩 push_back하는 방법은 좋지 않고 고정된 크기의 값에 사용하는 것이 좋다.
- 또한 새로운 메모리를 할당받아 모든 요소를 옮기기(memcpy로추정) 때문에 참조 포인터가 댕글링 될 수 있어서 위험하기도 하다.
- 값을 갖는 vector라면 데이터를 넣고 빼면서 복사 대입 연산자가 계속 호출되는데 이를 명시적으로 정의하지 않으면 컴파일이 되지 않고 명시적으로 정의해도 개별 멤버변수의 복사를 제대로 해줘야 문제가 발생하지 않기 때문에 가능한 포인터 요소를 담도록 하는 것이 좋다.
vector 복사하기
- std::copy
- copy 전에 resize해줘야한다.
- std::assgin
- 복사 대입 연산자
- memcpy는 절대 사용하지 말자.
- 메모리를 잘 못써서 나는 에러는 찾기가 정말 힘들다.
이동지점 Waypoint
- relativeXY xy
- absoluteXY absXY
- waitTimeAfterMove