Урок Инициализация в Windows я начинаю это пособие непосредственного с кода, разбитого на секции, каждая из которых будет подробно комментироваться. Первое, что вы должны сделать это создать проект в Visual C++



страница12/27
Дата17.11.2018
Размер7.85 Mb.
ТипУрок
1   ...   8   9   10   11   12   13   14   15   ...   27
anti ИСТИНА. Если это так, то мы разрешаем сглаживание линий.

 

  if (anti)                   // Anti TRUE?

  {

    glEnable(GL_LINE_SMOOTH); // Если так, то разрешить сглаживание



  }

 

Чтобы сделать игру немного проще я добавил специальный предмет. Этим предметом будут песочные часы. Когда Вы касаетесь песочных часов, противники замерзают на определенное количество времени. Следующий раздел кода ответственен за вывод песочных часов.



Для песочных часов мы используем x и y, чтобы позиционировать таймер, но в отличие от нашего игрока и противников, мы не используем fx и fy для точного позиционирования. Вместо этого мы будем использовать fx, чтобы следить, действительно ли часы отображаются. fx будет равно 0, если часы не видимы, и 1, если они видимы, и 2, если игрок коснулся часов. fy будет использоваться как счетчик, для отслеживания как давно видны или не видны часы.

Поэтому вначале мы проверяем, видны ли часы. Если нет, мы обходим код вывода часов. Если часы видны, мы сбрасываем матрицу вида модели, и позиционируем часы. Поскольку наша первая точка сетки находится на 20 пикселей слева, мы добавим 20 к hourglass.x умножим на 60. Мы умножаем hourglass.x на 60, потому что точки на нашей сетке слева направо отстоят друг от друга на 60 пикселей. Затем мы позиционируем песочные часы по оси Y. Мы добавляем 70 к hourglass.y умножаем на 40, потому что мы хотим начать рисовать на 70 пикселей вниз от верхнего края экрана. Каждая точка на нашей сетке сверху внизу отстоит друг от друга на 40 пикселей.

После того, как мы завершили позиционирование песочных часов, мы можем вращать их по оси Zhourglass.spinиспользуется, чтобы следить за вращением, так же как player.spin следит за вращением игрока. Прежде, чем мы начинаем выводить песочные часы, мы выбираем случайный цвет.

 

  if (hourglass.fx==1)              // Если fx=1 нарисовать песочные часы



  {

    glLoadIdentity();               // Сброс матрицы вида модели

    glTranslatef(20.0f+(hourglass.x*60),70.0f+(hourglass.y*40),0.0f); // Поместим часы

    glRotatef(hourglass.spin,0.0f,0.0f,1.0f);      // Вращаем по часовой стрелке

    glColor3ub(rand()%255,rand()%255,rand()%255);  // Зададим случайный цвет часов

 

Вызов функции glBegin(GL_LINES) сообщает OpenGL, что мы хотим нарисовать линии. Вначале мы смещаемся на 5 пикселей влево и вверх от нашего текущего положения. При этом мы получим левую верхнюю вершину наших песочных часов. OpenGL начнет рисовать линию от этого положения. Конец линии будет вправо и вниз на 5 пикселей от нашего первоначального положения. При этом наша линия, пройдет от левой верхней точки до правой нижней точки. Сразу же после этого мы выводим вторую линию, проходящую от правой верхней точки до левой нижней точки. Это даст нам символ 'X'. В конце мы соединяем две нижние точки вместе, и затем две верхние точки, чтобы создать объект типа песочных часов :).



 

    glBegin(GL_LINES);            // Начало рисования наших песочных часов линиями

      glVertex2d(-5,-5);          // Лево Верх песочных часов

      glVertex2d( 5, 5);          // Право Низ песочных часов

      glVertex2d( 5,-5);          // Право Верх песочных часов

      glVertex2d(-5, 5);          // Лево Низ песочных часов

      glVertex2d(-5, 5);          // Лево Низ песочных часов

      glVertex2d( 5, 5);          // Право Низ песочных часов

      glVertex2d(-5,-5);          // Лево Верх песочных часов

      glVertex2d( 5,-5);          // Право Верх песочных часов

    glEnd();                      // Конец рисования песочных часов

  }


 

