Здравствуйте. В игрострое я новичок, вот пытаюсь разобраться как в играх рендерится 2D тайловая карта игрового уровня. Применительно к OpenGL.
С ходу в голову пришло два варианта:
1. Создается плоскость в размер экрана и массив текстурных координат. Берем координаты из массива и натягиваем тайлы на плоскость.
2. Сдвигаем 0 системы координат в левый нижний/верхний угол (ортографическая проекция) и рисуем сетку QUADов, на которые натягиваем текстуры.
Вопрос, собственно, какой подход лучше, может вообще какой-то третий или четвертый до которого я еще не додумался? И где будет меньше проблем с прокруткой карты на экране?
я обычно делаю так.... массив[0..MapWidth,0..MapHeight], значения массива - тип тайла(или одно из значений записи, если нужна еще карта проходимости и прочая инфа), ну а дальше просто отображаешь квады, используя положение в массиве как координаты, а текстурные координаты берешь исходя из значения массива либо для каждого тайла отдельную текстуру.
Если нужно прокрутить карту, то просто сдвигаешь камеру.
IvanN
Первый вариант не понял совершенно, как простыми средствами можно натянуть на одну плоскость(полигон?) несколько тайлов с разными текстурными координатами? Только если шейдерами.
Предложенный CoderInTank вариант очень простой, и для 2д вполне годный.
База всегда примерно одна:
У тебя есть массив тайлов(каждый элемент - либо номер тайла в сете либо 2 текстурные координаты сдвига). При каждой отрисовке просто рисуется куча квадов стык в стык с динамически вычисляемыми из массива тайлов текстурными координатами.
Я обычно двигал камеру не трансформом, а делал чуть сложнее - заводил 2 координаты сдвига и при отрисовке каждого квада вычислял его координату, а потом отнимал сдвиг. Координаты сдвига и есть камера. Правда в более поздних реализациях Я сделал сразу управление матрицами камеры, так что получилось одно и то же.
Сдвигать камеру в ортопроекцию с основанием от угла можно, но совершенно не обязательно, для многих случаев центральное расположение куда удобнее.
Насчет оптимизаций можно предложить 2 основных.
Первая - сделать сразу массивы вершин, текстур, цветов и т.д. и рисовать все поле тайлов за раз. Будет быстрее и через glBegin / glEnd и если рисовать сразу буфферами. Подходит больше для неизменяемых тайлов, для персонажей и прочего надо координаты менять.
Вторая - самому считать видимое количество тайлов и отрисовывать поле не от начала до конца, а только то, что видно.
примерно так:
MapXB:= trunc(Camera.X / TILE_SIZE); // сдвиги камеры от начала
MapYB:= trunc(Camera.Y / TILE_SIZE);
MapXE:= MapXB + trunc(Window.Width / TILE_SIZE) + 1; // сдвиг к концу
MapYE:= MapYB + trunc(Window.Height/ TILE_SIZE) + 1;
// проверки на выход за границы массива
if MapXB < 0 then MapXB:= 0;
if MapYB < 0 then MapYB:= 0;
if MapXE > MAP_X then MapXE:= MAP_X;
if MapYE > MAP_Y then MapYE:= MAP_Y;
ViewLeft:= -Camera.X;
ViewTop := -Camera.Y;
// отрисовка только видимой области, от начала до конца будет от 0 до MAP_X / MAP_Y
for i:= MapXB to MapXE do
for j:= MapYB to MapYE do
begin
Resources.TileTexture.Draw(ViewLeft + i * 64, ViewTop + j * 64, Color, Map[i,j].Tile);
end;
Здесь сдвиги находятся для обычных оконных координат, если камера рисует от центра, то MapXB, MapYB, MapXE, MapYE находятся чуть по другому:
Я обычно двигал камеру не трансформом, а делал чуть сложнее - заводил 2 координаты сдвига и при отрисовке каждого квада вычислял его координату, а потом отнимал сдвиг.
Пошевелив полдня своей извилиной я примерно к этому и пришел. Вы подтвердили мои подозрения. Спасибо за пример кода. Мне так проще разбираться.
Первая - сделать сразу массивы вершин, текстур, цветов и т.д. и рисовать все поле тайлов за раз. Будет быстрее и через glBegin / glEnd и если рисовать сразу буфферами. Подходит больше для неизменяемых тайлов, для персонажей и прочего надо координаты менять.
Вторая - самому считать видимое количество тайлов и отрисовывать поле не от начала до конца, а только то, что видно.
Вот ради второй, собственно, я и задавал вопрос. На glBegin/Еnd я даже не смотрел, в стандарте они deprecated.
Подскажите мне еще одну вещь, пожалуйста:
я правильно понял, при вызове glBind* выполняется копирование данных и память занятую массивами можно освободить?
Никогда так не делал(оперативки всегда больше, чем видеопамяти, и проще при необходимости обновлять локальные массивы, поэтому храню. Да и вообще, на спичках экономить), но должно быть именно так.
Я так понимаю, уже с 3-го ОГЛ выпилены все иные способы передачи вершинных данных, кроме как бинд массивов в видеопамять.
Кстати - если дело касается только тайлов поверхности, то даже для сравнительно больших уровней(100*100) будет дешевле по производительности вывести все поле одним мешем, чем использовать оптимизацию процессора.
Для еще больших уровней оптимизацию применяют опять же не для отдельных квадов, а для кусков, к примеру 20*20
Ну текстура с тайлами/спрайтами скушает не так уж мало оперативы, особенно если поддерживать несколько разрешений экрана. Я в основном про это. И теоритически файл можно вообще на загружать в буфер оперативы, можно "сказать" процессору что "вот этот файл" есть страница памяти, передать на нужный его участок указатель а потом его закрыть. Для мобильных устройств это может дать нехилый прирост и сэкономить память. Вроде как есть расширения которые позволяют передавать сжатые текстуры? Но все это при условии что Bind действительно выполняет копирование.
Не на чем проверить, но загруженную в память текстуру/буфер можно удалять, по крайней мере я так делаю. Только не после bind, а после непосредственной загрузки. glTexImage2D, glBufferData и прочего.