Давайте сделаем рогалик (Допиливаем игру 1)
Хорошая практика — время от времени остановиться и детально взглянуть на игру
которая у нас получается. Что в ней должно быть переделано для того чтобы
улучшить ее, исправить кукую либо ошибку, или сделать программу немного более
отзывчивой. «Допиливанием» я называю незначительные исправления, а не
капитальный ремонт. Обычно такие исправления заключаются в добавлении или
удалении всего лишь одной либо двух строк кода. Если для внесения изменений
требуется переработать достаточно много кода, то к такому исправлению следует
подходить не как к быстрой правке, а как к основательному пересмотру кода
программы. Мелкие исправления не должны затрагивать основной игровой процесс
вообще. Если мы увидим, что внесенные изменения не работают так как нам бы
хотелось, то всегда должна быть возможность быстро вернуть все назад. После
пересмотра текущей версии игры я нашел несколько мест, которые могут быть
переработаны для ее улучшения.
Первое исправление,
которое необходимо внести, это исправить ошибку отображении глубины текущего
уровня подземелья в левом верхнем углу экрана. Основная идея заключалась в том,
чтобы не перерисовывать каждый раз фоновое изображение экрана, а только
изменяемые области: область карты, сообщений и параметров, что несомненно
увеличит производительность программы. Однако, номер уровня, в результате, у нас
выводится на не обновляемой области и, при смене уровня подземелья, может
отображаться не всегда корректно. Исправляется это очень просто. Нужно добавить
перерисовку фонового изображения при смене уровня.
dod.bas
Sub DrawMainScreen (db As Integer = FALSE) Dim As Integer x, y, j, pct, row, col Dim As Double pctd Dim As Uinteger clr Dim As String txt, idesc Dim As terrainids terr Dim inv As invtype Dim spl As spelltype Screenlock 'Нарисовать фон главного экрана If db = TRUE Then DrawBackground mainback() End If ...
Мы добавили для процедуры DrawMainScreen параметр, который указывает —
нужно ли перерисовывать фон. Так как мы добавили параметр со значением по
умолчанию, то нам не нужно вносить изменения везде, где вызывается эта
подпрограмма, а только в том месте, где персонаж перемещается по
лестницам.
dod.bas: Main Game Loop
... 'Главный цикл игры. If mm <> mmenu.mQuit Then 'Установим первый ровень. level.LevelID = 1 'Построим первый уровень подземелья level.GenerateDungeonLevel 'Отобразим главный экран. DrawMainScreen TRUE ... 'Проверим на передвижение вглубь по лестнице. If ckey = ">" Then 'Убедимся, что лестница есть. If level.GetTileID(pchar.Locx, pchar.Locy) = tstairdn Then 'Сменим номер уровня. level.LevelID = level.LevelID + 1 'Построим новый уровень. level.GenerateDungeonLevel 'Отобразим главный экран. DrawMainScreen TRUE End If Endif ...
Мы должны обновить вызов процедуры в двух местах. В самом начале, после
старта игры из главного меню, и при передвижении на персонажем по лестницам. Мы
просто добавляем к вызову дополнительный параметр TRUE, в результате чего
фоновое изображение будет также перерисовываться. Как видите — изменения в коде
минимальны.
Следующее обновление связано с расходом опыта персонажа.
Ткущая цена улучшения атрибутов, это 10 Опыта за 1 пункт атрибута. В результате
персонаж может развиваться очень быстро, что позволяет ему «обмануть» игру. (я
написал «обмануть» в кавычках потому, что на самом деле это не настоящий обман,
а ошибка в игре, которой может воспользоваться проницательный игрок). Если игрок
не будет тратить очки опыта до перехода на следующий уровень, а после перехода
улучшить атрибуты персонажа, то его герой станет значительно сильнее монстров и
запросто со всеми ими разделаться. Происходит это из за того, что мы
масштабируем монстров, во время их создания, относительно текущих атрибутов
персонажа.
Исправить это мы можем просто изменив соотношение опыта к
атрибутам то 10 к 1 до 100 к 1. Это замедлит развитие персонажа и будет
удерживать его характеристики в соответствии с характеристиками монстров. Игрок
все еще может накапливать опыт до перехода на новый уровень, но теперь его
превосходство над монстрами не будет таким большим.
dod.bas
'Применить опыт к атрибутам персонажа. Sub ImproveCharacter () Dim As Integer sel, xp, reqamt = 100 'Убедимся что у нас достаточно опыта. If pchar.CurrXP > reqamt Then Do If pchar.CurrXP > reqamt Then 'Print Current stats. pchar.PrintStats PutTextShadow "Cost = " & reqamt & " XP for 1 point improvement.", 49, 10 PutTextShadow "Enter attribute 1 to 5 to improve, enter to exit: ", 51, 10 'Позиция для ввода. Locate 53, 10 'Ввод номера атрибута. Input sel 'Изменим выбранный атрибут. If sel = 1 Then pchar.ChangeStrength 1 'Subtract the xp amount. xp = pchar.CurrXP xp -= reqamt pchar.CurrXP = xp Endif If sel = 2 Then pchar.ChangeStamina 1 'Subtract the xp amount. xp = pchar.CurrXP xp -= reqamt pchar.CurrXP = xp Endif If sel = 3 Then pchar.ChangeDexterity 1 'Subtract the xp amount. xp = pchar.CurrXP xp -= reqamt pchar.CurrXP = xp Endif If sel = 4 Then pchar.ChangeAgility 1 'Subtract the xp amount. xp = pchar.CurrXP xp -= reqamt pchar.CurrXP = xp Endif If sel = 5 Then pchar.ChangeIntelligence 1 'Subtract the xp amount. xp = pchar.CurrXP xp -= reqamt pchar.CurrXP = xp Endif Else sel = 0 End If Loop Until sel = 0 Else PrintMessage "Not enough XP to spend." End If End Sub
Мы добавили новую переменную reqamt в подпрограмму ImproveCharacter,
которая указывает на количество затрачиваемого опыта для увеличения параметра
персонажа. При использовании переменной, а не жестко заданного значения, мы, при
необходимости, можем изменить это значение в одном месте, а не в нескольких.
Может оказаться, что 100 это слишком много, поэтому это значение может
потребоваться изменить еще раз. Нам будет легко попробовать разные значения,
пока мы нащупаем правильную комбинацию.
Следующая настройка также связана
с изменением параметров персонажа. Если обновляется сила или выносливость, то
текущее и максимальное здоровье устанавливаются в соответствии с новыми
значениями этими атрибутов. Этим может воспользоваться игрок для уменьшения
проблем в игре. Исправим это, обновляя только значение максимально уровня
здоровья, но не трогая текущее.
character.bi
... Declare Sub ChangeStrength(change As Integer, docurrhp As Integer = FALSE) Declare Sub ChangeStamina(change As Integer, docurrhp As Integer = FALSE) ... 'Изменение атрибута и обновление связанных параметров. Sub character.ChangeStrength(change As Integer, docurrhp As Integer = FALSE) 'Обновим аттрибут _cinfo.stratt(idxAttr) = _cinfo.stratt(idxAttr) + change If _cinfo.stratt(idxAttr) < 1 Then _cinfo.stratt(idxAttr) = 1 'Обновим HP. _cinfo.maxhp = _cinfo.stratt(idxAttr) + _cinfo.staatt(idxAttr) If docurrhp <> FALSE Then _cinfo.currhp = _cinfo.maxhp End If _cinfo.ucfsk(idxAttr) = _cinfo.stratt(idxAttr) + _cinfo.aglatt(idxAttr) _cinfo.acfsk(idxAttr) = _cinfo.stratt(idxAttr) + _cinfo.dexatt(idxAttr) _cinfo.cdfsk(idxAttr) = _cinfo.stratt(idxAttr) + _cinfo.aglatt(idxAttr) End Sub 'Изменение атрибута и обновление связанных параметров. Sub character.ChangeStamina(change As Integer, docurrhp As Integer = FALSE) 'Обновим аттрибут _cinfo.staatt(idxAttr) = _cinfo.staatt(idxAttr) + change If _cinfo.staatt(idxAttr) < 1 Then _cinfo.staatt(idxAttr) = 1 'Обновим HP. _cinfo.maxhp = _cinfo.stratt(idxAttr) + _cinfo.staatt(idxAttr) If docurrhp <> FALSE Then _cinfo.currhp = _cinfo.maxhp End If _cinfo.currmana = _cinfo.intatt(idxAttr) + _cinfo.staatt(idxAttr) _cinfo.maxmana = _cinfo.currmana _cinfo.mcfsk(idxAttr) = _cinfo.intatt(idxAttr) + _cinfo.staatt(idxAttr) End Sub
В процедуры ChangeStength и ChangeStamina мы добавили флаг docurrhp со
значением по умолчанию FALSE. И добавили условие, что если он равен TRUE (не
равен FALSE), то только тогда изменяем также и текущий уровень здоровья. Больше
никаких изменений в коде не требуется. Теперь, при увеличении атрибутов
персонажа, его текущее здоровье не измениться.
Зачем мы добавили здесь
какой то флаг вообще? Возможно мы захотим, в будущем, изменить и текущее
здоровье при изменении атрибутов персонажа. Предположим что у нас есть
заклинание, которое позволяет персонажу полностью восстановить здоровье при
увеличении сили или выносливости. Теперь у нас есть способ сделать это без
внесения дополнительных изменений в код. Даже если мы не будем добавлять никаких
подобных механизмов, мы оставляем такую возможность открытой. В любом случае,
это не повредит, даже если мы никогда не будем использовать эту
функциональность.
Следующее изменение косметическое, но это одна из тех
дополнительных возможностей, которые помогают игроку. Сейчас в инвентаре
персонажа выделяются неопознанные предметы, мы также добавим выделение
магических предметов, чтобы игроку не приходилось проверять все подряд для
поиска предметов содержащих магические свойства.
dod.bas:
DrawInventoryScreen
... 'Покажем игроку что предмет неопознан. If ret = FALSE Then txt &= "(*)" Else 'Если содержит магию. If inv.classid = clWeapon Then If inv.weapon.spell.id <> splNone Then txt &= " (M)" End If Endif Endif ...
Мы проверяем, опознан ли предмет, и если нет, то, как обычно, рисуем
звездочку. Если же предмет опознан, то мы проверяем, оружие ли это и содержит ли
оно магию, и, если это так, то мы отметим предмет буквой M. Это даст игроку
возможность быстро узнать, без необходимости заходить в описание предмета,
содержит ли он магию. Конечно, если предмет не опознан игроком, то он, все еще,
не узнает, содержаться ли в нем какие либо магические свойства.
Последнее
изменение также косметическое. Оно позволит игроку получить о персонаже более
подробную информацию. Так как у нас теперь есть заклинания которые добавляют, на
время, бонусы к атрибутам персонажа, то было бы неплохо отображать значение этих
бонусов и их продолжительность, а не только сумму текущего значения и бонуса.
dod.bas: DrawMainScreen
... If pchar.BonStr > -1 Then txt = " +" Else txt = " -" Endif PutText "Str: " & pchar.CurrStr & txt & pchar.BonStr & " (" & pchar.BonStrCnt & ")", row, col ...
Приведенный пример кода показывает отображение бонуса для силы персонажа.
Бонусы остальных атрибутов отображаются аналогичным образом. Так как мы
позволяем использовать только один бонус одновременно, то эта информация поможет
игроку решить, использовать ли предмет модифицирующий бонус параметра, или
сохранить текущее значение.
Как вы можете видеть, все внесенные изменения
очень просты в реализации, но имеют огромное влияние на игру. Изменение влияния
опыта само по себе меняет всю динамику игры, будем надеяться, к лучшему. Это
только первый этап настроек игрового процесса, в будущем, мы будем снова к этому
возвращаться. Настройка игрового процесса это непрерывный процесс, и всегда есть
что либо, что можно улучшить.
Перевод на русский: Fantik
содержание | назад | вперед