Почему после последней строки почти везде отсутствует перевод строки? Это же неполноценная строка получается!
Вот, в wiki нарыл
Даже в современных изданиях ОС UNIX и Linux отсутствие перевода строки в конце системных конфигурационных файлов приводит к тому, что последняя строка не учитывается[2], а казалось бы правильно составленный файл не работает, представляясь головоломкой для пользователя, не предупреждённого об этой самобытной особенности. См. раздел Конец строки.
entryway
> после последней строки почти везде отсутствует перевод строки?
По стандарту С это вообще UB, компилятор может смело винт стирать. Но про С++ мне тут недавно говорили, что эта проблема в стандарте решена.
гцц с -pedantic не ругается, значит норм )
я не знаю про переводы
кому интересно, в icweapons.h лежат ТТХ оружия
TarasB
> > Если вся память задана фиксированно прямо из кода, то это и есть говнокод в
> > чистом виде, так как такую игру нельзя ни модифицировать ни доработать
> лолшто, сто лет игры только так и писали, чёто с модификациями и доработками
> всё в порядке было
Посмотри на код UE и удивись ...
innuendo
Почитай логику и роль единичного примера и удивись.
TarasB
> Почитай логику и роль единичного примера и удивись.
Есть не только UE, есть иные. Хотя некоторые игры для конзолей так и делают до сих пор. С такими бюджетами и огромной командой можно переписывать код по многу раз )
TarasB
> Почитай
TarasB
> и удивись.
Ты в курсе, что раньше игры писались только на asm ?
Короче, я решил написать статейку про свою игру, про то, как сделать такие прикольные картинки.
Итак, часть первая.
В старых играх часто картинка лучше выглядит в низком разрешении, потому что это скрывает угловатость геометрии, это тоже приём такой. Но я не про него.
Есть очень избитый приём, я не знаю как он называется, но я дохрена часто его замечаю. Посмотрите на эту картинку:
тут только два цвета - тёмно-зелёный и розово-жёлтый. По сути цвета всех пикселей находятся примерно на одной плоскости, если изобразить их в кубе RGB, то есть пространство цветов получается не трёхмерное, а двухмерное.
Для особо криворуких удобна оранжево-голубая цветовая плоскость:
то есть это все цвета, для которых
G=(R+B)/2;
ну можно немного от неё в стороны уходить, но недалеко.
Любая вырвиглазная картинка становится охренеть какой стильной, если её цвета спроецировать на эту плоскость.
Наверное, у художников этот приём имеет название, типа "цветовая компрессия" или что-то такое, но я не в курсе.
Смотрите, вот вырвиглазный оригинал:
а теперь я беру и нахрен проецирую на эту оранжево-голубую плоскость:
вааааау как стильно!!!!
Возьмите любой фотоконкурс, наверняка в призёрах будет такая оранжево-голубая фигня. Короче, отличный приёмчик для нубов. Я тоже не побрезговал)
можно другой фильтр применить, тоже стильно выходит:
Короче, одна из причин няшной картинки в игре - это кастрированная палитра. Вообще 8-битный фон был вынужденной мерой, потому что фон является огромной картинкой на 14 мегабайт, если б не 8 бит, он был бы намного больше. Ну и ограниченный набор пришлось как-то придумывать, как получше разметить. Ну и я как раз применил такую вот почти плоскую палитру:
// первые 11 цветов служебные for (int i=11; i<256; ++i) { const int r = ( i-11)%7; const int g = ( i-11)/7%5; const int b = ( i-11)/35; colorTable[i] = tbal::Color ( r*28+g*8+b*3+37, r*14+g*18+b*13+21, r*6+g*10+b*29+5); }
Наглядно вот так:
цветов хоть завались, выбирай любой
Ну да, она не совсем плоская, но рядом
Ещё видно, что у меня нет чёрного, есть коричневый, это необходимо, чтобы провалы не были невидимыми, а ещё это добавляет ламповости, это увеличивает долю жёлтого цвета ламп накаливания.
Тот же вырвиглазный скрин, но в этой палитре, будет таким:
мутновато, но тут контрастность надо как-то поправить, это да, и нифига не заметно, что 8 бит
TarasB
> вааааау как стильно!!!!
Если честно, то стало выглядеть как говно.
Для зерна-по-умолчанию (15071987) генерируется такая топология:
треугольнички - это однонаправленные стрелочки, то есть можно пройти только в одну сторону. При этом горизонтальные односторонние переходы на самом деле становятся двусторонними после первого же прохода с нужной стороны (дверь открылась и так и осталась), но генератор это не учитывает. Односторонние проходы наверх в генераторе запрещены.
Цветные линии - это двери, требующие ключ соответствующего цвета (буква "К"). Цветные клетки - это до которых можно добраться только зайдя за соответствующую линию.
Итак, как такую картинку сгенерировать?
Ну, для начала надо построить один кольцевой маршрут что ли.
Итак, план такой.
Мы выходит из клетки (0,0) и идём тупо куда глаза глядят. Приоритетное направление - на новые пустые клетки.
При этом горизонтальные переходы мы делаем двусторонними при переходе в новые клетки и односторонними при переходе на старую, вертикальные переходы вверх всегда двусторонние, вертикальные переходы вниз всегда односторонние.
Условие конца - дойти до клетки (0,0), либо до другой клетки, где мы были до первого необратимого хода (это клетка (0,0) и ещё две следующие). В алгоритме стоит ограничение на число попыток, и через эн шагов мы попали хрен знает куда, а вовсе не в начало:
Ну, в таком случае алгоритм берёт и просто прокладывает кратчайший путь к началу, при этом двигаясь по клеткам, где мы уже были.
В итоге получается так:
некоторые проходы стали обратимыми, плюс добавились два новых прохода, за счёт рандома один из них стал односторонним, один стал двусторонним.
Итак, петля замкнулась. Получился уже неплохой лабиринт, при этом полностью проходимый из любой точки в любую.
Но нам этого мало. Давайте к этой петле новую присвинячим. Берём произвольную посещённую точку и идём из неё в случайную сторону, приоритетно выбирая путь в сторону пустой клетки. Через эн шагов, если мы не вернулись на старый маршрут, идём назад кратчайшим путём к клетке, начинающей новый маршрут, избегая предыдущих маршрутов.
Итак, добавили вторую петлю:
я покрасил в цвет клетки второй петли.
Уже сейчас можно ставить дверь-на-ключе перед жёлтыми клетками, как видите. При этом жёлтый ключ ставится на любую чёрную клетку.
Повторять, пока не надоест:
я ещё один новый маршрут покрасил цветом.
Всего число маршрутов может превышать число ключей, но можно объединять несколько соседних маршрутов в один:
клетки каждого маршрута покрашены одним цветом. Ещё сделаны кой-какие ограничения, чтобы переходы на новый маршрут не были вертикальными, но это нюансы уже.
Осталось повесить двери-на-ключах (угадайте куда) и расставить сами ключи.
Алгоритм расстановки ключей простой: жёлтый ключ можно ставить на любую чёрную клетку, красный - на любую жёлтую, зелёный - на любую красную итд. Но я ставлю не на любую. Я выбираю самую дальнюю клетку от предыдущего ключа, МУАХАХАХАХАХА.
В итоге получается та картинка, что вы видели изначально.
Дальше мы каждой клетке назначаем номер, в каком порядке её проходить. В зависимости от номера выбирается, какие ништяки в эту клетку размещать и каких монстров. Для клеток, расположенных в стороне от кратчайшего пути, слегка понижается вероятность напороться на кучу монстров и повышается до 100% вероятность найти ништяк. То есть боковые отростки и тупички, как правило, хранят хорошие вещи. Так делаются "секретики".
Алгоритм подходит и для 3Д, как вы видите.
Прежде чем нагенерить пещены, надо порезать уровень на сегменты. Сегмент - это горизонтальная часть уровня от стены до стены. Итак, режем уровень на сегменты, вот я все сегменты нарисовал:
Для каждого сегменты выбирается его внешний вид. Для верхних сегментов всегда выбирается лес, для нижних всегда выбирается ад.
Для остальных - случайным образом либо туннель с кирпичными стенами, либо пещера просто, либо туннель с бетонными стенами, либо туннель в клеточку, либо лавовая пещера. Ну, не совсем случайно, выбор зависит от того, в начале или в конце игры мы доходим до этого сегмента.
Для туннелей выбор формы пещеры простой - тупо прямоугольник, высота случайная в небольших пределах.
Для пещер хитрее. Форма пещеры задаётся двумя линиями y(x), одна линия для пола, другая для потолка.
Линии можно генерировать перлином наверное, но я сделал иначе.
Я генерирую координаты последовательно.
Сначала генерируется пол. Потом аналогично потолок, только он прибавляется к полу и у краёв сегмента делается наклонный потолок.
Самый простой способ - это
y[x+1] = y[x] + random(-1..1)
но это приведёт к отсутствию длинных наклонных участков, это не очень красиво
я сделал так, чтобы рандом как бы запоминал профиль последнего участка
я запоминаю профиль в отдельной переменной
этот профиль случайно меняется на random(-1..1) при переходе к следующей координате, при этом он ограничено диапазоном -3..3
приращение берётся как
(профиль + random( -2..2)).clamp( -1..1)
то есть к профилю прибавляем этот рандом, результат урезаем между -1 и 1, и это и есть то, на сколько надо изменить координату при переходе к следующему пикселю.
Так я генерирую кривую линию, идущую из точки x1 вправо. Аналогично генерирую линию из x2 влево. А потом миксую их, причём с разным весом. Вблизи точки x1 вес первой линии единичен, вблизи точки x2 вес второй линии единичен.
Сгенерировался такой сегмент короче (хранится как два массива y[x]):
Теперь надо сегменты раскидать по карте. Ну кидаем их снизу вверх. Адские сегменты пихаем на самый низ карты. Каждый следующий стараемся расположить как можно ниже, чтобы он не пересекался с нижестоящими сегментами и оставался запас для толстого пола.
Раскидав сегменты, получаем такую картинку:
То есть просто берём и сегменты кидаем друг на друга, это просто.
Теперь ещё надо заполнить вертикальные проходы. Форма прохода зависит от того, какие сегменты он связывает. Если нижний проход - туннель (либо снизу ад, а сверху туннель), то проход прямой, иначе кривой. Кривой проход тоже генерируется тем же алгоритмом генерации кривой линии, только немного не так: сначала генерируется форма осевой линии, а форма левой и правой границы отсчитается от осевой линии.
Не буду показывать, как выглядит карта с вертикальными проходами, это вы и так знаете.
Вопрос, где именно рисовать проход, простой, потому что мы знаем, на какой высоте находится какая точка какого сегмента. Скажу ещё, что вертикальные проходы через один сдвигаются либо влево от середины клетки, либо вправо. Чтобы при падении из дыры не провалиться дальше.
Ну и ещё надо в проходах нарисовать лестницы, тут не вижу сложностей.
Все текстуры имеют размер 64х64.
Итак, вот так выглядит типичный Перлин:
мы видим зацикленное говно.
Я применил несколько приёмов расцикливания.
Приём1: смиксовать несколько текстур, растянутых в разное число раз, в идеале - чтобы коэффициенты растяжения имели иррациональное соотношение.
Чтобы его расциклить, надо сгенерировать два перлина, потом один из них растянуть в 7/16 раз, другой в 17/16 раз (чтобы числа плохо делились друг на друга и в итоге период был побольше) и взять полусумму:
int k = int(level.bunker1[sy*17/16][sx*17/16]<<7)+int( level.bunker2[sy*7/16][sx*7/16]<<7); pixel = ColorToByte( tbal::Color( k,k,k)); //примечание - для текстур оператор [] сам берёт остаток по модулю
Получается совсем иная картинка, где цикл найти довольно трудно:
Вот такой вот фокус-покус! Основное условия, чтобы это проканало - это возможность заранее нарисовать огромный битмап.
Впрочем, для тридэ игор тоже можно, особенно если движок поддерживает кеширование текстур.
Приём2: сделать криво.
Вот так выглядит клеточная текстура без примесей:
ну вы понели
и видно, где даже центры клеток находятся
А теперь делаем так: заводим текстуру-невидимку, ну заполняеем по Перлину. Или две текстуры-невидимки. Растягиваем в кривое число раз. Делаем выборки. Но выбираем из них не цвет... а смещение! Из первой невидмки выбираем dx, из второй dy, а потом на карту уже выводим из клеточной текстуры пиксел с координатами не (x,y), а (x+dx, y+dy). Тем самым прямые линии становятся кривыми. А за счёт растяжения невидимок в кривое число раз кривизна становятся неповторяющейся:
int dx = int(level.caves2[sy*17/16][sx*17/16]<<5); int dy = int( level.caves2[sy*5/4][sx*5/4]<<5); int r = std::min( 255, int ( level.caves1[sy+dy][sx+dx]<<8)); int g = 0; pixel = ColorToByte( tbal::Color( r,g, std::min( r,g)));
ой, ну у меня тут вообще одна и та же невидимка, только растяжение разное. Ну и похххх
Короче, результат такой:
зацикленность всё равно видна, но она теперь стала немного неявной.
Кривая кирпичная кладка сделана аналогично, ну и ещё пару перлинов намиксовано сверху.
Для комнат-в-клеточку использовался тупо шум. Только он растягивался в 16 раз и между текселей рисовались клеточки.
Никаких миксов и прочих хитростей, растяжения в 16 раз достаточно.
Для лавовых пещер бралась волновая текстура, причём она тоже искажается аналогично пещерной текстуре. Хотя можно и две волновые смиксовать, растянув одну из них.
Тут короч не о чем разговаривать, кроме одного нюанса.
В пещерах встречаются и синие, и жёлтые источники света. Цветное освещение в 8 битах это уже необычно и проблемно.
Смотрите, какая эта текстура на самом деле мерзско-белёсая:
Но кто про это узнает, если рядом с жёлтым источником света оно и незаметно нифига?
Зато без этой белёсости не будут работать синие источники света. Поэтому пришлось вот так вот текстуру испоганить.
Предметы - деревья и кустики это тупо фракталы. Провода тоже тупо фракталы. Фонарики - дуга с кружочком. Лампочки - линия с кружочками на концах, или кружочек на палочке. Ящики - квадратики в полосочку. Для расстановки ящиков берётся хитрый рандом, который избегает мест под лестницей или над проёмами. Короче, про предметы нечего говорить тоже.
В лесу фоновая картинка - тупо небо со случайными точками, снизу рисуются бугры (тот же алгоритм рисования кривой линии), над ними деревья.
Из-за того, что отсчёт цветов делается от коричневого, а не от чёрного, картинка сильно зажелтена. Кстати, небо тут не синее, а красное, проверьте пипеткой, будет (40,34,34).
В аду сверху - две случайные линии, снизу - сжатый по вертикали перлин, между ними случайные отрезочки:
Вот и всё.
Про двери и монстров говорить не буду, нарисовано всё в пеинте. Количество контента достигнуто за счёт мэдскиллзности, в ассетах картинки все есть, можете оценить, называется "программисты рисуют, пытаясь поскорее".
Spartan
Надо ещё, чтоб игра изначально под такую палитру делалась, пока что при проекции просирается контраст. Но миникарта стала выглядеть намного интереснее, например.
Тема в архиве.