Давайте сделаем рогалик (Главный интерфейс)
Здесь вы видите главный экран игры. Идея в том, чтобы на главном экране отображалась вся основная информация. У нас не так много параметров персонажа и основных команд, т. к. наша игра достаточно простая, поэтому мы можем отобразить на основном экране как параметры, так и подсказку по клавишам команд. Однако учтите, что это просто набросок, и, в результате дальнейшей нашей разработки, данный экран может быть изменен.
Как можно увидеть, наш экран разделен на регионы. Карта отображается слева, а параметры персонажа справа. Мы могли бы поменять их местами, если бы захотели, но такое расположение, обычно, является стандартном для рогаликов. В нижней части экрана мы будем выводить список сообщений. Список сообщений будет отображать последних 4 сообщения, сгенерированных программой. В случае необходимости мы могли бы добавить журнал сообщений, чтобы иметь возможность отобразить более 4-х последних сообщений, но, как правило, этого не требуется.
В нашем предыдущем коде карта уровня начинала выводится с координаты 1, но для того, чтобы у нас была окантовка вокруг карты, нам необходимо добавить смещение на 1 в процедуру ее отображения DrawMap. Вот пример изменений:
DrawMap:map.bi
PutText mtile, y + 1, x + 1, tilecolor
Все процедуры вывода, включая вывод значка персонажа, смешаются по
горизонтали и вертикали на единицу. Визуально, отображение локации у нас
сместилось вниз и вправо на 1 символ, что даст зазор для окантовки.
Большинство действий по отображению главного экрана у нас будет происходить в подпрограмме DrawMainDisplay, описанной в файле dod.bas
dod.bas
'Отображает главный экран игры. Sub DrawMainScreen () Dim As Integer x, y, j, pct, row, col Dim As Double pctd Dim As Uinteger clr Dim As String txt Dim As terrainids terr Screenlock level.DrawMap 'Нарисуем область сообщений. PrintMessage "" 'Нарисуем область для вывода информации. y = 2 For j = 0 To vh - 1 For x = vw + 3 To txcols - 1 PutText acBlock, y + j, x, fbBlack Next Next 'Номер уровня. DrawStringShadow charw, 0, "Dungeon Level: " & level.LevelID 'Имя персонажа. row = 3 col = vw + 4 PutText pchar.CharName, row, col, fbYellowBright row += 2 'Нарисуем полоску здоровья персонажа. pctd = pchar.CurrHP / pchar.MaxHP pct = Int(pctd * 100) If pct > 74 Then clr = fbGreen Elseif (pct > 24) And (pct < 75) Then clr = fbYellow Else clr = fbRed Endif 'Построение полосы здоровья. txt = String((txcols - 1) - (col + 4), acBlock) PutText " " & txt, row, col, fbBlack txt = String((txcols - 1) - (col + 4), Chr(176)) PutText " " & txt, row, col, fbGray txt = String(Len(txt) * pctd, acBlock) PutText " " & txt, row, col, clr PutText "HP: ", row, col 'Выведем значение здоровья персонажа. txt = pchar.CurrHP & "/" & pchar.MaxHP DrawStringShadow (txcols - (Len(txt) + 2)) * charw, (row - 1) * charh, txt 'Выведем основные характеристики. row += 2 PutText "Stats", row, col, fbYellowBright row += 2 If pchar.BonStr > -1 Then txt = " +" Else txt = " -" Endif PutText "Str: " & pchar.CurrStr & txt & pchar.BonStr, row, col row += 1 If pchar.BonSta > -1 Then txt = " +" Else txt = " -" Endif PutText "Sta: " & pchar.CurrSta & txt & pchar.BonSta, row, col row += 1 If pchar.BonDex > -1 Then txt = " +" Else txt = " -" Endif PutText "Dex: " & pchar.CurrDex & txt & pchar.BonDex, row, col row += 1 If pchar.BonAgl > -1 Then txt = " +" Else txt = " -" Endif PutText "Agl: " & pchar.CurrAgl & txt & pchar.BonAgl, row, col row += 1 If pchar.BonInt > -1 Then txt = " +" Else txt = " -" Endif PutText "Int: " & pchar.CurrInt & txt & pchar.BonInt, row, col row += 1 PutText "Curr XP: " & pchar.CurrXP, row, col row += 2 PutText "Combat Factors", row, col, fbYellowBright row += 2 If pchar.BonUcf > -1 Then txt = " +" Else txt = " -" Endif PutText "UCF: " & pchar.CurrUcf & txt & pchar.BonUcf, row, col row += 1 If pchar.BonAcf > -1 Then txt = " +" Else txt = " -" Endif PutText "ACF: " & pchar.CurrAcf & txt & pchar.BonAcf, row, col row += 1 If pchar.BonPcf > -1 Then txt = " +" Else txt = " -" Endif PutText "PCF: " & pchar.CurrPcf & txt & pchar.BonPcf, row, col row += 1 If pchar.BonMcf > -1 Then txt = " +" Else txt = " -" Endif PutText "MCF: " & pchar.CurrMcf & txt & pchar.BonMcf, row, col row += 1 If pchar.BonCdf > -1 Then txt = " +" Else txt = " -" Endif PutText "CDF: " & pchar.CurrCdf & txt & pchar.BonCdf, row, col row += 1 If pchar.BonMdf > -1 Then txt = " +" Else txt = " -" Endif PutText "MDF: " & pchar.CurrMdf & txt & pchar.BonMdf, row, col row += 2 PutText "Equipment", row, col, fbYellowBright row += 2 PutText "Gold: " & pchar.CurrGold, row, col row += 1 PutText "RT Hand: ", row, col row += 1 PutText "LT Hand: ", row, col row += 1 PutText "Armor: ", row, col row += 2 PutText "Keys", row, col, fbYellowBright row += 2 PutText "Move:.Arrows or Numpad", row, col row += 1 PutText "?.....Help", row, col row += 1 PutText "v.....Inventory", row, col row += 1 PutText "x.....Improve Character", row, col row += 1 PutText "l.....Spell Book", row, col row += 1 PutText ">.....Down Level", row, col row += 1 PutText "<.....Up Level", row, col row += 1 PutText "i.....Inspect Tile", row, col row += 1 PutText "s.....Search Area", row, col row += 1 PutText "t.....Target Enemy", row, col row += 1 PutText "r.....Read Scroll", row, col row += 1 PutText "c.....Cast Spell", row, col row += 1 PutText "e.....Drink/Eat Item", row, col row += 1 PutText "g.....Get Item", row, col row += 1 PutText "d.....Drop Item", row, col row += 1 PutText "p.....Pick Lock", row, col row += 1 PutText "b.....Bash Door", row, col 'Проверим, стоит ли персонаж на предмете. If level.HasItem(pchar.Locx, pchar.Locy) = TRUE Then 'Получим описание предмета. Else 'Стоит ли персонаж на специфической местности. terr = level.GetTileID(pchar.Locx, pchar.Locy) If (terr = tstairup) OrElse (terr = tstairdn) Then txt = level.GetTerrainDescription(pchar.Locx, pchar.Locy) PrintMessage txt End If Endif Screenunlock End Sub
Как вы видите, программа вначале отображает карту, потом очищает
пространство для вывода сообщений и информации, а затем заполняет их. В наших
переменных мы используем значения номеров строк и столбцов, для того, чтобы
можно было использовать расчетные а не абсолютные значения для места
расположения текста. Это делает обновление экрана достаточно простым, т. к. мы
просто устанавливаем начальное значение позиции строки и столбца, а для
добавления новой строки, достаточно просто увеличить переменную указывающую на
номер строки на единицу. Если нам нужно будет что то поменять местами, нам не
нужно будет заботится о номере строки, так как он рассчитается
автоматически.
Большую часть кода легко понять, т. к. это просто вывод текста на экран. Основную часть информации мы берем из объекта персонажа, поэтому нам нужно добавить в его объект дополнительные свойства для получения доступа к необходимым параметрам. Ниже приведен список новых свойств.
character.bi
Declare Property CurrHP(hp As Integer) 'Задает текущее здоровье. Declare Property CurrHP() As Integer 'Возвращает текущее здоровье. Declare Property MaxHP() As Integer 'Возвращает максимальное здоровье. Declare Property CurrStr() As Integer 'Возвращает текущую силу. Declare Property CurrStr(amt As Integer) 'Задает текущую силу. Declare Property BonStr() As Integer 'Возвращает бонус силы. Declare Property BonStr(amt As Integer) 'Задает бону силы. Declare Property CurrSta() As Integer 'Возвращает текущую выносливость. Declare Property CurrSta(amt As Integer) 'Задает ткущую выносливость. Declare Property BonSta() As Integer 'Возвращает бонус выносливости. Declare Property BonSta(amt As Integer) 'Задает бонус выносливости. Declare Property CurrDex() As Integer 'Возвращает текущую ловкость. Declare Property CurrDex(amt As Integer) 'Задает текущую ловкость. Declare Property BonDex() As Integer 'Возвращает бонус ловкости. Declare Property BonDex(amt As Integer) 'Задает бонус ловкости. Declare Property CurrAgl() As Integer 'Возвращает текущую подвижность. Declare Property CurrAgl(amt As Integer) 'Задает текущую подвижность. Declare Property BonAgl() As Integer 'Возвращает бонус подвижности. Declare Property BonAgl(amt As Integer) 'Задает бонус подвижности. Declare Property CurrInt() As Integer 'Возвращает текущий интеллект. Declare Property CurrInt(amt As Integer) 'Задает текущий интеллект. Declare Property BonInt() As Integer 'Возвращает бонус интеллекта. Declare Property BonInt(amt As Integer) 'Задает бонус интеллекта. Declare Property CurrUcf() As Integer 'Возвращает значение безоружного боя. Declare Property BonUcf() As Integer 'Задает значение безоружного боя. Declare Property BonUcf(amt As Integer) 'Задает бонус безоружного боя. Declare Property CurrAcf() As Integer 'Возвращает значение боя с оружием. Declare Property BonAcf() As Integer 'Возвращает бонус боя с оружием. Declare Property BonAcf(amt As Integer) 'Задает бонус боя с оружием. Declare Property CurrPcf() As Integer 'Возвращает значение использования дистанционного оружия. Declare Property BonPcf() As Integer 'Возвращает бонус для дистанционного оружия. Declare Property BonPcf(amt As Integer) 'Задает бонус для дистанционного оружия. Declare Property CurrMcf() As Integer 'Возвращает силу магических атак. Declare Property BonMcf() As Integer 'Возвращает бонус магических атак. Declare Property BonMcf(amt As Integer) 'Задает бонус магических атак. Declare Property CurrCdf() As Integer 'Возвращает текущую защиту персонажа. Declare Property BonCdf() As Integer 'Возвращает бонус защиты персонажа. Declare Property BonCdf(amt As Integer) 'Задает бонус защиты персонажа. Declare Property CurrMdf() As Integer 'Возвращает магическую защиту персонажа. Declare Property BonMdf() As Integer 'Возвращает бонус магической зашиты. Declare Property BonMdf(amt As Integer) 'Задает бонус магической зашиты. Declare Property CurrXP() As Integer 'Возвращает текущее значение опыта. Declare Property CurrXP(amt As Integer) 'Задает текущее значение опыта. Declare Property TotXP() As Integer 'Возвращает общее кол-во опыта. Declare Property TotXP(amt As Integer) 'Задает общее коли-во опыта. Declare Property CurrGold() As Integer 'Возвращает текущее золото. Declare Property CurrGold(amt As Integer) 'Задает текущее золото. Declare Property TotGold() As Integer 'Возвращает общее кол-во золота. Declare Property TotGold(amt As Integer) 'Задает общее кол-во золота.
Единственное назначение этих свойств, это задать и получить значения
локальных переменных объекта. Нам они понадобятся и в последствии, в различных
местах программы, например в боевой ее части, так что мы будем их использовать
не только для отображения состояния персонажа. Код реализации этих свойств
аналогичен приведенному ниже коду для свойства параметра силы.
character.bi
'Возвращает текущее значение силы персонажа. Property character.CurrStr() As Integer Return _cinfo.stratt(0) End Property 'Задает текущее значение силы персонажа. Property character.CurrStr(amt As Integer) _cinfo.stratt(0) = amt End Property
Также мы добавили в объект нашей карты несколько новых методов. Которые
нам понадобятся для отображения сообщений. Если персонаж находится на какой либо
специфической местности, например лестница вниз, то мы хотим, что бы появилось,
уведомляющее игрока, сообщение об этом факте. Это сообщение поможет игроку
быстрее разобраться в том, что означает в нашей игре та или иная иконка. При
добавлении новых элементов в игру, мы будет поступать аналогичным образом.
map.bi
'Возвращает описание местности. Function levelobj._GetTerrainDesc(x As Integer, y As Integer) As String Dim tile As terrainids Dim ret As String 'Must be a tile. tile = _level.lmap(x, y).terrid Select Case tile Case twall ret = "Wall" Case tfloor ret = "Floor" Case tstairup ret = "Stairs up" Case tstairdn ret = "Stairs down" Case tdooropen ret = "Open door" Case tdoorclosed ret = "Closed door" Case Else ret = "Uknown" End Select Return ret End Function 'Возвращает описание предмета, находящегося по координатам x,y. Function levelobj.GetTerrainDescription(x As Integer, y As Integer) As String Return _GetTerrainDesc(x, y) End Function 'Возвразает истина, если по координатам x,y находится какой либо предмет. Function levelobj.HasItem(x As Integer, y As Integer) As Integer Return _level.lmap(x, y).hasitem End Function
Публичная функция GetTerrainDescription вызывает приватную функцию
GetTerrainDesc, которая, в свою очередь, вернет описание типа местности по
координатам x и y. Функция HasItem возвращает «истина», если на карте по
координатам x, y лежит какой либо предмет. Оба этих метода будут использоваться
в представленном ниже коде процедуры DrawMainDisplay.
dod.bas
'Проверим, стоит ли персонаж на предмете. If level.HasItem(pchar.Locx, pchar.Locy) = TRUE Then 'Получим описание предмета. Else 'Стоит ли персонаж на специфической местности. terr = level.GetTileID(pchar.Locx, pchar.Locy) If (terr = tstairup) OrElse (terr = tstairdn) Then txt = level.GetTerrainDescription(pchar.Locx, pchar.Locy) PrintMessage txt End If Endif
Тут мы проверяем, если ли какой либо предмет на ячейке карты, на который
находится персонаж (в будущем мы будем выводить описание предмета, а пока тут
просто заглушка), если нет, то проверяем тип местности, и если это лестница
вверх или вниз, то выводим об этом сообщение при помощи подпрограммы
PrintMessage.
dod.bas
'Выводит на экран любые сообщения. Sub PrintMessage(txt As String) Dim As Integer i, x, y If Len(txt) > 0 Then 'Сдвинуть все сообщения на одну позицию вниз. For i = 3 To 1 Step -1 mess(i + 1) = mess(i) Next mess(1) = txt End If 'Очищаем регион для сообщений. ClearMessageArea 'Выведем сообщения на экран. y = 1 + vh + 2 x = 3 For i = 1 To 4 PutText mess(i), y, x, messcolor(i) y += 1 Next End Sub
Мы используем два массива, один для хранения сообщений, и второй, для
хранения цвета каждой линии.
defs.bi
'Список сообщений. Dim Shared mess(1 To 4) As String Dim Shared messcolor(1 To 4) As Uinteger = {fbWhite, fbWhite1, fbWhite2, fbWhite3}
Мы хотим, чтобы последнее сообщение отображалось белым цветом, а а все
предыдущие — все более и более темным серым. Это концентрирует внимание
пользователя на последнем сообщении.
Когда новое сообщение передается в PrintMessage, мы сдвигаем все сообщения на единицу в низ по массиву и записываем его в массив на первое место. Это делает вывод сообщений очень простым. Мы просто перебираем элементы массива и каждую строку печатаем своим цветом.
Последнее, что нам осталось рассмотреть, это фон, отображаемый вокруг границы экрана. На самом деле это фоновое изображение из mainback.bi, который отображается в начале программы.
dod.bas
'Главный цикл игры. If mm <> mmenu.mQuit Then 'Установим первый уровнь. level.LevelID = 1 'Построим подземелье первого уровня level.GenerateDungeonLevel 'Установим фон для главного экрана. DrawBackground mainback() 'Display the main screen. DrawMainScreen Do ...
Рисовать фон нужно только один раз, т. к. мы обновляем только области
отображения карты, сообщений и информации. Это сэкономит нам процессорное время,
т. к. дисплей обновляется после каждого действия персонажа.
Это все, что касается отображения главного экрана. Совсем немного кода, но, в результате, наша программа выглядит как настоящая игра.
Перевод на русский: Fantik
содержание | назад | вперед