지렁이 프로젝트 (완료) API


최종 완성본...

드디어 뭔가 끝냈다는 성취감이 드네요.

리소스를 입히진 못했지만, 게임같은 게임의 구성은 완료.

인터페이스 조금에... 난이도별 점수 및 속도 차등화까지...

다만, 아쉬운것은... 제대로 공부하지 않고, 책만 보고 했기 때문에... 옳게 썼는지, 틀리게 썼는지...

구별하기가 힘든 정도...

내생에 더러운 goto를 쓸 줄이야...

지금보니, 소스코드도 정말 더럽고...

(일부는 for문을로 돌린게 아니라, 직접 작성했다는... 이건 수정대상이네요.)

공부하면 할수록 도전의식이 드는 느낌이네요.

방학때는 더 심도있는 공부를 토대로, 여러가지 프로젝트를 혼자서 해볼까 생각중.



// Jirungii.cpp : 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"
#include "Jirungii.h"

#define MAX_LOADSTRING 100

// 전역 변수:
HINSTANCE hInst;        // 현재 인스턴스입니다.
TCHAR szTitle[MAX_LOADSTRING];     // 제목 표시줄 텍스트입니다.
TCHAR szWindowClass[MAX_LOADSTRING];   // 기본 창 클래스 이름입니다.

#define random(num) (rand()%(num))
#define J_MAX 25
#define UP    0x111111
#define DOWN  0x222222
#define LEFT  0x333333
#define RIGHT 0x444444

RECT rt_start = {100, 100, 100, 100}; // 지렁이 움직임을 통제.
RECT rt_HEAD;
RECT* rt;
int mode = 0; // 최초 방향모드 : 없음
int flag = 0; // 최초 방향모드 : 없음
int j_size = 0;  // 맨 최초 지렁이 몸 길이 : 3
int Score = 0;  // 점수임 : 최초 0점
int Life = 3;  // 최초잔기 : 3개
int Stage = 1;  // 최초 스테이지 : 1스테이지
int item_count = 27;
RECT item = {0}; // 아이템을 그리는 사각형
RECT Wall[92];  // 사각 테두리 벽의 배열임
BOOL DotEat = TRUE; // 점이 랜덤하게 생길 때 먹으면 트루
BOOL TIME = FALSE; // 타임키(스페이스)를 누르면 TRUE로 바뀐다.

TCHAR score_output[100]; // 점수출력 문자열
TCHAR stage_output[100]; // 스테이지출력 문자열
TCHAR life_output[100];  // 목숨출력 문자열
TCHAR bool_time[100];  // 타임여부출력 문자열
TCHAR item_counter[100]; // 아이템개수출력 문자열
TCHAR user_place[300];  // 사용자위치정보(헤드) 문자열
TCHAR item_place[300];  // 아이템위치정보 문자열

RECT output[7];
int temp;
int temp1;