Теперь мы рисуем нашего игрока. Мы сбрасываем матрицу вида модели, и позиционируем игрока на экране. Заметьте, что мы позиционируем игрока, используя fx и fy. Мы хотим, чтобы игрок двигался плавно, поэтому мы используем точное позиционирование. После позиционирования игрока, мы вращаем игрока относительно оси Z, используяplayer.spin. Мы задаем светло зеленный цвет и начинаем рисовать. Примерно так же как мы вывели песочные часы, мы выводим символ 'X'. Начинаем с левой верхней точки до правой нижней точки, затем с правой верхней точки до левой нижней точки.

 

  glLoadIdentity();              // Сброс матрицы вида модели



  glTranslatef(player.fx+20.0f,player.fy+70.0f,0.0f); // Перемещение игрока в точную позицию

  glRotatef(player.spin,0.0f,0.0f,1.0f);              // Вращение по часовой стрелки

  glColor3f(0.0f,1.0f,0.0f);      // Установить светло-зеленный цвет

  glBegin(GL_LINES);              // Начать рисование нашего игрока из линий

    glVertex2d(-5,-5);            // Лево Верх игрока

    glVertex2d( 5, 5);            // Право Низ игрока

    glVertex2d( 5,-5);            // Право Верх игрока

    glVertex2d(-5, 5);            // Лево Низ игрока

  glEnd();                        // Конец рисования игрока

 

Рисование не слишком разнообразных объектов может разочаровать. Я не хотел бы, чтобы игрок выглядел скучновато, поэтому я добавил следующий раздел кода, для того чтобы создать большое и быстро вращающиеся лезвие поверх игрока, которого мы только что нарисовали выше. Мы вращаем относительно оси Z лезвие наplayer.spin умножив его на 0.5f. Поскольку мы вращаем еще раз, будет казаться, что эта часть игрока перемещается немного быстрее, чем первая часть игрока.



После выполнения нового вращения, мы меняем цвет на более темный оттенок зеленного. Так, чтобы казалось, что игрок, сделан из различных цветов / частей. Затем мы выводим большой '+' сверху первой части игрока. Он будет больше, потому что мы используем -7 и +7 вместо -5 и +5. Также заметьте, что вместо рисования от одного угла до другого, я рисую эту часть игрока слева направо и сверху вниз.

 

  glRotatef(player.spin*0.5f,0.0f,0.0f,1.0f); // Вращаем по часовой



  glColor3f(0.0f,0.75f,0.0f);     // Задаем цвет игрока темно-зеленный

  glBegin(GL_LINES);              // Начало рисования нашего игрока используя линии

    glVertex2d(-7, 0);            // Влево от центра игрока

    glVertex2d( 7, 0);            // Вправо от центра игрока

    glVertex2d( 0,-7);            // Вверх от центра игрока

    glVertex2d( 0, 7);            // Вниз от центра игрока

  glEnd();                        // Конец рисования игрока

 

Теперь нам осталось вывести противников, и мы закончим рисование :). Вначале мы организуем цикл по числу всех противников, которые есть на текущем уровне. Мы вычисляем, сколько противников надо рисовать, умножив нашу текущую игровую стадию на внутренний игровой уровень. Вспомните, что каждый уровень имеет 3 стадии, и максимальное значение внутреннего уровня равно 3. Поэтому мы можем иметь максимум 9 противников.



Внутри цикла мы сбрасываем матрицу просмотра вида, и позиционируем текущего противника (enemy[loop1]). Мы позиционируем противника, используя его точные значения x и y (fx и fy). После позиционирования текущего противника мы задаем розовый цвет и начинаем рисование.

Первая линия пройдет от 0,-7 (7 пикселей верх от начального положения) к -7,0 (7 пикселей влево от начального положения). Вторая линия пройдет от -7,0 до 0,7 (7 пикселей вниз от начального положения). Третья линия пройдет от 0,7 до 7,0 (7 пикселей вправо от нашего начального положения), и последняя линия пройдет от 7,0 назад к началу первой линии (7 пикселей верх от начального положения). При этом на экране получится не вращающийся розовый алмаз.

 

  for (loop1=0; loop1<(stage*level); loop1++) // Цикл рисования противников



  {

    glLoadIdentity();             // Сброс матрицы просмотра вида

    glTranslatef(enemy[loop1].fx+20.0f,enemy[loop1].fy+70.0f,0.0f);

    glColor3f(1.0f,0.5f,0.5f);    // Сделать тело противника розовым

    glBegin(GL_LINES);            // Начало рисования противника

      glVertex2d( 0,-7);          // Верхняя точка тела

      glVertex2d(-7, 0);          // Левая точка тела

      glVertex2d(-7, 0);          // Левая точка тела

      glVertex2d( 0, 7);          // Нижняя точка тела

      glVertex2d( 0, 7);          // Нижняя точка тела

      glVertex2d( 7, 0);          // Правая точка тела

      glVertex2d( 7, 0);          // Правая точка тела

      glVertex2d( 0,-7);          // Верхняя точка тела

    glEnd();                      // Конец рисования противника

 

Мы не хотим, чтобы враги выглядели невзрачно, поэтому мы добавим темно красное вращающиеся лезвие ('X') сверху алмаза, который мы только что нарисовали. Мы вращаем его относительно оси Z на enemy[loop1].spin, и затем выводим 'X'. Мы начинаем с левого верхнего угла и рисуем линию к правому нижнему углу. Затем мы рисуем вторую линию с правого нижнего угла до левого нижнего угла. Эти две линии пересекают друг с другом, и при этом получается символ 'X' (или клинок... смешок).



 

    glRotatef(enemy[loop1].spin,0.0f,0.0f,1.0f); // Вращение клинка противника

    glColor3f(1.0f,0.0f,0.0f);    // Сделаем клинок противника красным

    glBegin(GL_LINES);            // Начало рисования клинка противника

      glVertex2d(-7,-7);          // Лево верх противника

      glVertex2d( 7, 7);          // Право низ противника

      glVertex2d(-7, 7);          // Лево низ противника

      glVertex2d( 7,-7);          // Право верх противника

    glEnd();                      // Конец рисования противника

  }


  return TRUE;                    // Все OK

}

 



Я добавил вызов функции KillFont() в конце KillGLWindow(). При этом мы будем уверены, что список отображения шрифта удален, когда окно будет уничтожено.

 

GLvoid KillGLWindow(GLvoid) // Корректное удаление окна



{

  if (fullscreen)           // Мы в полноэкранном режиме?

  {

    ChangeDisplaySettings(NULL,0);  // Если это так, то переключиться на рабочий стол



    ShowCursor(TRUE);       // Показать курсор мыши

  }


 

  if (hRC)                  // У нас есть контекст визуализации?

  {

    if (!wglMakeCurrent(NULL,NULL)) // Мы можем освободить контексты DC и RC?



    {

      MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",

                 MB_OK | MB_ICONINFORMATION);

    }


 

    if (!wglDeleteContext(hRC))     // Мы можем удалить RC?

    {

      MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",



                 MB_OK | MB_ICONINFORMATION);

    }


    hRC=NULL;               // Задать RC в NULL

  }


 

  if (hDC && !ReleaseDC(hWnd,hDC))  // Мы можем освободить DC?

  {

    MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",



               MB_OK | MB_ICONINFORMATION);

    hDC=NULL;               // Задать DC в NULL

  }

 

  if (hWnd && !DestroyWindow(hWnd)) // Мы можем уничтожить окно?



  {

    MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",

               MB_OK | MB_ICONINFORMATION);

    hWnd=NULL;              // Задать hWnd в NULL

  }

 

  if (!UnregisterClass("OpenGL",hInstance)) // Мы можем удалить регистрацию класса?



  {

    MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",

               MB_OK | MB_ICONINFORMATION);

    hInstance=NULL;         // Задать hInstance в NULL

  }

 

  KillFont();               // Уничтожить фонт, который мы сделали



}

 

Код CreateGLWindow() и WndProc() не изменил, поэтому идите вниз пока не встретите следующий раздел кода.



 

int WINAPI WinMain(

          HINSTANCE hInstance,     // Экземпляр

          HINSTANCE hPrevInstance, // Предыдущий экземпляр

          LPSTR     lpCmdLine,     // Параметры командной строки

          int       nCmdShow)      // Показать состояние окна

{

  MSG  msg;        // Структура сообщения окна



  BOOL done=FALSE; // Булевская переменная выхода из цикла

 

  // Запросим пользователя какой режим отображения он предпочитает



  if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?",

      "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)

  {

    fullscreen=FALSE;              // Оконный режим



  }

 

 



Этот раздел кода не много изменился. Я изменил заголовок окна на " Урок по линиям NeHe", и я добавил вызов функции ResetObjects(). При этом игрок позиционируется в левой верхней точке сетки, и противникам задаются случайные начальные положения. Враги будут всегда стартовать, по крайней мере, на 5 ячеек от Вас. ФункцияTimerInit() корректно инициализирует таймер.

 

  // Создадим наше окно OpenGL



  if (!CreateGLWindow("NeHe's Line Tutorial",640,480,16,fullscreen))

  {


    return 0;              // Выходим если окно не было создано

  }


 

  ResetObjects();          // Установка стартовых позиций Игрока / Противников

  TimerInit();             // Инициализация таймера

 

  while (!done) // Цикл, который продолжается пока done=FALSE



  {

    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Есть ожидаемое сообщение?

    {

      if (msg.message==WM_QUIT) // Мы получили сообщение о выходе?



      {

        done=TRUE; // Если так done=TRUE

      }

      else // Если нет, продолжаем работать с сообщениями окна



      {

        TranslateMessage(&msg); // Переводим сообщение

        DispatchMessage(&msg);  // Отсылаем сообщение

      }


    }

    else // Если сообщений нет

    {

 

Теперь cделаем работу по синхронизации. Вначале запомните, что перед выводом нашей сцены, мы запоминаем время в переменной с плавающей запятой, которая названа start. Затем мы выводим сцену и переключаем буфера.



Сразу же после того, как мы переключили буфера, мы делаем задержку. Мы делаем при помощи сравнения текущего значения таймера (TimerGetTime()) с нашим стартовым значением плюс шаг скорости игры умноженный на 2. Если текущее значение таймера меньше чем значение, которое мы хотим, мы повторяем цикл, пока текущее значение таймера не будет равно или большее чем значение, которое мы хотим. Это ДЕЙСТВИТЕЛЬНО замедляет быстрые системы.

Поскольку мы используем шаги скорости (с помощью adjust) программа будет всегда выполняться с той же самой скоростью. Например, если бы наш шаг скорости был бы 1, мы ждали бы, пока таймер не был равен или больше чем 2 (1*2). Но если мы увеличим шаг скорости до 2 (что вызовет перемещение игрока на удвоенное число пикселей одновременно), задержка увеличиться на 4 (2*2). Поэтому даже при том, что мы перемещаем в два раза быстрее, задержка также удвоится, поэтому игра будет выполняться с той же самой скоростью :).

Есть один прием, который многие делают – берут текущее время, и вычитают из него старое время, чтобы выяснить, сколько времени прошло. Затем они перемещают объекты на некоторое расстояние, основанное на значении времени, которое прошло. К сожалению, я не могу этого сделать в этой программе, потому что точное перемещение должно быть таким, чтобы игрок мог попасть на линии сетки. Если текущая точная позиция x была 59, и компьютер решил переместить игрока, на два пикселя, игрок никогда не попадет на вертикальную линию в позиции 60 на сетке.

 

      float start=TimerGetTime(); // Захват времени до начала рисования



 

      // Нарисовать сцену. Отследить нажатие на клавишу ESC и

      // приход сообщения о выходе из DrawGLScene()

      if ((active && !DrawGLScene()) || keys[VK_ESCAPE])  // Активно?  Выход принят?

      {

        done=TRUE;                // ESC или DrawGLScene сигнализирует о выходе



      }

      else                        // Не время выходить, надо обновить сцену

      {

        SwapBuffers(hDC);         // Переключить буфера (Двойная Буферизация)



      }

 

      // Отбросим циклы на быстрой системе



      while(TimerGetTime()

 

Следующий код мало изменился. Я изменил заголовок окна на "Урок NeHe по линиям".



 

      if (keys[VK_F1])          // Была нажата кнопка F1?

      {

        keys[VK_F1]=FALSE;      // Если так - установим значение FALSE



        KillGLWindow();         // Закроем текущее окно OpenGL 

        fullscreen=!fullscreen; // Переключим режим "Полный экран"/"Оконный"

        // Заново создадим наше окно OpenGL

        if (!CreateGLWindow("NeHe's Line Tutorial",640,480,16,fullscreen))

        {

          return 0;             // Выйти, если окно не было создано

        }

      }


 

В этой секции кода проверяется, нажата ли клавиша и не удерживается ли она. Если 'A' нажата, ap станет ИСТИНА (сообщая нашей программе, что ‘A’ опущена вниз), и anti переключается из ИСТИНЫ в ЛОЖЬ или из ЛОЖИ в ИСТИНУ. Помните, что значение anti проверяется в коде рисования, чтобы узнать включено ли сглаживание или нет.

Если клавиша 'A' была отпущена (ЛОЖЬ) тогда значение ap будет ЛОЖЬ сообщая программе, что эта клавиша больше не удерживается.

 

      if (keys['A'] && !ap) // Если клавиша 'A' нажата и не удерживается



      {

        ap=TRUE;            // ap равно TRUE

        anti=!anti;         // Переключим сглаживание

      }


      if (!keys['A'])       // Если клавиша 'A' отпущена

      {


        ap=FALSE;           // ap равно FALSE

      }


 

Теперь как перемещать противников. Я стремился сделать этот раздел кода как можно проще. Здесь очень немного логики. В основном, враги следят за тем, где Вы находитесь, и они двигаются в этом направлении. Поскольку я проверяю фактические x и y позиции игроков и не проверяю точные значения, игрокам может казаться, что враги имеют некоторый интеллект. Враги могут видеть, что Вы наверху экрана. Но к тому времени, когда точные значения совпадут с верхом экрана, Вы можете уже быть в другом месте. Это заставляет их иногда двигаться мимо Вас, прежде чем они поймут, что Вы больше не там, где они думают. Они как будто глухи, но поскольку они иногда двигаются мимо Вас, Вы может оказаться в окружении.

Мы начнем с проверки того, что игра еще не закончена, и что окно (если в оконном режиме) является все еще активным. При помощи этой проверки active делаем так, чтобы враги не двигались, когда окно свернуто. Это даст Вам удобную паузу, когда Вы захотите перерваться :).

После того, как мы проверили то, что враги должны быть перемещены, мы запускаем цикл. В этом цикле мы проходим по всем видимым противникам. Снова мы вычисляем, сколько противников должно быть на экране, умножая текущую стадию на текущий внутренний уровень.

 

      if (!gameover && active) // Если игра не окончена и программа активна – передвинуть объекты



      {

        for (loop1=0; loop1<(stage*level); loop1++)  // Цикл по противникам

        {

 

Теперь мы перемещаем текущего противника (enemy[loop1]). Вначале мы проверяем меньше ли x позиция противника, чем x позиция игрока, и мы контролируем, что точная позиция y противника выровнена с горизонтальной линией. Мы не можем перемещать противника влево или вправо, если он не на горизонтальной линии. Если бы мы сделали, враг прошел бы через середину ячейки, сделав игру очень сложной :).



Если x позиция противника меньше, чем x позиция игрока, и точная позиция y противника выровнена с горизонтальной линией, мы передвигаем противника по x на одну клетку ближе к текущей позиции игрока.

Подобным образом мы делаем это, чтобы переместить противника, влево, вниз и вверх. При перемещении вверх или вниз, мы должны проконтролировать, что точная позиция x противника выровнена с вертикальной линией. Мы не хотим, чтобы враг срезал через верх или низ ячейки.

Примечание: изменение x и y позиций противников не перемещает противника на экране. Вспомните, что, когда мы рисовали противников, мы использовали точные позиции, чтобы разместить противников на экране. Изменение x и yпозиций только сообщает нашей программе, где мы ХОТИМ, чтобы противники двигались.

 

          if ((enemy[loop1].x


          {

            enemy[loop1].x++;    // Сдвиг противника вправо

          }

 

          if ((enemy[loop1].x>player.x) && (enemy[loop1].fy==enemy[loop1].y*40))



          {

            enemy[loop1].x--;    // Сдвиг противника влево

          }

 

          if ((enemy[loop1].y


          {

            enemy[loop1].y++;    // Сдвиг противника вниз

          }

 

          if ((enemy[loop1].y>player.y) && (enemy[loop1].fx==enemy[loop1].x*60))



          {

            enemy[loop1].y--;    // Сдвиг противника вверх

          }

 

В этом коде фактически реализовано перемещение. Мы проверяем, больше ли значение переменной delay, чем 3 минус текущий внутренний уровень. Т.е., если наш текущий уровень равен 1 программа, сделает цикл 2 раза (3-1) прежде, чем враги фактически сдвинутся. На уровне 3 (самый высокий уровень) враги будут перемещаться с той же самой скоростью как игрок (без задержек). Мы также контролируем, что hourglass.fx не равен 2. Вспомните, еслиhourglass.fx равно 2, то это означает, что игрок коснулся песочных часов. Враги при этом не должны перемещаться.



Если delay больше, чем с 3-level, и игрок не коснулся песочных часов, мы перемещаем противников, изменения точные позиции противников (fx и fy). Вначале мы присваиваем delay снова 0 так, чтобы мы могли запустить счетчикdelay снова. Затем мы запускаем цикл, который проходит по всем видимым противникам (stage * level).

 

          // Если наша задержка истекла, и игрок не коснулся песочных часов



          if (delay>(3-level) && (hourglass.fx!=2))

          {

            delay=0;          // Сброс задержки

            for (loop2=0; loop2<(stage*level); loop2++)  // Цикл по всем противникам

            {

 

Для перемещения противников, мы проверяем, нужно ли текущего противника (enemy[loop2]) двигать в заданном направлении, чтобы установить противника в x и y позицию, которую мы хотим. В первой строке ниже мы проверяем, является ли точная позиция противника по оси X меньше, чем нужная позиции x умноженная на 60. Вспомните, что размер каждой клетки равен 60 пикселям по горизонтали. Если точная позиция x меньше, чем xпозиция противника умноженная на 60, мы сдвигаем противника направо на steps[adjust] (скорость нашей игры зависит от значения adjust). Мы также вращаем противника по часовой стрелке, чтобы казалось, что он катится направо. Для этого мы увеличиваем 


Каталог: forum
forum -> Лечение гепатозов
forum -> Система ведения овцеводства в крестьянско-фермерских и личных хозяйствах населения
forum -> Yaesuft-857 (ft-897) – переходник для подключения cat интерфейса к микрофонному разъему трансивера
forum -> Васильев Владимир Юрьевич
forum -> tl-wr1043ND Беспроводной гигабитный маршрутизатор серии n copyright & trademarks
forum -> Цели и задачи Контакта rtf docx
forum -> Назовите не менее трех результатов революции 1905- 1907гг. Приведите не менее трех положений, отражающих значение революции для отечественной истории начала 20 века


Поделитесь с Вашими друзьями:
1   ...   8   9   10   11   12   13   14   15   ...   27


База данных защищена авторским правом ©vossta.ru 2019
обратиться к администрации

    Главная страница