- 전체
- 게임 일반 (make game basics)
- 모바일 기획 및 디자인
- GameMaker Studio
- Unity3D
- Cocos2D
- 3D Engine OGRE
- 3D Engine irrlicht
- copperCube
- corona SDK
- Windows Basic Game
- BaaS (Mobile Backend)
- phnegap & cordova
- ionic & anguler
- parse (backend)
- firebase (backend)
- Game Backend Server / Opt
- web assembly
- Smart Makers
- pyGame & Ren'Py
- 머드(MUD) 게임 만들기
- Xamarin(자마린)
- flutter (플루터 앱 개발)
- construct 2 / 3
- pocketbase
- RPG Maker 시리즈
- godot engine
- playmaker(unity)
- react native
머드(MUD) 게임 만들기 무작정 배우는 C언어 머드(MUD)게임 만들기 Part4 게임스랩
2020.10.17 23:55
무작정 배우는 C언어 머드(MUD)게임 만들기 Part4 게임스랩
예제 게임 소스 : MUD_TurtleAdventure (1).zip
지난 시간에 이어서 지도에 몬스터와 이벤트 등을 설정하여 본격적인 게임다운 게임을 만들어 보겠습니다. 우선 몬스터를 만나고 전투를 할 수 있으려면 몬스터를 설정해야겠습니다. 몬스터는 종류도 많고 각각 능력의 수치가 다르게 설정할 필요가 있겠지요. 하지만 몬스터들은 공통된 능력을 가지고 있습니다. 예를 들면 HP, 공격력 같은 능력은 모든 몬스터들이 가지고 있어야 할 것들이란 것이죠.
int hp;
int attack;
이런식으로 우리가 기존에 배운 방법으로 능력치를 설정할 수 있겠습니다. 하지만 저렇게 선언했다간 수많은 몬스터를 표현하기에는 많은 무리가 생기겠죠.
int hp;
int attack;
int hp_1;
int attack_1;
…
int hp_n
int attack_n;
수 많은 변수를 선언해야 할것입니다. 하지만 이걸 대체 할 수 있는 것이 바로 배열입니다. 배열은 동일한 데이터타입을 한번에 여러 개 선언하는 방법입니다.
int hp[100];
위와 같이 같이 [ ] 안에 원하는 숫자를 넣으면 해당 개수만큼의 변수를 선언하는 효과를 가지게 됩니다. 해당 변수의 각각은 다음과 같이 접근할 수 있습니다.
hp[0] = 1;
hp[1] = 2;
…
hp[99] = 100;
[ ]안의 숫자를 첨자라고 하는데 0부터 시작하게 됩니다. int hp[100]; 100개를 선언했으니 0~99까지의 첨자만 사용가능하겠지요. 아무튼 이렇게 우리는 1차 배열을 선언하는 방법을 알아봤습니다. 2차 배열은 int hp[100][90]; 이렇게 선언합니다. 2차 이상의 다차원배열은 복잡해지므로 웬만하면 2차배열 이하로 프로그래밍을 할 수 있도록 하는 것이 좋습니다.
다시 몬스터 능력치 설정으로 돌아와서 각각의 능력치를 int hp[100]; int attack[100]; 처럼 100개씩 선언할 수도 있지만 배열과 다르게 동일한 데이터타입이 아닌 다양한 데이터 타입을 선언해서 그것을 배열로 사용하는 방법도 있습니다. 바로 구조체 struct 라는 것입니다. 일단 다음 코드부터 보겠습니다.
struct MONSTER
{
char name[20]; //이름
char say[64]; //등장대사
int hp; //체력
int mp; //마력
int atk; //공격력
int def; //방어력
float criPro; //크리티컬확률
int dropGold; //드랍골드
int dropItemIndex; //드랍아이템
float itemDropPro; //아이템드랍확률
… 중략
};
MONSTER g_monster[100];
struct 뒤에 사용자가 사용할 데이터 타입 이름을 정하고(int, float 등과 같은 데이터형을 사용할 때 쓰는 이름, 우리가 정수형을 사용할 때 int라고 선언하듯) 이제는 MONSTER 라는 새로운 데이터타입을 만든것입니다. 그 안에는 2개의 문자열과 6개의 정수형 2개의 실수형 데이터를 처리할 수 있도록 선언되어 있습니다.
또한 그렇게 선언한 데이터형을 다음과 같이 MONSTER g_monster[100]; 배열로 선언하여 가뿐하게 100개의 몬스터형 데이터타입을 선언하게 되었습니다. 어떠세요? 엄청 간편해지지 않았나요?
이제 여러분들이 몬스터를 창조하는 일만 남은거 같습니다. 저 변수에 실제 수치를 넣는 일 말이죠.
void monsterStatusSet(int _index, char* _name, char* _say, int _hp, int _mp, int _atk, int _def, float _criPro,
int _dropGold, int _dropItemIndex, int _dropExp, float _itemDropPro)
{
strcpy(g_monster[_index].name, _name);
strcpy(g_monster[_index].say, _say);
g_monster[_index].hp = _hp;
g_monster[_index].mp = _mp;
g_monster[_index].atk = _atk;
g_monster[_index].def = _def;
g_monster[_index].criPro = _criPro;
g_monster[_index].dropExp = _dropExp;
g_monster[_index].dropGold = _dropGold;
g_monster[_index].dropItemIndex = _dropItemIndex;
g_monster[_index].itemDropPro = _itemDropPro;
}
위 코드처럼 구조체로 선언된 변수의 데이터 접근하기 위해선 “.” 을 통해서 접근이 가능합니다. 배열로 선언을 안했다는 가정하에 g_monster 구조체안에 있는 여러 변수들은 g_monster.hp 이렇게 접근한다는 것이죠. 다음은 몬스터 생성입니다.
void monsterData()
{
monsterStatusSet(0, "다람쥐", "앞니빨로 등껍질을 긁어주랴!?", 50, 0, 5, 3, 5.0f, 10, 0, 10, 0.0f);
monsterStatusSet(1, "날다람쥐", "나는 3초 날라 다닌다.", 50, 0, 5, 5, 5.0f, 15, 0, 15, 0.0f);
…중략
}
위와 같이 지도에 배치할 여러 몬스터를 셋팅하였습니다. strcpy(문자열1, 문자열2) 함수는 문자열을 복사하는 함수인데 문자열1에 문자열2의 내용을 복사하는 것입니다. 일반적인 형태의 데이터들은 바로 g_monster[_index].hp = _hp; 대입이 가능하지만 문자열의 경우 좀 특별히 처리를 합니다. 이 부분에 대한 상세한 내용은 복잡하므로 생략하기로 하고 우리는 그저 문자열 복사는 이렇게 하는구나 라는 정도만 이해하면 되겠습니다.
자! 이제 지도에 배치할 일만 남았군요. 지도에 배치 하기 위해서 지도의 정보가 필요하겠습니다. 500 X 500 사이즈의 지도를 가상으로 만들어서 이동에 대한 부분을 만들었던걸 기억하시죠?
이 가상의 공간에 몬스터나 이벤트등의 정보를 담기 위해선 역시 저장할 무엇인가가 필요합니다. 그것이 바로 지도의 정보겠죠. 다음 코드를 보겠습니다.
struct MAP_INFO
{
int eventIndex;
int monsterIndex;
int npcIndex;
};
MAP_INFO g_map[500][500];
저는 시작점을 (200,200) 의 위치에서 시작했었습니다. 그 얘기는 현재 지도정보상에서 g_map[200][200]; 이 곳에 있다는 말입니다. 이곳을 중심으로 상하좌우에 몬스터를 배치해보겠습니다.
void oneAreaSet(int _x, int _y, int _monsterIndex, int _eventIndex, int _npcIndex)
{
g_map[_x][_y].monsterIndex = _monsterIndex;
g_map[_x][_y].eventIndex = _eventIndex;
g_map[_x][_y].npcIndex = _npcIndex;
}
void mapInfoSetting()
{
oneAreaSet(199,198,0,0,0);
oneAreaSet(199,199,-1,-1,-1); //현재 시작위치
oneAreaSet(199,200,0,0,0);
oneAreaSet(200,199,1,0,0);
oneAreaSet(198,199,2,0,0);
}
위와 같이 함수를 만들어 상하좌우에 몬스터 0~2까지의 종류를 배치했습니다. 해당 위치로 이동했을 때 몬스터의 등장시켜보도록 하겠습니다.
void checkMapEvent()
{
if(g_map[g_player.PosX][g_player.PosY].eventIndex != -1)
{
}
if(g_map[g_player.PosX][g_player.PosY].monsterIndex != -1)
{
char tmp[256] = "";
sprintf_s(tmp, 256, "%s이(가) 나타났습니다.\n\n", g_monster[g_map[g_player.PosX][g_player.PosY].monsterIndex].name);
notice(tmp);
}
if(g_map[g_player.PosX][g_player.PosY].npcIndex != -1)
{
}
}
g_map[g_player.PosX][g_player.PosY] 지도정보를 저장하고 있는 변수의 배열 첨자에 현재 플레이어의 위치를 넣어 현재 지도의 정보를 가져옵니다. 그리고 해당 지도에서 몬스터의 정보가 -1 이 아닌 유효한 값(저는 여기서 -1을 무효값으로 정했습니다.)을 확인하고 몬스터의 인덱스, 즉 g_monster[n] 의 n에 해당하는 부분을 몬스터의 인덱스로 활용하였습니다. 그래서 바로 지도의 몬스터 인덱스 값을 g_monster[n]의 n값에 전달하여 몬스터의 정보를 알아옵니다. 이러한 일들을 매번 캐릭터가 움직일때마다 확인해야 하므로 전에 작성했던 코드에서는
case COMMAND_MOVE_WEST:
{
if(g_player.PosX > 0)
{
g_player.PosX--;
checkMapEvent();
}
else
{
notice("막다른길입니다. 다른곳으로이동해주세요.\n\n");
}
}
break;
매번 움직임마다 checkMapEvent(); 함수를 실행시켜 이벤트나 몬스터가 있는지 확인을 합니다. 그럼 이동때 마다 다음과 같은 결과를 얻을 수 있겠지요.

(200,200)의 시작점을 기준으로 상하좌우의 지역에 몬스터들이 배치되었고 해당 지역으로 이동할 때마다 몬스터를 만나게 되었습니다. 이벤트도 이와 크게 다르지 않습니다. 상상은 여러분의 몫으로 남겨두고 다음 시간은 전투에 대해 배워보기로 하겠습니다. 기대해주세요.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
댓글 0
| 번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
|---|---|---|---|---|
| 5 |
[3D 수학] 사원수(Quaternion : 쿼터니언)
| 가을의곰 | 2017.06.10 | 836 |
| 4 |
각도와 거리로 좌표 구하기
| 가을의곰 | 2017.06.10 | 1330 |
| 3 | [OpenGL]Quaternion (사원수)를 이용한 회전 | 가을의곰 | 2017.06.10 | 785 |
| 2 |
Games on GitHub 오픈소스 게임 모음
| 졸리운_곰 | 2016.01.19 | 4148 |
| 1 |
나우프레스 c게임 프로그래밍 예제 [이태성]
| 졸리운_곰 | 2015.11.03 | 787 |