// 이 코드 모듈에 들어 있는 함수의 정방향 선언입니다.
ATOM    MyRegisterClass(HINSTANCE hInstance);
BOOL    InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
 HINSTANCE hPrevInstance,
 LPTSTR    lpCmdLine,
 int       nCmdShow)
{
 UNREFERENCED_PARAMETER(hPrevInstance);
 UNREFERENCED_PARAMETER(lpCmdLine);

 // TODO: 여기에 코드를 입력합니다.
 MSG msg;
 HACCEL hAccelTable;
 int i;

 // 상단 가로벽의 초기화
 Wall[0].left = J_MAX;
 Wall[0].top = 0;
 Wall[0].right = 2*J_MAX;
 Wall[0].bottom = J_MAX;

 for(i=1; i<=24; i++)
 {
  Wall[i].left = Wall[i-1].left + J_MAX;
  Wall[i].top = Wall[i-1].top;
  Wall[i].right = Wall[i-1].right + J_MAX;
  Wall[i].bottom = Wall[i-1].bottom;
 } // 루프 탈출후 i : 25

 // 오른쪽 세로벽의 초기화
 Wall[25].left = Wall[24].left;
 Wall[25].right = Wall[24].right;
 Wall[25].top = Wall[24].top + J_MAX;
 Wall[25].bottom = Wall[24].bottom + J_MAX;

 for(i=26; i<=45; i++)
 {
  Wall[i].left = Wall[i-1].left;
  Wall[i].right = Wall[i-1].right;
  Wall[i].top = Wall[i-1].top + J_MAX;
  Wall[i].bottom = Wall[i-1].bottom + J_MAX;
 }

 Wall[46].left = Wall[45].left - J_MAX;
 Wall[46].right = Wall[45].right - J_MAX;
 Wall[46].bottom = Wall[45].bottom;
 Wall[46].top = Wall[45].top;

 for(i=47; i<=70; i++)
 {
  Wall[i].left = Wall[i-1].left - J_MAX;
  Wall[i].right = Wall[i-1].right - J_MAX;
  Wall[i].top = Wall[i-1].top;
  Wall[i].bottom = Wall[i-1].bottom;
 }

 Wall[71].left = Wall[70].left;
 Wall[71].right = Wall[70].right;
 Wall[71].bottom = Wall[70].bottom - J_MAX;
 Wall[71].top = Wall[70].top - J_MAX;

 for(i=72; i<=91; i++)
 {
  Wall[i].left = Wall[i-1].left;
  Wall[i].right = Wall[i-1].right;
  Wall[i].bottom = Wall[i-1].bottom - J_MAX;
  Wall[i].top = Wall[i-1].top - J_MAX;
 }

 // 스테이지의 벽은 가로세로 25칸으로 하자. // 라이트,보턴값이 25*25 = 625네.


 // 전역 문자열을 초기화합니다.
 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
 LoadString(hInstance, IDC_JIRUNGII, szWindowClass, MAX_LOADSTRING);
 MyRegisterClass(hInstance);

 // 응용 프로그램 초기화를 수행합니다.
 if (!InitInstance (hInstance, nCmdShow))
 {
  return FALSE;
 }

 hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_JIRUNGII));

 // 기본 메시지 루프입니다.
 while (GetMessage(&msg, NULL, 0, 0))
 {
  if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }

 }

 return (int) msg.wParam;
}

 

//
//  함수: MyRegisterClass()
//
//  목적: 창 클래스를 등록합니다.
//
//  설명:
//
//    Windows 95에서 추가된 'RegisterClassEx' 함수보다 먼저
//    해당 코드가 Win32 시스템과 호환되도록
//    하려는 경우에만 이 함수를 사용합니다. 이 함수를 호출해야
//    해당 응용 프로그램에 연결된
//    '올바른 형식의' 작은 아이콘을 가져올 수 있습니다.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
 WNDCLASSEX wcex;

 wcex.cbSize = sizeof(WNDCLASSEX);

 wcex.style   = CS_HREDRAW | CS_VREDRAW;
 wcex.lpfnWndProc = WndProc;
 wcex.cbClsExtra  = 0;
 wcex.cbWndExtra  = 0;
 wcex.hInstance  = hInstance;
 wcex.hIcon   = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_JIRUNGII));
 wcex.hCursor  = LoadCursor(NULL, IDC_ARROW);
 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wcex.lpszMenuName = MAKEINTRESOURCE(IDC_JIRUNGII);
 wcex.lpszClassName = szWindowClass;
 wcex.hIconSm  = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

 return RegisterClassEx(&wcex);
}

//
//   함수: InitInstance(HINSTANCE, int)
//
//   목적: 인스턴스 핸들을 저장하고 주 창을 만듭니다.
//
//   설명:
//
//        이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
//        주 프로그램 창을 만든 다음 표시합니다.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
 HWND hWnd;

 hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

 if (!hWnd)
 {
  return FALSE;
 }

 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);

 return TRUE;
}

