Давайте сделаем рогалик (Вступление)
Хотя это и не является обязательным — добавить в игру экран с вводной информацией, но это добавит изюминку в нашу игру, и вы можете его использовать для того, чтобы рассказать немного об игре. Что можно ожидать от игры, какие цели преследует персонаж, что случиться если персонаж проиграет или выиграет. Это не должно быть описанием реализованных в игре возможностей, вступление должно быть интересным для игрока и передавать некоторую информацию о игре. Если вы прочитали текст на изображении выше, то вы видите, что это предшествующая игре история оформленная в виде письма персонажу, которое рассказывает игроку о том, почему он собирается в Подземелье Судьбы, и что случиться если он потерпит неудачу или справится со всеми испытаниями.
Это поможет настроить игрока на игру, окуная в созданный вами мир, который игроку будет интересно исследовать а не просто играть ради игры. Используя имя персонажа, которое игрок ввел в начале, вы помещаете персонаж непосредственно в созданный мир. Поэтому я хотел, чтобы при создании персонажа у него уже был какой то опыт и золото. В результате, будет казаться, будто бы персонаж, и, как следствие, игрок, уже жил в этом мире.
То, что вы не можете видеть на картинке выше, это анимация огня за текстом. Огонь на заднем фоне дополняет тему письма, в котором говорится о «пожарах зла» и амулете «кристалл огня», добавляя к письму чувство бедствий, а также, намекает на грядущее. Будем надеяться, что вопросы, поднятые в письме, завлекут игрока в нашу игру, чтобы найти ответы.
Весь код для введения мы поместим в intro.bi. Так как вступление нам больше нигде не понадобиться, обернем его в пространство имен intro. В нем будет описана подпрограмма DoIntro, которую мы будем вызывать сразу после создания персонажа.
dod.bas
'Обработаем выбранный пункт меню. If mm = mmenu.mNew Then 'Сгенерируем персонажа. Var ret = pchar.GenerateCharacter 'Не выходим из меню, если пользователь нажмет ESC. If ret = FALSE Then 'Установим переменную, чтобы не выйти из цикла. mm = mmenu.mInstruction Else 'Вызовем вступление. intro.DoIntro Endif
Если при создании персонажа игрок нажал клавишу Escape, то нам не нужно отображать вступление, поэтому мы поместили его вызов в секцию Else. Так как код отображения вступления у нас в пространстве имен intro, то перед вызовом подпрограммы мы должны указать его идентификатор. Сам код вступления достаточно прост. Это классический эффект огня, поверх которого рисуется изображение пергамента, и обычный текст, напечатанный в последнюю очередь. Эффект горящего пламени достигается достаточно просто, он требует только цветовую палитру, массив частиц и карту охлаждения для получения нескольких вариаций пламени.
intro.bi
Dim Shared pal(maxage) As Uinteger = { _ &hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4, _ &hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4, _ &hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4,&hFFF9F7D4, _ &hFFF9F7D4,&hFFF9F6B6,&hFFF8F48E,&hFFF8F364,&hFFF8F139,&hFFF9EC14,&hFFFAD51B,&hFFFCBE22, _ &hFFFDA52A,&hFFFF8C31,&hFFFA802E,&hFFF4752A,&hFFEE6A26,&hFFE95E22,&hFFE3531D,&hFFDE471A, _ &hFFD53613,&hFFCC250D,&hFFC31207,&hFFBB0100,&hFFAC0000,&hFF9D0000,&hFF8E0000,&hFF7F0000, _ &hFF700000,&hFF610000,&hFF5A0000,&hFF550000,&hFF510000,&hFF4D0000,&hFF480000,&hFF440000, _ &hFF3F0000,&hFF3B0000,&hFF370000,&hFF320000,&hFF2E0000,&hFF2A0000,&hFF250000,&hFF210000, _ &hFF1C0000,&hFF180000,&hFF130000,&hFF100000,&hFF0B0000,&hFF070000,&hFF020000,&hFF000000, _ &hFF000000,&hFF000000,&hFF000000,&hFF000000,&hFF000000,&hFF000000,&hFF000000,&hFF000000}
Это палитра цвета огня, которая будет использоваться для задания пламени различного цвета. Частицы огня начинают свое движение в нижней части экрана и движутся вверх. При перемещении вверх растет их возраст, а с ним меняется и их цвет. Значение возраста частицы используется как индекс в палитре цвета.
intro.bi
'Перемещение каждой частицы на экране с возможностью перемещения из стороны в сторону. Sub MoveParticles Dim As Integer x, y, tage, xx Dim As Single r For x = 0 To txcols - 1 For y = 1 To txrows - 1 'Получить текущий возраст частицы. tage = fire(x, y) 'Cдвинуть частицу влево (-1) или вправо (1) или не двигать (0). xx = RandomRange(-1, 1) + x 'Перенести частицу на другую сторону эерана, если вышла за границы. If xx < 0 Then xx = txcols - 1 If xx > txcols - 1 Then xx = 0 'Установить возраст частицы. tage += coolmap(xx, y - 1) + 1 'Убедиться, что возраст попадает в диапазон. If tage < 0 Then tage = 0 If tage > (maxage - 1) Then tage = maxage - 1 fire(xx, y - 1) = tage Next Next End Sub
Каждая частица в массиве огня перемещается на одну позицию вверх, и,
возможно, влево или вправо, в зависимости от возвращаемого значения функцией
RandomRange(-1, 1). Это пример использования нашей функции случайных значений,
когда мы задаем диапазон с отрицательными и положительными числами. Когда
частица перемещается вверх, ее возраст комбинируется со значением из карты
охлаждения coolmap, в результате чего он увеличивается или уменьшается.
Используя карту охлаждения, наподобие этой, вы можете создать несколько
вариантов огня, чтобы он выглядел более реалистичным.
После того, как старые частицы переместятся вверх, новые будут добавлены в нижней части экрана.
intro.bi
'Добавить частицы огня вдоль нижнего края экрана. Sub AddParticles Dim As Integer x For x= 0 To txcols - 1 fire(x, txrows - 1) = RandomRange(0, 20) Next End Sub
Возраст вновь созданных частиц задается случайным числом от 0 до 20. Это
даст разные варианты пламени, которое будет распространяться вверх, создавая
симпатичный эффект. Как только новые частицы добавлены, экран готов к
отображению.
intro.bi
'Рисует пламя или пергамент на экране. Sub DrawScreen(egg As Integer) Dim As Integer x, y, cage, tx, ty, wid = 68 Dim As Uinteger clr Dim As String st, tt Screenlock MoveParticles AddParticles For x = 0 To txcols - 1 For y = 0 To txrows - 1 If fire(x, y) < maxage Then cage = Smooth(fire(), x, y) cage += 10 If cage > maxage Then cage = maxage 'Если цвет фона равен 0, то использовать цвет огня. clr = introback(x + y * txcols) 'Проверим, если рисуется пергамент. If clr = &hFF000000 Then clr = pal(cage) 'Тут пасхалка. If egg = TRUE Then clr = pal(cage) Endif Draw String (x* 8, y* 8), Chr(219), clr End If Next Next ...
Вместо того, чтобы рисовать экран 2 раза, мы перебираем массив
изображения пергамента и ищем прозрачный цвет hFF000000. Если обнаружен
прозрачный цвет, мы рисуем огонь, если цвет отличный от hFF000000, тогда рисуем
его — это наше изображение пергамента. Как только фоновое изображение
нарисовано, мы выводим текст нашего письма.
intro.bi
'Вывод текста письма. tx = 6 * charw ty = 3 * charh st = "Dear " & pchar.CharName & "," DrawStringShadow tx, ty, st ty += (charh * 2) st = "My friend and ally... Dark days are upon us and the fires of evil are threatening the beloved land. " st &= "The evil wizard Deboza has escaped from her prison, the magical crystal shard in the center of the " st &= "Amulet of Crystal Fire. How she escaped from this prison, I do not know, but it must have required " st &= "great magic to accomplish. She is even now gathering an army to do battle with the good folk of the " st &= "land and it is only a matter of time before she overruns the weak defenses of the of the King. Long " st &= "peace, though welcome, has its own dangers." Do tt = WordWrap(st, wid) DrawStringShadow tx, ty, tt ty += charh + 2 Loop Until Len(tt) = 0
Мы используем уже знакомую нам процедуру DrawStringShadow и новую функцию
WordWrap, которую также необходимо добавить в utils.bi. Функция WordWrap
принимает текст абзаца (переменная st) и ширину, в которую нужно уместить
выводимый текст (переменная wid). Функция возвращает строку такой длинны, чтобы
поместилась в указанную ширину. Возвращаемая строка начинается с начала
переданного ей текста и заканчивается обязательно целым словом. Также, функция
удаляет возвращаемую строку из первоначального текста, поэтому мы вызываем ее в
цикле, чтобы напечатать возвращаемые функцией строки на новой строке экрана.
Когда текст в переменной st закончится (все строки напечатаны), программа выйдет
из цикла.
intro.bi
'Запустить вступление. Sub DoIntro() Dim As Single t Dim As String eg Dim As Integer doegg = FALSE CreateCoolMap Do eg = Inkey If eg = "f" Then doegg = Not doegg eg = "" Endif t = Timer 'Перерисовываем экран. DrawScreen doegg Do While (Timer - t) < FD Sleep 1 Loop Loop Until eg <> "" ClearKeys End Sub
Первое, что мы сделаем, это создаем карту охлаждения
intro.bi
'Создает карту охлаждения, которая объединяясь с огнем даст наилучший эффект Sub CreateCoolMap Dim As Integer i, j, x, y For x = 0 To txcols - 1 For y = 0 To txrows - 1 coolmap(x, y) = RandomRange(-10, 10) Next Next For j = 1 To 10 For x = 1 To txcols - 2 For y = 1 To txrows - 2 coolmap(x, y) = Smooth(coolmap(), x, y) Next Next Next End Sub
Карта охлаждения повторяет карту огня, за исключением того, что она
содержит статические значения для вычисления возраста огненных частиц. Функция
Smoth сглаживает значения карты охлаждения вокруг определенной координаты, для
того чтобы огонь имел более естественный вид. После создания карты охлаждения мы
входим в цикл перерисовки экрана
intro.bi
Do While (Timer - t) < FD Sleep 1 Loop
Эта часть кода приостанавливает отображения следующего кадра на время
заданное константой FD, равной 1/60, для того, чтобы анимация огня
перерисовывалась с одинаковой скорость на компьютерах с различной частотой
процессора (В самой первой строке процедуры DoIntro переменная t объявлена как
Single, что неверно. Функция Timer возвращает Double переменную, а из-за
неточности типа Single в результат выражения Timer - t может быть даже
отрицательным, что вызовет неприятный эффект «подвисания» программы в цикле Do
While (Timer — t) < FD, при этом, разумеется, вы не сможете даже закрыть
обычным способом окно приложения. Поэтому, чтобы этого не происходило, строку
«Dim As Single t» нужно заменить на «Dim As Double t» (примечание
переводчика)).
Возможно вы заметили в коде выше, что мы добавили в эту заставку «пасхальное яйцо». Если пользователь нажмет клавишу f, то пергамент не будет отображаться и мы будем наблюдать текст в огне. Совершенно бесполезная опция, но зато весело.
Теперь, когда мы разобрались со вступлением, мы можем переходить к «мясу» нашей игровой программы, это генерация подземелья и передвижения по нему нашего персонажа.
Перевод на русский: Fantik
содержание | назад | вперед