#include<Windows.h>
#include <iostream>
using namespace std;
void Gotoxy(int x, int y)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //출력핸들?
COORD cursorPos = { x,y };
SetConsoleCursorPosition(handle, cursorPos);
}
void SetColor(int color)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, color);
}
int main()
{
int x = 0;
int y = 0;
for (int i = 0; i <= 15; ++i)
{
SetColor(i);
cout << "color number: " << i;
Gotoxy(x++, y++);
}
}
<1. 메모리와 빌드>
1. 코드 영역
- 상수 등
2. 데이터(data) 영역
- 전역 변수, 정적 변수
- 프로그램 시작 ~ 종료까지 존재
3. 스택(stack) 영역
- 지역, 매개 변수 등
- 함수의 시작~ 종료까지 존재
※ Stack Overflow
4. 힙(heap)영역
- 동적할당
- 사용자에 의해 설정
★ 사용자가 반드시 관리해야 하는 영역
※ Heap Overflow
2. 빌드 과정
※ 빌드(build) 과정
(=컴파일 + 링킹)
1. 전처리
- 주석 제거: 컴퓨터는 필요 없음.
- 헤더 파일 삽입: #include 복사
=> cpp에 있는 함수 원형은 링킹할 때 오브젝트 파일과 결합함.
- 매크로 치환: #define 매크로 적용
2. 컴파일
컴파일러는 최종 소스를 기계어(어셈블리어)로 변환하여 오브젝트 파일(object)을 만든다.
- 문법 검사
- Data 영역 메모리 할당
3. 링크
- obj파일 묶어서 실행 파일 제작
- 라이브러리 연결
※ 유의사항
- cpp 파일들은 각각 독립적으로 컴파일 되며, 컴파일이 완료된 cpp파일들을 링킹한다.
- #include 하는 헤더파일은 컴파일이 아니라 cpp파일로 복사될 뿐이다.
3. 헤더(.h)와 소스파일(.cpp)을 분리하자.
1)#include ???
< >: 표준으로 사용하는 것
" ": 내가 정의한 것.(경로에서 찾는다.)
=> 헤더 파일 분리 방법: 선언은 헤더파일에, 정의는 cpp파일로 정리한다.
Q) 헤더 파일을 사용 및 분리하는 이유
- 파일 하나에 길게 코딩하면 복잡한 프로그램을 제작할 때 힘들기 때문에 분리한다.
- 다른 소스 파일에 포함시킬 목적으로 재사용하기 위해 분리한다.
2) 헤더 가드
- 헤더 가드 종류
1. #ifdef, #endif
2. #pragma once
:C++ 공식 언어는 아님, MS에서 지원
- 헤더 가드(#pragma once)가 필요한 이유
=> 정의가 중복되면 안되기 때문에!
<2. 아스키 아트와 사운드>
1) 아스키 아트
※ 참고 사이트
- 원하는 폰트로 글을 직접 입력하여 사용하는 사이트
: https://patorjk.com/software/taag
- 만들어진것들 키워드별로 찾아 사용하는 사이트
: https://ascii.co.uk/art
- 직접 그린것을 아스키아트로 만들어주는 사이트
: https://www.i2symbol.com/ascii-art-generator-for-facebook-comments
- 파일을 첨부하면 아스키아트를 생성해주는 사이트
: https://wepplication.github.io/tools/asciiArtGen/
- TEXT-IMAGE(첨부파일o)
: https://www.text-image.com/convert/ascii.html
※ 인코딩이란?
: 컴퓨터에서 인코딩은 사람의 언어(문자 집합)에서 컴퓨터 언어(0,1)로 변환하는 과정을 통틀어 의미. 코드화,
암호화를 의미.
컴퓨터는 on/off(1, 0)의 2진 신호로 데이터를 처리하게 되어있고, 따라서 숫자는 진법 변환으로 인간과 컴퓨터가 같이 이해를 할 수 있다. 그러나 문자는 그렇지 못하기 때문에 특정 문자와 숫자(코드)를 연결하는 코드가 필요해지는데 이것을 문자 인코딩이라고 한다.
2)멀티바이트 문자집합과 유니코드 문자집합의 차이점
- 멀티바이트
: 한 문자에 할당하는 공간이 일정하지 않다
(영어 : 1바이트, 다국어 2바이트)
ex. char str = "문자열";
cout, cin는 멀티바이트 형식을 사용한다.
- 유니코드
: 항상 2바이트 할당. 외국어 윈도우에서도 한글이 깨지지 않고, 다국어버전을 만들기 쉽다
ex. wchar_t str = L"문자열"; //유니코드
wcout, wcin은 유니코드 형식을 사용한다.
구분 | 유니코드(_UNICODE) | 멀티바이트(_MBCS) |
기본 자료형 | whcar_t | char |
단일 문자의 크기 | 2Byte (16bit) | 1Byte ~ 2Byte (영문, 숫자를 포함한 ASCII는 1바이트로 표현되고 나머지 한글, 한자, 일본 가나 등은 2바이트로 표현) |
포함하는 문자셋 | 와이드 문자 및 utf-16으로 인코딩된 문자열 | 유니코드를 제외한 문자셋(ANSI UTF-8등) |
5. 아스키 아트 출력을 위한 함수들
1) _setmode()
: mode에서 제공하는 파일 변환 모드인 fd로 설정됨.
_O_TEXT를 mode로 전달하면 텍스트(즉, 변환된) 모드가 설정됨.
#include<io.h> 필요: input. output 헤더
- 성공하면 이전 변환 모드를 반환함.
_FileHandle: 파일 설명자, _Mode: 새 변환 모드
ex. _setmode(_fileno(stdout), _O_U16TEXT);
=> 알파벳 O임!!
2) _fileno
: 스트림에 연결된 파일 설명자를 가져온다.
#include<stdio.h> 필요 //_fileno(File *_Stream)
#include<fnctl.h> 필요: file control options 헤더 => file형태 //_O_U16TEXT
6. Sound 생성
: 컴퓨터 게임에서 출력되는 화면도 중요하지만 사운드의 역할도 굉장히 중요하다. 우리가 자주 듣는 파일은 mp3파일이지만, 콘솔용 게임이 주로 나왔던 시기에는 mp3 보다는 스피커에서 간단한 톤을 생성해서 그 톤으로 음악을 만들었다. 여기서 그러한 사운드를 만들기 위한 간단한 방법에 대해서 이야기해 보고 샘플로 '반짝반짝 작은 별'을 스피커음으로 들어 보자.
※ Beep함수
: 마이크로소프트제작
- 함수의 원형:
- dwFreq: 헤르츠에서 소리의 주파수, 이 매개변수는 37에서 32,767(0x25 ~ 0x7FFF) 범위에 있어야 한다.
- dwDuration: 소리의 지속 시간(단위: ms)
- 음에 대한 기준 주파수는 440Hz이다. 이것은 1834년 독일 자연 연구회의에서 결정된 표준 음고로 계명은 A4(라)에 해당한다. 피아노 건반은 한 옥타브당 12개이고 한 옥타브 올라가는 데 2배 주파수의 톤이 나온다.
예제 게임
<1. 절대 음감 게임>
- 게임 설명:
1. 8음계를 Beep함수를 이용하여 먼저 차례대로 들려준다.
2. 난수를 발생해서 선택된 한 음을 들려준다.
3. 플레이어는 그 음이 무엇인지 선택하고, 맞거나 틀린 정보를 보여준다.
7. 콘솔 함수 다루기(console.h, console.cpp)
1) 콘솔 창 관련
1. 제목 설정
- system("title 제목"); ex. system("title test");
- SetConsoleTitle("제목"); ex. SetConsoleTitle(L"미로 게임"); SetConsoleTitle("my game");
2. 콘솔 창 크기 설정
- system("mode con cols=가로길이 lines=세로길이);
=> 응용 system("mode con cols= 가로길이 lines=세로길이 | title 제목");
ex. system("mode con cols=50 lines=50 | title test");
※ 가로가 세로보다 짧음. 가로를 50문자가 출력될 정도, 세로를 50줄이 출력될 정도. 띄어쓰기 주의!!
※ 핸들이란? 리소스의 메모리 주소를 정수로 치환한 값
3. 콘솔 창 전체화면 설정
: 지정한 표준 디바이스에 대한 핸들을 검색함.
※ STD_OUTPUT_HANDLE: 표준 출력 디바이스
ex. void Fullscreen()
{ //CONSOLE_FULLSCREE_MODE가 전체화면모드, CONSOLE_WINDOWED_MODE가 윈도우모드
//SetConsoleDisplayMode(GetStdHandle(STD_OUTPUT_HANDLE), CONSOLE_FULLSCREEN_MODE, 0);
ShowWindow(GetConsoleWindow(), SW_MAXIMIZE);
}
2) 프로그램 관련
- 종료: exit(0);
- 일시정지: system("pause");
3) 커서 제어
※ 콘솔 좌표계
1. 좌표 이동 함수(Gotoxy)
: 커서의 위치를 저장하는 구조체.
=> #include<Windows.h> 필요
참고글: https://geundung.dev/14
ex. void Gotoxy(int x, int y)
{
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 콘솔창 핸들
COORD Cur = {x,y }; // {x*2, y};처럼 게임마다 2칸씩
SetConsoleCursorPosition(hOut, Cur);
}
2. 깜빡거리는 커서 보이지 않게 하기
: 지정된 콘솔 화면에 대한 커서의 크기와, 표시 유형을 설정함.
: 콘솔 커서의 정보를 저장하는 구조체.
ex. void Setcursor(bool _bVis, DWORD _size)
{
CONSOLE_CURSOR_INFO curinfo;
curinfo.dwSize = _size; // 커서 굵기(1~100)
curinfo.bVisible = _bVis; // True: 보임, FALSE: 숨김
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curinfo);
}
4) 색깔 칠하기
: “콘솔의 핸들 값”과 “색상 값”을 받아서 글자 색깔을 변경해주는
함수.
ex. void Setcolor(int color, int bgcolor)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (bgcolor << 4) | color);
}
- 글자 배경색은 글자색의 16배이다. ex. 글자색의 2와 글자 배경색의 32는 같은 색깔
- WORD는 short형(2바이트=16비트)임. 배경색상을 바꾸려면 5~8번째 4개의 비트를 조작해야 함.
ex. 겜프는 허니잼 : 밝은 흰색 바탕에 연한 녹색 글씨 => 0000 0000 1010 1111
※ 색상표 => 아래와 같이 enum class 활용!
< 예제 2 Gotoxy()와 Setcolor() 함수로 아래 화면을 만들어보세요. >
5) 키보드 처리
1. _getch(), _putch()
↑: -32 -> 72
←: -32 -> 75
→: -32 -> 77
↓: -32 -> 80
※ 스페이스바: 32, 엔터키: 13
=> 두 개 이상의 키를 한번에 입력받지 못해 대각선을 못함. Q) _kbhit()는 왜 써야 할까??
=> _getch()로 입력을 받는 과정에서 키보드가 입력되지 않으면 키보드 입력을 기다리면서 입력할 때까지 게
임이 진행되지 않는 문제점이 생김.
2. GetAsyncKeyState
:동시에 입력받을 때 사용. ex. 대각선 이동
: 인자 값으로 키보드의 가상키코드를 받음.
- 키가 눌려진 상태에서는 최상위 비트(0x8000)이 1이 되며, 처음 입력되었을 때는 0x8001 비트가 1임.
ex. if(GetAsyncKeyState(VK_UP) & 0x8000)
- 가상 키코드
값 | 가상키 코드 | 설명 |
0x01 | VK_LBUTTON | 마우스 왼쪽 버튼 |
0x02 | VK_RBUTTON | 마우스 오른쪽 버튼 |
0x04 | VK_MBUTTON | 마우스 가운데 버튼 |
0x08 | VK_BACK | Backspace |
0x09 | VK_TAB | Tab |
0x0D | VK_RETURN | Enter |
0x10 | VK_SHIFT | Shift |
0x11 | VK_CONTROL | Ctrl |
0x12 | VK_MENU | Alt |
0x1B | VK_ESCAPE | Esc |
0x20 | VK_SPPACE | Space Bar |
0x21 | VK_PRIOR | Page Up |
0x22 | VK_NEXT | Page Down |
0x23 | VK_END | End |
0x24 | VK_HOME | Home |
0x25 | VK_LEFT | ← |
0x26 | VK_UP | ↑ |
0x27 | VK_RIGHT | → |
0x28 | VK_DOWN | ↓ |
반환 값 | 설명 |
0(0x0000) | 이전에 누른 적이 없고 호출 시점에서 안눌린 상태 |
0x8000 | 이전에 누른 적이 없고 호출 시점에서 눌린 상태 |
0x8001 | 이전에 누른 적이 있고 호출 시점에서 눌린 상태 |
1(0x0001) | 이전에 누른 적이 있고 호출 시점에서 안눌린 상태 |
=> 이 시점을 판단하는 기준은! 이전 GetAsyncKeyState 후 ~ 호출시의 GetAsyncKeyState의 바로 전까지의
기간임.
※ 시간 관련 함수
clock()
: c 함수(#include<time.h>), 응용 프로그램이 시작된 이후 CPU 틱수를 반환함.(ms)
GetTickCount64()
: win32 api 함수(#include<Windows.h>), 시스템이 시작된 이후 경과된 시간을 반환함.
1초에 1000씩 틱 카운트를 증가시킴.
※ 64가 붙은 이유: 윈도우가 시작되고 1초에 1000틱씩 카운트를 증가시키는데, 카운트는 32비트 값이라. 최대
49.7일간만 유지할 수 있어 이후에는 오버플로우라 0으로 초기화됨. 64비트로 증가하여 오버플로우 시점이 약 5억 8천정도로늘어남.
'학교 수업 > 게임프로그래밍' 카테고리의 다른 글
제1강. 난수와 시간 1 (0) | 2024.04.17 |
---|