Game Programming: 두 판 사이의 차이
imported>Drake 잔글 (→게임 만들기) |
imported>Drake (→준비) |
||
| 3,657번째 줄: | 3,657번째 줄: | ||
* 우분투에서는 [[우분투소프트웨어센터]]에서 검색해서 받는게 좋다. | * 우분투에서는 [[우분투소프트웨어센터]]에서 검색해서 받는게 좋다. | ||
* 만약 윈도우라면 Visual C++과 MinGW용 라이브러리가 있다. | * 만약 윈도우라면 Visual C++과 MinGW용 라이브러리가 있다. | ||
=== Hello World === | |||
* 뭐든 헬로월드부터 찍어봐야지.. 안그래? | |||
<source lang="c"> | |||
/* sdltest.c */ | |||
/* | |||
SDL Test | |||
Demonstrates SDL being initialized and shut down. | |||
written by Sayuri L. Kitsune (2012) | |||
*/ | |||
/* Includes */ | |||
#include <SDL.h> | |||
#include <stdio.h> | |||
/* Main */ | |||
int main(int argn,char **argv) | |||
{ | |||
/* Init */ | |||
if(SDL_Init(SDL_INIT_VIDEO) != 0) | |||
fprintf(stderr,"Could not initialize SDL: %s\n",SDL_GetError()); /* Error? */ | |||
printf("SDL Initialized\n"); | |||
/* Exit */ | |||
SDL_Quit(); | |||
printf("SDL Shutdown\n"); | |||
/* Done */ | |||
return 0; | |||
} | |||
</source> | |||
* 그럼 하던대로 컴파일을 해볼까! | |||
<source lang="bash"> | |||
$ gcc -c sdltest.c | |||
</source> | |||
* 으메, 헤더파일 없다고 나오네 | |||
* 헤더파일은 /usr/include/SDL에 있당 | |||
* 그러니까 이렇게 | |||
<source lang="bash"> | |||
$ gcc -I"/usr/include/SDL" -c sdltest.c | |||
</source> | |||
* 자, 컴파일은 됐어. | |||
* 이제 링크! | |||
<source lang="bash"> | |||
$ gcc -o sdltest sdltest.o | |||
</source> | |||
* 또 에러네? 젠장 | |||
* 라이브러리 링크해줘야함 | |||
<source lang="bash"> | |||
$ gcc -o sdltest sdltest.o -lSDL | |||
</source> | |||
* 그냥 한번에 다 할라면 | |||
<source lang="bash"> | |||
$ gcc -o sdltest sdltest.c -I"/usr/include/SDL" -lSDL | |||
</source> | |||
* 실행해보면 그냥 초기화되고 꺼졌다는 메세지 나와 | |||
* 그럼 성공 | |||
=== SDL_PollEvent === | |||
* 그래도 화면 나오는게 보고 싶겠지. | |||
* 화면 초기화하고 어떤 입력을 받는 루틴을 짜보자. | |||
<source lang="c"> | |||
/* demo.c */ | |||
/* | |||
SDL Video Demo | |||
demonstrates SDL's video working by moving dots around a screen | |||
written by Sayuri L. Kitsune (2012) | |||
*/ | |||
/* Includes */ | |||
#include <SDL.h> | |||
/* Globals */ | |||
SDL_Surface *demo_screen; | |||
/* Main */ | |||
int main(int argn,char **argv) | |||
{ | |||
SDL_Event ev; | |||
int active; | |||
/* Initialize SDL */ | |||
if(SDL_Init(SDL_INIT_VIDEO) != 0) | |||
fprintf(stderr,"Could not initialize SDL: %s\n",SDL_GetError()); | |||
/* Open main window */ | |||
demo_screen = SDL_SetVideoMode(320,240,0,SDL_HWSURFACE|SDL_DOUBLEBUF); | |||
if(!demo_screen) | |||
fprintf(stderr,"Could not set video mode: %s\n",SDL_GetError()); | |||
/* Main loop */ | |||
active = 1; | |||
while(active) | |||
{ | |||
/* Handle events */ | |||
while(SDL_PollEvent(&ev)) | |||
{ | |||
if(ev.type == SDL_QUIT) | |||
active = 0; /* End */ | |||
} | |||
} | |||
/* Exit */ | |||
SDL_Quit(); | |||
return 0; | |||
} | |||
</source> | |||
* 솔직히 옵션 넣어야 할게 좀 있고 귀찮으니 Makefile도 하나 만들자. | |||
<source lang="make"> | |||
test: | |||
make build; | |||
./demo; | |||
build: | |||
gcc -I"/usr/include/SDL" -c demo.c; | |||
gcc demo.o -lSDL -o demo; | |||
clean: | |||
-rm demo.o; | |||
-rm demo; | |||
</source> | |||
* 창 하나가 뜬다. | |||
=== SDL_app === | |||
* 간단하게 화면출력하는거 하나 만들어봅시다. | |||
<source lang="c"> | |||
/* sdl_app.c */ | |||
#include <SDL/SDL.h> | |||
int main ( int argc, char** argv ) | |||
{ | |||
SDL_Init(SDL_INIT_EVERYTHING); | |||
SDL_Surface* screen = SDL_SetVideoMode(320, 240, 16, SDL_SWSURFACE); | |||
SDL_Surface* bmp = SDL_LoadPNG("back.png"); | |||
SDL_Rect dstrect; | |||
dstrect.x = 0; | |||
dstrect.y = 0; | |||
SDL_BlitSurface(bmp, NULL, screen, &dstrect); | |||
SDL_Flip(screen); | |||
SDL_Delay(5000); | |||
SDL_FreeSurface(bmp); | |||
SDL_FreeSurface(screen); | |||
SDL_Quit(); | |||
return 0; | |||
} | |||
</source> | |||
다음은 Makefile.sdl_app | |||
<source lang="make"> | |||
test: | |||
make build; | |||
build: | |||
gcc -I"/usr/include/SDL" -c sdl_app.c; | |||
gcc demo.o -lSDL -o sdl_app; | |||
clean: | |||
-rm sdl_app.o; | |||
-rm sdl_app; | |||
</source> | |||
* 아무 그림파일이나 320x240 해상도에 맞춰 bmp로 저장하고 make -f Makefile.sdl_app 이런식으로 하면 되겠다. | |||
* 아 솔직히 귀찮으면 디렉토리별로 나누면 그게 짱 | |||
* 대충 snail 애니메이션을 해보자. | |||
<source lang="c"> | |||
/* sdl_ani.c */ | |||
#include <SDL/SDL.h> | |||
int main ( int argc, char** argv ) | |||
{ | |||
SDL_Init(SDL_INIT_EVERYTHING); | |||
SDL_Surface* screen = SDL_SetVideoMode(480, 272, 16, SDL_SWSURFACE); | |||
SDL_Surface* bmp = SDL_LoadBMP("character.bmp"); | |||
SDL_Rect dstrect; | |||
dstrect.y = 100; | |||
for (int x = 0; x < 480; x++) { | |||
dstrect.x = x; | |||
SDL_BlitSurface(bmp, NULL, screen, &dstrect); | |||
SDL_Flip(screen); | |||
SDL_Delay(10); | |||
} | |||
SDL_FreeSurface(bmp); | |||
SDL_FreeSurface(screen); | |||
SDL_Quit(); | |||
return 0; | |||
} | |||
</source> | |||
* 다음은 Makefile.sdl_ani | |||
<source lang="make"> | |||
test: | |||
make build; | |||
build: | |||
gcc -I"/usr/include/SDL" -c sdl_ani.c; | |||
gcc demo.o -lSDL -o sdl_ani; | |||
clean: | |||
-rm sdl_ani.o; | |||
-rm sdl_ani; | |||
</source> | |||
* 대충 100x100짜리 bmp파일을 character.bmp이라고 명명해서 같은 디렉토리에 넣어두고 실행해보자. | |||
2014년 7월 16일 (수) 14:31 판
C++을 이용한 게임 만들기
- C++로 게임 제작의 기본기를 쌓는것이 목적
- Beginnning C++ Through Game Programming의 내용을 간략화했다.
왜 C++인가
- C++은 빠른편이고, 고급언어인 편에 속한다.
- 사실상 업계 표준임.
- C에 비해 Object Oriented 프로그래밍이 쉽다.
Game Over
- C++로 Game Over를 짜보자
Hello world랑 뭐가 다른건데!!
<source lang="c"> // Game Over // A first C++ program
- include <iostream>
int main() {
std::cout << "Game Over!" << std::endl; return 0;
} </source>
- Game Over 2.0을 짜보자
조또 바뀐것 없다. - 네임스페이스를 써서
<source lang="c"> // Game Over 2.0 // Demonstrates a using directive
- include <iostream>
using namespace std;
int main() {
cout << "Game Over!" << endl; return 0;
} </source>
- Game Over 3.0이다.
반복학습 쩐다
<source lang="c"> // Game Over 3.0 // Demonstrates using declarations
- include <iostream>
using std::cout; using std::endl;
int main() {
cout << "Game Over!" << endl; return 0;
} </source>
비싼 계산기
- 연산자를 이해하기 위해 비싼 계산기를 짜보자.
<source lang="c"> // Expensive Calculator // Demonstrates built-in arithmetic operators
- include <iostream>
using namespace std;
int main() {
cout << "7 + 3 = " << 7 + 3 << endl; cout << "7 - 3 = " << 7 - 3 << endl; cout << "7 * 3 = " << 7 * 3 << endl;
cout << "7 / 3 = " << 7 / 3 << endl; cout << "7.0 / 3.0 = " << 7.0 / 3.0 << endl;
cout << "7 % 3 = " << 7 % 3 << endl; cout << "7 + 3 * 5 = " << 7 + 3 * 5 << endl; cout << "(7 + 3) * 5 = " << (7 + 3) * 5 << endl;
return 0;
} </source>
게임 스탯
- 비행 슈팅게임에서 상태출력에 대해 생각하려 한다.
<source lang="c"> // Game Stats // Demonstrates declaring and initializing variables
- include <iostream>
using namespace std;
int main() {
int score; double distance; char playAgain; bool shieldsUp;
short lives, aliensKilled;
score = 0; distance = 1200.76; playAgain = 'y'; shieldsUp = true; lives = 3; aliensKilled = 10; double engineTemp = 6572.89;
cout << "\nscore: " << score << endl; cout << "distance: " << distance << endl; cout << "playAgain: " << playAgin << endl; // Bool 표현은 애매하므로 shieldup은 생략한다. cout << "lives: " << lives << endl; cout << "aliensKilled: " << aliensKilled << endl; cout << "engineTemp: " << engineTemp << endl;
int fuel; cout << "\nHow much fuel? ": cin >> fuel; cout << "fuel: " << fuel << endl;
typedef unsigned short int ushort; ushort bonus = 10; cout << "\nbonus: " << bonus << endl;
return 0;
} </source>
- 어디서 많이 보던 것들이지?
겜 좀 해봐야 겜을 만들징 - 이제 좀 계산하는 방법에 대해 알아보자.
<source lang="c"> /* game_stats2.cc */ // Game Stats 2.0 // Demonstrates arithmetic operations with variables
- include <iostream>
using namespace std;
int main() {
unsigned int score = 5000; cout << "score: " << score << endl;
// 값을 살짝 바꾼다. score = score + 100; cout << "score: " << score << endl;
// 값 증가의 다른 버전 score += 100; cout << "score: " << score << endl;
// 값 증가의 또다른 버전 int lives = 3; ++ lives; cout << "lives: " << lives << endl;
lives = 3; lives++; cout << "lives: " << lives << endl;
lives = 3; int bonus = ++lives * 10; cout << "lives, bonus = " << lives << ", " << bonus << endl;
lives = 3; bonus = lives++ * 10; cout << "lives, bonus = " << lives << ", " << bonus << endl;
// 정수 최대값은 결국? score = 4294967295; cout << "\nscore: " << score << endl; ++score; cout << "score: " << score << endl;
return 0;
} </source>
- 자. 이제 3.0이다
뭔 이따위것까지 3.0이여
<source lang="c"> /* game_stats3.cc */ // Game Stats 3.0 // Demonstrates constants
- include <iostream>
using namespace std;
int main() {
const int ALIEN_POINTS = 150; int aliensKilled = 10; int score = aliensKilled * ALIEN_POINTS; cout << "score: " << score << endl;
enum difficulty (NOVICE, EASY, NORMAL, HARD, UNBEATABLE); difficulty myDifficulty = EASY;
enum ship (FIGHTER = 25, BOMBER, CRUISER = 50, DESTROYER = 100); ship myShip = BOMBER; cout << "\nTo upgrade my ship to Cruiser will cost " << (CRUISER - myShip) << " Resource Points.\n"; return 0;
} </source>
- 상수의 사용법이다.
- 안바뀌는건 const 지정을 해놓으면 나중에 편하다.
- 열거형 변수. 짜증나게 값 입력을 안해놨으니 이해가 힘들거임.
좀 복붙이라도 하던지. - 한번 돌려보면 어느정도 감이 잡히는거고, 내용을 설명하기엔 좀..
이럴때 책이 필요한거임. 다른 언어 해본사람은 금방 눈에 들어올거고.
Lost Fortune
- 기본을 어느정도 했으면 진짜 간단한거 하나 해보자.
<source lang="c"> /* lost_fortune.cc */ // Lost Fortune // A personalized adventure
- include <iostream>
- include <string>
using std::cout; using std::cin; using std::endl; using std::string;
/* string을 포함했다. 이건 표준 라이브러리의 일종인데, 문자열 처리를 위한 라이브러리다. */
/* 사용자 정보를 입력받자. */ int main() {
const int GOLD_PIECES = 900; int adventurers, killed, survivors; string leader;
// 플레이어 정보를 입력받는다. cout << "Welcome to Lost Fortune\n\n"; cout << "Please enter the following for your personalized adventure\n";
cout << "Enter a number: "; cin >> adventurers;
cout << "Enter a number, samller than the first: "; cin >> killed;
survivors = adventurers - killed;
cout << "Enter your last name: "; cin >> leader;
/* GOLD_PIECES는 모험가들이 찾는 행운의 금 조각이다. adventurers는 모험가의 숫자, killed는 모험하다 뒈진 사람의 숫자다. 여기서는 당신이 생존자 리더다. */
// 스토리텔링 cout << "\n A brave group of " << adventurers << " set out on a quest "; cout << "-- in search of the lost treasure of the Ancient Dwarves. "; cout << "The group was led by that legendary rogue. " << leader << ".\n"; cout << "\n Along the way. a band of marauding ogres ambushed the aprty. "; cout << "All fought bravely under the command of " << leader; cout << ", and the ogres were defeated, but at a cost. "; cout << "Of the adventurers. " << killed << " were vanquished. "; cout << "leaving just " << survivors << " in the group.\n"; cout << "\nThe party was about to give up all hope. "; cout << "But while laying the deceased to rest. "; cout << "they stumbled upon the buried fortune. "; cout << "So the adventurers split " << GOLD_PIECES << " gold pieces."; cout << leader << " held on to the extra " << (GOLD_PIECES % survivors); cout << " pieces to keep things fair of course.\n";
return 0;
} </source>
- 별 게임도 아닌것 같은데.. 스토리텔링하고 끝나네..
뭐 첨엔 다 그런거야
Score Rater
- 점수 매기기, 조건문에 대한 이해를 해보자.
<source lang="c"> /* score_rater.cc */ // Score Rater // Demonstrates the if statement
- include <iostream>
using namespace std;
int main() {
if(true)
cout << "This is always displayed.\n\n";
if(false)
cout << "This is never displayed.\n\n";
int score = 1000;
if(score)
cout << "Okay, at least you didn't score zero.\n\n";
if(score > 500)
cout << "You scored over 500. Nice.\n\n";
if(score == 1000)
{
cout << "You scored a perfect 1000!\n";
cout << "Now that's impressive.\n";
}
if(score > 500)
{
cout << "You scored at least 500.\n";
if(score >= 1000)
cout << "You scored 1000 or more!\n";
}
return 0;
} </source>
- 점수매기기 2.0. 이번엔 입력을 받아보자구.
<source lang="c"> /* score_rater2.cc */ // Score Rater 2.0 // Demonstrates the else caluse
- include <iostream>
using namespace std;
int main() {
int score; cout << "Enter your score: "; cin >> score;
if(score > 500)
cout << "\nYou got over 500. Nice score.\n";
else
cout << "\nYou got 500 or less. Nothing to brag about.\n";
return 0;
} </source>
- 메뉴 입력을 받아보자.
<source lang="c"> /* menu_chooser.cc */ // Menu menu_chooser // Demonstrates the switch statement
- include <iostream>
using namespace std;
int main() {
cout << "Difficulty Levels\n\n"; cout << "1 - Easy\n"; cout << "2 - Normal\n"; cout << "3 - Hard\n";
int choice; cout << "Choice: "; cin >> choice;
switch(choice)
{
case 1:
cout << "You picked Easy.\n";
break;
case 2:
cout << "You picked Normal.\n";
break;
case 3:
cout << "You picked Hard.\n";
break;
default:
cout << "You made an illegal choice.\n"
}
return 0;
} </source>
- switch문 별거 없다.
Play Again
- Play Again 루프를 돌려보자.
<source lang="c"> /* play_again.cc */ // Play Again // Demonstrates while loops
- include <iostream>
using namespace std;
int main() {
char again = 'y';
while(again == 'y')
{
cout << "\n** Played an exciting game **";
cout << "\nDo you want to play again? (y/n): ";
cin >> again;
}
cout << "\nOkay, bye.";
return 0;
} </source>
- do while로 좀더 직관적으로 해보자.
<source lang="c"> /* play_again2.cc */ // Play Again 2.0 // Demonstrates do loops
- include <iostream>
using namespace std;
int main() {
char again;
do
{
cout << "\n** Played an exciting game**";
cout << "\nDo you want to play again? (y/n)";
cin >> again;
} while (again == 'y');
cout << "\nOkay, bye.";
return 0;
} </source>
- 좀더 직관적이 되었다.
Counter
- 다음은 break와 continue를 위한 카운터를 돌려보자.
<source lang="c"> /* finicky_counter.cc */ // Finicky Counter // Demonstrates break and continue statements
- include <iostream>
using namespace std;
int main() {
int count = 0;
while(true)
{
count += 1;
// count가 10보다 크면 루프를 끝냄
if(count > 10)
break;
// 5는 스킵
if(count == 5)
continue;
cout << count << endl; } return 0;
} </source>
- 이해를 돕기 위한
쓸모없는프로그램
Designers Network
<source lang="c"> /* designers_network.cc */ // Designers Network // Demonstrates logical operators
- include <iostream>
- include <string>
using namespace std;
int main() {
cout << "\tGame Designer's Network\n"; int security = 0;
string username; cout << "\nUsername: "; cin >> username;
string password; cout << "Password: "; cin >> password;
if(username = "S.Meier" && password == "bar")
{
cout << "\nHey, Sid.":
security = 5;
}
if(username = "S.Miyamoto" && password == "mariobros")
{
cout << "\nWhat's up, Shigeru?";
security = 5;
}
if(username == "W.Wright" && password == "thesims")
{
cout << "\nHow goes it, Will?";
security = 5;
}
if(username == "guest" || password == "guest")
{
cout << "\nWelcome, Guest.";
security = 1;
}
if(!security)
cout << "\nYour login failed.";
return 0;
} </source>
- 패스워드 처리는 이따위로 하면 안되겠지? ㅋㅋ
Die Roller
- 한국어로 주사위.
- 랜덤을 써보자
<source lang="c"> /* die_roller.cc */ // Die Roller// Demonstrates generating random numbers
- include <iostream>
- include <cstdlib>
- include <ctime>
using namespace std;
int main() {
srand(time(0)); // 랜덤시드를 현재시간으로 설정
int randomNumber = rand(); // 랜덤수 하나 뽑아옴
int die = (randomNumber % 6) + 1; // 1부터 6까지 수 중에 하나 cout << "You rolled a " << die << endl;
return 0;
} </source>
- 자, 게임에서 가장 중요한 랜덤뽑기다.
- 초단위
Guess My Number
- 일반적으로 대부분의 콘솔게임은 다음과 같은 진행으로 이루어진다.
- 플로챠트 내용은 다음과 같다.
- Setting up - 보통, 초기 세팅을 하고 로딩하고 사운드, 그래픽을 활성화한다.
- Getting player input - 키보드, 마우스, 조이스틱, 트랙볼같은 디바이스로부터 플레이어 입력을 받는다.
- Updating game internals - 사용자 입력과 게임 로직(AI 포함)에 따라 게임 내부의 좌표 등의 업데이트를 한다.
- Updating display - 게임 프로그래밍이라고 하면 보통 이쪽을 많이 생각하는데, 실제 어려운건 위의 내용이다.
- Checking whether the game is over - 게임 로직에 따라, 게임 오버를 결정한다.
- Shutting down - 마지막으로 하이스코어 등의 정보를 사용자에게 보여주고 프로그램을 종료시키거나 새로 시작하거나 한다.
- Guess My Number는 다음과 같은 순서로 이루어진다.
- 잉여들을 위한 프로그래밍에서도 언급했듯, 플로챠트는 대충 기본 흐름만 보여줄 수 있으면 된다. 본인이 알아볼 정도면 된다.
<source lang="c"> /* guess_my_number.cc */ // Guess My Number // The classic number guessing game
- include <iostream>
- include <cstdlib>
- include <ctime>
using namespace std;
inat main() {
srand(time(0)); // 랜덤시드를 현재기각으로.
int theNumber = rand() % 100 + 1; // 1부터 100까지 숫자중에 하나 int tries = 0, guess;
cout << "\tWelcome to Guess My Number\n\n";
do {
cout << "Enter a guess: ";
cin >> guess;
++tries;
if(guess > theNumber)
cout << "Too high!\n\n";
if(guess < theNumber)
cout << "Too low!\n\n";
} while (guess != theNumber);
cout << "\nThat's it! You got it in " << tries << " guesses!\n";
return 0;
} </source>
Counter
- 숫자를 앞으로 세고, 뒤로 세는 프로그램이다.
별거 없는데 이름만 그럴싸한데
<source lang="c"> /* counter.cc */ // Counter // Demonstrates for loops
- include <iostream>
using namespace std;
int main() {
cout << "Counting forward.\n";
for(int i = 0; i < 10; ++i)
cout << i << " ";
cout << "\n\nCounting backward:\n":
for(int i = 0; i >= 0; --i)
cout << i << " ";
cout << "\n\nCounting by fives:\n";
for(int i = 0; i <= 50; i += 5)
cout << i << " ";
cout << "\n\nCounting with null statements:\n";
int count = 0;
for (; count < 10;)
{
cout << count << " ";
++count;
}
cout << "\n\nCounting with nested for loops:\n";
const int ROWS = 5;
const int COLUMNS = 3;
for(int i = 0; i < ROWS; ++i)
{
for(int j = 0; j < COLUMNS; ++j)
cout << i << "," << j << " ";
cout << endl;
}
return 0;
} </source>
String Tester
- String에 대해서 좀 알아보자.
<source lang="c"> /* string_tester.cc */ // String Tester // Demonstrates string objects
- include <iostream>
- include <string>
using namespace std;
int main() {
string word1 = "Game";
string word2("Over");
string word3(3, '!');
string phrase = word1 + " " + word2 + word3; cout << "The phrase is: " << phrase << "\n\n";
cout << "The phrase has " << phrase.size() << " characters in it.\n\n";
cout << "The character at position 0 is: " << phrase[0] << "\n\n";
cout << "Changing the character at position 0.\n"; phrase[0] = 'L'; cout << "The phrase is now: " << phrase << "\n\n";
for(int i = 0; i < phrase.size(); ++i)
cout << "Character at position " << i << " is: " << phrase[i] << endl;
cout << "\nThe sequence 'Over' begins at location " << phrase.find("Over") << endl;
if(phrase.find("eggplant") == string::npos)
cout << "'eggplant' is not in the phrase.\n\n";
phrase.erase(4, 5); cout << "The phrase is now: " << phrase << endl;
phrase.erase(4); cout << "The phrase is now: " << phrase << endl;
phrase.erase(); cout << "The phrase is now: " << phrase << endl;
if(phrase.empty())
cout << "\nThe phrase is no more.\n";
return 0;
} </source>
- Ansi-C보다 문자열만 봐도 C++이 좀더 쓰기 쉽다
그만큼 디버깅은 빡세지지
Hero's Inventory
- 배열에 대한 이야기
<source lang="c"> /* heros_inventory.cc */ // Hero's Inventory // Demonstrates arrays
- include <iostream>
- include <string>
using namespace std;
int main() {
const int MAX_ITEMS = 10; string inventory[MAX_ITEMS];
int numItems = 0; inventory[numItems++] = "sword"; inventory[numItems++] = "armor"; inventory[numItems++] = "shield";
cout << "Your items:\n";
for(int i = 0; i < numItems; ++i)
cout << inventory[i] << endl;
cout << "\nYou trade your sword for a battle axe.";
inventory[0] = "battle axe";
cout << "\nYour items:\n";
for(int i = 0; i < numItems; ++i)
cout << inventory[i] << endl;
cout << "\nThe Item name '" << inventory[0] << "' has "; cout << inventory[0].size() << " letters in it.\n";
cout << "\nYou find a healing potion.";
if(numItems < MAX_ITEMS)
inventory[numItems++] = "healing potion";
else
cout << "You have too many items and can't carry another.";
cout << "\nYour items:\n";
for(int i = 0; i < numItems; ++i)
cout << inventory[i] << endl;
return 0;
} </source>
- 배열에 대해 알아보았다.
- 이제 2.0을 돌려보자.
<source lang="c"> /* heros_inventory2.cc */ // Hero's Inventory 2.0 // Demonstrates Vectors
- include <iostream>
- include <string>
- include <vector>
using namespace std;
int main() {
vector<string> inventory;
inventory.push_back("sword");
inventory.push_back("armor");
inventory.push_back("shield");
cout << "You have " << inventory.size() << " items.\n";
cout << "\nYour items:\n";
for(int i = 0; i < inventory.size(); ++i)
cout << inventory[i] << endl;
cout << "\nYou trade your sword for a battle axe.";
inventory[0] = "battle axe";
cout << "\nYour items:\n";
for(int i = 0; i < inventory.size(); ++i)
cout << inventory[i] << endl;
cout << "\nThe item name '" << inventory[0] << "' has "; cout << inventory[0].size() << " letters in it.\n";
cout << "\nYour shield is destroyed in a fierce battle.";
inventory.pop_back();
cout << "\nYour items:\n";
for(int i = 0; i < inventory.size(); ++i)
cout << inventory[i] << endl;
cout << "\nYou were robbed of all of your possessions by a thief.";
inventory.clear();
if(inventory.empty())
cout << "\nYou have nothing.\n";
else
cout << "\nYou have at least one item.\n";
return 0;
} </source>
- C++의 강력한 기능중 하나인 벡터다.
- 이제 이터레이터라는게 있다. 그걸로 좀더 향상된 3.0을 만들어보자.
<source lang="c"> /* heros_inventory3.cc */ // Hero's Inventory 3.0 // Demonstrates iterators
- include <iostream>
- include <string>
- include <vector>
using namespace std;
int main() {
vector<string> inventory;
inventory.push_back("sword");
inventory.push_back("armor");
inventory.push_back("shield");
vector<string>::iterator myIterator; vector<string>::const_iterator iter;
cout << "Your items:\n";
for(iter = inventory.begin(); iter != inventory.end(); ++iter)
cout << *iter << endl;
cout << "\nYou trade your sword for a battle axe.";
myIterator = inventory.begin();
*myIterator = "battle axe";
cout << "\nYour Items:\n";
for(iter = inventory.begin(); iter != inventory.end(); ++iter)
cout << *iter << endl;
cout << "\nThe item name '" << *myIterator << "' has "; cout << (*myIterator).size() << " letters in it.\n";
cout << "\nThe item name '" << *myIterator << "' has "; cout << myIterator->size() << " letters in it.\n";
cout << "\nYou recover a crossbow from a slain enemy.";
inventory.insert(inventory.begin(), "crossbow");
cout << "\nYour items:\n";
for(iter = inventory.begin(); iter != inventory.end(); ++iter)
cout << *iter << endl;
cout << "\nYour armor is destroyed in a fierce battle.";
inventory.erase((inventory.begin() + 2));
cout << "\nYour items:\n";
for(iter = inventory.begin(); iter != inventory.end(); ++iter)
cout << *iter << endl;
return 0;
} </source>
- 웬지 좀 더 복잡해진것 같지만, 좀 더 직관적이 되었다.
- 명시적 표현은 항상 신경쓰는게 좋다.
Tic-Tac-Toe Board
- 여러군데서 자주 나오는 틱탁토
탁탁탁이 아니다예제다.
<source lang="c"> /* tic-tac-toe_board.cc */ // Tic-Tac-Toe Board // Demonstrates multidimensional arrays
- include <iostream>
using namespace std;
int main() {
const int ROWS = 3;
const int COLUMNS = 3;
char board[ROWS][COLUMNS] = {
{ 'O', 'X', 'O' },
{ ' ', 'X', 'X' },
{ 'X', 'O', 'O' }
};
cout << "Here's the tic-tac-toe board:\n";
for(int i = 0; i < ROWS; ++i)
{
for(int j = 0; j < COLUMNS; ++j)
cout << board[i][j];
cout << endl;
}
cout << "\n'X' moves to the empty location.\n\n"; board[1][0] = 'X';
cout << "Now the tic-tac-toe board is:\n";
for(int i = 0; i < ROWS; ++i)
{
for(int j = 0; j < COLUMNS; ++j)
cout << board[i][j];
cout << endl;
}
cout << "\n'X' wins!";
return 0;
} </source>
- 다른 책들보다 좀 허접한 느낌이 든다.
- 뭐 어쨌거나 2차원배열에 대해서 약간 감이 왔으리라 생각한다.
Word Jumble
- 단어 뒤섞기 게임이다.
<source lang="c"> // Word Jumble // The classic word jumble game where the player can ask for a hint
- include <iostream>
- include <string>
- include <cstdlib>
- include <ctime>
using anmespace std;
int main() {
enum fields (WORD, HINT, NUM_FIELDS);
const int NUM_WORDS = 5;
const string WORDS[NUM_WORDS][NUM_FIELDS] =
{
{"wall", "Do you feel you're banging your head against something?"},
{"glasses", "These might help you see the answer."},
{"labored", "Going slowly, is it?"},
{"persistent", "Keep at it."},
{"jumble", "It's what the game is all about."}
};
string jumble = theWord;
int length = jumble.size();
for(int i = 0; i < length; ++i)
{
int index1 = (rand() % length);
int index2 = (rand() % length);
char temp = jumble[index1];
jumble[index1] = jumble[index2];
jumble[index2] = temp;
}
cout << "\t\tWelcome to Word Jumble!\n\n"; cout << "Unscramble the letters to make a word.\n"; cout << "Enter 'hint' for a hint.\n"; cout << "Enter 'quit' for quit the game.\n\n"; cout << "The jumble is: " << jumble;
string guess; cout << "\n\nYour Guess: "; cin >> guess;
while (( guess != theWord) && (guess !="quit"))
{
if(guess == "hint")
cout << theHint;
else
cout << "Sorry, that's not it.";
cout << "\n\nYour guess: ";
cin >> guess;
}
if(guess == theWord)
cout << "\nThat's it! You guessed it!\n";
cout << "\nThanks for playing.\n";
return 0;
} </source>
- 아주 기초적이고 재미도 없다.
- 하지만, 한번 해본것과 안 해본것은 엄청난 차이가 있다.
High Scores
- 알고리즘 표현법을 알아보기 위해 하이스코어 보드를 만들어 보자.
<source lang="c"> /* high_scores.cc */ // High Scores // Demonstrates algorithms
- include <iostream>
- include <vector>
- include <algorithm>
- include <ctime>
- include <cstdlib>
using namespace std;
int main() {
vector<int>::const_iterator iter;
cout << "Creating a list of scores."; vector<int> scores; scores.push_back(1500); scores.push_back(3500); scores.push_back(7500);
cout << "\nHigh Scores:\n";
for(iter = scores.begin(); iter != scores.end(); ++iter)
cout << *iter << endl;
cout << "\nRandomizing scores.";
srand(time(0));
random_shuffle(scores.begin(), score.end());
cout << "\nHigh Scores:\n";
for(iter = scores.begin(); iter != scores.end(); ++iter)
cout << *iter << endl;
cout << "\nSorting scores.";
sort(scores.begin(), scores.end());
cout << "\nHigh Scores:\n";
for(iter = scores.begin(); iter != scores.end(); ++iter)
cout << *iter << endl;
cout << "\nCreating another list of scores."; vector<int> moreScores; moreScores.push_back(2000); moreScores.push_back(4000); moreScores.push_back(8000);
cout << "\nMore High Scores:\n";
for(iter = moreScores.begin(); iter != moreScores.end(); ++iter)
cout << *iter << endl;
cout << "\nMerging both lists."; vector<int> allScores(6); // 결과를 저장하기 위해 충분히 큰 공간 할당 merge(scores.begin(), scores.end(), moreScores.begin(), moreScores.end(), allScores.begin());
cout << "\nAll High Scores:\n";
for(iter = allScores.begin(); iter != allScores.end(); ++iter)
cout << *iter << endl;
return 0;
} </source>
- Ansi-C를 하던 사람이라면, 살짝 보기 힘든 코드다.
Hangman
- 아이폰에도 있고, 뭐 본 사람도 있고 안 본 사람도 있을거다.
- pseudocode를 이용해 짜보자.
- Hangman은 다음과 같은 순서로 진행된다.
- 단어 그룹을 만든다.
- 랜덤으로 하나를 찍어서 secret word로 지정한다.
- 플레이어가 너무 많이 실패하기 전까지 진행한다.
- secret word는
- 얼마나 틀리면 죽는지 알려준다.
- 몇자인지 보여줘야 한다.
- 몇번 시도했는지 보여줘야 한다.
- 다음 시도를 입력받는다.
- 플레이어가 지금까지 무슨 글자를 입력했는지 보여줘야 한다.
- 만약 모든 글자가 채워졌다면 플레이어에게 승리 메세지를 띄우고 다른 게임을 시작한다.
- 그게 아니라면 틀렸다고 띄우고, 시도 횟수를 늘린다.
- 만약 너무 많이 틀렸다면 게임오버.
<source lang="c"> // Hangman // The classic game of Hangman
- include <iostream>
- include <string>
- include <vector>
- include <algorithm>
- include <ctime>
- include <cctype>
using namespace std;
int main() {
// Seetup const int MAX_WRONG = 8; // 최대 8번
vector<string> words; // 가능한 단어의 모음
words.push_back("GUESS");
words.push_back("HANGMAN");
words.push_back("DIFFICULT");
srand(time(0)); random_shuffle(words.begin(), words.end()); const string THE_WORD = words[0]; // 선택된 단어 int wrong = 0; // 시도 횟수 초기화 string soFar(THE_WORD.size(), '-'); // 단어까지 string used = ""; // 사용된 글자
cout << "Welcome to Hangman. Good luck!\n";
// main loop
while((wrong < MAX_WRONG) && (soFar != THE_WORD))
{
cout << "\n\nYou have " << (MAX_WRONG - wrong) << " incorrect guesses left.\n";
cout << "\nYou've used the following letters:\n" << used << endl;
cout << "\nSo far, the word is: \n" << soFar << endl;
}
char guess;
cout << "\n\nEnter your guess: ";
cin >> guess;
guess = toupper(guess); // 대문자로 바꿈
while(used.find(guess) != string::npos)
{
cout << "\nYou've already guessed " << guess << endl;
cout << "Enter your guess: ";
cin >> guess;
guess = toupper(guess);
}
used += guess;
if(THE_WORD.find(guess) != string::npos)
{
cout << "That's right! " << guess << " is in the word.\n";
// soFar에 선택단어 추가
if(for i = 0; i < THE_WORD.length(); ++i)
if(THE_WORD]i] == guess)
soFar[i]=guess;
} else {
cout << "Sorry. " << guess << " isn't in the word.\n";
++wrong;
}
}
// shut down
if(wrong == MAX_WRONG)
cout << "\nYou've been hanged!";
else
cout << "\nYou guessed it!";
cout << "\nThe word was " << THE_WORD << endl;
return 0;
} </source>
- C에서 보지 못했던 몇가지가 있을거다.
- 문자열 처리 나쁘지 않은 정도다.
Instructions
- 함수 이해를 위해 간단한 프로그램을 짜보자.
<source lang="c"> /* instructions.cc */ // Instructions // Demonstrates writing new functions
- include <iostream>
using namespace std;
// function prototype (declaration) void instructions();
int main() {
instructions(); return 0;
}
// function definition void instructions() {
cout << "Welcome to the most fun you've ever had with text!\n\n"; cout << "Here's how to play the game...\n";
} </source>
- 전통적인 C 함수다.
Yes or No
- 마찬가지로, 함수에 대해 좀더 알아보기 위한 프로그램이다.
<source lang="c"> /* yes_or_no.cc */ // Yes or nO // Demonstrates return values and parameters
- include <iostream>
- include <string>
using namespace std;
char askYesNo1(); char askYesNo2(string question);
int main() {
char answer1 = askYesNo1(); cout << "Thanks for answering: " << answer1 << "\n\n";
char answer2 = askYesNo2("Do you with save your game?");
cout << "Thanks for answering: " << answer2 << "\n";
return 0;
}
char askYesNo1() {
char response1;
do {
cout << "Please enter 'y' or 'n': ";
cin >> response1;
} while(reponse1 != 'y' && response1 != 'n');
return response1;
}
char askYesNo2(string question) {
char response2;
do {
cout << question << " (y/n): ";
cin >> response2;
} while(response2 != 'y' && response2 != 'n');
return response2;
} </source>
- 반환값에 대한 이해를 돕기 위한 프로그램이다.
Scoping
- 중복된 이름의 변수에 대해 알아보자.
<source lang="c"> /* scoping.cc */ // Scoping // Demonstrates scopes
- include <iostream>
using namespace std;
void func();
int main() {
int var = 5; // local variable in main() cout << "In main() var is: " << var << "\n\n";
func();
cout << "Back in main() var is: " << var << "\n\n";
{
cout << "In main() in a new scope var is: " << var << "\n\n";
cout << "Creating new var in new scope.\n";
int var = 10; // {}로 싼 새 영역의 새 변수다.
cout << "In main() in a new scope var is: " << var << "\n\n";
}
cout << "At end of main() var created in new scope no longer exists.\n"; cout << "At end of main() var is: " << var << "\n";
return 0;
}
void func() {
int var = -5; // func()의 지역변수 cout << "In func() var is: " << var << "\n\n";
} </source>
- 지역 변수의 개념에 대해 알 수 있도록 좀 이상하게 만들었다.
- 오래된 C 컴파일러에서는 동작하지 않을 수 있다.
존나 90년대 컴파일러 아니면 웬만한건 다 된다고 보면 된다.
Global Reach
빌어먹을전역변수에 대해 알아보자.
<source lang="c"> /* global_reach.cc */ // Global Reach // Demonstrates global variables
- include <iostream>
using namespace std;
int glob = 10; // 전역변수
void access_global(); void hide_global(); void change_global();
int main() {
cout << "In main() glob is: " << glob << "\n\n"; access_global();
hide_global(); cout << "In main() glob is: " << glob << "\n\n";
change_global(); cout << "In main() glob is: " << glob << "\n\n";
return 0;
}
void access_global() {
cout << "In access_global() glob is: " << glob << "\n\n";
}
void hide_global() {
int glob = 0; // 전역변수를 재설정하여 값을 숨김 cout << "In hide_global() glob is: " << glob << "\n\n";
}
void change_global() {
glob = -10; // 전역변수 glob의 값을 바꿈 cout << "In change_global() glob is : " << glob << "\n\n";
} </source>
- 전역변수를 쓰는 방법인데, 전역변수는 여러사람 골치아프게 한다.
- 물론 goto처럼, 쓰는 사람에 따라 좀더 직관적으로 변할 수 있는 것이기도 하다.
하지만 너는 아니에요
Give Me a Number
- 숫자 하나 주세요 프로그램은 범위 내의 숫자를 선택하는 프로그램이다.
- 함수 사용에 대해 이해하기 위한 예제다.
<source lang="c"> /* give_me_a_number.cc */ // Give Me a Number // Demonstrates default function arguments
- include <iostream>
- include <string>
using namespace std;
int askNumber(int high, int low = 1);
int main() {
int number = askNumber(5); cout << "Thanks for entering: " << number << "\n\n";
number = askNumber(10, 5); cout << "Thanks for entering: " << number << "\n\n";
return 0;
}
int askNumber(int high, int low) {
int num;
do {
cout << "Please enter a number" << " (" << low << " - " << high << "): ";
cin >> num;
} while(num > high || num < low);
return num;
} </source>
- 코드를 잘 보면, 초기값을 준 게 보일거다.
Triple
- 함수 오버로딩을 이해하기 위해 프로그램을 하나 짜보자.
<source lang="c"> /* triple.cc */ // Triple // Demonstrates function overloading
- include <iostream>
- include <string>
using namespace std;
int triple(int number); string triple(string text);
int main() {
cout << "Tripling 5: " << triple(5) << "\n\n";
cout << "Tripling 'gamer': " << triple("gamer");
return 0;
}
int triple(int number) {
return (number * 3);
}
string triple(string text) {
return (text + text + text);
} </source>
- 같은 이름의 함수라도, 타입에 따라 다르게 호출될 수 있다.
- 이걸 이용해서 코드를 매우 복잡하게 짤수도 있다.
웬만하믄 그르지말자
Taking Damage
- 인라인함수에 대해 알아보기 위해 데미지딜링을 짜보자.
<source lang="c"> /* taking_damage.cc */ // Taking Damage // Demonstrates function inlining
- include <iostream>
int radiation(int health);
using namespace std;
int main() {
int health = 80; cout << "Your health is " << health << "\n\n";
health = radiation(health); cout << "After radiation exposure your health is " << health << "\n\n";
health = radiation(health); cout << "After radiation exposure your health is " << health << "\n\n";
health = radiation(health); cout << "After radiation exposure your health is " << health << "\n\n";
return 0;
}
inline int radiation(int health) {
return (health / 2);
} </source>
- 뭐, 별거 없다. 요즘은 컴파일러가 다 알아서 해주니..
- 예전 컴파일러에서는, 즉시 처리가 되지 않아 값이 제대로 안 나왔었다.
예전 컴파일러를 구할수가 없어 보여줄수가 없네
Mad-Lib
- 사용자 입력을 받아 스토리를 만드는
미친 애드립프로그램
<source lang="c"> /* mad_lib.cc */ // Mad-Lib // Creates a story based on user input
- include <iostream>
- include <string>
using namespace std;
string askText(string prompt); int askNumber(string prompt); void tellStory(string name, string noun, int number, string bodyPart, string verb);
int main() {
cout << "Welcome to Mad Lib.\n\n"; cout << "Answer the following questions to help create new story.\n";
string name = askText("Please enter a name: ");
string noun = askText("Please enter a plural noun: ");
int number = askNumber("Please enter a number: ");
string bodyPart = askText("Please enter a body part: ");
string verb = askText("Please enter a verb: ");
tellStory(name, noun, number, bodyPart, verb);
return 0;
}
string askText(string prompt) {
string text; cout << prompt; cin >> text; return text;
}
int askNumber(string prompt) {
int num; cout << prompt; cin >> num; return num;
}
void tellStory(string name, string noun, int number, string bodyPart, string verb) {
cout << "\nHere's your story:\n"; cout << "The famous explorer "; cout << name; cout << " had nearly given up a life-long quest to find\n"; cout << "The Lost City of "; cout << noun; cout << " when one day, the "; cout << noun; cout << " found the explorer.\n"; cout << "Surrounded by "; cout << number; cout << " " << noun; cout << ", a tear came to "; cout << name << "'s"; cout << bodyPart << ".\n"; cout << "After all this time, the quest was finally over. "; cout << "And then, the "; cout << noun << "\n"; cout << "promptly devoured "; cout << name << ". "; cout << "The moral of the story? Be careful what you "; cout << verb; cout << " for.";
} </source>
- 스토리텔링. 별거없다.
Referencing
- 레퍼런스 변수에 대해.
<source lang="c"> /* referencing.cc */ // Referencing // Demonstrates using references
- include <iostream>
using namespace std;
int main() {
int myScore = 1000; int& mikesScore = myScore; // 레퍼런스 변수를 만듦
cout << "myScore is: " << myScore << "\n"; cout << "mikesScore is: " << mikesScore << "\n\n";
cout << "Adding 500 to myScore\n"; myScore += 500; cout << "myScore is: " << myScore << "\n"; cout << "mikesScore is: " << mikesScore << "\n\n";
cout << "Adding 500 to mikesScore\n"; mikesScore += 500; cout << "myScore is: " << myScore << "\n"; cout << "mikesScore is: " << mikesScore << "\n\n";
return 0;
} </source>
- 레퍼런스 변수는 대충 심볼릭 링크랑 비슷한 개념으로 보면 된다.
틀린 내용이라 생각되면 토론에 틀렸다고 하던지 니가 직접 고치던지
Swap
- 우와. Call by reference가 나왔다.
- 예제니까 편한데, 실무에서 이거 디버깅 완전 뒤짐
<source lang="c"> /* swap.c */ // Swap // Demonstrates passing references to alter argument variables
- include <iostream>
using namespace std;
void badSwap(int x, int y); void goodSwap(int& x, int& y);
int main() {
int myScore = 150; int yourScore = 1000; cout << "Original values\n"; cout << "myScore: " << myScore << "\n"; cout << "yourScore: " << yourScore << "\n\n";
cout << "Calling badSwap()\n"; badSwap(myScore, yourScore); cout << "myScore: " << myScore << "\n"; cout << "yourScore: " << yourScore << "\n\n";
cout << "Calling goodSwap()\n"; goodSwap(myScore, yourScore); cout << "myScore: " << myScore << "\n"; cout << "yourScore: " << yourScore << "\n";
return 0;
}
void badSwap(int x, int y) {
int temp = x; x = y; y = temp;
} void goodSwap(int& x, int& y) {
int temp = x; x = y; y = temp;
} </source>
- 보면, 함수 자체는 별 차이 없다.
- 이런거 디버깅 하는건 거의 경험이나 다름없는것 같다.
Inventory Displayer
- 위에 Heros inventory가 있는데, 그 확장판이라고 생각하면 된다.
<source lang="c"> /* inventory_displayer.cc */ // Inventory Displayer // Demonstrates constant references
- include <iostream>
- include <string>
- include <vector>
using namespace std;
// vec 함수는 상수 레퍼런스를 스트링 벡터로 집어넣은거. void display(const vector<string>& vec);
int main() {
vector<string> inventory;
inventory.push_back("sword");
display(inventory);
return 0;
}
// vec 파라미터는 상수 레퍼런스를 스트링 벡터로 집어넣은거. void display(const vector<string>& vec) {
cout << "Your items:\n";
for(vector<string>::const_iterator iter = vec.begin(); iter != vec.end(); ++iter)
cout << *iter << endl;
} </source>
- 벡터를 함수로 넘겨주기도 가능한걸 볼 수 있다.
Inventory Referencer
- 리턴값도 좀 그렇게 받을수 없을까?
- 한번 해보자.
<source lang="c"> /* inventory_referencer.cc */ // Inventory Referencer // Demonstrates returning a references
- include <iostream>
- include <string>
- include <vector>
using namespace std;
// 레퍼런스에서 스트링을 뽑아서 반환 string& refToElement(vector<string>& vec, int i);
int main() {
vecotr<string> inventory;
inventory.push_back("sword");
inventory.push_back("armor");
inventory.push_back("shield");
// 변환된 참조가 참조하는 문자열을 표시 cout << "Sending the returned reference to cout:\n"; cout << refToElement(inventory, 0) << "\n\n";
// 다른 하나의 참조를 할당 - 대안책 cout << "Assigning the returned reference to another reference.\n"; string& rStr = refToElement(inventory, 1); cout << "Sending the new reference to cout:\n"; cout << rStr << "\n\n";
// 스트링 카피 cout << "Assigning the returned reference to a string object.\n"; string str = refToElement(inventory, 2); cout << "Sending the new string object to cout:\n"; cout << str << "\n\n";
// 반환된 레퍼런스의 스트링을 바꿈 cout << "Altering an object through a returned reference.\n"; rStr = "Healing Potion"; cout << "Sending the altered object to cout:\n"; cout << inventory[1] << endl;
return 0;
}
// 레퍼런스에서 스트링을 뽑아서 반환 string& refToElement(vector<string>& vec, int i) {
return vec[i];
} </source>
- 와, 보기 힘들고 추적도 힘들다.
- C 스타일에서 많이 쓰던 방식.
Pointing
- 포인터 사용방법을 좀 알아보자.
드디어 나왔다 개빡치는 포인터
<source lang="c"> /* pointing.cc */ // Pointing // Demonstrates using pointers
- include <iostream>
- include <string>
using namespace std;
int main() {
int* pApointer; // 포인터 선언 int* pScore = 0; // 포인터 선언 및 초기화 int score = 1000; pScore = &score; // 포인터 pScore에 변수 score의 주소를 지정
cout << "Assigning &score to pScore\n"; cout << "&score is: " << &score << "\n"; // 변수 score의 주소 cout << "pScore is: " << pScore << "\n"; // 포인터에 저장된 주소 cout << "score is: " << score << "\n"; cout << "*pScore is: " << *pScore << "\n\n"; // 포인터가 가리키는 값
cout << "Adding 500 to score\n"; score += 500; cout << "score is: " << score << "\n"; cout << "*pScore is: " << *pScore << "\n\n";
cout << "Adding 500 to *pScore\n"; *pScore += 500; cout << "score is: " << score << "\n"; cout << "*pScore is: " << *pScore << "\n\n";
cout << "Assigning &newScore to pScore\n"; int newScore = 5000; pScore = &newScore; cout << "&newScore is: " << &newScore << "\n"; cout << "pScore is: " << pScore << "\n"; cout << "newScore is: " << newScore << "\n"; cout << "*pScore is: " << *pScore << "\n\n";
cout << "Assigning &str to pStr\n"; string str = "score"; string* pStr = &str; // 문자열 객체에 포인터 지정 cout << "str is: " << str << "\n"; cout << "*pStr is: " << *pStr << "\n"; cout << "((pStr).size() is: " << (*pStr).size() << "\n";
return 0;
} </source>
- 사실 포인터는 익숙해지기 전엔 쓰기 힘들다.
Swap Pointer
- 포인터를 이용해서 스왑을 해보자.
<source lang="c"> /* swap_pointer.cc */ // Swap Pointer // Demonstrates passing constant pointers to alter argument variables
- include <iostream>
using namespace std;
void badSwap(int x, int y) void goodSwap(int* const pX, int* const pY);
int main() {
int myScore = 150; int yourScore = 1000; cout << "Original values\n"; cout << "myScore: " << myScore << "\n"; cout << "yourScore: " << yourScore << "\n\n";
cout << "Calling badSwap()\n"; badSwap(myScore, yourScore); cout << "myScore: " << myScore << "\n"; cout << "yourScore: " << yourScore << "\n\n";
cout << "Calling goodSwap()\n"; goodSwap(&myScore, &yourScore); cout << "myScore: " << myScore << "\n"; cout << "yourScore: " << yourScore << "\n";
return 0;
}
void badswap(int x, int y) {
int temp = x; x = y; y = temp;
}
void goodSwap(int* const pX, int* const pY) {
// pX가 가리키는 값을 temp에 저장 int temp = *pX; // pY가 가리키는 값을 pX가 가리키는 주소에 덮어씀 *pX = *pY; // 저장해둔 원래 pX가 가리키고 있던 값을 pY가 가리키는 주소에 덮어씀 *pY = temp;
} </source>
- Call by reference와 비교해보면 별 차이 없다는걸 느낄 수 있다.
Inventory Pointer
- 인벤토리 프로그램이다
맨날 나오냐 이놈은
<source lang="c"> /* inventory_pointer.cc */ // Inventory Pointer // Demonstrates returning a pointer
- include <iostream>
- include <string>
- include <vector>
using namespace std;
// 포인터로 문자열 요소를 받는다. string* ptrToElement(vector<string>* const pVec, int i);
int main() {
vector<string> inventory;
inventory.push_back("sword");
inventory.push_back("armor");
inventory.push_back("shield");
// 벡터 포인터를 함수로 넘겨 문자열을 반환받는다. cout << "Sending the objected pointed to by returned pointer:\n"; cout << *(ptrToElement(&inventory, 0)) << "\n\n";
// 또 하나의 포인터 등록 - 대안책 cout << "Assigning the returned pointer to another pointer.\n"; string* pStr = ptrToElement(&inventory, 1); cout << "Sending the object pointed to by new pointer to cout:\n"; cout << *pStr << "\n\n";
// 문자열 객체 복사 cout << "Assigning object pointed by pointer to a string object.\n"; string str = *(ptrToElement(&inventory, 2)); cout << "Sending the new string object to cout:\n"; cout << str << "\n\n";
// 반환된 포인터로 문자열 객체를 변경 cout << "Altering an object through a returned pointer.\n"; *pStr = "Healing Potion"; cout << "Sending the altered object to cout:\n"; cout << inventory[1] << endl;
return 0;
}
string* ptrToElement(vector<string>* const pVec, int i) {
// pVec가 가리키는 벡터의 i번째 요소를 반환 return &((*pVec)[i]);
} </source>
- 이정도를 한번보고 이해한다면 변태거나, 이해를 못했거나 둘 중 하나다.
그리고 이런걸 이해할수록 이성과의 소통이 힘들어진다
Array Passer
- 배열에 대해 알아보자.
<source lang="c"> /* array_passer.cc */ // Array Passer // Demonstrates relationship between pointers and arrays
- include <iostream>
using namespace std;
void increase(int* const array, const int NUM_ELEMENTS); void display(const int* const array, const int NUM_ELEMENTS);
int main() {
cout << "Creating an array of high scores.\n\n";
const int NUM_SCORES = 3;
int highScores[NUM_SCORES] = { 5000, 3500, 2700 };
cout << "Displaying scores using array name as a constant pointer.\n"; cout << *highScores << endl; cout << *(highScores + 1) << endl; cout << *(highScores + 2) << "\n\n";
cout << "Increasing scores by passing array as a constant pointer.\n\n"; increase(highScores, NUM_SCORES);
cout << "Displaying scores by passing array as a constant pointer to a constant.\n"; display(highScores, NUM_SCORES);
return 0;
}
void increase(int* const array, const int NUM_ELEMENTS) {
for(int i = 0; i < NUM_ELEMENTS; ++i)
array[i] += 500;
}
void display(const int* const array, const int NUM_ELEMENTS) {
for(int i = 0; i < NUM_ELEMENTS; ++i)
cout << array[i] << endl;
} </source>
- 상수포인터에 대한 예제다.
Tic-Tac-Toe Game
- 어? 틱탁토 왜 또 나오냐구? 먼젓번꺼는 Board고, 이건 실제 플레이할 수 있는거.
<source lang="c"> // Tic-Tac-Toe // Plays the game of tic-tac-toe against a human opponent
- include <iostream>
- include <string>
- include <vector>
- include <algorithm>
using namespace std;
// 전역상수 const char X = 'X'; const char O = 'O'; const char EMPTY = ' '; const char TIE = 'T'; const char NO_ONE = 'N';
// 함수 원형 void instructions(); char askYesNo(string question); int askNumber(string question, int high, int low = 0); char humanPiece(); char opponent(char piece); void displayBoard(const vector<char>& board); char winner(const vector<char>& board); bool isLegal(const vector<char>& board, int move); int humanMove(const vector<char>& board, char human); int computerMove(vector<char> board, char computer); void announceWinner(char winner, char computer, char human);
int main(int argc, char const *argv[]) {
/* code */ int move; const int NUM_SQUARES = 9; vector<char> board(NUM_SQUARES, EMPTY);
instructions(); char human = humanPiece(); char computer = opponent(human); char turn = X; displayBoard(board);
while(winner(board) == NO_ONE)
{
if(turn == human)
{
move = humanMove(board, human);
board[move] = human;
} else {
move = computerMove(board, computer);
board[move] = computer;
}
displayBoard(board);
turn = opponent(turn);
}
announcewinner(winner(board), computer, human);
return 0;
}
void instructions() {
cout << "Welcome to the ultimate man-machine showdown: Tic-Tac-Toe.\n"; cout << "-where human brain is pit against silicon processor\n\n";
cout << "Make your move known by entering a number. 0 - 8. The number\n"; cout << "corresponds to the desired board position. as illustrated:\n\n";
cout << " 0 | 1 | 2\n"; cout << " ---------\n"; cout << " 3 | 4 | 5\n"; cout << " ---------\n"; cout << " 6 | 7 | 8\n\n";
cout << "Prepare yourself, human. The battle is about to begin.\n\n";
}
char askYesNo(string question) {
char response;
do {
cout << question << "(y/n): ";
cin >> response;
} while(response != 'y' && response != 'n');
return response;
}
int askNumber(string question, int high, int low) {
int number;
do {
cout << question << " (" << low << " - " << high << "): ";
cin >> number;
} while(number > high || number << low);
return number;
}
char humanPiece() {
char go_first = askYesNo("Do you require the first move?");
if(go_first == 'y')
{
cout << "\nThen take the first move. You will need it.\n";
return X;
} else {
cout << "\nYour bravery will be your undoing... I will go first.\n";
return O;
}
}
char opponent(char piece) {
if(piece == X)
return O;
else
return X;
}
void displayBoard(const vector<char>& board) {
cout << "\n\t" << board[0] << " | " << board[1] << " | " << board[2]; cout << "\n\t" << "---------"; cout << "\n\t" << board[3] << " | " << board[4] << " | " << board[5]; cout << "\n\t" << "---------"; cout << "\n\t" << board[6] << " | " << board[7] << " | " << board[8]; cout << "\n\n";
}
char winner(const vector<char>& board) {
// 이기는 모든 경우
const int WINNING_ROWS[8][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};
const int TOTAL_ROWS = 8;
// 한줄에 3개의 값이 안 비고 같으면 승자가 있는거
for(int row = 0; row < TOTAL_ROWS; ++row)
{
if((board[WINNING_ROWS[row][0]] != EMPTY) && (board[WINNING_ROWS[row][0]] == board[WINNING_RPWS[row][1]]) && (board[WINNING_ROWS[row][2]])) {
return board[WINNING_ROWS[row][0]];
}
}
// 이긴사람이 없으면 비김 체크
if(count(board.begin(), board.end(), EMPTY) == 0)
return TIE;
// 위의 상황이 아니면 아직 겜이 안끝난거임 return NO_ONE;
}
inline bool isLegal(int move, const vector<char>& board) {
return (board[move] == EMPTY);
}
int humanMove(const vector<char>& board, char human) {
int move = askNumber("Where will you move?", (board.size() - 1));
while(!isLegal(move, board))
{
cout << "\nThat square is already occupied. foolish human.\n";
move = askNumber("Where will you move?", (board.size() - 1));
}
cout << "Fine...\n";
return move;
}
/* AI는 매우 단순하게. 1. 움직여야 한다. 2. 지지 않으려면 막아야 한다. 3. 중간 > 코너 > 변 순으로 남는쪽에 찍는다. */ int computerMove(vector<char> board, char computer) {
cout << "I shall take square number ";
// 찍어서 이길 수 있는곳이 있다면 거길 찍는다.
for(int move = 0; move < board.size(); ++move)
{
if(isLegal(move, board))
{
board[move] = computer;
if(winnner(board) == compter)
{
cout << move << endl;
return move;
}
// 확인했으면 취소한다.
board[move] = EMPTY;
}
}
// 사람이 이길것 같으면 막는다.
char human = opponent(computer);
for(int move = 0; move < board.size(); ++move)
{
if(isLegal(move, board))
{
board[move] = human;
if(winner(board) == human)
{
cout << move << endl;
return move;
}
// 확인했으면 취소한다.
board[move] = EMPTY;
}
}
// 우선순위에 따라간다.
const int BEST_MOVES[] = { 4, 0, 2, 6, 8, 1, 3, 5, 7 };
// 이번에 찍어서 승자가 안 나온다면 우선순위에 따라간다.
for(int i = 0; i < board.size(); ++i)
{
int move = BEST_MOVES[1];
if(isLegal(move, board))
{
cout << move << endl;
return move;
}
}
}
void announceWinner(char winner, char computer, char human) {
if(winner == computer)
{
cout << winner << "'s won!\n";
cout << "As I predicted, human. I am triumphant once more - proof\n";
cout << "that computers are superior to humans in all regards.\n";
} else if(winner == human) {
cout << winner << "'s won!\n";
cout << "No, no! It cannot be! Somehow you tricked me. human.\n";
cout << "But never again! I, the computer, so swear it!\n";
} else {
cout << "It's a tie.\n";
cout << "You were most lucky, human, and somehow managed to tie me.\n";
cout << "Celebrate... for this is the best you will ever achieve.\n";
}
} </source>
- 아직 손볼 구석이 많긴 하지만 어쨌든 동작하는거 하나 만들어 보았다.
Simple Critter
- C++에서 사용할 수 있는 클래스라는 타입을 소개하기 위해 간단한걸 하나 만들어보자.
<source lang="c"> /* simple_critter.cc */ // Simple Critter // Demonstrates creating a new type
- include <iostream>
using namespace std;
class Critter // 클래스 선언. 새로운 타입, 크리터 { public:
int m_Hunger; // 데이터 멤버. 보통 m_으로 시작하는건 멤버 변수다. void Greet(); // 멤버 함수 프로토타입
};
void Critter::Greet() // 멤버 함수 정의 {
cout << "Hi, I'm a critter. My hunger level is " << m_Hunger << ".\n";
}
int main() {
Critter crit1; Critter crit2;
crit1.m_Hunger = 9; cout << "crit1's hunger level is " << crit1.m_Hunger << ".\n";
cirt2.m_Hunger = 3; cout << "crit2's hunger level is " << crit2.m_Hunger << ".\n\n";
crit1.Greet(); crit2.Greet();
return 0;
} </source>
- C++의 꽃 클래스다.
- C++이 객체지향으로 불리우는 이유다.
- 뭐 객체니 뭐니.. 뭉탱이라고 생각하면 될듯 싶은데..
먹기 좋게 썰었다고 표현하면 되려나
Constructor Critter
- 생성자에 대해 알아보자.
<source lang="c"> /* constructor_critter.cc */ // Constructor Critter // Demonstrates constructors
- include <iostream>
using namespace std;
class Critter { public:
int m_Hunger;
Critterint hunger = 0); // 생성자 프로토타입 void Greet();
};
Critter::Critter(int hunger) // 생성자 정의 {
cout << "A new critter has been born!" << endl; m_Hunger = hunger;
}
void Critter::Greet() {
cout << "Hi, I'm a critter. My hunger level is " << m_Hunger << ".\n\n";
}
int main() {
Critter crit(7); crit.Greet();
return 0;
} </source>
- 생성자 별것 없다.
- 생성할때 뭐 실행하는거임.
Private Critter
- 보호에 대해 좀 알아보자.
<source lang="c"> /* private_critter.cc */ // Private Critter // Demonstrates setting member access Levels
- include <iostream>
using namespace std;
class Critter { public: // public 영역 시작
Critter(int hunger = 0); int GetHunger() const; void SetHunger(int hunger);
private: // private 영역 시작
int m_Hunger;
};
Critter::Critter(int hunger): m_Hunger(hunger) {
cout << "A new critter has been born!" << endl;
}
int Critter::GetHunger() const {
return m_Hunger;
}
void Critter:SetHunger(int hunger) {
if(hunger < 0)
cout << "You can't set a critter's hunger to a negative number.\n\n";
else
m_Hunger = hunger;
}
int main() {
Critter crit(5); //cout << crit.m_Hunger; // 안됨. m_Hunger는 private라서. cout << "Calling GetHunger(): " << crit.GetHunger() << "\n\n";
cout << "Calling SetHunger() with -1.\n"; crit.SetHunger(-1);
cout << "Calling SetHunger() with 9.\n"; crit.SetHunger(9); cout << "Calling GetHunger(): " << crit.GetHunger() << "\n\n";
return 0;
} </source>
- 일반적으로 API들은 getXX, setXX 등의 함수가 클래스 안에 있다.
- 뭐 대충 이런식으로 다들 짠다.
Static Critter
- static에 대해 좀 알아보자.
<source lang="c"> /* static_critter.cc */ // Static Critter // Demonstrates static data members and functions
- include <iostream>
using namespace std;
class Critter { public:
static int s_Total; // Static. 크리터 객체의 총 갯수
Critter(int hunger = 0): m_Hunger(hunger)
{
cout << "A critter has been born!" << endl;
++s_Total;
}
static int GetTotal() // Static 멤버 함수
{
return s_Total;
}
private:
int m_Hunger;
};
int Critter::s_Total = 0; // 스태틱 데이터 멤버 초기화
int main() {
cout << "The total number of critters is: "; cout << Critter::s_Total << "\n\n";
Critter crit1, crit2, crit3;
cout << "\nThe total number of critters is: "; cout << Critter::GetTotal() << "\n";
return 0;
} </source>
- 클래스 내 전역변수처럼 사용할 수 있는걸 볼 수 있다.
Critter Caretaker
- 간단한 다마고찌를 만들어보자.
<source lang="c"> /* critter_caretaker.cc */ // Critter Caretaker // Simulates caring for a virtualpet
- include <iostream>
using namespace std;
class Critter { public:
Critter(int hunger = 0, int boredom = 0); void Talk(); void Eat(int food = 4); void Play(int fun = 4);
private:
int m_Hunger; int m_Boredom;
int GetMood() const; void PassTime(int time = 1);
};
Critter::Critter(int hunger, int boredom): m_Hunger(hunger), m_Boredom(boredom) {}
inline int Critter::GetMood() const {
return (m_Hunger + m_Boredom);
}
void Critter::PassTime(int time) {
m_Hunger += time; m_Boredom += time;
}
void Critter::Talk() {
coud << "I'm a critter and I feel ";
int mood = GetMood();
if(mood > 15)
cout << "mad.\n";
else if(mood > 10)
cout << "frustrated.\n";
else if(mood > 5)
cout << "okay.\n";
else
cout << "happy.\n";
PassTime();
}
void Critter::Eat(int food) {
cout << "Brruppp.\n";
m_Hunger -= food;
if(m_Hunger < 0)
m_Hunger = 0;
PassTime();
}
void Critter::Play(int fun) {
cout << "Wheee!\n";
m_Boredom -= fun;
if(m_Boredom < 0)
m_Boredom = 0;
Passtime();
}
int main() {
Critter crit; crit.Talk();
int choice;
do {
cout << "\nCritter Caretaker\n\n";
cout << "0 - Quit\n";
cout << "1 - Listen to your critter\n";
cout << "2 - Feed your critter\n";
cout << "3 - Play with your critter\n\n";
cout << "Choice: ";
cin >> choice;
switch(choice) {
case 0:
cout << "Good-bye.\n";
break;
case 1:
crit.Talk();
break;
case 2:
crit.Eat();
break;
case 3:
crit.Play();
break;
default:
cout << "\nSorry, but " << choice << " isn't a valid choice.\n";
}
} while(choice != 0);
return 0;
} </source>
- 다마고찌같은 게임들은 거의 이런식이다.
Critter Farm
- 좀 더 키워보자.
<source lang="c"> /* critter_farm.cc */ // Critter Farm // Demonstrates object containment
- include <iostream>
- include <string>
- include <vector>
using namespace std;
class Critter { public:
Critter(const string& name = ""): m_Name(name) {}
string GetName() const { return m_Name; }
private:
string m_Name;
};
class Farm { public:
Farm(int spaces = 1) { m_Critters.reserve(spaces); }
void Add(const Critter& aCritter) { m_Critters.push_back(aCritter); }
void RollCall() const
{
for(vector<Critter>::const_iterator iter = m_Critters.begin(); iter != m_Critters.end(); ++iter)
cout << iter->GetName() << " here.\n";
}
private:
vector<Critter> m_Critters;
};
int main() {
Critter crit("Poochie");
cout << "My critter's name is " << crit.GetName() << endl;
cout << "\nCreating critter farm.\n"; Farm myFarm(3);
cout << "\nAdding three critters to the farm.\n";
myFarm.Add(Critter("Moe"));
myFarm.Add(Critter("Larry"));
myFarm.Add(Critter("Curly"));
cout << "\nCalling Roll...\n"; myFarm.RollCall();
return 0;
} </source>
- 벡터를 쓰니 직관적이다.
Friend Critter
- Friend Class를 알아보기 위해 프로그램을 하나 작성해보자.
<source lang="c"> /* friend_critter.cc */ // Friend Critter // Demonstrates friend functions and operator overloading
- include <iostream>
- include <string>
using namespace std;
class Critter {
// 전역함수는 Critter의 friend class friend void Peek(const Critter& aCritter); friend void ostream& operator<<(ostream& os, const Critter& aCritter);
public:
Critter(const string& name = ""): m_name(name) {}
private:
string m_Name;
};
void Peek(const Critter& aCritter); ostream& operator<<(ostream& os, const Critter& aCritter);
int main() {
Critter crit("Pochie");
cout << "Calling Peek() to access crit's private data member, m_Name: \n"; Peek(crit);
cout << "\nSending crit object to cout with the << operator:\n"; cout << crit;
return 0;
}
// 전역 friend 함수는 Critter object의 멤버를 직접 참조할 수 있다. void Peek(const Critter& aCritter) {
cout << aCritter.m_Name << endl;
}
// 전역 friend 함수는 Critter object의 멤버를 직접 참조할 수 있다. // << 연산자로 cout에 Critter object의 내용을 전달할 수 있다. ostream& operator << (ostream& os, const Critter& aCritter) {
os << "Critter Object - "; os << "m_Name: " << aCritter.m_Name; return os;
} </source>
- 점점 고급 기법을 사용할수록 이해하기 힘들다.
Heap
- 동적 할당에 대해 알아보자.
<source lang="c"> /* heap.cc */ // Heap // Demonstrates dynamically allocating memory
- include <iostream>
using namespace std;
int* intOnHeap(); // int형 변수를 힙에 올려 반환 void leak1(); // 메모리 누수를 만든다. void leak2(); // 다른 메모리 누수를 만든다.
int main() {
int* pHeap = new int; *pHeap = 10; cout << "*pHeap: " << *pHeap << "\n\n";
int* pHeap2 = intOnHeap(); cout << "*pHeap2: " << *pHeap2 << "\n\n";
cout << "Freeing memory pointed to by pHeap.\n\n"; delete pHeap;
cout << "Freeing memory pointed to by pHeap2.\n\n"; delete pHeap;
// 애매한 포인터를 지운다. pHeap = 0; pHeap2 = 0;
return 0;
}
int* intOnHeap() {
int* pTemp = new int(20); return pTemp;
}
void leak1() {
int* drip1 = new int(30);
}
void leak2() {
int* drip2 = new int(50); drip2 = new int(100); delete drip2;
} </source>
- 바로 이게 c 계열이
좆같다고힘들다고 하는 메모리관리다. - 왜냐하면 메모리 누수 죽어라 많이 일어나거든.
Heap Data Member
- 멤버를 동적할당해보자.
<source lang="c"> /* heap_data_member.cc */ // Heap Data Member // Demonstrates an object with a dynamically allocated data member
- include <iostream>
- include <string>
using namespace std;
class Critter { public:
Critter(const string& name = "")
{
cout << "Constructor called\n";
m_pName = new string(name);
}
~Critter() // 소멸자
{
cout << "Destructor called\n";
delete m_pName;
}
Critter(const Critter& c) // 복사 생성자
{
cout << "Copy Constructor called\n";
m_pName = new string;
*m_pName = c.GetName();
}
Critter& operator=(const Critter& c) // 연산자 지정 오버로드
{
cout << " Overloaded Assignment Operator called\n";
if(this == &c)
{
return *this;
} else {
*m_pName = c.GetName();
return *this;
}
}
string GetName() const { return *m_pName; }
void SetName(const string& name = "") { *m_pName = name; }
void SayHi() const { cout << "Hi, my name is " << GetName() << "\n"; }
private:
string* m_pName;
};
void testDestructor(); void testCopyConstructor(Critter copy); void testAssignmentOp();
int main() {
testDestructor(); cout << endl;
Critter crit("Poochie");
crit.SayHi();
testCopyConstructor(crit);
cout << endl;
testAssignmentOp();
return 0;
}
void testDestructor() {
Critter crit("Rover");
crit.SayHi();
}
// 값으로 받은 object를 복사 생성자로 invoke void testCopyConstructor(Critter copy) {
copy.SayHi();
}
void testAssignmentOp() {
Critter crit1("crit1");
Critter crit2("crit2");
crit1 = crit2;
crit1.SayHi();
crit2.SayHi();
cout << "Setting name of crit1 back to 'crit1'\n";
crit1.SetName("crit1");
crit1.SayHi();
crit2.SayHi();
Critter crit("crit");
crit = crit;
} </source>
- 더럽게 짜증나는거 다 안다.
왜냐하면 나도 쓰면서 짜증났거든
Game Lobby
- 온라인 게임에서 대기실같은게 있을 수 있다.
- Starcraft같은거 해봤으면 다들 알거임.
- 간략하게 기반을 잡아보자.
<source lang="c"> /* game_lobby.cc */ // Game Lobby // Simulates a game lobby where players wait
- include <iostream>
- include <string>
using namespace std;
class Player { public:
Player(const string& name = ""): m_Name(name), m_pNext(0) {}
string GetName() const { return m_Name; }
Player* GetNext() const { return m_pNext; }
void SetNext(Player* next) { m_pNext = next; }
private:
string m_Name; Player* m_pNext; // 리스트중에 다음 플레이어 포인팅
};
class Lobby {
friend ostream& operator << (ostream& os, const Lobby& aLobby);
public:
Lobby(): m_pHead() {}
~Lobby() { Clear(); }
void AddPlayer();
void RemovePlayer();
void Clear();
private:
Player* m_pHead;
};
void Lobby::AddPlayer() {
// 플레이어 노드 생성 cout << "Please enter the name of the new player: "; string name; cin >> name; Player* pNewPlayer = new Player(name);
// 리스트가 비었으면 새 플레이어를 헤드로 설정
if(m_pHead == 0)
{
m_pHead = pNewPlayer;
}
// 그게 아니면 맨 끝에 붙임
else
{
Player* pIter = m_pHead;
while(pIter->GetNext() != 0)
{
pIter = pIter->GetNext();
}
pIter->SetNext(pNewPlayer);
}
}
void Lobby::RemovePlayer() {
if(m_pHead == 0)
{
cout << "The game lobby is empty. No one to remove!\n";
} else {
Player* pTemp = m_pHead;
m_pHead = m_pHead->GetNext();
delete pTemp;
}
}
void Lobby::Clear() {
while(m_pHead != 0)
{
RemovePlayer();
}
}
ostream& operator << (ostream& os, const Lobby& aLobby) {
Player* pIter = aLobby.m_pHead;
os << "\nHere's who's in the game lobby:\n";
if(pIter == 0)
{
os << "The lobby is empty.\n";
} else {
while(pIter != 0)
{
os << pIter->GetName() << endl;
pIter = pIter->GetNext();
}
}
return os;
}
int main() {
Lobby myLobby; int choice;
do {
cout << myLobby;
cout << "\nGAME LOBBY\n";
cout << "0 - Exit the program.\n";
cout << "1 - Add a player to the lobby.\n";
cout << "2 - Remove a player from the lobby.\n";
cout << "3 - Clear the lobby.\n";
cout << endl << "Enter choice: ";
cin >> choice;
switch(choice)
{
case 0: cout << "Good-bye.\n"; break;
case 1: myLobby.AddPlayer(); break;
case 2: myLobby.RemovePlayer(); break;
case 3: myLobby.Clear(); break;
default: cout << "That was not a valid choice.\n";
}
} while(choice != 0);
return 0;
} </source>
- 이런식으로 좀 신경써놓으면 나중에 기능 추가할때도 편하다.
Simple Boss
- 상속에 대해 알아보기 위한 프로그램을 하나 만들어보자.
<source lang="c"> /* simple_boss.cc */ // Simple Boss // Demonstrates inheritance
- include <iostream>
using namespace std;
class Enemy { public:
int m_Damage;
Enemy(): m_Damage(10) { }
void Attack() const {
cout << "Attack inflicts " << m_Damage << " damage points!\n";
}
};
class Boss : public Enemy { public:
int m_DamageMultiplier;
Boss(): m_DamageMultiplier(3) { }
void SpecialAttack() const
{
cout << "Special Attack inflicts " << (m_DamageMultiplier * m_Damage);
cout << " damage points!\n";
}
};
int main() {
cout << "Creating an enemy.\n"; Enemy enemy1; enemy1.Attack();
cout << "\nCreating a boss.\n"; Boss boss1; boss1.Attack(); boss1.SpecialAttack();
return 0;
} </source>
- 좀 더 알아보기 위해 프로그램을 향상시켜보자.
<source lang="c"> /* simple_boss2.cc */ // simple Boss 2.0 // Demonstrates access control under inheritance
- include <iostream>
using namespace std;
class Enemy { public:
Enemy(): m_Damage(10) { }
void Attack() const {
cout << "Attack inflicts " << m_Damage << " damage points!\n";
}
protected:
int m_Damage;
};
class Boss : public Enemy { public:
Boss(): m_DamageMultiplier(3) { }
void SpecialAttack() const {
cout << "Special Attack inflicts " << (m_DamageMultiplier * m_Damage);
cout << " damage points!\n";
}
private:
int m_DamageMultiplier;
};
int main() {
cout << "Creating an enemy.\n"; Enemy enemy1; enemy1.Attack();
cout << "\nCreating a boss.\n"; Boss boss1; boss1.Attack(); boss1.SpecialAttack();
return 0;
} </source>
- Protected에 대해서 좀 알았을거당
- Overriding을 봅시다.
<source lang="c"> /* overriding_boss.cc */ // Overriding Boss // Demonstrates calling and overriding base member functions
- include <iostream>
using namespace std;
class Enemy { public:
Enemy(int damage = 10): m_Damage(damage) { }
void Taunt() const {
cout << "The enemy says he will fight you.\n";
}
void Attack() const {
cout << "Attack! Inflicts " << m_Damage << " damage points.";
}
private:
int m_Damage;
};
class Boss : public Enemy { public:
Boss(int damage = 30): Enemy(damage) { } // 기본 생성자에 인자를 주고 호출
void Taunt() const {
Enemy::Attack();
cout << " And laughs heartily at you.\n";
}
};
int main() {
cout << "Creating an enemy.\n"; Enemy enemy1; enemy1.Taunt(); enemy1.Attack();
cout << "\n\nCreating a boss.\n"; Boss boss1; boss1.Taunt(); boss1.Attack();
return 0;
} </source>
- 크게 달라보이는게 없당
- Virtual Member에 대해서 알아보자.
<source lang="c"> /* virtual_boss.cc */ // Virtual Boss // Demonstrates virtual functions
- include <iostream>
using namespace std;
class Enemy { public:
Enemy(int damage = 10) {
m_pDamage = new int(damage);
}
virtual ~Enemy()
{
cout << "m_pDamage deleted\n";
delete m_pDamage;
}
void Taunt() const {
cout << "The enemy says he will fight you.\n";
}
void virtual VTaunt() const {
cout << "The enemy says he will fight you.\n";
}
protected:
int* m_pDamage;
};
class Boss : public Enemy { public:
Boss(int multiplier = 3) {
m_pDamageMultiplier = new int(multiplier);
}
virtual ~Boss() {
cout << "m_pDamageMultiplier deleted\n";
delete m_pDamageMultiplier;
}
void Taunt() const {
cout << "The boss says he will end your pitiful existence.\n";
}
void virtual VTaunt() const {
cout << "The boss says he will end your pitiful existence.\n";
}
protected:
int* m_pDamageMultiplier;
};
int main() {
cout << "Pointer to Enemy that points to a Boss object:\n"; Enemy* pBadGuy = new Boss(); pBadGuy->Taunt(); pBadGuy->VTaunt();
cout << "\nDeleting pointer to Enemy:\n"; delete pBadGuy; pBadGuy = 0;
return 0;
} </source>
- 역시 Virtual 멤버도 그다지 달라보이는건 없다.
Abstract Creature
- 추상화라고들 이야기하는것에 대해 알아보도록 하자.
<source lang="c"> /* abstract creature */ // Abstract Creature // Demonstrates abstract classes
- include <iostream>
using namespace std;
class Creature // Abstract class { public:
Creature(int health = 100): m_Health(health) { }
virtual void Greet() const = 0; // 순수한 virtual 멤버 함수
virtual void DisplayHealth() const {
cout << "Health: " << m_Health << endl;
}
protected:
int m_Health;
};
class Orc : public Creature { public:
Orc(int health = 120); Creature(health) { }
virtual void Greet() const {
cout << "The orc grunts hello.\n";
}
};
int main() {
Creature* pCreature = new Orc(); pCreature->Greet(); pCreature->DisplayHealth();
return 0;
} </source>
- 계속 진행될수록 메인함수는 작아지는것 같은 느낌인데?
Critter Project
- 파일을 나눠보자.
- 아래는 지금까지처럼의 소스코드
<source lang="c">
- include <iostream>
using namespace std;
class Critter { public:
void Greet();
};
void Critter::Greet() {
cout << "Hi, I'm a critter.\n";
}
int main() {
cout << "Instantiating a Critter object.\n\n";
Critter crit; crit.Greet();
return 0;
} </source>
- 요걸 세개로 나눌거다.
- Header file - Critter class의 정의만.
- Implementation file - Critter class의 내용.
- Application file - Critter class를 사용하는 main()
- 아래는 헤더
<source lang="c"> /* critter.h */ // Header
- ifndef CRITTER_H
- define CRITTER_H
class Critter { public:
void Greet();
}; </source>
- 나중에 엄청 많은 include가 있을 경우 중복 선언을 방지하기 위한 디파인이다.
- 아래는 구현
<source lang="c"> /* critter.cc */ // Implementation file
- include <istream>
- include "critter.h"
using namespace std;
void Critter::Greet() {
cout << "Hi. I'm a critter.\n";
} </source>
- 헤더 참조를 하게 했다는것 외에 별다를건 없다.
- 아래는 어플리케이션
<source lang="c"> /* critter_app.cc */ // Application file
- include <iostream>
- include "critter.h"
using namespace std;
int main() {
cout << "Instantiating a Critter object.\n\n";
Critter crit; crit.Greet();
return 0;
} </source>
- 파일 안 나누고 그냥 통짜로 하면 되지 않을까 하는데, c++을 포함한 객체지향 프로그래밍 언어들은 통짜로 짜면 일단 본인이 '내가 뭘 했는지' 까먹게 되고, 유지보수도 잘 안 되는 경향이 있다.
- 짧은 프로그램에서는 별 차이 안 나지만, 파일별로 기능정리를 한다던가 하면 좀 더 집중할 수 있는 편이다.
- 그리고, 보통 클래스를 사용하는 경우 협업이라고 하고 혼자 기능 하나를 만든다는 샌드박스 개념으로 협업을 하게 된다.
- 테스트 역시 클래스 단위로 이루어진다면 어떤 기능이 부족하더라도 dummy를 만들어 돌릴 수 있으니, 평생 혼자
여자친구도 없이모든 프로그래밍을 하지 않을거면 파일 관리를 좀 해보는게 좋다.
Blackjack
- 블랙잭은 다음과 같은 순서로 진행된다.
- 플레이어와 하우스는 각 두장의 초기 카드를 받는다.
- 하우스의 첫카드는 히든
- 플레이어와 하우스의 카드를 보여준다.
- 플레이어가 카드를 더 받을건지 결정한다.
- 하우스가 카드를 깐다
- 하우스가 카드를 더 받을건지 결정한다.
- 만약 하우스가 버스트하면
- 버스트 안한 플레이어 전부 승리
- 그렇지 않으려면
- 각각 플레이어가
- 플레이어가 버스트하지 않았다면
- 플레이어가 하우스보다 수가 높으면 플레이어 승리
- 그렇지 않고 플레이어보다 하우스가 높으면 플레이어 패배
- 숫자가 같으면 플레이어 푸시
- 플레이어가 버스트하지 않았다면
- 각각 플레이어가
- 카드 모두 회수
- 자, 그럼 시작해볼까
<source lang="c"> // Blackjack // Plays a simple version of the casino game of blackjac; for 1 - 7 players
- include <iostream>
- include <string>
- include <vector>
- include <algorithm>
- include <ctime>
using namespace std;
class Card { public:
enum rank { ACE = 1, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING };
enum suit { CLUBS, DIAMONDS, HEARTS, SPADES };
// << 연산자 오버로딩은 카드 오브젝트를 표준출력으로 내보낸다. friend ostream& operator << (ostream& os, const Card& aCard);
Card(rank r = ACE, suit s = SPADES, bool ifu = true);
// 카드의 상태 반환, 1 - 11 int GetValue() const;
// 카드 뒤집기; 정면이든 아니든 같아야 한다. void Flip();
private:
rank m_Rank; suit m_Suit; bool m_IsFaceUp;
};
Card::Card(rank r, suit s, bool ifu): m_Rank(r), m_Suit(s), m_IsFaceUp(ifu) { }
int Card::GetValue() const {
// card가 뒤집혀 있으면 0
int value = 0;
if(m_IsFaceUp)
{
// 카드의 내용이 value
value = m_Rank;
// value가 10보다 클 수 없음
if(value > 10)
value = 10;
}
return value;
}
void Card::Flip() {
m_IsFaceUp = !(m_IsFaceUp);
}
class Hand { public:
Hand();
virtual ~Hand();
// 카드 추가 void Add(Card* pCard);
// 카드 회수 void Clear();
// 카드의 값을 구한다. ace는 1이나 11이 될 수 있는데 그걸 판단해줘야 한다. int GetTotal() const;
protected:
vector<Card*> m_Cards;
};
Hand::Hand() {
m_Cards.reserve(7);
}
Hand::~Hand() // 클래스 바깥에서 쓸땐 virtual을 빼야함 {
Clear();
}
void Hand::Add(Card* pCard) {
m_Cards.push_back(pCard);
}
void Hand::Clear() {
// 처음부터 끝까지 반복하면서 벡터의 내용을 지운다. 힙의 메모리를 전부 반환한다.
vector<Card*>::iterator iter = m_Cards.begin();
for(iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
{
delete *iter;
*iter = 0;
}
// 벡터 포인터도 클리어한다.
m_Cards.clear();
}
int Hand::GetTotal() const {
// 카드가 없으면 0 반환
if(m_Cards.empty())
return 0;
// 첫카드가 0이면 카드는 뒤집힌 상태이므로 0 반환
if(m_Cards[0]->GetValue() == 0)
return 0;
// 카드 추가, 각 ace는 1로 취급
int total = 0;
vector<Card*>::const_iterator iter;
for(iter = m_Cards.begin(); iter !=m_Cards.end(); ++iter)
total += (*iter)->GetValue();
// 만약 카드에 ace가 포함돼 있으면 ace를 결정
bool containsAce = false;
for(iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
if((*iter)->GetValue() == Card::ACE)
containsAce = true;
// 만약 ace가 있고 토탈이 10보다 작으면 ace를 11로 if(containsAce && total <= 11) // 전에 ace를 1 추가했으니, 10만 더 추가하면 된다. total += 10;
return total;
}
class GenericPlayer : public Hand {
friend ostream& operator << (ostream& os, const GenericPlayer& aGenericPlayer);
public:
GenericPlayer(const string& name = "");
virtual ~GenericPlayer();
// 플레이어가 힛을 하는지 나타내는 값 virtual bool IsHitting() const = 0;
// 플레이어가 버스트했는지 나타내는 값 - 버스트는 카드의 총합이 21이 넘는 경우. bool IsBusted() const;
// 버스트했다는걸 표시해주는 함수 void Bust() const;
protected:
string m_Name;
};
GenericPlayer::GenericPlayer(const string& name) : m_Name(name) { }
GenericPlayer::~GenericPlayer() { }
bool GenericPlayer::IsBusted() const {
return (GetTotal() > 21);
}
void GenericPlayer::Bust() const {
cout << m_Name << " busts.\n";
}
class Player : public GenericPlayer { public:
Player(const string& name = "");
virtual ~Player();
// 플레이어가 힛할지 안할건지 리턴 virtual bool IsHitting() const;
// 플레이어가 이겼음을 표시 void Win() const;
// 플레이어가 졌음을 표시 void Lose() const;
// 플레이어 푸시를 표시 void Push() const;
};
Player::Player(const string& name) : GenericPlayer(name) { }
Player::~Player() { }
bool Player::IsHitting() const {
cout << m_Name << ", do you want a hit? (Y/N): "; char response; cin >> response; return (response == 'y' || response == 'Y');
}
void Player::Win() const {
cout << m_Name << " wins.\n";
}
void Player::Lose() const {
cout << m_Name << " loses.\n";
}
void Player::Push() const {
cout << m_Name << " pushes.\n";
}
class House : public GenericPlayer { public:
House(const string& name = "House");
virtual ~House();
// 하우스가 힛하는걸 표현 - 카드 토탈이 16이거나 작으면 무조건 힛 virtual bool IsHitting() const;
// 첫장 뒤집기 void FlipFirstCard();
};
House::House(const string& name) : GenericPlayer(name) { }
House::~House() { }
bool House::IsHitting() const {
return (GetTotal() <= 16);
}
void House::FlipFirstCard() {
if(!(m_Cards.empty()))
m_Cards[0]->Flip();
else cout << "No card to flip!\n";
}
class Deck : public Hand { public:
Deck(); virtual ~Deck();
// 52장 카드로 표준 덱을 만듦 void Populate();
// 카드 섞기 void Shuffle();
// 카드 딜링 void Deal(Hand& aHand);
// 추가 카드를 일반 플레이어에게 딜링 void AdditionalCards(GenericPlayer& aGenericPlayer);
};
Deck::Deck() {
m_Cards.reserve(52); Populate();
}
Deck::~Deck() { }
void Deck::Populate() {
Clear();
// 스탠다드 덱을 만듦
for(int s = Card::CLUBS; s <= Card::SPADES; ++s)
for(int r = Card::ACE; r < Card::KING; ++r)
Add(new Card(static_cast<Card::rank>(r), static_cast<Card::suit>(s)));
}
void Deck::Shuffle() {
random_shuffle(m_Cards.begin(), m_Cards.end());
}
void Deck::Deal(Hand& aHand) {
if(!m_Cards.empty()) {
aHand.Add(m_Cards.back());
m_Cards.pop_back();
} else {
cout << "Out of cards. Unable to deal.";
}
}
void Deck::AdditionalCards(GenericPlayer& aGenericPlayer) {
cout << endl;
// 플레이어가 버스트하지 않고 힛을 하면 카드를 딜링한다.
while(!(aGenericPlayer.IsBusted()) && aGenericPlayer.IsHitting()) {
Deal(aGenericPlayer);
cout << aGenericPlayer << endl;
if(aGenericPlayer.IsBusted())
aGenericPlayer.Bust();
}
}
class Game { public:
Game(const vector<string>& names);
~Game();
// 게임 플레이! void Play();
private:
Deck m_Deck; House m_House; vector<Player> m_Players;
};
Game::Game(const vector<string>& names) {
// 플레이어 이름으로 벡터를 만든다.
vector<string>::const_iterator pName;
for(pName = names.begin(); pName != names.end(); ++pName)
m_Players.push_back(Player(*pName));
srand(time(0)); m_Deck.Populate(); m_Deck.Shuffle();
}
Game::~Game() { }
void Game::Play() {
// 초기카드 2장을 모두에게 딜링
vector<Player>::iterator pPlayer;
for(int i = 0; i < 2; ++i)
{
for(pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
m_Deck.Deal(*pPlayer);
m_Deck.Deal(m_House);
}
// 하우스의 첫 카드는 히든 m_House.FlipFirstCard();
// 모두의 카드를 표시
for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
cout << *pPlayer << endl;
cout << m_House << endl;
// 추가카드를 플레이어들에게 딜링
for(pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
m_Deck.AdditionalCards(*pPlayer);
// 하우스 히든 공개 m_House.FlipFirstCard(); cout << endl << m_House;
// 하우스 카드 딜링 m_Deck.AdditionalCards(m_House);
if(m_House.IsBusted())
{
// 버스트 안한 모든 플레이어 승리
for(pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
if(!(pPlayer->IsBusted()))
pPlayer->Win();
} else {
// 각각의 플레이어가 하우스를 이겼는지 확인
for(pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
if(!(pPlayer->IsBusted())) {
if(pPlayer->GetTotal() > m_House.GetTotal())
pPlayer->Win();
else if(pPlayer->GetTotal() < m_House.GetTotal())
pPlayer->Lose();
else
pPlayer->Push();
}
}
// 모든 카드 회수
for(pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
pPlayer->Clear();
m_House.Clear();
}
// 함수 프로토타입 ostream& operator << (ostream& os, const Card& aCard); ostream& operator << (ostream& os, const GenericPlayer& aGenericPlayer);
int main() {
cout << "\t\tWelcome to Blackjack!\n\n";
int numPlayers = 0;
while(numPlayers < 1 || numPlayers > 7) {
cout << "How many palyers? (1 - 7): ";
cin >> numPlayers;
}
vector<string> names;
string name;
for(int i = 0; i < numPlayers; ++i)
{
cout << "Enter player name: ";
cin >> name;
names.push_back(name);
}
cout << endl;
// 게임 루프
Game aGame(names);
char again = 'y';
while(again != 'n' && again != 'N') {
aGame.Play();
cout << "\nDo you want to play agin? (Y/N): ";
cin >> again;
}
return 0;
}
// 카드 오브젝트에서 << 연산자 오버로드 ostream& operator << (ostream& os, const Card& aCard) {
const string RANKS[] = { "0", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };
const string SUITS[] = { "c", "d", "h", "s" };
if(aCard.m_IsFaceUp)
os << RANKS[aCard.m_Rank] << SUITS[aCard.m_Suit];
else
os << "XX";
return os;
}
// 플레이어 오브젝트에서 << 연산자 오버로드 ostream& operator << (ostream& os, const GenericPlayer& aGenericPlayer) {
os << aGenericPlayer.m_Name << ":\t";
vector<Card*>::const_iterator pCard;
if(!aGenericPlayer.m_Cards.empty())
{
for(pCard = aGenericPlayer.m_Cards.begin(); pCard != aGenericPlayer.m_Cards.end(); ++pCard)
os << *(*pCard) << "\t";
if(aGenericPlayer.GetTotal() != 0)
cout << "(" << aGenericPlayer.GetTotal() << ")";
} else {
os << "<empty>";
}
return os;
} </source>
- 블랙잭 정도는 껌인가?
Multiplayer Game Programming
SDL
- http://www.dreamincode.net/forums/topic/290187-linux-game-programming-tutorial-1-introduction-first-sdl/ 이쪽의 내용 번역
- Simple Directmedia Layer
웬지 DirectX를 연상시키지만, 웬지 플랫폼 인디펜던트하다 - 홈페이지는 http://www.libsdl.org
- 비디오, 오디오, 사용자 입력을 지원한다.
- 기본적으로 2D 그래픽 라이브러리를 지원하며, 3D는 OpenGL을 통해서 지원한다고 한다.
- 지원되는 플랫폼은 다음과 같다.
- Linux/X11
- Windows
- Windows CE
- Mac OSX
- iOS
- Android
- 사용되는 IDE
- Code::Blocks
- Visual C++
- Eclipse
- 라이센스는 LGPL이다.
- 자유롭게 상용프로그램 개발이 가능
- 상용으로 개발할 경우 SDL과 게임은 Dynamic Link되어야 함.
- 일단 간단하게 만들기에 짱좋은 라이브러리
준비
- 개발킷을 설치한다(다음은 데비안/우분투 기준)
<source lang="bash"> $ sudo apt-get install libsdl1.2-dev </source>
- 우분투에서는 우분투소프트웨어센터에서 검색해서 받는게 좋다.
- 만약 윈도우라면 Visual C++과 MinGW용 라이브러리가 있다.
Hello World
- 뭐든 헬로월드부터 찍어봐야지.. 안그래?
<source lang="c"> /* sdltest.c */ /*
SDL Test Demonstrates SDL being initialized and shut down. written by Sayuri L. Kitsune (2012)
- /
/* Includes */
- include <SDL.h>
- include <stdio.h>
/* Main */ int main(int argn,char **argv) {
/* Init */
if(SDL_Init(SDL_INIT_VIDEO) != 0)
fprintf(stderr,"Could not initialize SDL: %s\n",SDL_GetError()); /* Error? */
printf("SDL Initialized\n");
/* Exit */
SDL_Quit();
printf("SDL Shutdown\n");
/* Done */
return 0;
} </source>
- 그럼 하던대로 컴파일을 해볼까!
<source lang="bash"> $ gcc -c sdltest.c </source>
- 으메, 헤더파일 없다고 나오네
- 헤더파일은 /usr/include/SDL에 있당
- 그러니까 이렇게
<source lang="bash"> $ gcc -I"/usr/include/SDL" -c sdltest.c </source>
- 자, 컴파일은 됐어.
- 이제 링크!
<source lang="bash"> $ gcc -o sdltest sdltest.o </source>
- 또 에러네? 젠장
- 라이브러리 링크해줘야함
<source lang="bash"> $ gcc -o sdltest sdltest.o -lSDL </source>
- 그냥 한번에 다 할라면
<source lang="bash"> $ gcc -o sdltest sdltest.c -I"/usr/include/SDL" -lSDL </source>
- 실행해보면 그냥 초기화되고 꺼졌다는 메세지 나와
- 그럼 성공
SDL_PollEvent
- 그래도 화면 나오는게 보고 싶겠지.
- 화면 초기화하고 어떤 입력을 받는 루틴을 짜보자.
<source lang="c"> /* demo.c */ /*
SDL Video Demo demonstrates SDL's video working by moving dots around a screen written by Sayuri L. Kitsune (2012)
- /
/* Includes */
- include <SDL.h>
/* Globals */ SDL_Surface *demo_screen;
/* Main */ int main(int argn,char **argv) {
SDL_Event ev;
int active;
/* Initialize SDL */
if(SDL_Init(SDL_INIT_VIDEO) != 0)
fprintf(stderr,"Could not initialize SDL: %s\n",SDL_GetError());
/* Open main window */
demo_screen = SDL_SetVideoMode(320,240,0,SDL_HWSURFACE|SDL_DOUBLEBUF);
if(!demo_screen)
fprintf(stderr,"Could not set video mode: %s\n",SDL_GetError());
/* Main loop */
active = 1;
while(active)
{
/* Handle events */
while(SDL_PollEvent(&ev))
{
if(ev.type == SDL_QUIT)
active = 0; /* End */
}
}
/* Exit */
SDL_Quit();
return 0;
} </source>
- 솔직히 옵션 넣어야 할게 좀 있고 귀찮으니 Makefile도 하나 만들자.
<source lang="make"> test:
make build; ./demo;
build:
gcc -I"/usr/include/SDL" -c demo.c; gcc demo.o -lSDL -o demo;
clean:
-rm demo.o; -rm demo;
</source>
- 창 하나가 뜬다.
SDL_app
- 간단하게 화면출력하는거 하나 만들어봅시다.
<source lang="c"> /* sdl_app.c */
- include <SDL/SDL.h>
int main ( int argc, char** argv ) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Surface* screen = SDL_SetVideoMode(320, 240, 16, SDL_SWSURFACE);
SDL_Surface* bmp = SDL_LoadPNG("back.png");
SDL_Rect dstrect;
dstrect.x = 0;
dstrect.y = 0;
SDL_BlitSurface(bmp, NULL, screen, &dstrect);
SDL_Flip(screen);
SDL_Delay(5000);
SDL_FreeSurface(bmp);
SDL_FreeSurface(screen);
SDL_Quit();
return 0;
} </source> 다음은 Makefile.sdl_app <source lang="make"> test:
make build;
build:
gcc -I"/usr/include/SDL" -c sdl_app.c; gcc demo.o -lSDL -o sdl_app;
clean:
-rm sdl_app.o; -rm sdl_app;
</source>
- 아무 그림파일이나 320x240 해상도에 맞춰 bmp로 저장하고 make -f Makefile.sdl_app 이런식으로 하면 되겠다.
- 아 솔직히 귀찮으면 디렉토리별로 나누면 그게 짱
- 대충 snail 애니메이션을 해보자.
<source lang="c"> /* sdl_ani.c */
- include <SDL/SDL.h>
int main ( int argc, char** argv ) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Surface* screen = SDL_SetVideoMode(480, 272, 16, SDL_SWSURFACE);
SDL_Surface* bmp = SDL_LoadBMP("character.bmp");
SDL_Rect dstrect;
dstrect.y = 100;
for (int x = 0; x < 480; x++) {
dstrect.x = x;
SDL_BlitSurface(bmp, NULL, screen, &dstrect);
SDL_Flip(screen);
SDL_Delay(10);
}
SDL_FreeSurface(bmp);
SDL_FreeSurface(screen);
SDL_Quit();
return 0;
} </source>
- 다음은 Makefile.sdl_ani
<source lang="make"> test:
make build;
build:
gcc -I"/usr/include/SDL" -c sdl_ani.c; gcc demo.o -lSDL -o sdl_ani;
clean:
-rm sdl_ani.o; -rm sdl_ani;
</source>
- 대충 100x100짜리 bmp파일을 character.bmp이라고 명명해서 같은 디렉토리에 넣어두고 실행해보자.