netlib.narod.ru< Назад | Оглавление | Далее >

Сетки в качестве уровней

Возможно, вы сочтете меня сумасшедшим, но нет ничего проще использования сетки в качестве уровня. В этом случае вы легко можете использованть базовые возможности графического ядра. Недостаток использования единой сетки в том, что вы визуализируете весь уровень сразу, а значит даже невидимые части пройдут через конвейер визуализации, чтобы быть отброшенными. Говоря по-русски, вы будете зря терять время.

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

Чтобы увидеть, о чем я говорю, загрузите демонстрационную программу MeshLvl, находящуюся на прилагаемом к книге CD-ROM (посмотрите в папке BookCode\Chap08\MeshLvl\). Программа показывает загрузку нескольких сеток и их совместное рисование в виде большого уровня. На рис. 8.1 показано окно программы MeshLvl.


Рис. 8.1. Программа MeshLvl в действии

Рис. 8.1. Программа MeshLvl в действии. Обратите внимание, что для рисования различных комнат используются только две сетки


Вот как работает программа MeshLvl: главная функция (которую мы разберем здесь) загружает две сетки — коридор и комнату. Каждый коридор и комната в игре представлены объектом cObject из графического ядра, с которым в свою очередь связана соответствующая сетка. Для визуализации сцены вам необходимо ориентировать коридоры и комнаты относительно местоположения зрителя и нарисовать их, используя функции визуализации сеток, такие как cObject::Render. В демонстрационной программе вы можете использовать клавиши управления курсором для перемещения по коридорам и комнатам и мышь для вращения камеры.

Загрузка уровней

Как я упоминал в предыдущем разделе, уровни конструируются из различных сеток, которые необходимо загрузить. Для наших целей эти сетки загружаются в набор объектов сеток графического ядра (cMesh) и экземпляры класса трехмерных объектов (cObject). Для загрузки используемых в программе MeshLvl сеток и их назначения трехмерным объектам применяется следующий код:

// Объявления в классе
cMesh m_LevelMeshes[2];
cObject m_LevelObjects[8];

// ... далее в коде инициализации

// Загрузка сеток комнат
m_RoomMeshes[0].Load(&m_Graphics,
                     "..\\LevelData\\Corridor.x",
                     "..\\LevelData\\");
m_RoomMeshes[1].Load(&m_Graphics,
                     "..\\LevelData\\Room.x",
                     "..\\LevelData\\");

// Установка объектов комнат
m_RoomObjects[0].Create(&m_Graphics, &m_RoomMeshes[1]);
m_RoomObjects[1].Create(&m_Graphics, &m_RoomMeshes[0]);
m_RoomObjects[2].Create(&m_Graphics, &m_RoomMeshes[1]);
m_RoomObjects[3].Create(&m_Graphics, &m_RoomMeshes[0]);
m_RoomObjects[4].Create(&m_Graphics, &m_RoomMeshes[0]);
m_RoomObjects[5].Create(&m_Graphics, &m_RoomMeshes[1]);
m_RoomObjects[6].Create(&m_Graphics, &m_RoomMeshes[0]);
m_RoomObjects[7].Create(&m_Graphics, &m_RoomMeshes[1]);

В предшествующем коде показана загрузка двух сеток: Corridor.x и Room.x. Для хранения сеток используется пара объектов cMesh. Затем создается восемь объектов (каждый из которых является экземпляром cObject), представляющих комнаты (или соединяющие их коридоры). Теперь комнаты и коридоры необходимо ориентировать:

// m_RoomMeshes[0], [2], [5], [7] используют сетку комнаты (room.x)
// m_RoomMeshes[1], [3], [4], [6] используют сетку коридора (corridor.x)
m_RoomObjects[0].Move(-2000.0f, 0.0f,  2000.0f);
m_RoomObjects[1].Move(    0.0f, 0.0f,  2000.0f);
m_RoomObjects[2].Move( 2000.0f, 0.0f,  2000.0f);
m_RoomObjects[3].Move(-2000.0f, 0.0f,     0.0f);
m_RoomObjects[4].Move( 2000.0f, 0.0f,     0.0f);
m_RoomObjects[5].Move(-2000.0f, 0.0f, -2000.0f);
m_RoomObjects[6].Move(    0.0f, 0.0f, -2000.0f);
m_RoomObjects[7].Move( 2000.0f, 0.0f, -2000.0f);

m_RoomObjects[0].Rotate(0.0f,  0.00f, 0.0f);
m_RoomObjects[1].Rotate(0.0f,  1.57f, 0.0f);
m_RoomObjects[2].Rotate(0.0f,  1.57f, 0.0f);
m_RoomObjects[5].Rotate(0.0f, -1.57f, 0.0f);
m_RoomObjects[6].Rotate(0.0f, -1.57f, 0.0f);
m_RoomObjects[7].Rotate(0.0f,  3.14f, 0.0f);

