Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
게임 프로그래밍으로 배우는 C# 1권
2007-01-27
문기영
소개
이 책은 새로운 프로그래밍 언어 C#을 게임 프로그래밍으로 배우는 책입니다. 게임 프로그
래밍은 프로그래밍의 꽃이라 할 정도로 매우 다양한 프로그래밍...
1권에서는 DirectX를 이용한 그래픽 프로그래밍은 다루지 않지만 1권에서 다루는 그래픽
프로그래밍의 내용으로 충분히 게임을 만들 수 있으며 그것은 단지 도구의 선택 문제에 있
습니다. 1권에서는 Flight라는 총알...
스 코드를 돌려보기 위해서는 비주얼 스튜디오 2005와 닷넷 2.0이 설치 되어야 합니다.
DirectX
DirectX는 Microsoft가 만든 멀티미디어 도구입니다. 도스 시절에는 비디오 메모리의 접근
에 아무런 문...
부록
실제로 이 책을 쓰면서 참고했던 모든 관련 자료들은 부록에서 다루고 있습니다. 게임 프로
그래밍에 사용되는 특별한 테크닉 들이 존재하는데 이러한 것들은 모두 부록에서 다루도록
하였고 책을 읽는 도중에 참고해야 합니...
로운 것을 배우는 것이라고 정의해도 될 것 같습니다. 필자의 학습법은 무조건 일단 돌아가
는 것을 눈으로 보거나 체험해 본 이후에야 무언가를 알 정도로 똑똑하지 않기 때문에 이
책에서도 똑같이 적용하였습니다.
일단 만들...
<그림1>
그림1과 같이 csc 실행파일을 찾을 수 없다고 나오게 됩니다. 만일 이 부분을 해결할 수 없
다면 [부록 : 환경변수 등록]을 참고 하시길 바랍니다.
자, 이제 우리는 csc를 이용해서 컴파일을 할 수 있게...
<그림2>
이제 도스 커맨드에서 firstprogram.exe를 실행해 보면 화면에 우리가 작성했던 문장이 나
오는걸 보실 수 있습니다.
<그림3>
VisualStudio 2005를 이용한 C#프로그램
비주얼스튜디오는 ...
일이 1만 개가 넘는 경우가 허다한데 그걸 전부 도스 커맨드로 치고 있다거나 배치 파일로
만들어서 실행하기에는 잔작업이 너무 많습니다. 그리고 메모장의 경우에 C#에서 제공하는
키워드를 다른 색상으로 구분해 주거나 하는...
<그림5>
그림5와 같이 새로 만들기를 하여 프로젝트를 생성하도록 합시다.
<그림6>
새로 만들기를 시도하면 그림5과 같이 프로젝트 형식을 고를 수 있습니다. 비주얼스튜디오
는 C#뿐만 아니라 다른 언어 VisualBasic, J#, C++모두 똑 같은 도구를 사용하여 프로그
램을 제작할 수 ...
그림7은 그림6의 우측 메뉴의 확대 부분인데 기본적으로 제공하는 템플릿들이 존재합니다.
주로 사용하는 것은 Windows 응용 프로그램이나 콘솔 응용 프로그램입니다. 필자는 여기
에서 콘솔 응용 프로그램을 선택하였습니다...
이 코드를 어디에 두어야 하느냐가 관건인데 처음 언어를 배울 때는 무작정 그대로 따라배
우는 방법밖에 없습니다.
using System;
using System.Collections.Generic;
using Syste...
<그림8>
빌드했습니다. 라는 말은 말 그대로 빌드를 했다는 말입니다. 즉 컴파일이 완료 되어서 실
행 파일이 나왔다는 의미입니다. C, C++프로그래머의 경우에는 컴파일과 링크 과정을 분류
해서 설명하는데 근래에는 대...
<그림9>
프로그램을 실행하면 이전에 우리가 작성했던 문장이 화면에 나타나는 것을 보실 수 있습니
다. 이 프로그램은 비주얼스튜디오를 이용하거나 단순히 메모장을 이용해서 C# 컴파일러로
직접 컴파일하거나 결과는 동일함을...
2장. C#언어 문법
데이터 타입
C#에는 내장 타입과 사용자 정의 타입이 존재합니다. 객체중심에서 사용자 정의 타입은 클
래스라고 합니다. 사용자 정의 타입은 말 그대로 프로그래머가 타입을 새롭게 정의할 수 있
는 것...
사실 8byte를 사용해서 모든 데이터 타입을 사용해도 32byte밖에 안되고 4byte로 사용하
면 16byte밖에 되지 않습니다. 문제는 이 선수들이 몇 명이냐에 따라서 결과가 엄청나게
달라집니다.
선수가 15000...
CLR타입으로도 프로그래밍이 가능합니다.2
필자는 CLR타입으로 코드를 작성하는 것이 손
에 익숙하지 않아서 C#타입으로 모든 코드를 작성했습니다.
내장 타입 선택
C#의 내장 타입 목록은 다음과 같습니다.
닷넷 타입 ...
타입 변환을 필요로 하는데 예를 들어서 자릿수를 자르고 싶을 경우
float PI = 3.141593F;
int PI_Int = (int)PI;
System.Console.WriteLine("PI = " + PI);
S...
간과하기 쉽습니다. 왜냐하면 3.14에서 자릿수 0.14가 잘려나간다고 해도 크게 문제될 것
없이 돌아가는 프로그램도 있기 때문입니다. 하지만 해당 로직이 게이머 3천명이 접속되어
있는 서버에 있다거나, 이미 제품화되어...
public struct Int32 : IComparable, IFormattable, IConvertible,
IComparable<int>, IEquatable<int>
{
public const int MaxVal...
Convert는 static이기 때문에 모든 영역에서 사용이 가능합니다.
using System.Runtime.InteropServices;
namespace System
{
// 요약:
// 기본 데이터 형식을 다른 ...
<그림11>
결국, 입력 문자열 형식이 잘못되었다는 예외가 발생하고 프로그래머가 Convert의 ToInt32
메소드를 호출하기 이전에 strData가 숫자형인지 검사를 하거나 다음과 같이 예외를 사용해
서 문제를 해결...
<그림12>
예외 사용에 대한 자세한 사항은 예외 절을 참고 하시길 바랍니다.
변수 선언과 다중 변수
변수를 선언하는 방법은 앞서 배운 예제들을 통해서 알 수 있지만 다시 한번 정리 해보도록
하겠습니다. 변수를 선언하는...
int DataA, DataB;
물론 C#에서는 C/C++과 동일하게 초기화 및 선언이 동시에 가능하기 때문에 다음과 같은
코드도 가능합니다.
int DataA = 10, DataB = 20;
하지만 일반적으로 프로그래...
상수
상수(constant)는 값이 변하지 않는 수라는 의미의 변수입니다. 일반적으로 설정된 타입의
변수를 선언하고 나면 해당하는 변수에 프로그래머가 작성한 데이터가 들어가게 됩니다. 그
값은 런타임 도중에 바뀌는 것이...
const double PI = 3.1415926535897;5
만일 PI라는 상수값에 값을 대입하려고 한다면 다음과 같은 오류를 얻게 됩니다.
// 아무래도 아닌 것 같아. PI값을 수정하자.
PI = 3.141592...
cRedAmor = 30
}
노란색 아머는 10으로 설정되어 있고 빨간색 아머는 30초로 설정되어 있습니다.6
이 값은
프로그램이 실행되고 난 후에는 변하지 않는 값입니다. 이 값을 일반 변수처럼 사용하면 됩
니다.
연...
하나는 나머지 값을 계산하기 위해서 사용합니다.
연산자 의미
+ 덧셈
- 뺄셈
* 곱하기
/ 나누기
% 나머지
나머지 연산자
사칙연산 연산자의 4가지(+,-,*,/) 연산은 너무나 일반적이라 더 이상 설명할 것이 없습니...
0~100이 되는데 그 결과값이 60이상이라면 100을 100%라고 본다면 무작위 수의 결과에
서 60%이상일 때 ActionKick이라는 동작을 한다라는 의미입니다. ActionKick()은 함수인데
그것은 해당 이야...
실제로 C/C++문법에서는 이 표현식은 유효합니다. 그러므로 결과는 nResult의 값에 따라
0이 아닐 경우에 참이 되어 로직이 실행됩니다. 즉 원래 의도했던것과는 전혀 다른 제어가
되고 더군다나 nResult값까지 ...
nAge = 1 + 1;
결과로써 nAge에는 2라는 숫자가 들어가게 됩니다. C#에는 증가 연산자와 감소 연산자를
제공하고 있는데 그것은 ++, --입니다. 이 연산은 해당 변수에 1의 단위로 피연산자의 값
을 +하거...
위의 코드는 CurrentAge에 2라는 값이 들어갑니다. 이 두 가지가 바로 후위형과 전위형의
차이입니다.
결론적으로는 메시지가 출력된 이후에 nAge의 값을 1증가 시킵니다. 하지만 두 가지 형태
는 증가가 일어나는...
비트 연산자
비트 연산자는 C언어에서 유래한 것입니다. 비트 연산자는 두 개의 변수를 비트 단위로
AND, OR, NOT연산을 수행하여 결과 값을 만들어 냅니다.
식 의미
& 비트 단위 And
| 비트 단위 Or
~ 1...
0 0 0 0 0 1 0 0
른쪽에서 왼쪽의 순서대로 비트 크기가 커져갑니다. 3번째 비트에 1이라는 값이 설정되어
연산은 AND게이트 연산을 합니다. AND진리표는 아래와 같습니다.
오
있습니다. 2의 2승은 4이기 ...
각의 비트 들을 AND 게이트 정의에 맞게 계산해 보면 그 결과 비트는 0이 됩니다. 그렇
연산은 OR 게이트 연산을 합니다. OR진리표는 다음과 같습니다.
각
기 때문에 nBitValue를 왼쪽으로 3번 쉬프트하고 A...
그 역의 비트열을 얻게 됩니다.
1 1 0 1 1 0 0 1
yte nAge = 38;
(byte)~nAge;
);
b
byte nReverseBit =
System.Console.WriteLine( nAge );
Sy...
System.Console.WriteLine( nA ^ nB );
필자는 지금 XOR게이트를 이용한 재미있는 기법을 쓰고 싶지만 현재는 문법을 다루고 있
>와 <<는 쉬프트 연산입니다. 현재 우리는 8개의 비트로 이루어...
a >= b 같다.a가 b보다 크거나
상등 연산자 설명
a == b a가 b보다 작다.
a != b a가 b보다 작거나 같다.8
이들 연산자는 주로 조건문에 나타납니다. 조건문에 대해서는 해당 설명에서 자세하게 이야
t...
byte nA = 66;
(nA == 66 && nB == 33)
System.Console.WriteLine("Action");
byte nB = 33;
if
{
}
위의 코드는 && 연산을 알아보기 위한 코드입니다....
위의 코드에서 bLoop는 false로 초기화 되었지만 bResult는 True값을 가집니다. 역을 취하
ool bAlive = true;
는 !연산자는 프로그래밍에서 매우 자주 쓰이는 연산입니다. 예를 들어서 살아 있...
사운드 엔진
물리 엔진
그래픽 엔진
인공지능 엔진
게임 시스템
<그림14>
하나의 게임을 만들기 위해서 이러한 여러 가지 미들웨어(Middleware)를 사용합니다. 이러
한 코드들을 사용할 때 문제점이 생겨납니다. 바...
Animation.Controller혹은 Physics.Controller와 같이 나뉠 수 있다면 더 좋은 코드를 만들
수 있습니다.
그래서 생겨난 것이 네임스페이스(Namespace)입니다. 프로그래머는 닷넷 프레임워...
System.Console.WriteLine(...);
WriteLine메소드는 System안에 존재하는 Console안에 있는 공용 메소드입니다.
[한마디]
네임스페이스를 만들 때는 함축적인 의미를 담은 것 보다는 S...
구조 의미
if…else 조건이 참이라면 실행문을 실행한다.
switch 조건에 따라 분기한다.
goto 무조건 분기한다.
if…else구문
if…else구문은 조건에 따라서 분기를 수행합니다. 표현식은 다음과 같습니다...
과 같은 코드 보다는 사실 다음과 같은 코드가 훨씬 알아보기 쉽습니다.(만일 독자가 처음
프로그래밍을 한다면 더더욱!)
만약 에너지가 10보다 크다면
{
// 암거나 해라
}
그렇지 않다면
{
// 위험하니 피해라!
}...
다음의 코드를 보면서 알아 보도록 하지요.
using System;
using System.Collections.Generic;
using System.Text;
namespace if_else
{
class Progr...
<그림15>
에너지가 2보다 작다는 것을 알 수 있습니다. 하지만 우리의 코드는 처음에 if조건문과 조
금 다릅니다. 바로 괄호로 채워져 있다는 것을 알 수 있습니다.
<그림16>
if의 조건문이 참일 경우에 실행되는 부분에서 {, }중괄호를 사용하였습니다. 만일 실행문이
하나가 아니라 둘일 경우에 다음과 같이 써보면 원하지 않은 결과를 얻습니다.
int nEnergyOfCharact...
<그림18>
확실히 원하지 않는 결과입니다. 이길 확률이 크다는건 2보다 작아야 하는데 조건에 해당하
지 않으면 그 내부의 실행문을 모두 괄호로 묶어 주어야 else구문에 의해서 실행이 되지 않
습니다. 이로써 중괄호의...
}
}
else
{
System.Console.WriteLine("nA는 100이 아닙니다.");
System.Console.WriteLine("nB가 어떻게 되던 관심이 없습니다.");
}
nA는 초기값 100으로 설...
switch문
switch문을 사용하면 변수 하나에 가능한 목록 값을 제공하고 그 값의 참 여부에 따라서
실행문을 실행할 수 있습니다.
using System;
using System.Collections.Generic...
System.Console.WriteLine("O형입니다.");
break;
}
}
}
}
사용법은 매우 간단합니다. 비주얼 베이직에서는 select case를 사용하지만 C#에서는
C/C++과 같이 switch 키워드...
[한마디]
예제로 나온 코드는 독자가 이후로 프로그래밍을 하게되면서 다시는 보지 못할 최악의 코
드입니다!!! 독자의 기억력이 좋다면 열거 상수를 통해서 더 좋은 코드로 변경할 수 있을거
라고 생각합니다.
goto문
g...
}
else
{
System.Console.WriteLine("이 코드는 실행이 될 수 없습니다.");
System.Console.WriteLine("nA는 100이 아닙니다.");
}
}
}
}
nA는 초기 100으로...
using System.Text;
namespace firstprogramwithvs
{
class Program
{
enum Foot
{
LEFT,
RIGHT
};
static void Main(string[] arg...
while문
while문은 “조건이 참인 동안 반복”하는 것입니다.
기본적인 문법은 다음과 같습니다.
while ( 표현식 ) 문장
using System;
using System.Collections.Generic;
...
참일 때만 문장이 실행되기 때문에 i의 초기값이 100이라면 while문은 실행되지 않습니다.
int i = 100;
while (i < 100)
{
System.Console.WriteLine("현재 나이 : " + ...
습니다. 하지만 거대하고 수십만 라인이 되는 코드, 혹은 제 3자가 만들어 놓은 라이브러리
에서 변수의 의미가 반대라면 다른 프로그래머가 해당 코드, 라이브러리를 사용할 때 오해
를 하게 됩니다. 그래서 위와 같은 코드...
namespace do_while
{
class Program
{
static void Main(string[] args)
{
int i = 0;
do
{
System.Console.WriteLine(i + "번째");...
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
System.Console.WriteLine(i + "번째 반복을 수행...
}
}
}
이 코드에서는 기존에 보았던 코드와 다른 형식으로 화면에 문자열을 출력해 보았습니다.
우선 프로그램의 원리는 간단합니다. nDan이라는 변수에 출력할 단을 넣어주고 for반복문을
이용해서 1부터 9까지 반복하...
}
}
}
이 코드에서는 Directory클래스와 DirectoryInfo클래스 그리고 FileInfo클래스를 이용하였
습니다. 이것은 System.IO네임스페이스에 존재하기 때문에 using System.IO를 반드시...
using System.IO;
namespace foreach_code
{
class Program
{
static void Main(string[] args)
{
DirectoryInfo dir = new Direct...
클래스를 만드는 방법
클래스를 만들기 위해서는 먼저 클래스명을 적고 클래스가 가져야 할 메소드와 멤버 변수를
적어 줍니다. 다음은 클래스 선언의 완전한 문법입니다.
[속성] [접근제어자] class 식별자 [:기본 클래...
public void PrintLevel()
{
System.Console.WriteLine("레벨 : " + Level);
}
public void PrintEnergy()
{
System.Console.WriteLi...
public class Character
{
public string Name;
public int Level;
public int Energy;
public void PrintName()
{
System.Console...
Knight.Energy = 10;
Wizard.Name = "마법사";
Wizard.Level = 2;
Wizard.Energy = 5;
Knight.PrintAllInformation();
Wizard.PrintAl...
클래스 멤버
우리는 간단하게나마 클래스를 만드는 방법을 배웠고 클래스로부터 객체를 생성하는 방법도
배웠습니다. 앞서 우리는 데이터 타입으로 만들 수 있는 변수를 배웠는데 이러한 변수들이
클래스 내부에 있을 때는 다른 용...
internal internal로 설정된 것은 코드가 놓이는 위치
에 따라서 public이나 protected로 선택됩니
다.
이제 다음과 같은 코드가 어떻게 해서 가능했는지 알 수 있습니다.
Character Knig...
public void SetEnergy(int energy_)
{
Energy = energy_;
}
우리는 클래스 외부에서 멤버 변수를 접근하는 것을 허용하지 않기 때문에 위의 코드와 같
이 Name, Level, E...
}
public int GetEnergy()
{
return Energy;
}
처음에 우리가 디자인했던 클래스와 접근제어자를 수정해서 관련 메소드들을 만든것과 크기
면에서 차이가 많이 납니다. 사용법도 꽤 다르고 말이죠...
5명의 케릭터를 만들어 보았고 이제 이름, 레벨, 에너지를 설정해야 합니다.
static void Main(string[] args)
{
Character Knight = new Character();
Character...
우리는 우리가 관심 있는 기사와 마법사를 제외한 모든 사람들은 에너지를 100으로 통일하
려고 합니다. 우리는 변수를 선언할 때 초기화 값을 정할 수 있는 것을 알고 있습니다.
int Level = 10;
클래스에 선언...
}
위의 생성자를 이용해서 객체를 생성하려면 Main함수에서 객체를 생성할 때 다음과 같이
해야 합니다.
Character Knight = new Character("기사",1,10);
Character Wizard =...
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
You’ve finished this document.
Download and read it offline.
Upcoming SlideShare
유니티로 해보는 게임 프로토타이핑
Next
Upcoming SlideShare
유니티로 해보는 게임 프로토타이핑
Next
Download to read offline and view in fullscreen.

8

Share

게임프로그래머에게 배우는 C#1권(버전1)

Download to read offline

게임 프로그래밍으로 배우는 C# 1권 (버전1) 입니다.
오래전에 집필했던 책인데 오프라인 책으로 출간하지 못한 책이었습니다. C#버전도 오래된 버전이지만 지금이라도 도움이 되시는 분들이 계실까봐 올려 봅니다.

Related Audiobooks

Free with a 30 day trial from Scribd

See all

게임프로그래머에게 배우는 C#1권(버전1)

  1. 1. 게임 프로그래밍으로 배우는 C# 1권 2007-01-27 문기영 소개 이 책은 새로운 프로그래밍 언어 C#을 게임 프로그래밍으로 배우는 책입니다. 게임 프로그 래밍은 프로그래밍의 꽃이라 할 정도로 매우 다양한 프로그래밍 기술을 필요로 합니다. 새 로운 언어를 공부할 때 문법을 공부하고 그것으로 프로그램을 만들게 되는데 실제로 문법을 익힌 후 프로그램을 만들어 내는 것은 매우 추상적이거나 흥미가 없게 마련입니다. 게임은 누구나 흥미롭게 접근할 수 있고 특히나 게임 프로그래밍은 다양한 프로그래밍 테크닉을 필 요로 하기 때문에 이러한 것들을 C#언어로 구현하는 방법을 습득하는 것은 C#언어를 보다 더 자세하게 알 수 있는 계기가 됩니다. 그래서 이 책을 저술하게 되었습니다. 필자가 생각하는 독자들 필자는 이 책의 독자를 두 부류로 생각하고 있습니다. 첫째: C#문법은 어렴풋이 알고 있으나 실제로 어떠한 프로그램을 만들어 보아야 하는지 모 르는 독자. 둘째: 게임 프로그래밍에 대한 기술들을 조금 더 쉽게 접근하고자 하는 독자. 소개 글에서 말씀 드린 바와 같이 문법을 알고 있다고 해서 프로그램을 바로 만들 수 있는 것은 아닙니다. 분명한 것은 프로그램을 만들어 봄으로써 문법을 더욱 더 확실하게 다지고 프로그램을 어떻게 만들 수 있는지 알 수 있다는 것입니다. 일반 응용프로그램을 만들어 내 는 것이 게임 프로그램보다 흥미가 떨어진다는 점을 생각하면(지극히 주관적이지만) 이 책 을 통해서 게임을 만들어 보고 그것을 통해서 C#이라는 언어에 대해서 좀더 세부적인 지식 과 게임 프로그래밍에 대한 기술들을 알아보는 것은 괜찮은 언어 학습법이라고 봅니다. 이 책의 구성 필자는 초기에 이 책을 1권의 책으로 저술을 하고 있었으나 내용의 방대함과 필자의 피치 못할 사정으로 인하여 2권으로 나누어서 저술하게 되었습니다. 1권의 내용은 C#문법, 윈도우 프로그래밍, 그래픽 프로그래밍으로 이루어져 있으며 2권의 경우 DirectX를 이용한 2D게임 프로그래밍, 3D게임 프로그래밍, 툴 프로그래밍, 그리고 제 가 관심있게 공부한 내용들을 다루려고 합니다.
  2. 2. 1권에서는 DirectX를 이용한 그래픽 프로그래밍은 다루지 않지만 1권에서 다루는 그래픽 프로그래밍의 내용으로 충분히 게임을 만들 수 있으며 그것은 단지 도구의 선택 문제에 있 습니다. 1권에서는 Flight라는 총알 피하기 게임과 RopePong 그리고 BMS Player를 제작 하였습니다. BMS Player는 비트매니아 혹은 Ez2DJ와 같은 리듬 게임을 C#으로 구현한 예 제입니다. C#, VisualBasic, C++ C#은 2000년 7월 닷넷 플랫폼과 함께 새로운 프로그래밍 언어로 발표된 언어입니다. 본질 적으로 닷넷 플랫폼은 윈도우 2000을 포함하는 기존 운영체제에서 제공하는 서비스들과 새 로운 API를 제공하는 개발 프레임워크입니다. 닷넷 프레임워크에서 중요한 전제사항은 “모 든 프로그래밍 언어는 (최대한)동등하게 취급하자”라는 것입니다. 닷넷 프레임워크를 구성하 는 요소들은 다음과 같습니다. C#, VB.NET, Managed C++, Jscript.NET의 4개의 공식 프로그래밍 언어 CLR(Common Language Runtime) : 모든 개발 언어가 공통으로 사용하는 윈도우 및 웹 개 발을 위한 객체중심 플랫폼. FCL(Framework Class Library) : 다양한 관련 클래스 라이브러리들 필자는 1999년에 비주얼 베이직6.0 게임 프로그래밍에 관한 책을 저술한 바 있습니다. 비 주얼 베이직은 매우 쉽고 강력했습니다. .Net플랫폼으로 오면서 비주얼베이직은 비주얼베이 직 답지 않다. 라는 인상이 매우 강했고 실제로 매우 많은 비주얼 베이직 프로그래머로부터 현재 외면을 받고 있습니다. 그도 그럴 것이 비주얼 베이직만의 장점이 사라졌다고 볼 수 있기 때문입니다. 그리고 C++역시 .Net플랫폼으로 오면서 Managed C++이 되었는데 이 로써 C계열 언어만의 장점이 사라졌다고 보여집니다. 실제로 매우 많은 게임 프로그래머들 은 하드코어 프로그래밍을 좋아하고 프로그래밍 할 때 매우 저 수준의 코드를 작성하기 때 문입니다. 그런 게임 프로그래머들에게 Managed는 별로 달가워하지 않게 되었습니다. C++이 C++답지 않다면, 비주얼 베이직이 비주얼 베이직답지 않다면 닷넷 플랫폼에 걸맞 는 C#을 배우는 것이 낫다는 결론에 이릅니다. 그래서 많은 프로그래머들이 .Net 플랫폼에 서 C#을 택하고 있는 것입니다. C#컴파일러와 .NET그리고 DirectX C#문법을 배우기 이전에 C#코드를 작성하고 실행시켜야 할 환경을 구성해야 합니다. 대부 분의 사람들은 비주얼스튜디오로 시작할 것이라고 생각합니다. 현재 필자의 경우는 비주얼 스튜디오 2005를 사용하고 있고 .NET 2.0을 지원합니다. 이 책에서 제공하고 있는 모든 소
  3. 3. 스 코드를 돌려보기 위해서는 비주얼 스튜디오 2005와 닷넷 2.0이 설치 되어야 합니다. DirectX DirectX는 Microsoft가 만든 멀티미디어 도구입니다. 도스 시절에는 비디오 메모리의 접근 에 아무런 문제점이 없었기 때문에 프로그래머가 마음만 먹으면 화면을 이리 저리 조작할 수 있었습니다. 도스 시절에 정말 큰 문제점은 그래픽 카드의 제조사가 다르면 그 구현 방 법이 달라지기 때문에 그래픽 카드에 맞게 비디오 관련 코드를 모두 작성해 주었어야 했습 니다. 프로그래머들에게 이것은 매우 어려운 작업이었는데 윈도우로 OS가 옮겨감에 따라 Microsoft는 비디오를 포함한 멀티미디어에 관련된 표준 인터페이스 DirectX를 만든 것입 니다. DirectX를 이용하면 그래픽 카드의 구현에 상관없이 하나의 코드만 작성하면 어느 하 드웨어에서도 올바르게 동작합니다. 게임 프로그래머는 화면에 매우 많은 그래픽 데이터를 그려주어야 하고 동시에 많은 사운드 도 들려주어야 합니다. 물론 사용자로부터 입력까지 말입니다. 게임 프로그램은 종합 멀티 미디어 프로그램이기 때문에 DirectX가 거의 필수로 사용됩니다. 하지만 1권에서는 DirectSound만을 이용하여 게임을 제작할 것입니다. 디자인 패턴 프로그램을 만들 때 경험 있는 프로그래머들은 만들어낼 프로그램, 사용하는 언어에 따라 주로 사용하는 어떠한 개발 패턴들이 존재합니다. 디자인 패턴이라는 것이 나오기 전까지는 그저 경험 있는 프로그래머들의 머릿속에 있는 것이었지만 GoF의 디자인 패턴이 책으로 나 오게 됨으로써 여러 프로그래머들이 배울 수 있는 학문이 되었습니다. 필자는 이 책을 집필 할 때 디자인 패턴에 대한 이야기를 하려고 생각조차 못하고 있었지만 윈도우즈 프로그래밍 에 관련된 글을 쓰다가 디자인 패턴에 대한 이야기를 하지 않을 수 없어서 몇 가지 패턴에 대해서 다루어 보았습니다. 이 책을 읽기 위해서 디자인 패턴을 꼭 알아야 할 필요는 없습 니다. 필자는 최대한 딱딱하지 않게 실제 디자인 패턴을 어떻게 사용하는지 핵심만 꼬집어 서 이야기 하였습니다. 아무래도 패턴이라 함은 실제로 프로그램을 만들면서 느껴보고 작성 해보아야 알 수 있는 것이니까요. UML UML은 프로그래머라면 한번은 들어볼만큼 매우 유명한 모델링 언어입니다. UML의 용도는 스케치, 청사진, 프로그래밍 언어로써의 용도가 있는데 필자는 스케치 용도로 UML을 이용 하여 프로그램을 이해하는데 도움이 되도록 하였습니다. 한마디 필자의 작은 팁이나 이야기
  4. 4. 부록 실제로 이 책을 쓰면서 참고했던 모든 관련 자료들은 부록에서 다루고 있습니다. 게임 프로 그래밍에 사용되는 특별한 테크닉 들이 존재하는데 이러한 것들은 모두 부록에서 다루도록 하였고 책을 읽는 도중에 참고해야 합니다. 참고문헌 필자가 이 글을 쓰면서 참고했던 문헌들을 모아놓았습니다. 참고문헌은 번호로 간단히 표기 하였고 글 혹은 각주로써 참고할 문헌들을 제시하였습니다. 감사의 말 이 책이 완성되기 까지 많은 분들이 도와주셨습니다. www.devpia.com에 여러 질문을 올려 주신 분들에게 진심으로 감사 합니다. 필자가 질문을 해결하면서 좋은 예제들을 어떻게 만 들어야 하는지 알 수 있었습니다. 그리고 Microsoft의 Visual C#을 개발하신 장희제님께 감 사 드립니다. 필자가 Visual C#을 사용할 때 모르는 부분에 있어서 물심양면으로 많은 도움 을 주셨습니다. 한국 프로그래머들의 모임 장소인 www.gpgstudy.com의 모든 회원 분들에 게 감사 드립니다. Gpgstudy는 매우 많은 질문과 알찬 답변들로 하여금 필자의 생활에 활 력소가 되었고 많은 분들의 생각을 읽어낼 수 있었습니다. www.dexgame.com의 주인장님 께도 감사드립니다. 필자가 만든 프로그램들을 거의 항상 실행해서 테스트 해주셨고 필자가 프로그램을 올릴 수 있는 공간도 마련해 주셨습니다. 참고문헌에 나와 있는 모든 저자분들 에게 감사드립니다. 이 책은 저 혼자 작성한 것이 아닙니다. 그분들의 훌륭한 서적으로 인 해서 배우고 지금의 필자가 있게 하였습니다. 교보문고, 영풍문고, 반디앤루니스의 사장님들 께도 감사드립니다. 그분들이 아니였다면 제가 책을 사서 보지 못했을 테니까요. 마지막으 로 필자를 끝까지 믿어주시고 항상 버팀목이 되어 주시는 어머니, 누나, 매형, 여자친구에게 진심으로 감사합나다. 1장. 기초학습 가장 간단한 C#프로그램 프로그래밍 언어를 학습하는 것은 영어나 일본어를 배우는것과 다를바가 없습니다. 아니 새
  5. 5. 로운 것을 배우는 것이라고 정의해도 될 것 같습니다. 필자의 학습법은 무조건 일단 돌아가 는 것을 눈으로 보거나 체험해 본 이후에야 무언가를 알 정도로 똑똑하지 않기 때문에 이 책에서도 똑같이 적용하였습니다. 일단 만들어 보자 필자는 독자의 컴퓨터에 VisualStudio 2005와 더불어 닷넷 2.0이 설치가 되어 있다는 가정 을 하겠습니다. 우선 메모장에 다음과 같은 내용을 쳐봅시다. class FirstCSharpProgram { public static void Main() { System.Console.WriteLine("안녕~ C#"); } } 이 코드를 작성하고 firstintro.cs라고 저장합니다. 사실 확장자는 중요하지 않은데 C#소스 코드는 일반적으로 확장자를 cs로 하며 비주얼베이직은 vb, C는 c, C++은 cpp와 같이 사 용합니다. 이제 이렇게 작성한 코드를 실행할 수 있는 파일로 만들어야 하는데 그렇게 하기 위해서 컴 파일러가 필요로 합니다. C#컴파일러는 csc.exe인데 도스 창에서 실행해보면
  6. 6. <그림1> 그림1과 같이 csc 실행파일을 찾을 수 없다고 나오게 됩니다. 만일 이 부분을 해결할 수 없 다면 [부록 : 환경변수 등록]을 참고 하시길 바랍니다. 자, 이제 우리는 csc를 이용해서 컴파일을 할 수 있게 되었습니다. 다음과 같이 커맨드를 써봅시다. csc firstprogram.cs 만일 코드에 아무런 오타가 없다면 다음과 같이 exe파일이 생성된 것을 보실 수 있습니다.
  7. 7. <그림2> 이제 도스 커맨드에서 firstprogram.exe를 실행해 보면 화면에 우리가 작성했던 문장이 나 오는걸 보실 수 있습니다. <그림3> VisualStudio 2005를 이용한 C#프로그램 비주얼스튜디오는 프로그래밍을 개발하기 위한 통합 환경입니다. 앞서 아무런 도구 없이 컴 파일러와 메모장만으로 프로그램을 작성해 보았는데 사실 프로그램을 만드는데는 코드와 컴 파일러 그리고 데이터만 있으면 됩니다. 하지만 이미 보셨듯이 도스 커맨드를 이용하여 컴 파일을 하기 위해서는 해야할 것들이 상당히 많습니다. 특히 대규모 프로그램의 경우에 파
  8. 8. 일이 1만 개가 넘는 경우가 허다한데 그걸 전부 도스 커맨드로 치고 있다거나 배치 파일로 만들어서 실행하기에는 잔작업이 너무 많습니다. 그리고 메모장의 경우에 C#에서 제공하는 키워드를 다른 색상으로 구분해 주거나 하는 기능이 전혀 없는데 그도 그럴것이 메모장은 파일형식에는 관심이 없고 단지 내용을 보여주기만 하기 때문입니다. 통합 환경은 프로그래머의 잔작업을 덜어주고 여러가지 강력한 기능들을 모두 사용할 수 있 으므로 메모장으로 프로그램을 만드는 것 보다는 비주얼 스튜디오를 이용하는 것이 엄청난 이득입니다. 필자는 실제 현업에서도 비주얼스튜디오를 사용하고 이 책을 쓰는데도 비주얼 스튜디오를 이용하였습니다. 아마 독자분도 비주얼스튜디오를 이용하게 될 것입니다. ☺ 자, 이제 앞서 만들었던 코드를 비주얼스튜디오를 이용해서 만들어 보기로 합시다. 비주얼스튜디오 실행 <그림4> 비주얼 스튜디오를 처음 실행하면 그림4와 같은 화면을 보실 수 있습니다.
  9. 9. <그림5> 그림5와 같이 새로 만들기를 하여 프로젝트를 생성하도록 합시다.
  10. 10. <그림6> 새로 만들기를 시도하면 그림5과 같이 프로젝트 형식을 고를 수 있습니다. 비주얼스튜디오 는 C#뿐만 아니라 다른 언어 VisualBasic, J#, C++모두 똑 같은 도구를 사용하여 프로그 램을 제작할 수 있게 합니다. 우리의 관심사는 C#이므로 C#으로 프로젝트 형식을 선택합니 다. <그림7>
  11. 11. 그림7은 그림6의 우측 메뉴의 확대 부분인데 기본적으로 제공하는 템플릿들이 존재합니다. 주로 사용하는 것은 Windows 응용 프로그램이나 콘솔 응용 프로그램입니다. 필자는 여기 에서 콘솔 응용 프로그램을 선택하였습니다. 그리고 확인을 클릭하면 다음과 같은 C#코드 가 만들어 집니다. using System; using System.Collections.Generic; using System.Text; namespace firstprogramwithvs { class Program { static void Main(string[] args) { } } } 여기까지 우리가 해준 코드 작업은 아무것도 없습니다. 현재 작성된 코드는 단지 템플릿 코 드입니다. 이런 템플릿 종류는 앞서 본것과 같이 매우 다양한데 그 이유는 만들어낼 프로그 램에 따라서 처음에 기본적인 외형은 모두 똑같기 때문입니다. 만일 템플릿이 존재하지 않 았다면 프로그래머가 처음부터 이런 코드를 항상 작성해 주어야겠지요. 그리고 더불어서 이 런 기본적인 골격을 외우기도 했습니다. 필자의 경우에도 DirectX를 처음 배울 때, Windows 프로그래밍을 처음 배울 때 골격 코드를 외우기도 했습니다. 문제는 이런 골격 코드는 거의 변하지 않는다는 점이고 저희가 만들어낼 프로그램의 핵심 부분이 결코 아닙니 다. 그렇기 때문에 이런 부분은 항상 템플릿 코드를 이용해 주는 것이 정신건강에 좋습니다. ☺ 코드 작성 자, 이제 앞서 문장을 출력하기 위해서 사용했던 코드를 그대로 사용해 보겠습니다. System.Console.WriteLine("안녕~ C#");
  12. 12. 이 코드를 어디에 두어야 하느냐가 관건인데 처음 언어를 배울 때는 무작정 그대로 따라배 우는 방법밖에 없습니다. using System; using System.Collections.Generic; using System.Text; namespace firstprogramwithvs { class Program { static void Main(string[] args) { System.Console.WriteLine("안녕~ C#"); } } } 필자는 이미 알고 있는 문제이니 Main함수 안에 넣었습니다. 이제 코드도 다 작성했고 실 행파일로 만들어야 하는데 그 방법이 다릅니다. 이전에는 도스 커맨드에서 csc파일을 이용 해서 직접 프로그래머가 실행해 주었는데 이 부분은 자동화가 가능합니다. 여기서 먼저 [부록 : 비주얼 스튜디오 2005 키보드 설정]을 참고하셔서 필자와 똑같은 단 축키 설정으로 하시는 것이 좋을 것 같습니다. 자, 부록을 읽으셨다고 가정하고 바로 F7을 눌러 보도록 하지요.
  13. 13. <그림8> 빌드했습니다. 라는 말은 말 그대로 빌드를 했다는 말입니다. 즉 컴파일이 완료 되어서 실 행 파일이 나왔다는 의미입니다. C, C++프로그래머의 경우에는 컴파일과 링크 과정을 분류 해서 설명하는데 근래에는 대부분 빌드라는 말 하나로 사용합니다. 즉, 코드를 컴퓨터가 이 해하는 기계어로 바꾼 후에 목적어 코드를 생성하고 하나의 exe파일을 만들어 내는 모든 과정이 이 ‘빌드’라는 말 하나로 압축이 됩니다. C#이나 비주얼베이직 모두 ‘빌드’만을 사용 하기 때문에 앞으로도 필자가 빌드라고 말하면 실행 파일을 얻는다. 와 같은 의미입니다. 이제 프로그램을 실행해야 하는데 이 방법에는 2가지 방법이 존재합니다. F5와 Ctrl + F5입니다. F5로 실행하면 실행 프로그램에 디버거가 붙어서 실행이 되는 경우이고 Ctrl + F5는 디버 거를 붙이지 않고 실행 프로그램을 실행한다는 의미입니다. [부록 : 디버거] 콘솔 응용 프로그램의 경우에 실행을 하면 바로 프로그램이 종료가 되어서 우리가 작성했던 문장이 제대로 화면에 나타났는지 확인할 수 없기 때문에 콘솔 응용 프로그램의 경우에는 프로그램의 마지막에 잠시 응용 프로그램을 멈추는 코드를 작성하거나 Ctrl + F5로 실행하 여 그 결과를 확인해 볼 수 있습니다. 우선 여기에서는 Ctrl + F5로 실행을 해보도록 합시 다.
  14. 14. <그림9> 프로그램을 실행하면 이전에 우리가 작성했던 문장이 화면에 나타나는 것을 보실 수 있습니 다. 이 프로그램은 비주얼스튜디오를 이용하거나 단순히 메모장을 이용해서 C# 컴파일러로 직접 컴파일하거나 결과는 동일함을 알 수 있습니다. 즉, 프로그램을 만드는데는 비주얼스 튜디오는 필수는 아니더라도 직접 경험해본바 좋은 도구를 이용하는 것이 생산성이나 가독 성인 면에서 엄청나게 많은 이득이 있다는 것을 몸소 체험하셨을 거라고 생각합니다. 필자는 앞으로 만들어낼 모든 프로그램을 비주얼 스튜디오를 이용해서 작성하였습니다. 메 모장을 이용해서 만들어 내는것도 문제는 없지만 필자는 하드코어 해커는 아닙니다. ☺ 자~ 시작이 반이라고 했습니다. C#프로그램을 콘솔단에서 만들어도 보았고 윈도우 도구를 사용해서도 만들어 보았습니다. 프로그램을 처음 하시는 분들이라면 명령어도 바꾸어 보고 개수도 늘려 보시면서 재미난 것들을 하실 수 있을 거라고 봅니다. 시작이 반이라는 속담도 있지만 반은 아직 끝은 아니란 것을 염두에 두시면서 계속 공부해 보도록 합시다.
  15. 15. 2장. C#언어 문법 데이터 타입 C#에는 내장 타입과 사용자 정의 타입이 존재합니다. 객체중심에서 사용자 정의 타입은 클 래스라고 합니다. 사용자 정의 타입은 말 그대로 프로그래머가 타입을 새롭게 정의할 수 있 는 것을 뜻합니다. 우선 현재는 클래스를 배우지 않았으므로 타입이 무엇인지에 대해서 알 아보고 내장 타입에 대해서 알아보도록 하겠습니다. 타입이란 무엇인가 컴퓨터 프로그램은 프로그래머가 입력한 명령어 대로 순서대로 명령어를 실행하는 역할밖에 하지 않습니다. 명령의 종류에는 상당히 많은데 기본적으로 조건 분기가 있습니다. 예를 들 어 현재 아군 플레이어의 동료 인공지능을 작성할 때 적군의 에너지가 내 에너지보다 더 많다면 도망간다. 우리의 관심사는 타입이므로 결국 에너지에 대해서 어떠한 형을 가지고 있느냐가 관건입니 다. 에너지가 정수인지, 실수인지, 문자인지가 중요한 이유는 바로 컴퓨터에서 사용되는 모 든 데이터는 메모리 양에 민감하게 반응하기 때문입니다. 예를 들어서 나이를 표현할 때 1,2,3살 과 같이 표현하지 3.2살 3.23살 이렇게 표현하지 않 습니다. 그러므로 나이라는 데이터를 컴퓨터에 저장하기 위해서는 실수보다는 정수형으로 저장하는 것이 메모리를 더 아낄 수 있는 것입니다. 데이터 타입을 선언할 때는 fit하다. 라 는 표현을 쓰는데 데이터 타입에 대해 데이터가 fit할 때 타입을 잘 결정했다고 말할 수 있 습니다. 예를 들어서 실수(double)형은 8byte를 차지하고 정수(int32)형은 4byte를 차지합니다. 그 리고 double은 정수형을 포함하고 있습니다. 그렇다고 모든 데이터에 대한 타입을 double 로 하면 메모리를 상당히 낭비하는 경우가 있을 수 있습니다. 예를 들어서 다음과 같이 축구 선수 데이터가 있습니다. 키 나이 체중 스피드 8byte 8byte 8byte 8byte 4byte 4byte 4byte 4byte
  16. 16. 사실 8byte를 사용해서 모든 데이터 타입을 사용해도 32byte밖에 안되고 4byte로 사용하 면 16byte밖에 되지 않습니다. 문제는 이 선수들이 몇 명이냐에 따라서 결과가 엄청나게 달라집니다. 선수가 15000명이라면 전자는 480kb를 사용하고 후자는 240kb를 사용합니다. 실제 게임 에서 사용하는 데이터는 필드가 단지 4개만 있지는 않습니다. 결국 엄청나게 많은 필드들이 사용되고 캐릭터 개수를 모두 곱하거나 주변 환경 데이터까지 고려하면 타입 하나 설정하는 것이 엄청나게 중요하다는 사실을 알 수 있습니다. 필자의 경험으로 480kb 역시 pc사용자 들은 코웃음이 나올 정도로 작은 양이지만 모바일 게임이 대략 3~500kb인 것을 감안하면 (그 안에 그래픽, 게임로직, 사운드) 결코 간단한 문제가 아닙니다.1 하지만! 프로그래밍 언어를 처음 배우는 분들에게 이런 모든 것들을 바라기에는 무리가 있 습니다. 왜냐하면 필자도 그랬고 지금 당장 언어를 배우는거 자체가 우선 순위가 가장 높기 때문이지요 ☺ 마지막으로 표를 통해서 C#에서 제공하는 타입에 대해서 알아보도록 합시다. CLR C++ C# SByte char sbyte Byte unsigned char byte Int16 short short UInt16 unsigned short ushort Int32 int int UInt32 unsigned int uint Single float float Double double double Boolean bool bool Char wchar_t char String String string Object Object object C++을 배워본 독자라면 C#이 C++과 매우 흡사함을 알 수 있습니다. C#에서는 당연히 1 필자가 만든 핀볼 게임도 용량이 3~400kb 사이였습니다.
  17. 17. CLR타입으로도 프로그래밍이 가능합니다.2 필자는 CLR타입으로 코드를 작성하는 것이 손 에 익숙하지 않아서 C#타입으로 모든 코드를 작성했습니다. 내장 타입 선택 C#의 내장 타입 목록은 다음과 같습니다. 닷넷 타입 크기(바이트) 설명 byte 1 부호 없는 8비트(0~255) char 2 유니코드 문자 bool 1 참 또는 거짓 sbyte 1 부호 있는 8비트(-128~127) short 2 부호 있는 16비트(-32768~32767) ushort 2 부호 없는 16비트(0~65535) int 4 부호 있는 32비트(- 2,147,483,648~2,147,483,647) uint 4 부호 없는 32비트(0~4,294,967,295) float 4 약 +/- 1.5 * 10-45승~ +/- 3.4 * 10의38승까지의 값을 유효숫자 7자리로 갖는 부동 소수점 수 double 8 약 +/- 5.0 * 10-324승~ +/- 1.7 * 10우ㅏ 308승 까지의 값을 유효숫자 15~16개의 자릿수 타입이란 무엇인가?에서 이미 알아보았듯이 타입 크기를 정하는 것은 메모리의 크기를 프로 그래머가 결정하는 것을 의미합니다. 예를 들어서 int Data; Data라는 변수는 부호 있는 32비트로 메모리를 차지하고 타입에서 제공할 수 있는 크기만 큼 숫자 영역을 사용할 수 있습니다. 타입의 변환 타입을 이용해서 데이터를 설정하고 이러한 데이터를 코드 로직을 통해서 컴퓨터에게 명령 을 내리거나 계산을 수행할 수 있습니다. 그런데 이렇게 데이터를 사용하다보면 언젠가는 2 닷넷 플랫폼을 지원하는 모든 언어가 가능합니다.
  18. 18. 타입 변환을 필요로 하는데 예를 들어서 자릿수를 자르고 싶을 경우 float PI = 3.141593F; int PI_Int = (int)PI; System.Console.WriteLine("PI = " + PI); System.Console.WriteLine("PI_Int = " + PI_Int); 위와 같이 가능합니다. 즉, PI_Int는 소수점이 모두 잘려나가 3이라는 값만을 가집니다. 이 것을 타입 캐스트, 타입 변환이라고 합니다. 대부분의 다른 언어도 마찬가지로 작은 데이터 타입에서 큰 데이터 타입으로의 직접 대입은 유효합니다. 예를 들어 다음의 코드 float PI = 3.141593F; int PI_Int = 5; PI = PI_Int; 의 경우는 아무런 문제 없이 실행되지만 float PI = 3.141593F; int PI_Int = 5; PI_Int = PI; 의 경우에는 다음과 같은 오류 메시지를 나타냅니다. <그림10> float에서 int로의 암묵적 타입 변환은 C#에서는 오류로 취급하고 있습니다. 왜냐하면 float 값은 3을 포함할 수 있지만 int값은 3.14를 포함할 수 없으므로 자릿수가 잘려나갈 위험성 이 있기 때문입니다. 프로그래밍을 처음 배울 때 대부분 이 타입 변환으로 인한 위험성을
  19. 19. 간과하기 쉽습니다. 왜냐하면 3.14에서 자릿수 0.14가 잘려나간다고 해도 크게 문제될 것 없이 돌아가는 프로그램도 있기 때문입니다. 하지만 해당 로직이 게이머 3천명이 접속되어 있는 서버에 있다거나, 이미 제품화되어서 패치할 수도 없는 패키지 게임이라면 이 별 것 아닌 것으로 인해서 눈물을 쏟는 경험을 하게 될지도 모릅니다. C++의 경우 경고 레벨에 따라서 이러한 경고를 오류로 분류하지만 C#은 기본적으로 오류 로 분류합니다. 만일 굳이 변환해야 한다면 명시적인 타입 변환을 해주어야 합니다. 숫자와 문자열 사이의 변환 필자가 느끼는 C#의 강력함은 기본적으로 제공하는 것들이 상당히 많다는 것입니다. 가장 최근에 나온 언어가 가장 큰 이유이기도 하지만 그만큼 많은 부분들에 있어서 편리한 부분 이 많습니다. 예를 들어서 정수를 문자열로 변환하기 위해서는 다음과 같은 코드로 해결이 가능합니다. string strText = "텍스트"; int Data = 1234; strText += Data.ToString(); System.Console.WriteLine(strText); 아마 이 코드를 보고 C++프로그래머들은 “앗! 이건 마법이다!”라고 할지도 모르겠습니다. 필자가 보기에도 충분히 마법과 같아 보이지만 익숙해지면 정말 편리한 마법이라 고마워 할 지도 모르겠습니다. ☺ C#에서 제공되는 모든 타입들은 모두 객체입니다. 심지어는 내장 타입도 객체입니다. Int의 내부를 들여다 보면 다음과 같이 구현이 되어 있습니다. using System.Globalization; using System.Runtime.InteropServices; namespace System { // 요약: // 부호 있는 32비트 정수를 나타냅니다. [Serializable] [ComVisible(true)]
  20. 20. public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int> { public const int MaxValue = 2147483647; public const int MinValue = -2147483648; public int CompareTo(int value); public int CompareTo(object value); public bool Equals(int obj); public override bool Equals(object obj); public override int GetHashCode(); public TypeCode GetTypeCode(); public static int Parse(string s); public static int Parse(string s, IFormatProvider provider); public static int Parse(string s, NumberStyles style); public static int Parse(string s, NumberStyles style, IFormatProvider provider); public override string ToString(); public string ToString(IFormatProvider provider); public string ToString(string format); public string ToString(string format, IFormatProvider provider); public static bool TryParse(string s, out int result); public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out int result); } } 그러한 이유로 Data.ToString()과 같은 마법도 부릴 수도 있는 것입니다. 사실 알고 보면 마법도 아닌것이지요. ☺ 모든 객체에 ToString 메소드가 제공됩니다. 그 결과로 문자열 객체를 만들어 냅니다. 혹은 Convert객체를 이용해도 가능합니다. string strText = "텍스트"; int Data = 1234; strText += Convert.ToString(Data); System.Console.WriteLine(strText);
  21. 21. Convert는 static이기 때문에 모든 영역에서 사용이 가능합니다. using System.Runtime.InteropServices; namespace System { // 요약: // 기본 데이터 형식을 다른 데이터 형식으로 변환합니다. public static class Convert { // 생략 } } 문자열을 숫자로 변환하는 것 역시 간단하게 해결이 가능합니다. string strData = "1234"; int intData = Convert.ToInt32(strData); 그렇다면 다음과 같은 생각을 해볼 수 있습니다. “만일 strData가 1234가 아니라 123AAA4라면?” C#컴파일러가 컴파일 과정에서 이러한 코드를 찾아낼 수도 있지만 필자는 그 정도까지 바 라진 않습니다. ☺ 그렇다면 런타임에서 문제가 생긴다는 것인데 실제로 실행해 보면 예외가 일어납니다.
  22. 22. <그림11> 결국, 입력 문자열 형식이 잘못되었다는 예외가 발생하고 프로그래머가 Convert의 ToInt32 메소드를 호출하기 이전에 strData가 숫자형인지 검사를 하거나 다음과 같이 예외를 사용해 서 문제를 해결해야 합니다. try { string strData = "123AAA4"; int intData = Convert.ToInt32(strData); } catch (Exception e) { System.Console.WriteLine(e.Message); } 프로그램을 실행하면 콘솔에 다음과 같이 메시지가 나타납니다.
  23. 23. <그림12> 예외 사용에 대한 자세한 사항은 예외 절을 참고 하시길 바랍니다. 변수 선언과 다중 변수 변수를 선언하는 방법은 앞서 배운 예제들을 통해서 알 수 있지만 다시 한번 정리 해보도록 하겠습니다. 변수를 선언하는 방법은 기본적으로 타입 변수명; 입니다. int Data; 여기서 비주얼베이직에서 변수를 선언하는 방법을 잠시 보면 Dim DataA as integer, DataB DataA라는 변수는 integer 타입을 갖지만 DataB는 variant 타입을 갖습니다. DataA와 DataB를 모두 integer로 선언하기 위해서는 다음과 같이 Dim DataA as integer, DataB as integer 으로 선언하여야 합니다. C#에서는 C/C++과 같이 한 라인에 같은 타입으로 변수들을 설정 할 수 있습니다.
  24. 24. int DataA, DataB; 물론 C#에서는 C/C++과 동일하게 초기화 및 선언이 동시에 가능하기 때문에 다음과 같은 코드도 가능합니다. int DataA = 10, DataB = 20; 하지만 일반적으로 프로그래머들은 변수들을 선언할 때 한 행에 변수 하나씩 쓰는 것을 주 로 이용하므로 다음과 같이 작성합니다.3 int DataA = 10; int DataB = 20; [한마디] 대부분의 프로그래머들은 다중 변수 선언이 된다고 해도 실제로는 줄을 바꾸어서 선언하고 정의하는걸 더 즐겨 쓰는데 그 이유는 추후에 삭제 혹은 주석 처리가 용이하다는 장점이 있습니다. 예를 들어 int DataA = 10, DataB = 20; 에서 DataA를 주석 처리 하고 싶으면 // int DataA = 10, int DataB = 20; 과 같이 코드를 다시 써주어야 하지만 int DataA = 10; int DataB = 20; 과 같이 되어 있으면 그냥 첫번째 라인만 //로 주석처리 해버리면 문제가 간단하게 해결이 되기 때문입니다. 3 변수를 선언하고 주석을 사용하기 위해서가 아닐까 생각합니다.
  25. 25. 상수 상수(constant)는 값이 변하지 않는 수라는 의미의 변수입니다. 일반적으로 설정된 타입의 변수를 선언하고 나면 해당하는 변수에 프로그래머가 작성한 데이터가 들어가게 됩니다. 그 값은 런타임 도중에 바뀌는 것이 대부분입니다. 그 값을 수정할 수 없게 하기 위해서는 const라는 키워드를 이용하면 됩니다. 물론 A라는 프로그래머가 B라는 프로그래머에게 “sData라는 데이터 값을 절대로 바꾸면 안돼!” 라고 이야기 해도 수 많은 코드를 작성하거나 라이브러리에 뒤엉키다 보면 변수를 수정할 가능성이 존재합니다. 하지만 const라는 키워드를 사용하면 프로그램이 실행되고 난 후에 절대로 변하지 않는 수가 됩니다. 그 값을 수정하려고 하는 코드는 컴파일 과정에서 컴파일 오류를 나타내어 줍니다. 상수는 리터럴(literal), 심볼 상수(symbolic constant), 열거(enumeration)의 세 종류가 있 는데 차례대로 알아보도록 합시다. 리터럴 상수 x = 65; 위 문장에서 65라는 수는 리터럴 상수입니다. 당연히 다음과 같은 방법은 통하지 않습니다. 65 = 23; 심볼 상수 심볼 상수는 이름에 상수값을 부여합니다. 그 사용 방법은 아래와 같습니다. const 타입 식별자 = 값; 예를 들어 파이 값은 변하지 않는 수이고 이것을 만들기 위해서 다음과 같은 코드를 작성합 니다.4 4 이 책이 출판되기 몇 년 전까지만 해도 소수점 값은 float형을 자주 사용했습니다. 하지만 실제 게임에서는 특히나 물리 연산이 많이 계산되는 게임에서는 소수점 값을 double로 사 용합니다. float의 경우는 작은 연산이 누적되었을 때 오차율이 존재하기 때문입니다.
  26. 26. const double PI = 3.1415926535897;5 만일 PI라는 상수값에 값을 대입하려고 한다면 다음과 같은 오류를 얻게 됩니다. // 아무래도 아닌 것 같아. PI값을 수정하자. PI = 3.1415926535; <그림13> 친절하게 오류가 나타납니다. 열거 상수 열거는 나열자라고도 합니다. 프로그래머가 지정한 값들만 가질 수 있는 타입을 열거 상수 라고 말합니다. 사실 열거 상수는 심볼 상수와 매우 흡사한데 다음의 예를 통해서 알아보도 록 합니다. 만들고 있는 게임은 퀘이크와 같은 1인칭 FPS입니다. 이 게임에는 아머(Amor)라는 것이 존재하는데 맵 상에서 아머를 먹고 나면 일정 시간이 지나고 나서 다시 아이템이 생겨납니 다. 이 때 이 시간값은 상수값으로 정해져 있는데 노란색 아머와 빨간색 아머는 각기 다른 시간값을 가지고 있습니다. 그것을 다음의 코드를 통해서 설정해 보도록 합니다. enum AMOR_TYPE { cYellowAmor = 10, 5 파이값은 13자리까지 게임계에서 주로 사용합니다.
  27. 27. cRedAmor = 30 } 노란색 아머는 10으로 설정되어 있고 빨간색 아머는 30초로 설정되어 있습니다.6 이 값은 프로그램이 실행되고 난 후에는 변하지 않는 값입니다. 이 값을 일반 변수처럼 사용하면 됩 니다. 연산자 이제 C#에서 제공하는 연산자들에 대해서 알아보도록 할 것입니다. 연산자란 컴파일러로 하여금 어떠한 행동을 취하게 만드는 기호입니다. 연산자는 피연산자(Operand)에 작용을 가하는데 C#에서 모든 피연산자는 수식입니다. 연산자에는 다음과 같은 종류가 있습니다. 대입 연산자 산술 연산자 관계 연산자 논리 연산자 대입 연산자 대입 연산자는 할당 연산이라고도 합니다. 이미 수학시간에 배웠던 연산자인만큼 그 의미는 이미 충분히 알고 계실 것입니다. int x = 10; int타입을 갖는 x라는 변수에 상수값 10을 대입하는 문장입니다. 문장은 때로는 구문이라고 도 부릅니다. 즉 대입 연산자의 오른쪽에 있는 피연산자 값을 왼쪽에 있는 피연산자에 대입 하는 연산입니다. 이미 리터럴 상수에서 배웠듯이 다음과 같은 구문은 에러입니다. 10 = x; // 도대체 무슨 생각을… 산술 연산자 C#은 다섯 개의 산술 연산자를 제공합니다. 네 개는 사칙연산을 위해서 사용하고 나머지 6 필자가 퀘이크에 한참 빠져있을 때는 모든 시간을 외우고 있었는데 지금은 모두 까먹어 버렸습니다.
  28. 28. 하나는 나머지 값을 계산하기 위해서 사용합니다. 연산자 의미 + 덧셈 - 뺄셈 * 곱하기 / 나누기 % 나머지 나머지 연산자 사칙연산 연산자의 4가지(+,-,*,/) 연산은 너무나 일반적이라 더 이상 설명할 것이 없습니 다. 하지만 나머지 연산에 대해서는 조금 자세히 알아 보아야 합니다. 나머지 연산은 기호 (%)를 사용합니다. int nData = 10; int nMod = nData % 3; System.Console.WriteLine("나머지 : " + nMod); nData라는 변수에 10이라는 값을 넣고 3으로 나눈 나머지를 nMod에 넣었습니다. 결과는 1이라는 값이 나옵니다. 이 나머지 연산은 게임 프로그래밍에서도 매우 유용하게 사용됩니 다. 버추얼 파이터 같은 게임에서 적 케릭터가 게이머에게 상단 발차기 공격을 시도하려고 합니다. 물론 이러한 행동의 의사 결정은 AI로직이 결정하게 됩니다. 그렇다면 AI로직이 상 단 발차기를 해야 되는지를 설정해야 하는데 그것을 간단한 나머지 연산으로 실행해 보겠습 니다. Random r = new Random(); int nResult = r.Next() % 100; if (nResult >= 60) { ActionKick(); } 조금 복잡해 보입니다만 매우 간단합니다. 우선 r이라는 랜덤 클래스의 인스턴스를 얻습니 다. 컴퓨터에서 무작위 수를 구하기 위해서 Random클래스를 사용합니다. 그 이후에 Random클래스의 Next라는 메소드를 통해서 음수가 아닌 숫자 int타입 범위 안에서 숫자값 을 구합니다. 그 값을 100으로 나눈 나머지 수는 100보다 작게 됩니다. 그 값의 범위는
  29. 29. 0~100이 되는데 그 결과값이 60이상이라면 100을 100%라고 본다면 무작위 수의 결과에 서 60%이상일 때 ActionKick이라는 동작을 한다라는 의미입니다. ActionKick()은 함수인데 그것은 해당 이야기에서 다루도록 하겠습니다. 나머지 연산자를 이용하는 주된 작업 중에는 어떠한 수의 배수를 구하는 경우가 있습니다. 예를 들어, 어떤 수를 n으로 나누어서 그 나 머지 값이 0이라면 해당 수는 n의 배수가 됩니다. 구체적인 예는 다음과 같습니다. Random r = new Random(); int nData = r.Next(); int nResult = nData % 2; if (nResult == 0) { System.Console.WriteLine(nData + "는 2의 배수입니다."); } else { System.Console.WriteLine(nData + "는 2의 배수가 아닙니다."); } r인스턴스로부터 무작위 수를 얻은 후에 2로 나눈 나머지가 0이라면 2의 배수이고 나누어 떨어지지 않으면 2의 배수가 아니라는 것을 알 수 있습니다. [한마디] 앞서 변수가 0인지 아닌지를 구분하기 위해서 다음과 같은 수식을 사용했습니다. if ( nResult == 0 ) { } 만일 프로그래머가 실수로 다음과 같은 코드를 작성했다면 어떻게 될까요. if ( nResult = 0 ) { }
  30. 30. 실제로 C/C++문법에서는 이 표현식은 유효합니다. 그러므로 결과는 nResult의 값에 따라 0이 아닐 경우에 참이 되어 로직이 실행됩니다. 즉 원래 의도했던것과는 전혀 다른 제어가 되고 더군다나 nResult값까지 바뀌어 버리니 이로 인해서 얻는 버그 혹은 Side Effect는 상 상을 초월합니다. 그래서 C/C++프로그래머는 다음과 같은 형식으로 조건식을 쓰도록 방법 을 만들어 냈습니다. if ( 0 == nResult ) { } 이렇게 되면 프로그래머가 실수로 0 = nResult로 한다고 해도 리터럴값에 변수를 대입하는 것으로 컴파일 과정에서 에러를 내뱉습니다. 지금까지 긴 이야기를 하였는데 C#에서는 이 러한 걱정을 전혀 안해도 됩니다. 왜냐하면 C#에서는 if ( nResult = 0 ) 과 같은 구문이 유효하지 않기 때문입니다. 증가연산자와 감소연산자 증가연산자와 감소연산자는 프로그램을 작성할 때 매우 자주 쓰이는 연산자 중에 하나입니 다. 변수에 값을 더하고 빼는 것 그리고 그러한 결과를 동일한 변수에 써넣는 것은 매우 자 주 일어납니다. 예를 들어서 사람의 나이를 저장하는 변수가 있습니다. byte nAge = 1; nAge라는 변수는 0~255까지 숫자를 저장할 수 있습니다. 필자가 살아온 지금까지 255세 이상까지 살아온 사람은 없기 때문에 nAge를 byte로 선택했습니다.7 처음에 태어난 나이는 1살입니다. 어떠한 로직에 의해서 (실 세계에서는 시간) nAge가 변동이 된다고 합시다. 일 반적으로 1년이 지나면 우리는 한 살을 먹기 때문에 다음과 같이 될 겁니다. nAge = nAge + 1; 위의 구문은 현재의 nAge값에 + 1을 하여 그 결과값을 nAge에 넣어라. 라고 하는 말과 같 습니다. 만일 nAge에 1이라는 값이 설정되어 있다면 다음과 같이 되었겠지요. 7 타입이란 무엇인가?란에서 자세히 알아 보았습니다.
  31. 31. nAge = 1 + 1; 결과로써 nAge에는 2라는 숫자가 들어가게 됩니다. C#에는 증가 연산자와 감소 연산자를 제공하고 있는데 그것은 ++, --입니다. 이 연산은 해당 변수에 1의 단위로 피연산자의 값 을 +하거나 –합니다. 예를 들어서 위의 코드는 다음과 같이 간단하게 표현이 가능합니다. nAge++; 이제 아시겠지요? 결과는 역시 nAge가 1이라고 할 때 2가 됩니다. 2살이 설정된 상태에서 1을 줄이고 싶다면 다음과 같이 감소 연산자를 사용하면 가능합니다. nAge--; 그 결과는 1이 됩니다. 전위 연산자와 후위 연산자 증가 연산자와 후위 연산자를 사용하는데 있어서 2가지 방법이 존재합니다. 전위형(Prepfix) : ++Variable, --Variable 후위형(Postfix) : Variable++, Variable-- 증가 연산자와 감소 연산자에서 알아본 것은 바로 후위형 연산입니다. 이 두 가지는 어떻게 사용하느냐에 따라서 미묘한 차이를 불러 버그를 만들어 내는 경우도 있습니다. 가령 다음 과 같은 코드를 보도록 합시다. byte nAge = 1; byte CurrentAge = nAge++; System.Console.WriteLine("현재 나이는 " + CurrentAge); 위의 코드를 실행 시켜 보면 CurrentAge에는 1이라는 값이 들어가 있습니다. 무언가 헷갈 리지 않습니까? 그렇다면 다음의 코드는 어떻게 될까요? byte nAge = 1; byte CurrentAge = ++nAge; System.Console.WriteLine("현재 나이는 " + CurrentAge);
  32. 32. 위의 코드는 CurrentAge에 2라는 값이 들어갑니다. 이 두 가지가 바로 후위형과 전위형의 차이입니다. 결론적으로는 메시지가 출력된 이후에 nAge의 값을 1증가 시킵니다. 하지만 두 가지 형태 는 증가가 일어나는 시기가 서로 다릅니다. 전위형의 경우 ++nAge는 변수 nAge의 값을 1 증가시킨 후에 대입을 실행하고 nAge++ 은 nAge의 값을 대입한 후에 1 증가 시킵니다. 그렇기 때문에 CurrentAge에는 증가되는 시기가 다른 것에 따라 그 결과값이 1과 2로 나누어 지게 되는 것입니다. 물론 해당 구문이 끝나고 nAge의 값은 전위형과 후위형에 상관없이 1이 증가된 2가 되어 있습니다. byte nAge = 1; byte CurrentAge = nAge; nAge++; System.Console.WriteLine("현재 나이는 " + CurrentAge); 물론 위와 같이 코드를 작성해도 문제 없습니다. ☺ 산술 대입 연산자 증가 연산자와 감소 연산자는 정말로 자주 쓰이는 연산이기에 따로 설정된 연산자이고 어떠 한 산술적인 연산을 하고 그 결과를 변수에 대입하는 연산을 쉽게 하기 위해서 C#은 아래 와 같은 연산자를 제공하고 있습니다. 식 의미 += n 해당 변수에 n을 더하여 변수에 대입 -= n 해당 변수에 n을 뺄셈하여 변수에 대입 *= n 해당 변수에 n을 곱하여 변수에 대입 /= n 해당 변수를 n으로 나누고 그 결과를 변수에 대입 %= n 해당 변수를 n으로 나눈 나머지를 변수에 대입 <<= n 해당 변수를 왼쪽으로 n번 쉬프트 하여 그 결과를 변수에 대입 >>= n 해당 변수를 오른쪽으로 n번 쉬프트 하여 그 결과를 변수에 대입 &= n 해당 변수와 n을 and연산하여 그 결과를 변수에 대입 |= n 해당 변수와 n을 or연산하여 그 결과를 변수에 대입 ^= n 해당 변수와 n을 베타적 Or(XOR)연산 하여 그 결과를 변수에 대입
  33. 33. 비트 연산자 비트 연산자는 C언어에서 유래한 것입니다. 비트 연산자는 두 개의 변수를 비트 단위로 AND, OR, NOT연산을 수행하여 결과 값을 만들어 냅니다. 식 의미 & 비트 단위 And | 비트 단위 Or ~ 1의 보수 ^ 베타적 Or >> 오른쪽 쉬프트 << 왼쪽 쉬프트 byte타입의 변수값은 8개의 비트값을 가지고 있습니다. 0 0 0 0 0 0 0 0 8개의 비트로 표현할 수 있는 값은 255입니다. 예를 들어서 모든 비트 플래그가 On으로 되 어 있다면 1 1 1 1 1 1 1 1 오른쪽부터 2의 지수로 표현한 값들로써 합을 구합니다. 2의 0승은 1 2의 1승은 2 2의 2승은 4 2의 3승은 8 2의 4승은 16 2의 5승은 32 2의 6승은 64 2의 7승은 128 이렇게 8개 비트값의 합계는 255입니다. 예를 들어 4라는 값은 다음과 같이 표현됩니다.
  34. 34. 0 0 0 0 0 1 0 0 른쪽에서 왼쪽의 순서대로 비트 크기가 커져갑니다. 3번째 비트에 1이라는 값이 설정되어 연산은 AND게이트 연산을 합니다. AND진리표는 아래와 같습니다. 오 있습니다. 2의 2승은 4이기 때문에 맞습니다. 우리가 숫자를 표현하는 것은 컴퓨터 내부에 서는 모두 2진수로 표현됩니다. 32비트라는 것은 말 그대로 비트수가 32개가 있다는 것이 고 64비트는 64개나 있다는 뜻입니다. 우선 비트 연산을 알아보기 위해서 우리는 8비트 즉 1 byte의 숫자를 다루는 것으로 조건을 두고 알아볼 것입니다. & A B A AND B 0 0 0 0 1 0 1 0 0 1 1 1 ND게이트는 전혀 복잡하지가 않습니다. A와 B가 둘 다 1인 경우에만 1입니다. 현이 가능 yte nAge = 4; 1; teLine( (nAge & nBitValue) ); A 이 AND게이트와 같은 동작을 하는 & 연산자는 다음과 같은 코드로 간단하게 구 합니다. b byte nBitValue = nBitValue <<= 2; System.Console.Wri System.Console.WriteLine( nBitValue); nAge에 4라는 값을 넣었습니다. 3번째 비트가 1이 되어 있을 겁니다. 그리고 nBitValue라 는 값에 1을 설정하고 그것을 왼쪽으로 2번 쉬프트 합니다. 그렇게 되면 이 값 1은 3번째 비트열을 1로 설정하는 것과 같은 효과를 만듭니다. 그리고 중요한 구문인 (nAge & nBitValue)의 결과는 4가 나옵니다. nBitValue <<= 3으로 하면 (nAge & nBitValue)의 결과 는 0이 됩니다. 그도 그럴 것이 다음과 같은 AND연산을 하였기 때문입니다. nAge 0 0 0 0 0 1 0 0 nBitValue 0 0 0 0 1 0 0 0 결과 0 0 0 0 0 0 0 0
  35. 35. 각의 비트 들을 AND 게이트 정의에 맞게 계산해 보면 그 결과 비트는 0이 됩니다. 그렇 연산은 OR 게이트 연산을 합니다. OR진리표는 다음과 같습니다. 각 기 때문에 nBitValue를 왼쪽으로 3번 쉬프트하고 AND연산을 하면 0이 나오는 것입니다. | A B A OR B 0 0 0 0 1 1 1 0 1 1 1 1 R게이트는 2개의 입력 중에 하나라도 1이라면 그 결과는 1이 됩니다. 를 들어서 다음의 코드 yte nAge = 128; ; nAge | nBitValue); O 예 b byte nBitValue = 1 byte nResult = (byte)( System.Console.WriteLine( nResult ); nAge는 128이고 nBitValue는 1입니다. 그리고 OR연산을 하였고 그 결과를 nResult라는 변수에 넣어서 화면에 출력해 보았습니다. nAge 1 0 0 0 0 0 0 0 nBitValue 0 0 0 0 0 0 0 1 결과 1 0 0 0 0 0 0 1 R 게이트 정의에 의해 결과는 129가 됩니다. 는 1의 보수를 구하는 비트 연산자입니다. 1의 보수란 해당 비트열의 역을 취하는 것입니 0 0 1 0 0 1 1 0 O ~ 다. 를 들어 위와 같은 비트 열이 존재할 때 ~(틸드)기호를 사용해서 1의 보수를 취해주면예
  36. 36. 그 역의 비트열을 얻게 됩니다. 1 1 0 1 1 0 0 1 yte nAge = 38; (byte)~nAge; ); b byte nReverseBit = System.Console.WriteLine( nAge ); System.Console.WriteLine( nReverseBit 00100110은 십진수 38을 뜻합니다. 11011001은 십진수 217인데 nAge에 1의 보수 값이 기호는 베타적 OR, 흔히들 XOR이라고 합니다. XOR 진리표는 다음과 같습니다. 정확히 217이 됨을 알 수 있습니다. ^ A B A XOR B 0 0 0 0 1 1 1 0 1 1 1 0 OR진리표에 따르면 입력이 둘 중에 하나만 1이면 결과가 1이지만 A와 B가 둘 다 0이거 래의 예를 보고 XOR값이 제대로 얻어 지는지 살펴 보겠습니다. X 나 1이면 0입니다. 확실히 OR게이트와는 다릅니다. 아 nA 0 0 0 0 1 0 0 0 nB 0 1 1 0 1 0 1 1 결과 0 1 1 0 0 0 1 1 0001000은 십진수로 8이며 01101011은 십진수로 107입니다. 이 두 입력으로 XOR을 취 yte nA = 8; 0 해보면 결과는 01100011이며 십진수로 99가 나옵니다. 다음의 코드는 확실히 XOR이 제대 로 돌아갑니다. b byte nB = 107;
  37. 37. System.Console.WriteLine( nA ^ nB ); 필자는 지금 XOR게이트를 이용한 재미있는 기법을 쓰고 싶지만 현재는 문법을 다루고 있 >와 <<는 쉬프트 연산입니다. 현재 우리는 8개의 비트로 이루어진 1 Byte데이터를 다루 0 1 0 0 0 0 1 0 기 때문에 부록으로 대신하였습니다. [부록 : XOR기법]을 참고 하시길 바랍니다. > 고 있습니다. 앞서 몇 가지 예에서 이미 쉬프트 연산을 사용해 보았었습니다. >>는 오른쪽 으로 비트를 이동시키고 <<는 왼쪽으로 비트들을 이동 시킵니다. 와 같은 비트열이 존재합니다. 이 비트열의 십진수 값은 66입니다. 이 비트열을 >> 오른 0 0 1 0 0 0 0 1 위 쪽으로 1번 쉬프트 하면 결과는 다음과 같습니다. 진수 값은 33입니다. 자 66에서 33으로 바뀌었습니다. 다시 한번 오른쪽으로 한번 더 쉬 0 0 0 1 0 0 0 0 십 프트 해보면 다음과 같은 값을 얻습니다. 진수 16을 얻습니다. 66 -> 33 -> 16을 얻어 내었습니다. 보면 2로 나누어 떨어지는 것 관계 연산자, 상등 연산자 연산자의 대소 관계와 상등 관계를 알기 위해서 사용되 십 같지 않습니까? 다른 수로 시도해 보아도 마찬가지입니다. 그래서 >> 오른쪽으로 1번 쉬프 트 하는 것은 2로 나눈 결과를 얻는 것입니다. 그렇다면 << 왼쪽으로 1번 쉬프트 하는 것 은 2로 곱한 결과를 얻는 다는 것을 알 수 있습니다. 관계 연산자와 상등 연산자는 두 피 는 연산자입니다. 관계 연산자를 사용하는 수식을 우리는 관계 연산식이라고 말합니다. 관 계 연산자 역시 많은 언어들이 비슷한데 특히 C#은 C와 매우 닮아 있습니다. 관계 연산자 설명 a < b a가 b보다 작다. a <= b 같다.a가 b보다 작거나 a > b a가 b보다 크다.
  38. 38. a >= b 같다.a가 b보다 크거나 상등 연산자 설명 a == b a가 b보다 작다. a != b a가 b보다 작거나 같다.8 이들 연산자는 주로 조건문에 나타납니다. 조건문에 대해서는 해당 설명에서 자세하게 이야 tring strNameA = "프로그래머"; "; 기 하겠습니다. 이들 연산자를 주로 조건문에 사용하지만 대입식에서도 마찬가지로 사용합 니다. 예를 들어서 s string strNameB = "게임프로그래머 bool bResult = strNameA == strNameB; System.Console.WriteLine( bResult ); strNameA은 문자열 타입을 가진 변수입니다.9 그 변수에 “프로그래머”라는 문자열을 넣었 논리 연산자 해서 다루지 않았지만 조건이 있는 논리 연산자에 대해서 알아 보도록 하 습니다. 그리고 strNameB에는 “게임프로그래머”라는 문자열을 넣었습니다. 그리고 상등 연 산자 == 을 사용해서 이 두 변수가 같은지 다른지 결과를 Boolean타입인 bResult변수에 넣었습니다. 물론 결과값은 False입니다.10 이처럼 관계 연산자, 상등 연산자의 결과값은 조 건문 혹은 대입식으로도 사용이 가능합니다. 아직 조건문에 대 겠습니다. 조건 논리 연산에는 3가지가 있습니다. 기호 설명 && 두 표현식이 모두 같을 때 참. || 두 표현식 중에 하나라도 참이라면 참. ! 표현식 결과의 역을 취함 앞서 비트 연산자에서 보인 비트 연산자와 비슷하게 보이지만 전혀 다른 동작을 합니다. 8 비주얼 베이직 프로그래머의 경우 헷갈릴 수 있는 사항입니다. 비주얼 베이직에서 같지 않다. 라는 것은 <>로 쓰이기 때문입니다. 9 정확하게는 변수가 아닌 객체입니다. 10 게임 프로그래머는 프로그래머의 집합에 속합니다.
  39. 39. byte nA = 66; (nA == 66 && nB == 33) System.Console.WriteLine("Action"); byte nB = 33; if { } 위의 코드는 && 연산을 알아보기 위한 코드입니다. nA에 66이, nB에 33이 되어 있습니다. ( True && True ) 이기 때문에 조건문의 실행문에 해당하는 문장 Action이 화면에 출력 yte nA = 66; (nA == 66 || nB == 33) System.Console.WriteLine("Action"); ==는 상등 연산자에서 배운 바와 같이 두 표현식이 같다면 참을 리턴 합니다. nA는 66이기 때문에 참이며 그 뒤의 표현식 nB == 33역시 참이기 때문에 결과는 참입니다. 즉 if 됩니다. b byte nB = 44; if { } 위의 코드에서는 nB가 44로 초기화 되어 있는데도 불구하고 Action이 화면에 나타납니다. ( True || False ) 이유는 두 표현식의 결과가 아래와 같기 때문입니다. if ||는 둘 중에 하나라도 참이라면 참을 리턴하기 때문에 if문의 조건에 해당하므로 실행문이 지막으로 !의 경우에는 모든 표현식의 논리 결과의 역을 취해 줍니다. ool bLoop = false; ; 실행됩니다. 마 b bool bResult = !bLoop
  40. 40. 위의 코드에서 bLoop는 false로 초기화 되었지만 bResult는 True값을 가집니다. 역을 취하 ool bAlive = true; 는 !연산자는 프로그래밍에서 매우 자주 쓰이는 연산입니다. 예를 들어서 살아 있음을 표현 하는 변수 bAlive라는 변수가 있다고 합시다. b 만일 bAlive가 false가 되었을 때 프로그램을 종료하고 싶을 때 다음과 같은 조건문이 가능 f ( bAlive == false ) 합니다. i 다음과 같이 작성하면 더 깔끔한 코드가 됩니다. f ( !bAlive )i bAlive의 역을 취해서 그 결과가 true라면 bAlive가 false라는 뜻이겠지요.11 네임스페이스 보면 프로그래머가 모든 코드를 작성하지 않는다는 것을 깨우치프로그램 코드를 작성하다 게 됩니다. Microsoft에서 제공하는 라이브러리 코드를 이용하기도 하며 관련 콤포넌트를 추가해서 사용하기도 합니다. 간단한 예제 프로그램의 경우가 아닌 상용 프로그램의 경우 프로그래머가 작성하는 코드는 매우 큽니다.12 이러한 거대한 코드는 또한 여러 명의 프로 그래머가 작성하는 경우가 대부분입니다. 요즘 추세로는 게임 업계에서 잘 만들어진 미들웨 어를 사용하고 있습니다. 사운드 엔진, 물리 엔진, 그래픽 엔진, 인공지능 엔진 등 말이죠. 11 프로그래머는 수학자와 마찬가지로 무언가를 길게 쓰는 것을 무척이나 싫어합니다. 12 필자가 참여했던 샤이야의 경우 툴 소스를 제외한 게임 자체는 20만 라인 정도였습니다.
  41. 41. 사운드 엔진 물리 엔진 그래픽 엔진 인공지능 엔진 게임 시스템 <그림14> 하나의 게임을 만들기 위해서 이러한 여러 가지 미들웨어(Middleware)를 사용합니다. 이러 한 코드들을 사용할 때 문제점이 생겨납니다. 바로 변수 이름의 충돌이나 함수 이름의 충돌 같은 경우가 생겨나는 것이죠. [한마디] 이름이 충돌 나는 경우에는 거의 찾아보기 힘들지만 프로그램 설계가 부분별로 잘 나누어 져 있거나 외부 라이브러리를 사용하면 변수 혹은 클래스명이 충돌 하는 경우가 허다합니 다. 예를 들어서 캐릭터 애니메이션 라이브러리와 물리 라이브러리가 있습니다. 캐릭터 애니메이션 라이브러리에서 애니메이션 데이터를 건드리기 위해서 Controller라는 클래스를 만들었습니다. 이 클래스는 애니메이션에 붙어서 해당 애니메이션을 건드리는 역 할을 하는 클래스입니다. 물리 라이브러리에서는 질점을 포함한 RigidBody, SoftBody를 건드리기 위한 Controller라 는 클래스가 또 존재합니다. 이 두개의 라이브러리를 동시에 사용하려면 그리고 이 라이브러리들이 네임스페이스화가 안되어 있으면 클라이언트쪽에서 이름 충돌을 회피하기 위해서 또 다른 레이어를 만들어 주어야 합니다. 물론 이렇게 된 경우에도 해결 방법이야 있겠지만 애초부터
  42. 42. Animation.Controller혹은 Physics.Controller와 같이 나뉠 수 있다면 더 좋은 코드를 만들 수 있습니다. 그래서 생겨난 것이 네임스페이스(Namespace)입니다. 프로그래머는 닷넷 프레임워크나 다 른 회사에서 제공하는 네임스페이스를 사용할 수 있으며 직접 만든 라이브러리에 네임스페 이스를 적용할 수 있습니다. 우리는 이미 앞서 보인 예제들을 통해서 네임스페이스를 적용 해서 사용하고 있었습니다. using System; using System.Collections.Generic; using System.Text; namespace gameengine_namespace { class Program { static void Main(string[] args) { } } } 네임스페이스의 생성은 namespace라는 키워드를 사용합니다. 코드를 보면 namespace gameengine_namespace라는 것을 보실 수 있습니다. 그 이후에 중괄호를 이용해서 네임스 페이스의 구역을 설정하였습니다. 네임스페이스를 사용하는 것은 using이라는 키워드를 이 용합니다. 코드의 가장 상단을 보면 using System; using System.Collections.Generic; using System.Text; System, System.Collections.Generic, System.Text가 있습니다. 이것은 우리가 닷넷 프레 임워크에서 제공하는 위의 3가지를 사용하겠다는 의미입니다. 우리가 사용했던
  43. 43. System.Console.WriteLine(...); WriteLine메소드는 System안에 존재하는 Console안에 있는 공용 메소드입니다. [한마디] 네임스페이스를 만들 때는 함축적인 의미를 담은 것 보다는 Specific하게 작성하는 것이 도 움이 됩니다. 아래에 좋은 가이드라인이 존재하여 여기에서 소개합니다. 더 자세한 사항은 참고문헌[22]를 참고하시길 바랍니다. <Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>] 타입 변환에서 알아본 Convert클래스는 System 네임스페이스안에 존재합니다. namespace System { public static class Convert { } } 만일 제 회사에서 제품을 만든다면 Kiyoung.AI.FSM 과 같이 디자인하면 다른 회사 제품과 충돌할 이유는 없겠지요.13 ☺ 제어문과 반복문 제어문 프로그램은 프로그래머가 작성한 코드대로 동작합니다. 의도하던 의도하지 않았던 프로그래 머가 작성한 코드를 그대로 실행하는 것이 바로 컴퓨터입니다. 그러한 컴퓨터를 제어하려면 제어할 수 있는 동작이 필요합니다. 이러한 제어문들은 다음과 같습니다. 13 같은 회사 이름이 있다면 오히려 친형제를 맺어야 할 것 같습니다.
  44. 44. 구조 의미 if…else 조건이 참이라면 실행문을 실행한다. switch 조건에 따라 분기한다. goto 무조건 분기한다. if…else구문 if…else구문은 조건에 따라서 분기를 수행합니다. 표현식은 다음과 같습니다. if ( 표현식 ) 문장 A else 문장 B 표현식은 우리가 작성할 수 있는 산술식이거나 문장 자체입니다. 다만 표현식에 들어가는 문장은 세미콜론으로 끝나지 않습니다. if라는 말 자체의 한국말 뜻은 [만약…]이라는 뜻 입니다. if문 자체의 뜻 그대로 만약 표현 식에 해당할 경우에 문장 A를 실행하고 그렇지 않을 경우 문장 B를 실행해라. 라는 뜻입니 다. [한마디] 만일 if의 뜻을 안다면 금세 눈치 채셨겠지만 뜻을 모르신다면 이해하기가 어려울 것 같습 니다. 사실 프로그래밍 언어는 정말 말그대로 언어입니다. 대부분의 프로그래밍 언어들이 영어권에서 만들어졌기 때문에 (컴퓨터도 역시) 영어권 사고 방식이 상당히 이득을 볼 때가 많습니다. if ( Energy > 10 ) { // DoSomething } else { // Avoid }
  45. 45. 과 같은 코드 보다는 사실 다음과 같은 코드가 훨씬 알아보기 쉽습니다.(만일 독자가 처음 프로그래밍을 한다면 더더욱!) 만약 에너지가 10보다 크다면 { // 암거나 해라 } 그렇지 않다면 { // 위험하니 피해라! } 사실 국내에서도 위와 같은 한글 방식의 언어를 개발하긴 했었습니다만 여러가지 이유로 지금의 C#혹은 C/C++만이 남아 있게 되었습니다. 필자는 가끔씩 상상합니다. “모든 언어가 한국말로 적을 수 있다면 얼마나 코드 짜는게 재밌었을까?”라고 말입니다. [한마디] 어느날 제 직장 동료 Chris Chung이 물어 왔습니다. “넌 코딩할 때 어떻게 배운거야?” “그러니까… 지금 C언어 같은거 다 영어잖아. 그거 다 어떻게 배운거야?” “필자 : 나야… 그냥 다 외웠지” “머?... 그럼 그 키워드들이랑 그런걸 다 외웠다구?” “필자 : 응” (당연하지요.) “와…우…” 그렇습니다. C#도 마찬가지로 abstract, class, instance, if, while, for, attribute등등 모든 것들이 영어로 작성되어 있습니다. 저희들은 그걸 외우지만(예를 들면 C++의 const메소드 에서 변수가 바뀔 수 있을 때 mutable를 사용하는) 영문권 친구들은 그냥 자연스럽게 씁니 다. 애초부터 우리는 패널티를 가지고 시작하는 겁니다. 즉, 2배는 더 열심히 해야죠!
  46. 46. 다음의 코드를 보면서 알아 보도록 하지요. using System; using System.Collections.Generic; using System.Text; namespace if_else { class Program { static void Main(string[] args) { int nEnergyOfCharacter = 5; if ( nEnergyOfCharacter > 2) { System.Console.WriteLine("케릭터의 에너지가 2보다 큽니다."); } else { System.Console.WriteLine("케릭터의 에너지가 2보다 작습니다."); } } } } 변수 nEnergyOfCharacter의 초기값을 5로 두었습니다. if조건문을 살펴 보면 nEnergyOfCharacter가 2보다 클 경우(이 부분은 관계 연산자 부분을 참고하세요.) “케릭터 의 에너지가 2보다 큽니다.”라고 화면에 메시지를 출력하고 그렇지 않다면 “케릭터의 에너 지가 2보다 작습니다.”라고 메시지를 출력합니다. 현재 프로그램은 에너지가 5라고 설정되 어 있기 때문에 실행하게 되면 “케릭터의 에너지가 2보다 큽니다.”라고 화면에 나올 것입니다. 그렇다면 에너지를 2보다 작게 즉 1로 설정해보면
  47. 47. <그림15> 에너지가 2보다 작다는 것을 알 수 있습니다. 하지만 우리의 코드는 처음에 if조건문과 조 금 다릅니다. 바로 괄호로 채워져 있다는 것을 알 수 있습니다.
  48. 48. <그림16> if의 조건문이 참일 경우에 실행되는 부분에서 {, }중괄호를 사용하였습니다. 만일 실행문이 하나가 아니라 둘일 경우에 다음과 같이 써보면 원하지 않은 결과를 얻습니다. int nEnergyOfCharacter = 5; if ( nEnergyOfCharacter > 2) { System.Console.WriteLine("케릭터의 에너지가 2보다 큽니다."); System.Console.WriteLine("전투를 하면 질 확률이 크겠군요."); } else { System.Console.WriteLine("케릭터의 에너지가 2보다 작습니다."); System.Console.WriteLine("전투를 하면 이길 확률이 크겠군요."); } 위의 코드를 실행하면 다음과 같은 결과를 얻습니다. <그림17> 하지만 다음과 같이 괄호로 묶지 않으면 원하지 않은 결과를 얻습니다. int nEnergyOfCharacter = 5; if ( nEnergyOfCharacter > 2) System.Console.WriteLine("케릭터의 에너지가 2보다 큽니다."); else System.Console.WriteLine("케릭터의 에너지가 2보다 작습니다."); System.Console.WriteLine("전투를 하면 이길 확률이 크겠군요.");
  49. 49. <그림18> 확실히 원하지 않는 결과입니다. 이길 확률이 크다는건 2보다 작아야 하는데 조건에 해당하 지 않으면 그 내부의 실행문을 모두 괄호로 묶어 주어야 else구문에 의해서 실행이 되지 않 습니다. 이로써 중괄호의 중요성을 알 수 있었습니다. 중괄호에 의해 묶여 있는 문장들은 단일 문장과 동등하기 때문에 분기 처리를 할 때 매우 큰 도움이 됩니다. 중첩된 if문 중첩된 if문을 이용하여 더욱 더 세부적인 조건 분기를 가능하게 합니다. // 중첩된 if문 Random r = new Random(); int nA = 100; int nB = r.Next() % 100; if (nA == 100) { System.Console.WriteLine("nA는 100입니다."); if (nB > 80) { System.Console.WriteLine("nB는 80초과입니다."); } else { System.Console.WriteLine("nB는 80이하입니다.");
  50. 50. } } else { System.Console.WriteLine("nA는 100이 아닙니다."); System.Console.WriteLine("nB가 어떻게 되던 관심이 없습니다."); } nA는 초기값 100으로 설정되고 2개의 분기로 이루어져 있습니다. nA가 100이거나 100이 아니거나. 현재는 100으로 초기화 되어 있고 변경되지 않기 때문에 언제나 참인 구문으로 실행이 됩니다. nB는 무작위 수를 얻고 100으로 나눈 나머지를 이용하여 그 값이 80초과일 때와 이하일 때 2가지로 분기됩니다. if문 안에 if문은 몇 개라도 작성할 수 있습니다. 다만 중첩되는 개수가 많아지면 많아질수록 코드 유지보수와 하루만 지나도 자신이 어떤 작업 코 드를 작성했는지 알아볼 수 없을 정도로 복잡한 문제를 만들게 됩니다. [한마디] 프로그래밍에 관련된 거의 모든 사람들이 하는 이야기는 “한 화면에 함수의 내용을 파악할 수 있도록 코드를 작성하라” 입니다. 사실 한 화면의 크기라는 것이 모니터 크기나 해상도에 달려 있기는 하지만 대부분 모니터의 크기는 비슷하니 이 말은 누구나 이해를 할 수 있습니다. 하지만 실제로 코드를 작성하다보면 if문이 중첩되는 경우가 있고 for문도 중첩을 하다 보면 함수 혹은 괄 호가 한 화면을 훌쩍 넘는 경우가 있습니다. 이것은 어떻게보나 좋은 코드는 아닌데 간단한 리펙토링을 통해서 좋은 코드(어떻게든 한 화면에 함수의 내용을 볼 수 있는)로 변환시킬 수 있습니다. 리펙토링 Extract Method은 “코드의 목적이 잘 드러나도록 메소드의 이름을 지어 별도의 메소드로 뽑아낸다.”를 수행합니다. 여기서 필자가 독자분들에게 드리고 싶은 말은… 이러한 이름 Extract Method를 외우는걸 원하지는 않는다는 것입니다. 더 자세한 내용은 참고문헌[23]을 참고하시길 바랍니다.
  51. 51. switch문 switch문을 사용하면 변수 하나에 가능한 목록 값을 제공하고 그 값의 참 여부에 따라서 실행문을 실행할 수 있습니다. using System; using System.Collections.Generic; using System.Text; namespace @switch { class Program { static void Main(string[] args) { // A형 0 // B형 1 // AB형 2 // O형 3 int BloodType = 1; switch (BloodType) { case 0: System.Console.WriteLine("A형입니다."); break; case 1: System.Console.WriteLine("B형입니다."); break; case 2: System.Console.WriteLine("AB형입니다."); break; case 3:
  52. 52. System.Console.WriteLine("O형입니다."); break; } } } } 사용법은 매우 간단합니다. 비주얼 베이직에서는 select case를 사용하지만 C#에서는 C/C++과 같이 switch 키워드를 사용합니다. 다만 다른점이 있다면 switch문에서 비교되는 변수는 정수, 문자 또는 하나의 문자열 타입이어야 합니다. 즉, 이런 상상도 못할 코드를 지원한다는 뜻입니다. string strName = "Super"; switch (strName) { case "Super": strName = "I'm Super"; break; case "Oooo": strName = "Oooo"; break; } System.Console.WriteLine(strName); 결과는 다음과 같습니다. <그림19>
  53. 53. [한마디] 예제로 나온 코드는 독자가 이후로 프로그래밍을 하게되면서 다시는 보지 못할 최악의 코 드입니다!!! 독자의 기억력이 좋다면 열거 상수를 통해서 더 좋은 코드로 변경할 수 있을거 라고 생각합니다. goto문 goto문은 어셈블리어에서 빼놓을 수 없는 jmp문입니다. 사실 모든 제어문은 goto문으로 이 루어져 있습니다. 불행하게도 요즘 시대에는 goto문의 사용을 자제하려는 경향이 있는데 필 자의 개인적인 경험으로는 goto문으로 스파게티 코드가 되어본 적도 없고 필요한 곳에 써 먹는 것은 goto문을 안쓰려고 애를 쓰는 것 보다 낫다고 생각하고 있습니다.14 goto문을 사용하기 위해서는 먼저 레이블을 설정해야 합니다. 레이블을 설정하기 위해서는 레이블명을 적고 콜론(:)으로 마무리 해주는 것입니다. 그리고 해당 레이블로 이동하기 위해서는 goto 레이블명; 으로 처리합니다. using System; using System.Collections.Generic; using System.Text; namespace goto_code { class Program { static void Main(string[] args) { int nA = 100; jmp_here: if (nA == 100) { System.Console.WriteLine("nA는 100입니다."); nA = 200; goto jmp_here; 14 하지만 여전히 많은 프로그래머들은 goto문을 안쓰려고 애쓰고 있습니다.
  54. 54. } else { System.Console.WriteLine("이 코드는 실행이 될 수 없습니다."); System.Console.WriteLine("nA는 100이 아닙니다."); } } } } nA는 초기 100으로 초기화 되고 “nA는 100입니다.”라는 메시지를 화면에 출력합니다. 그 이후에 nA를 200으로 변경하고 goto문으로 jmp_here라는 레이블로 점프합니다. 이제 if문 에서 nA는 200이기 때문에 else의 실행문이 실행됩니다. goto문만 있으면 앞서 소개해 드린 if…else, switch 그리고 이후에 설명할 루프문도 모두 만들어 낼 수 있습니다. 하지만 이러한 구문들이 goto보다 보기에도 편하고 작성하기에도 좋기 때문에 goto문으로 굳이 만들어낼 필요는 없습니다. goto문을 최대한 안 쓰려고 노력 해 보고 실제로 프로젝트를 접하다 보면 goto문을 써서 좋을 때도 발견하게 될 것입니다. [한마디] “goto문은 절대안돼!” 라고 말하기 이전에 모든 코드가 goto문으로 범벅이 되어 있어도 필자는 프로그램만 만들 수 있으면 Ok. 라고 생각합니다. ☺ [한마디] 만들어진 프로그램을 어셈블러로 보면 모든 제어문이 jmp로 이루어지는 것을 알 수 있습니 다. 조건연산자 ? C#에도 C/C++과 마찬가지로 조건 연산자가 존재합니다. 조건 연산자는 if문과 비슷한 구 조를 가지고 있지만 매우 작은 상황에서만 사용됩니다. using System; using System.Collections.Generic;
  55. 55. using System.Text; namespace firstprogramwithvs { class Program { enum Foot { LEFT, RIGHT }; static void Main(string[] args) { Foot PlayerFoot = Foot.LEFT; bool bFoot = PlayerFoot == Foot.LEFT ? true : false; } } } bFoot에는 단지 왼발이면 true, 오른발이면 false만을 설정하고 싶은 상태에서 굳이 if문으 로 작성할 이유가 없습니다. 그때에는 조건 연산자 ?를 이용하여 위와 같이 간단하게 표현 이 가능합니다. 반복문 반복문은 반복을 할 때 사용하는 구문입니다. C#에서 제공하는 반복문은 다음과 같습니다. 키워드 의미 while 조건이 참인 동안 반복 do while 한번 실행 한 후에 조건이 참인 동안 반복 for 주어진 명령어를 실행하고 조건이 참인 동안 반복 foreach 주어진 컬렉션의 원소를 반복
  56. 56. while문 while문은 “조건이 참인 동안 반복”하는 것입니다. 기본적인 문법은 다음과 같습니다. while ( 표현식 ) 문장 using System; using System.Collections.Generic; using System.Text; namespace while_code { class Program { static void Main(string[] args) { int i = 0; while (i < 100) { System.Console.WriteLine("현재 나이 : " + i); i++; } } } } i라는 변수를 0으로 설정하고 i < 100(관계 연산자 참고)가 참일 때까지 문장을 실행하는 것 입니다. [한마디] 이전에 영어권 프로그래머에 관련된 이야기를 했었는데 사실 while도 마찬가지입니다. 영어 로 그냥 해석하면 “~동안”입니다. 만일 i라는 값이 100이라면 어떻게 될 지 생각해 보아야 할 것 같습니다. while문의 조건이
  57. 57. 참일 때만 문장이 실행되기 때문에 i의 초기값이 100이라면 while문은 실행되지 않습니다. int i = 100; while (i < 100) { System.Console.WriteLine("현재 나이 : " + i); i++; } 게임 프로그램에서는 게임 루프라는 것을 사용합니다. 자세한 설명은 해당 챕터에서 다루도 록 하고 우선 앞서 배웠던 ! 연산자를 while문에서 어떻게 사용하는지 알아보겠습니다. bool bLoop = true; while ( bLoop ) { // 어떠한 로직을 수행, 루프를 끝내고 싶을 때 bLoop를 false로 설정 } 위의 코드는 bLoop가 true로 초기화 되고 계속해서 반복문을 실행하다가 false로 설정되었 을 때 반복문을 벗어나게 되어 있습니다. 위와 같은 코드를 다음과 같이 사용하기도 합니다. bool bExit = false; while (!bExit) { // 어떠한 로직 수행, 루프를 끝내고 싶을 때 bExit를 true로 설정 } 위의 코드에서 bExit는 초기에 false로 초기화 되고 bExit가 true가 설정되면 프로그램이 종료됩니다. 왜냐하면 bExit앞에 ! 연산자를 취해주었기 때문입니다. 앞서 보인 두 개의 코 드는 똑같은 일을 수행하고 있지만 그 사용 방법이 다르며 실제로 코드상에서 사용할 때 의 미가 다릅니다. bLoop라는 변수이름의 의미를 살펴 보면 bLoop가 참일 때 반복문을 실행 해야 할 것 같지만 bExit가 참일 때 반복문을 실행해야 할 것 같은 건 변수 이름의 의미가 일치하지가 않습니다. 간단하고 작은 코드에서는 이러한 의미가 별로 중요하지 않을 수 있
  58. 58. 습니다. 하지만 거대하고 수십만 라인이 되는 코드, 혹은 제 3자가 만들어 놓은 라이브러리 에서 변수의 의미가 반대라면 다른 프로그래머가 해당 코드, 라이브러리를 사용할 때 오해 를 하게 됩니다. 그래서 위와 같은 코드를 작성할 때는 변수의 이름과 그 목적을 확실히 해 두어야 합니다. [한마디] 변수 선언이나 클래스 이름 혹은 메소드 이름을 정하는 모든 행위를 포함해서 네이밍이라 고 합니다. 프로그래머에게 있어서 네이밍은 항상 중요하면서도 간과하기 쉬운데 예를 들어 다음과 같은 함수가 있다고 칩시다. bool IsNotPeople(); 이 함수는 사람이 아니면 true를 반환하고 사람이면 true를 반환합니다. 위의 함수보다 더 잘 지어진 이름의 함수는 다음과 같습니다. bool IsPeople(); 이 함수는 사람이면 true이고 사람이 아니면 false를 반환합니다. 다른 프로그래머나 코드 에서 사용할 때도 IsPeople이 훨씬 사용하기가 편합니다.15 do...while문 프로그램을 작성하다 보면 while루프만으로는 표현하기가 까다로운 부분도 있습니다. do...while문은 “최소한 한번은 실행하고 조건이 참인 동안”이라는 표현을 할 때 사용하는 반복문입니다. 기본적인 문법은 다음과 같습니다. do 문장 while ( 표현식 ); using System; using System.Collections.Generic; using System.Text; 15 사실 이것도 주관적인 의견입니다.
  59. 59. namespace do_while { class Program { static void Main(string[] args) { int i = 0; do { System.Console.WriteLine(i + "번째"); i++; } while (i < 10); } } } 처음 i가 0으로 초기화 되고 무조건 한번은 문장이 실행됩니다. 그 이후에 i를 후위형 증가 연산자를 이용하여 1을 증가합니다. 그리고 마지막에 조건문을 실행하여 조건이 참이라면 반복을 계속하고 조건을 벗어나면 반복문이 종료됩니다. while문 부분에서 가장 큰 차이점 이라면 문장을 끝내기 위해서 세미콜론(;)을 붙혀 주어야 합니다. for문 for문을 반복문을 처리하는 가장 자주 쓰이는 문법입니다. while문을 보시면 반복을 처리하 는 어떠한 패턴이 존재함을 아실 수 있습니다. 그 패턴은 변수값을 설정하고 조건을 검사한 후에 변수값을 수정하는 것 입니다. 이러한 과정을 for문은 구문 자체에 모두 기술할 수 있 습니다. 기본적인 문법은 다음과 같습니다. for ( 초기, 조건, 반복 ) 문장 using System; using System.Collections.Generic; using System.Text; namespace for_code
  60. 60. { class Program { static void Main(string[] args) { for (int i = 0; i < 10; i++) { System.Console.WriteLine(i + "번째 반복을 수행중입니다"); } } } } for구문의 초기에 해당하는 것은 i라는 변수를 선언하고 그 값을 0으로 초기화 하는 것 입 니다. 그리고 조건에 해당하는 것은 while문에서 표현식에 해당하던 i < 10입니다. 그리고 반복문이 한번씩 실행할 때 마다 반복이라는 부분에서 i변수를 후위형 증가 연산자를 사용 한 i++를 사용하여 반복을 수행합니다. for문을 이용하여 간단한 구구단 처리하는 코드를 보도록 합시다. using System; using System.Collections.Generic; using System.Text; namespace for_code_gugudan { class Program { static void Main(string[] args) { int nDan = 2; for (int i = 1; i <= 9; i++) { System.Console.WriteLine("{0} X {1} = {2}", nDan, i, nDan * i); }
  61. 61. } } } 이 코드에서는 기존에 보았던 코드와 다른 형식으로 화면에 문자열을 출력해 보았습니다. 우선 프로그램의 원리는 간단합니다. nDan이라는 변수에 출력할 단을 넣어주고 for반복문을 이용해서 1부터 9까지 반복하면서 nDan과 곱한 결과를 화면에 출력하는 것입니다. for반복 문 외에 눈 여겨 볼 것은 WriteLine메소드에 넘겨준 매개변수 값입니다. 단순히 + 연산자 를 이용해서 문자열을 연결 시켜주어도 되지만 {n}이라는 것을 이용해서 해당 숫자에 따른 매개변수 변수와 연결시켜서 화면에 출력하는 것 입니다. foreach문 foreach문은 C#에서 제공하는 배열이나 컬렉션의 원소를 반복해서 처리할 때 유용하게 사 용할 수 있는 구문입니다. 예를 들어서 전통적인 배열 순환에 관한 문제를 보도록 하겠습니 다. using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.IO; namespace foreach_code { class Program { static void Main(string[] args) { DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory()); FileInfo[] filesinfo = dir.GetFiles(); for (int i = 0; i < filesinfo.Length; i++) { System.Console.WriteLine(filesinfo[i].Name); }
  62. 62. } } } 이 코드에서는 Directory클래스와 DirectoryInfo클래스 그리고 FileInfo클래스를 이용하였 습니다. 이것은 System.IO네임스페이스에 존재하기 때문에 using System.IO를 반드시 해주 어야 합니다. 그 이후에 현재 디렉토리를 얻기 위해 Directory클래스의 GetCurrentDirectory메소드를 이용하였습니다. DirectoryInfo클래스는 매개변수로 전달된 디렉토리에 대한 정보를 가지고 있습니다. 그 이후에 GetFiles메소드를 통해 디렉토리에 존 재하는 파일에 대한 정보를 배열 형태로 얻고 있습니다. 그 이후에 for문을 이용해서 현재 디렉토리에 존재하는 파일의 이름을 화면에 출력합니다. <그림20> 이러한 경우 foreach를 사용하면 더 간편하게 반복문을 처리할 수 있습니다. using System; using System.Collections; using System.Collections.Generic; using System.Text;
  63. 63. using System.IO; namespace foreach_code { class Program { static void Main(string[] args) { DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory()); FileInfo[] filesinfo = dir.GetFiles(); foreach (FileInfo each in filesinfo) { System.Console.WriteLine(each.Name); } } } } foreach문의 each변수는 filesinfo에 모든 원소들을 돌면서 해당 원소에 대한 참조를 each 가 가집니다. 우리는 더 이상 배열 요소의 카운트를 세면서 배열을 순환하지 않아도 됩니다. foreach는 배열, 컬렉션 모두 지원하므로 이후에 윈도우즈 프로그래밍을 할 때도 자주 사용 됩니다. 클래스와 객체 우리는 2장에서 C#에서 기본적으로 제공하는 데이터 타입에 대해서 공부했었습니다. 이제 사용자 정의 타입을 설정할 수 있는 클래스라는 것을 공부할 것 입니다. 객체중심 언어의 가장 큰 특징이 클래스를 정의할 수 있는 기능입니다. 클래스를 선언하고 정의함으로써 새 로운 타입을 정의할 수 있습니다. 우리가 만들 게임에 등장하는 주인공 케릭터, 파이어볼, 매직 미사일 같은 것들을 모두 클래스로 표현 가능합니다.
  64. 64. 클래스를 만드는 방법 클래스를 만들기 위해서는 먼저 클래스명을 적고 클래스가 가져야 할 메소드와 멤버 변수를 적어 줍니다. 다음은 클래스 선언의 완전한 문법입니다. [속성] [접근제어자] class 식별자 [:기본 클래스] { 몸체 } public class Character { // 몸체 } 위의 코드는 하나의 Character라는 클래스를 정의한 것입니다. 이제 그 안에 필요한 몇 가 지 변수를 선언해 보도록 합니다. public class Character { public string Name; public int Level; public int Energy; } Character클래스에 이름과 레벨 그리고 에너지를 표현하는 int형 변수를 선언했습니다. 그 리고 메소드를 만들어 보겠습니다. public class Character { public string Name; public int Level; public int Energy; public void PrintName() { System.Console.WriteLine("이름 : " + Name); }
  65. 65. public void PrintLevel() { System.Console.WriteLine("레벨 : " + Level); } public void PrintEnergy() { System.Console.WriteLine("에너지 : " + Energy); } } 3개의 메소드는 각각 이름과 레벨 그리고 에너지를 화면에 출력합니다. 이 하나의 클래스를 이용해서 다양한 케릭터들을 만들어 낼 수 있습니다. 우선 하나의 케릭터를 만들어 보겠습 니다. static void Main(string[] args) { Character Knight = new Character(); } 기사 한 명을 Character로 하여금 생성하였습니다. 이렇게 만들어진 Knight를 객체 혹은 인스턴스(Instance)라고 합니다. 이제 마법사도 만들어 보죠. Character Wizard = new Character(); 이렇게 하나의 뼈대를 만들어 놓고 그것으로부터 실존하는 객체를 만들어 내는것이 객체중 심의 핵심입니다. 모든 사람은 동물이기 때문에 Animal클래스로 부터 사람이라는 실존하는 객체가 만들어 지는 것 입니다. 물론 강아지, 고양이 같은 동물들도 Animal로 부터 실존하 는 객체로 만들어 집니다. 다음의 코드를 또 한번 살펴 보도록 하죠. using System; using System.Collections.Generic; using System.Text; namespace class_code {
  66. 66. public class Character { public string Name; public int Level; public int Energy; public void PrintName() { System.Console.WriteLine("이름 : " + Name); } public void PrintLevel() { System.Console.WriteLine("레벨 : " + Level); } public void PrintEnergy() { System.Console.WriteLine("에너지 : " + Energy); } public void PrintAllInformation() { PrintName(); PrintLevel(); PrintEnergy(); } } class Program { static void Main(string[] args) { Character Knight = new Character(); Character Wizard = new Character(); Knight.Name = "기사"; Knight.Level = 1;
  67. 67. Knight.Energy = 10; Wizard.Name = "마법사"; Wizard.Level = 2; Wizard.Energy = 5; Knight.PrintAllInformation(); Wizard.PrintAllInformation(); } } } 위의 코드를 컴파일하고 실행 시켜 보면 다음과 같은 화면을 보실 수 있습니다. <그림21> Knight, Wizard는 실존하는 객체가 되었고 이름도 있고 레벨도 있고 에너지도 가지고 있습 니다.
  68. 68. 클래스 멤버 우리는 간단하게나마 클래스를 만드는 방법을 배웠고 클래스로부터 객체를 생성하는 방법도 배웠습니다. 앞서 우리는 데이터 타입으로 만들 수 있는 변수를 배웠는데 이러한 변수들이 클래스 내부에 있을 때는 다른 용어를 불립니다. 멤버 변수나 필드라고 말이죠. 이러한 용 어 차이를 확실하게 아는 것이 새로운 언어를 배울 때 상당히 중요합니다. 다음은 클래스를 이루는 주요한 3가지 요소입니다. 이름 설명 Fields C++에서는 멤버 변수 혹은 그냥 변수라고 합니다. 클래스 내부에 있느 냐 없느냐에 따라 멤버 변수 혹은 그냥 변수라고 합니다. 하지만 C#에서 는 이러한 멤버 변수를 필드라는 용어로 부릅니다.16 Methods 메소드는 함수와 같습니다. 클래스 내부에 있는 함수를 메소드라고 부르 는데 함수라고 불러도 아무런 문제가 없습니다. Properties 속성이라는 뜻으로 글자 그대로 프로퍼티라고도 부릅니다. 이후 프로퍼 티란에서 배우겠지만 C#에서 매우 유용하게 사용할 수 있는 기능입니 다.17 접근제어자 접근제어자(Access modifier)는 클래스 안에 선언된 메소드나 멤버 변수의 접근 범위를 설 정합니다. 앞서의 코드를 살펴 보면 public이라는 것을 보실 수 있었을 겁니다. 접근제어자 설명 public public으로 설정된 것은 클래스 외부, 파생클 래스에서 접근이 가능합니다. private private으로 설정된 것은 클래스 내부에서만 접근이 가능하고 외부, 파생클래스에서 접근 이 불가능합니다. protected protected로 설정된 것은 클래스 내부에서 접근이 가능하며 외부에서 접근이 불가능합 니다. 파생클래스에서는 접근이 가능합니다. 16 필자는 멤버 변수라는 용어를 사용합니다. 왜냐하면 필드라는 용어는 다른 곳에서 자주 사용되는 용어이기 때문입니다. 예를 들어서 데이터베이스에서 테이블의 한 열을 나타내는 용어와 같듯이 말이죠. 17 이 프로퍼티는 비주얼 베이직에서의 속성과 같습니다.
  69. 69. internal internal로 설정된 것은 코드가 놓이는 위치 에 따라서 public이나 protected로 선택됩니 다. 이제 다음과 같은 코드가 어떻게 해서 가능했는지 알 수 있습니다. Character Knight = new Character(); Character Wizard = new Character(); Knight.Name = "기사"; Knight.Level = 1; Knight.Energy = 10; Wizard.Name = "마법사"; Wizard.Level = 2; Wizard.Energy = 5; 위의 코드에서 우리는 기사와 마법사를 생성한 후에 해당하는 데이터를 Main함수에서 설정 해 주었습니다. 물론 이것이 가능했던 이유는 Name, Level, Energy라는 멤버 변수가 클래 스 내부에서 public으로 설정되어 있었기 때문에 가능한 것이었습니다. 객체중심에서는 일 반적으로 클래스 외부에서 멤버 변수에 대한 직접적인 접근을 허용하지 않습니다. 실제로 이러한 제약 때문에 코드가 다소 복잡해지는 경우가 있습니다. 만일 맴버 변수를 외부에서 접근이 불가능하게 하려면 다음과 같이 해야만 합니다. private string Name; private int Level; private int Energy; public void SetName(string name_) { Name = name_; } public void SetLevel(int level_) { Level = level_; }
  70. 70. public void SetEnergy(int energy_) { Energy = energy_; } 우리는 클래스 외부에서 멤버 변수를 접근하는 것을 허용하지 않기 때문에 위의 코드와 같 이 Name, Level, Energy의 접근제어자를 private로 설정하였습니다. private로 설정하면 클 래스 내부에서만 접근이 가능하고 외부에서는 불가능하게 됩니다. 그렇다면 Main함수에서 기사와 마법사의 이름, 레벨, 에너지를 설정할 수 있어야 하는데 그것은 클래스 멤버 메소 드를 통해서 가능하게 하고 있습니다. 코드를 보시면 SetName, SetLevel, SetEnergy라는 메소드가 그러한 역할을 합니다. 이 메소드들은 public으로 되어 있기 때문에 Main함수에서 접근이 가능합니다. 다음과 같이 말이죠. Character Knight = new Character(); Character Wizard = new Character(); Knight.SetName( "기사" ); Knight.SetLevel(1); Knight.SetEnergy(10); Wizard.SetName("마법사"); Wizard.SetLevel(2); Wizard.SetEnergy(5); 처음에 외부에서 클래스 멤버 변수에 직접 접근해서 변경하는 것과 크게 다르지는 않습니다. 현재 이름, 레벨, 에너지를 출력하는 것은 클래스 내부의 메소드를 통해서 출력하고 있는데 만약 클래스 외부에서도 클래스의 이름과 레벨, 에너지를 얻어야 한다면 그러한 메소드를 만들어 주어야 합니다. 다음과 같이 말이죠. public string GetName() { return Name; } public int GetLevel() { return Level;
  71. 71. } public int GetEnergy() { return Energy; } 처음에 우리가 디자인했던 클래스와 접근제어자를 수정해서 관련 메소드들을 만든것과 크기 면에서 차이가 많이 납니다. 사용법도 꽤 다르고 말이죠. 객체중심에서는 클래스 내부에 있 는 멤버 변수를 외부에서 직접 접근을 허용하지 않는다고 말씀 드렸습니다. 한눈에 보아도 처음에 우리가 디자인 했던 클래스가 훨씬 알아보기가 편하며 C 프로그래머들의 경우에는 그것이 더 익숙할 것 입니다. 일부 잘못된 시각을 가진 개발자들은 public으로 멤버 변수를 다루면 매우 큰 죄를 짓는 것이라고 생각하는 분들도 있는데 필자의 개인적인 견해로는 그 것은 아니라고 봅니다. [한마디] 사실 이 부분에 있어서는 필자의 지극히 개인적인 생각입니다. 필자는 객체중심으로 프로그 래밍 하기 때문에 무조건 멤버 변수들은 메소드나 프로퍼티를 이용해서 접근해야 한다고 강제하는건 별로 마음에 들지 않습니다. 필자는 그때 그때 필요한것만 사용하고 필요 없거 나 조금이라도 불편하면 아무렇지도 않게 무시해버리는 스타일의 개발자입니다. 다행히도 지금까지 잘 버티고 현업에서 일하고 있습니다. ☺ 생성자 우리는 클래스로부터 기사와 마법사라는 실존하는 객체를 만드는 방법을 알게 되었습니다. 그리고 기사의 이름이나 마법사의 에너지를 우리가 직접 대입해서 넣어 주었습니다. 우리는 만들어 놓았던 Character라는 클래스를 이용해서 5명의 케릭터를 만들려고 합니다. 그러면 다음과 같이 되어야 하겠죠. Character Knight = new Character(); Character Wizard = new Character(); Character PersonA = new Character(); Character PersonB = new Character(); Character PersonC = new Character();
  72. 72. 5명의 케릭터를 만들어 보았고 이제 이름, 레벨, 에너지를 설정해야 합니다. static void Main(string[] args) { Character Knight = new Character(); Character Wizard = new Character(); Character PersonA = new Character(); Character PersonB = new Character(); Character PersonC = new Character(); Knight.SetName( "기사" ); Knight.SetLevel(1); Knight.SetEnergy(10); Wizard.SetName("마법사"); Wizard.SetLevel(2); Wizard.SetEnergy(5); PersonA.SetName("지나가는 행인"); PersonA.SetLevel(1); PersonA.SetEnergy(100); PersonB.SetName("지나가다가 멈춘 행인"); PersonB.SetLevel(1); PersonB.SetEnergy(100); PersonC.SetName("지나가려는 행인"); PersonC.SetLevel(1); PersonC.SetEnergy(100); Knight.PrintAllInformation(); Wizard.PrintAllInformation(); PersonA.PrintAllInformation(); PersonB.PrintAllInformation(); PersonC.PrintAllInformation(); }
  73. 73. 우리는 우리가 관심 있는 기사와 마법사를 제외한 모든 사람들은 에너지를 100으로 통일하 려고 합니다. 우리는 변수를 선언할 때 초기화 값을 정할 수 있는 것을 알고 있습니다. int Level = 10; 클래스에 선언된 멤버 변수도 초기화 값을 지정할 수 있다면 좋겠죠? C#에서는 초기자 (Initalizer)라는 것이 존재합니다. 다음과 같이 사용할 수 있습니다. private string Name; private int Level; private int Energy = 100; Energy멤버 변수는 SetEnergy함수를 호출하여 값을 설정하지 않아도 새로 객체가 생성될 때 100이라는 값으로 초기화 됩니다. 그런데 초기화 값 100을 바꾸고 싶어졌습니다. 객체를 생성할 때 에너지 값을 넣을 수 있 으면 코드가 한결 나아질 수 있습니다. 그 작업은 생성자를 이용해서 가능합니다. 생성자는 클래스로부터 객체가 생성될 때 처음으로 호출되는 메소드 입니다. 생성자의 역할은 객체의 멤버 변수 값들을 일괄적으로 초기화 시킬 때 주로 사용합니다. 그 생김새는 아래와 같습니 다. public Character() { } 생성자는 클래스 이름과 똑같은 이름을 가진 메소드 입니다. 이 생성자에는 매개변수도 가 질 수 있습니다. 가령 이름, 레벨, 에너지를 객체가 생성할 때 설정할 수 있게 하려면 다음 과 같이 생성자를 만들면 가능합니다. public Character(string name_, int level_, int energy_) { Name = name_; Level = level_; Energy = energy_;
  74. 74. } 위의 생성자를 이용해서 객체를 생성하려면 Main함수에서 객체를 생성할 때 다음과 같이 해야 합니다. Character Knight = new Character("기사",1,10); Character Wizard = new Character("마법사",2,5); Character PersonA = new Character("지나가는 행인",1,100); Character PersonB = new Character("지나가다가 멈춘 행인", 1, 100); Character PersonC = new Character("지나가려는 행인", 1, 100); 이제 일일이 멤버 메소드를 호출해서 이름, 레벨, 에너지를 설정하지 않아도 됩니다. 하지만 위와 같이 생성자를 구축하면 다음과 같이 객체는 만들지 못합니다. Character Knight = new Character(); 왜냐하면 우리가 만든 생성자는 3개의 매개변수를 받게 만들어 놓았기 때문입니다. 그래서 생성자를 함수중첩(Overload)해서 0개의 매개변수를 받는 기본 생성자를 만들어 놓습니다. // 기본 생성자 public Character() { } [한마디] 일반적으로 대규모 수준의 코드들을 접하다 보면 수많은 개수의 생성자를 가진 클래스를 만나기 일쑤입니다. 그리고 이러한 클래스들은 자신이 직접 만든 것이 아닐 수도 있다는 것 입니다. (본인이 제작했다면 어떻게 사용하는지 알고 있겠지요. ) 본인이 직접 만든 클래스가 아닐 때 처음 그 클래스를 사용하는 개발자들은 클래스를 어떻 게 생성해야 하는지 모릅니다. 만일 클래스의 생성자가 하나만 있는 것이 아니라 여러 개가 있다면 제 3의 개발자는 어떻게 객체를 생성해야 하는지 모르기 때문에 처음 클래스를 만 든 개발자는 최대한 생성자의 매개변수에 의미를 부여해서 만들어야 합니다. 즉, 다음과 같은 클래스 생성자는 매우 나쁜 예입니다.
  • skchoi4

    Sep. 27, 2017
  • ssuser1d0571

    Mar. 15, 2017
  • JiniorChoe

    Feb. 2, 2017
  • whitepig70

    Nov. 18, 2016
  • nermes

    Oct. 26, 2016
  • KomuroTetsuya

    Apr. 24, 2016
  • seunggyunlee758

    Mar. 4, 2016
  • GyedoJeon

    Mar. 3, 2016

게임 프로그래밍으로 배우는 C# 1권 (버전1) 입니다. 오래전에 집필했던 책인데 오프라인 책으로 출간하지 못한 책이었습니다. C#버전도 오래된 버전이지만 지금이라도 도움이 되시는 분들이 계실까봐 올려 봅니다.

Views

Total views

2,442

On Slideshare

0

From embeds

0

Number of embeds

40

Actions

Downloads

62

Shares

0

Comments

0

Likes

8

×