//
//  함수: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  목적: 주 창의 메시지를 처리합니다.
//
//  WM_COMMAND - 응용 프로그램 메뉴를 처리합니다.
//  WM_PAINT - 주 창을 그립니다.
//  WM_DESTROY - 종료 메시지를 게시하고 반환합니다.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 
 HDC hdc;
 PAINTSTRUCT ps;
 HBRUSH MyBrush, OldBrush;
 int i = 0; // 지렁이 배열 인덱스 업!
 //int flag = false;

 output[0].left = 675;
 output[0].top = 20;
 output[0].right = 900;
 output[0].bottom = 40;

 output[1].left = 675;
 output[1].top = 60;
 output[1].right = 900;
 output[1].bottom = 80; 

 output[2].left = 675;
 output[2].top = 100;
 output[2].right = 900;
 output[2].bottom = 120;

 output[3].left = 675;
 output[3].top = 180;
 output[3].right = 900;
 output[3].bottom = 200;

 output[4].left = 675;
 output[4].top = 140;
 output[4].right = 900;
 output[4].bottom = 160;

 output[5].left = 675;
 output[5].top = 220;
 output[5].right = 900;
 output[5].bottom = 260;

 output[6].left = 675;
 output[6].top = 280;
 output[6].right = 900;
 output[6].bottom = 320;

 wsprintf(score_output, TEXT("획득점수 : %06d\n"), Score);
 wsprintf(stage_output, TEXT("스테이지 : %d\n"), Stage);
 wsprintf(life_output, TEXT("현재목숨 : %d개\n"), Life);
 wsprintf(bool_time, TEXT("게임재개 : 스페이스키\n"));
 wsprintf(item_counter, TEXT("%2d개를 더 드십시오.\n"), item_count);
 wsprintf(item_place, TEXT("아이템위치 : \n(%4d,%4d) - (%4d,%4d)\n"), (int)item.left, (int)item.top, (int)item.right, (int)item.bottom);
 wsprintf(user_place, TEXT("현재 위치 : \n(%4d,%4d) - (%4d,%4d)\n"), (int)rt_HEAD.left, (int)rt_HEAD.top, (int)rt_HEAD.right, (int)rt_HEAD.bottom);
 

 switch(iMessage)
 {
 case WM_CREATE :
  게임계속 :
  mode = 0; // 최초 방향모드 : 없음
  flag = 0; // 최초 방향모드 : 없음
  j_size = 3;  // 맨 최초 지렁이 몸 길이 : 3
  item_count = 27;
  SetTimer(hWnd, Stage, 400-(Stage-1)*60, NULL);  // 0.5초마다 지렁이 갱신
  rt = new RECT[30];
  rt[0].left = rt_start.left;
  rt[0].top = rt_start.top;
  rt[0].right = rt_start.right + J_MAX;
  rt[0].bottom = rt_start.bottom + J_MAX;

  item.left = (random(20)+1)*25;
  item.top =  (random(20)+1)*25;
  item.right = item.left + J_MAX;
  item.bottom = item.top + J_MAX;

  wsprintf(user_place, TEXT("현재 위치 : (%3d,%3d)-(%3d,%3d)\n"), (int)rt[0].left, (int)rt[0].top, (int)rt[0].right, (int)rt[0].bottom);

  for(i=1; i<j_size; i++)
  {
   rt[i].left = rt[i-1].left - J_MAX;
   rt[i].right = rt[i-1].right - J_MAX;
   rt[i].top = rt[i-1].top;
   rt[i].bottom = rt[i-1].bottom;
  }

  rt_HEAD = rt[0];

  return 0;
 
 case WM_COMMAND :
  switch(LOWORD(wParam))
  {
   case ID_NEWGAME :  // 새게임 메뉴를 누를 시
    KillTimer(hWnd, Stage);
    temp = MessageBox(hWnd, TEXT("게임을 새로 할까요?"), TEXT("새게임?"), MB_YESNO);
    if(temp == IDYES)
    {
     Score = 0;
     Stage = 1;
     Life = 3;
     KillTimer(hWnd, Stage);
     goto 게임계속;
    }
    else
    {
     SetTimer(hWnd, Stage, 400-(Stage-1)*60, NULL);
     break;
    }

   case ID_EXIT :   // EXIT 메뉴를 누를 시
    KillTimer(hWnd, Stage);
    delete []rt;
    goto 게임그만;
    break;

   case ID_INFOMATION : // 게임방법 메뉴를 누를 시
    KillTimer(hWnd, Stage);
    temp = MessageBox(hWnd, TEXT("##키조작##\n1. 방향 : 방향키를 누르세요.\n2. 타임 : 스페이스키\n\n##게임규칙##\n1. 맵에있는 아이템을 먹으면 지렁이가 길어집니다.\n2. 지렁이 몸체나 벽에 부딪치면 죽습니다.\n3. 최대한 먹으면 스테이지가 올라가고, 획득점수가 커집니다.\n4. 죽으면 점수를 잃게됩니다. 조심하세요."), TEXT("게임방법"), MB_OK);
    if(temp == IDOK)
     SetTimer(hWnd, Stage, 400-(Stage-1)*60, NULL);
    break;

   case ID_ABOUT :   // 정보 메뉴를 누를 시
    KillTimer(hWnd, Stage);
    temp = MessageBox(hWnd, TEXT("##하고싶은말##\n1. 제작 : 한해대 IT공학부 아치셈틀 KDS\n2. 도움주신분들 : 셈틀 여러분☆\n3. 11 연말전시회 출품작"), TEXT("제작자정보"), MB_OK);
    if(temp == IDOK)
     SetTimer(hWnd, Stage, 400-(Stage-1)*60, NULL);
    break;
  }
  return 0;

 case WM_TIMER :
  switch(wParam)
  {
  case 1 :
  case 2 :
  case 3 :
  case 4 :
  case 5 :
   switch(mode)
   {
   case LEFT :
    if(flag == RIGHT)
    {
     mode = RIGHT;
     break;
    }

    if(TIME) break;

    rt_HEAD = rt[0];

    for(i=j_size-1; i>0; i--)
    {
     rt[i].left = rt[i-1].left;
     rt[i].bottom = rt[i-1].bottom;
     rt[i].right = rt[i-1].right;
     rt[i].top = rt[i-1].top;
    }

    rt[0].right -= J_MAX;
    rt[0].left -= J_MAX;
    flag = LEFT;
    break;

   case RIGHT :
    if(flag == LEFT)
    {
     mode = LEFT;
     break;
    }

    if(TIME) break;

    rt_HEAD = rt[0];

    for(i=j_size-1; i>0; i--)
    {
     rt[i].left = rt[i-1].left;
     rt[i].bottom = rt[i-1].bottom;
     rt[i].right = rt[i-1].right;
     rt[i].top = rt[i-1].top;
    }

    rt[0].right += J_MAX;
    rt[0].left += J_MAX;
    flag = RIGHT;
    break;

   case UP :
    if(flag == DOWN)
    {
     mode = DOWN;
     break;
    }

    if(TIME) break;

    rt_HEAD = rt[0];

    for(i=j_size-1; i>0; i--)
    {
     rt[i].left = rt[i-1].left;
     rt[i].bottom = rt[i-1].bottom;
     rt[i].right = rt[i-1].right;
     rt[i].top = rt[i-1].top;
    }

    rt[0].top -= J_MAX;
    rt[0].bottom -= J_MAX;
    flag = UP;
    break;

   case DOWN :
    if(flag == UP)
    {
     mode = UP;
     break;
    }

    if(TIME) break;

    rt_HEAD = rt[0];

    for(i=j_size-1; i>0; i--)
    {
     rt[i].left = rt[i-1].left;
     rt[i].bottom = rt[i-1].bottom;
     rt[i].right = rt[i-1].right;
     rt[i].top = rt[i-1].top;
    }

    rt[0].top += J_MAX;
    rt[0].bottom += J_MAX;
    flag = DOWN;
    break;
   }

   if(rt[0].left == item.left && rt[0].bottom == item.bottom)
   {
    j_size++; // 지렁이도 늘리고
    Score += 500*Stage-200; // 점수도 늘리고
    item_count--;
    item.left = (random(20)+1)*25;
    item.top =  (random(20)+1)*25;// 아이템 자리도 새로 할당한다.
    item.right = item.left + J_MAX;
    item.bottom = item.top + J_MAX;
   } // 템 먹으면 아이템이 사라지고, 지렁이가 늘어난다.

   if(j_size == 30) //최대로 먹을 경우.
   {
    Score += 3000*Stage; // 점수 업
    KillTimer(hWnd, Stage);
    Stage++;    // 스테이지 업
    if(Stage == 5) // 스테이지 올클 시,
    {
     for(i=0; i<j_size; i++)
      rt[i].left = rt[i].top = rt[i].right = rt[i].bottom = 0;
     item.left = item.top = item.right = item.bottom = 0;
     rt_HEAD.left = rt_HEAD.top = rt_HEAD.right = rt_HEAD.bottom = 0;
     InvalidateRect(hWnd, NULL, TRUE);
     KillTimer(hWnd, Stage-1);
     delete []rt;
     temp = MessageBox(hWnd, TEXT("올클!! 더하실래요?"), TEXT("올클리어!!"), MB_YESNO);
     if(temp == IDYES)
     {
      Stage = 1;
      Score = 0;
      Life = 3;
      goto 게임계속;
     }
     else
      goto 게임그만;
    }
    delete []rt;
    goto 게임계속;
   }

   for(i=1; i<j_size; i++) // 몸과의 충돌을 검사한다.
    if(rt[0].left == rt[i].left && rt[0].top == rt[i].top)
    {
     Life--; //목숨도 잃고
     if(Score<3000)
      Score = 0;
     else
      Score -= 3000*Stage;
     KillTimer(hWnd, Stage);
     delete []rt;
     goto 게임계속;
     break;
    }

    for(i=0; i<=91; i++) // 벽에 충돌을 검사한다.
     if(rt[0].left == Wall[i].left && rt[0].top == Wall[i].top) // 충돌하면
     {
      Life--; //목숨도 잃고
      if(Score<3000)
       Score = 0;
      else
       Score -= 3000*Stage;
      goto 게임계속;
      break;
     }
     

     if(Life == 0)
     {
      for(i=0; i<j_size; i++)
       rt[i].left = rt[i].top = rt[i].right = rt[i].bottom = 0;
      item.left = item.top = item.right = item.bottom = 0;
      rt_HEAD.left = rt_HEAD.top = rt_HEAD.right = rt_HEAD.bottom = 0;
      InvalidateRect(hWnd, NULL, TRUE);
      KillTimer(hWnd, Stage);
      delete []rt;
      temp1 = MessageBox(hWnd, TEXT("지렁이가 모두 죽었습니다 ㅠㅠ\n 더 하실건지...?"), TEXT("게임오버!!"), MB_YESNO);
      if(temp1 == IDYES)
      {
       Stage = 1;
       Life = 3;
       Score = 0;
       goto 게임계속;
      }
      else
       goto 게임그만;
     }
  }

  InvalidateRect(hWnd, NULL, TRUE);
  return 0;

 case WM_KEYDOWN :
  switch(wParam)
  {
  case VK_SPACE :
   if(!TIME)
    TIME = TRUE;
   else
    TIME = FALSE;
   break;

  case VK_LEFT :
   mode = LEFT;
   break;

  case VK_RIGHT :
   mode = RIGHT;
   break;

  case VK_UP :
   mode = UP;
   break;

  case VK_DOWN :
   mode = DOWN;
   break;
  }
  //InvalidateRect(hWnd, NULL, TRUE);
  return 0;

 case WM_PAINT :
  hdc = BeginPaint(hWnd, &ps);
  MyBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
  OldBrush = (HBRUSH)SelectObject(hdc, MyBrush);
  Rectangle(hdc, rt[0].left, rt[0].top, rt[0].right, rt[0].bottom);
  Rectangle(hdc, item.left, item.top, item.right, item.bottom);  // 랜덤하게 아이템 출력
  SelectObject(hdc, OldBrush);
  MyBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
  OldBrush = (HBRUSH)SelectObject(hdc, MyBrush);
  for(i=0; i<=91; i++)
   Rectangle(hdc, Wall[i].left, Wall[i].top, Wall[i].right, Wall[i].bottom);
  SelectObject(hdc, OldBrush);
  for(i=1; i<j_size; i++)
   Rectangle(hdc, rt[i].left, rt[i].top, rt[i].right, rt[i].bottom);
  if(TIME)
   DrawText(hdc, bool_time, -1, &output[3], DT_LEFT);
  DrawText(hdc, score_output, -1, &output[0], DT_LEFT);
  DrawText(hdc, stage_output, -1, &output[1], DT_LEFT);
  DrawText(hdc, life_output, -1, &output[2], DT_LEFT);
  DrawText(hdc, item_counter, -1, &output[4], DT_LEFT);
  DrawText(hdc, user_place, -1, &output[5], DT_LEFT);
  DrawText(hdc, item_place, -1, &output[6], DT_LEFT);
  EndPaint(hWnd, &ps);
  return 0;


 case WM_DESTROY :
 게임그만 :
  PostQuitMessage(0);
  return 0;
 }

 return DefWindowProc(hWnd, iMessage, wParam, lParam);
}

// 정보 대화 상자의 메시지 처리기입니다.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
 UNREFERENCED_PARAMETER(lParam);
 switch (message)
 {
 case WM_INITDIALOG:
  return (INT_PTR)TRUE;

 case WM_COMMAND:
  if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  {
   EndDialog(hDlg, LOWORD(wParam));
   return (INT_PTR)TRUE;
  }
  break;
 }
 return (INT_PTR)FALSE;


트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://RFproject.egloos.com/tb/2882788 [도움말]

덧글

  • 레비테이션 2011/12/12 21:06 # 답글

    함수를 기능별로 쪼개면 더 좋은 소스가 될거 같아요
  • RF 2011/12/13 12:01 #

    물론 그렇긴 한데 ... 일주일만에 벼락치기하고 짜는 소스라 ... 그럴틈이 없었네요. (...)

    다음부턴 함수를 많이많이 만들어서 이용해야겠다는 생각이 듭니다. (...)
댓글 입력 영역