Ну вот! Все готово к рисованию комнат.

Рисование комнат

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

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

Клавиши управления курсором перемещают точку просмотра, а перемещение мыши изменяет направление взгляда. В каждом кадре экранные координаты мыши (по оси X) преобразуются в значение поворота относительно оси Y. При перемещении мыши влево или вправо соответственно влево иди вправо поворачивается направление взгляда. Поворот относительно оси Y сохраняется в объекте cCamera, представляющем местоположение зрителя в трехмерном мире.

Когда пользователь нажимает клавишу управления курсором, вращение по оси Y используется для перемещения зрителя. Зритель перемещается вперед, назад, влево или вправо на расстояние, определяемое по времени, прошедшему с предыдущего вызова Frame до текущего вызова Frame (оно хранится в переменной Elapsed). Направление перемещения определяется по повороту относительно оси Y, вычисляемому на основе координаты X мыши.

Взгляните на функцию, которая обрабатывает визуализацию и ввод:

BOOL cApp::Frame()
{
    static DWORD Timer = timeGetTime();
    unsigned long Elapsed;
    float XMove, ZMove;
    short i;

    // Вычисляем прошедшее время
    // (плюс повышение скорости)
    Elapsed = (timeGetTime() - Timer) * 2;
    Timer = timeGetTime();

    // Получаем ввод
    m_Keyboard.Read();
    m_Mouse.Read();

    // Обрабатываем ввод и обновляем все.
    // ESC завершает программу
    if(m_Keyboard.GetKeyState(KEY_ESC) == TRUE)
        return FALSE;

    // Обработка перемещения
    XMove = ZMove = 0.0f;

    // Обработка ввода с клавиатуры для
    // перемещения зрителя
    if(m_Keyboard.GetKeyState(KEY_UP) == TRUE) {
        XMove = (float)sin(m_Camera.GetYRotation()) * Elapsed;
        ZMove = (float)cos(m_Camera.GetYRotation()) * Elapsed;
    }
    if(m_Keyboard.GetKeyState(KEY_DOWN) == TRUE) {
        XMove = -(float)sin(m_Camera.GetYRotation()) * Elapsed;
        ZMove = -(float)cos(m_Camera.GetYRotation()) * Elapsed;
    }
    if(m_Keyboard.GetKeyState(KEY_LEFT) == TRUE) {
        XMove = (float)sin(m_Camera.GetYRotation() - 1.57f) * Elapsed;
        ZMove = (float)cos(m_Camera.GetYRotation() - 1.57f) * Elapsed;
    }
    if(m_Keyboard.GetKeyState(KEY_RIGHT) == TRUE) {
        XMove = (float)sin(m_Camera.GetYRotation() + 1.57f) * Elapsed;
        ZMove = (float)cos(m_Camera.GetYRotation() + 1.57f) * Elapsed;
    }

    // Обновление координат зрителя
    m_XPos += XMove;
    m_ZPos += ZMove;

    // Размещаем камеру и поворачиваем ее
    // согласно перемещению мыши
    m_Camera.Move(m_XPos + XMove, 400.0f, m_ZPos + ZMove);
    m_Camera.RotateRel((float)m_Mouse.GetYDelta() / 200.0f,
                       (float)m_Mouse.GetXDelta() / 200.0f,
                       0.0f);

    // Устанавливаем камеру
    m_Graphics.SetCamera(&m_Camera);

    // Визуализируем все
    m_Graphics.Clear(D3DCOLOR_RGBA(0,64,128,255));
    if(m_Graphics.BeginScene() == TRUE) {

        // Визуализируем каждую комнату
        for(i = 0; i < 8; i++)
            m_RoomObjects[i].Render();
        m_Graphics.EndScene();
    }
    m_Graphics.Display();
    return TRUE;
}

Усовершенствование базовой техники

Рисовать трехмерные миры не трудно, но появляется одна проблема. Хотя вы можете рисовать столько сеток, сколько хотите (персонажей, объекты и уровни), вы заметите, что ваш трехмерный движок замедляется с появлением каждого нового объекта.

Каждый добавленный в конвейер полигон замедляет всю работу, поскольку его данные необходимо обработать. Это справедливо для всех полигонов любых сеток, включая сетки, которые невидимы (находятся позади вас).

Идеально было бы отбросить сетки, находящиеся вне поля зрения, чтобы Direct3D имел дело только с теми сетками (и полигонами), которые видимы — это немного прибавит скорости. Но как узнать, находится ли что-либо в поле зрения до обработки? Здесь в игру вступает пирамида видимого пространства.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz