Биф хочет иметь таблицу рекордов в своей игре
 
Автор Lachie Dazdarian (Сентябрь, 2007)

Введение

Много раз меня спрашивали новички о наборе процедур, загружающих таблицу рекордов из внешнего файла, правильном вводе новых рекордов и сохранении измененной таблицы рекордов.
Используя тот же набор процедур для игры Ball Blazing Fantasy, я решил написать учебник по таблице рекордов, внеся несколько улучшений и исправлений.
Руководство также направить вас к некоторым полезным (для таблицы рекордов) дополнительным процедурам, как имя ввода и шифрования файлов (не мной написано).


Давайте сделаем это!

Это довольно очевидно, нам нужно две отдельные подпрограммы для нашей таблицы рекордов, одна для загрузки/чтения, другая для записи/модификации.
Мы начнем с загрузки/чтения таблицы рекордов, так как это часть проще и является логическим началом.
Подпрограмма для чтения таблицы рекордов должна работать относительно просто. Она откроет файл, который содержит имя и записи оценок, сохранит их в соответствующих переменных, а затем распечатает их на экране. Последняя часть является наиболее зависима от пожеланий и потребностей создателя (метод печати, положение таблицы рекордов, его форматирование и т.д.).
Во-первых, мы должны создать текстовый файл, содержащий наше имя и записи оценок. Создайте файл с именем 'high_scores.dat", откройте его с помощью блокнота и введите это:

FRED
10000
BILL
9000
SARAH
8000
BOB
7000
RED
6000
SUE
5000
DAVID
4000
GREG
3000
TIM
2000
GEORGE
1000


Файл содержит 10 оценок, отформатированных так, что сначала идет имя, а потом высокий балл. Я считаю, что это форматирование наиболее подходящее для редактирования, хотя вы можете выбрать такое, где все имена будут указаны первыми, а затем будут следовать баллы. Тем не менее, нет особых выгод от разницы форматирования, поэтому мы будем работать с тем, которое выбрал я.

Этот файл будет использоваться со следующей процедурой "ReadHighScore".

Давайте начнем нашу основную программу с некоторой необходимой инициализации:

#include "fbgfx.bi"
Using FB

Const num_of_entries = 10


'num_of_entries' будет флагом количества записей (имена и баллы в таблице рекордов), и должны соответствовать количеству записей в файле 'high_score.dat' (не кол-ву линий в файле, а общей оценке имени+балл).

Теперь мы должны продекларировать нашу процедуру:

Declare Sub ReadHighScore (highscore_file As String)


'highscore_file " - переменная имени файла, которое требуется для  процедуры 'ReadHighScore', чтобы открыть файл. На самом деле это шибко не нужно, но это добавляет некоторую гибкость.

После этого, мы должны объявить следующие переменные:

Dim Shared workpage As Integer
Dim Shared hname(num_of_entries) As String
Dim Shared hscore(num_of_entries) As String


'workpage' - переменная не связана с данной обучающей темой, она будет использоваться для замены страниц внутри цикла, в котором будет отрисовка таблицы рекордов на экране. 'hname' - массив будет содержать записи имен. 'hscore'  - массив будет содержать записи баллов.

Наконец, давайте инициализируем экран и рабочую/видимую страницу:

ScreenRes 640, 480, 32, 2, GFX_ALPHA_PRIMITIVES+GFX_WINDOWED
ScreenSet 1, 0


После этого кода мы должны поместить этот:

ReadHighScore "high_scores.dat"
End

Sub ReadHighScore (highscore_file As String)

End Sub


Вы можете скомпилировать этот код, но ничего не произойдет, так как процедура 'ReadHighScore' пуста. Давайте заполним ее!
Мы должны открыть файл 'high_scores.dat' и прочитать необходимые данные из него. Пожалуйста, обратитесь к документации FreeBASIC по функции OPEN (открытие файла), если не знакомы с этой функцией.

Так как мы хотим открыть файл со свободным дескриптором, мы должны определить переменную, которая будет хранить эту информацию и передать дескриптор в нее. Используйте этот код:

Dim free_filehandle As Integer

free_filehandle = FreeFile


Теперь мы должны открыть файл таблицы рекордов:

Open highscore_file For Input As #free_filehandle


После того как файл открыт для чтения (FOR INPUT), давайте использовать цикл для извлечения всех данных из него и сохраним эти данные в наших массивах 'hname' и 'hscore':

For count_entry As Integer = 1 To num_of_entries
Input #free_filehandle, hname(count_entry)
Input #free_filehandle, hscore(count_entry)
' Если конец файла достигнут, выходим из цикла FOR.
If EOF(free_filehandle) Then Exit For
Next count_entry


Обратите внимание, как используется переменная 'count_entry' и как для каждой записи имя сохраняется с последующим баллом. 'hname (1)' будет иметь имя для наивысшего балла, а 'hscore (1)' сам наивысший балл в таблице рекордов. 'hname (num_of_entries)' будет иметь имя для наименьшего балла, а  'hscore (num_of_entries)' сам наименьший балл в таблице рекордов.

Не забываем о закрытии файла:

Close #free_filehandle 


Все что нам нужно сейчас, это цикл, который будет отображать все эти имена и баллы, красиво расположенные в таблице.

Do

ScreenLock
ScreenSet workpage, workpage Xor 1

Line (0,0)-(639,479), RGBA(0, 0, 0, 255), BF

Draw String (285, 120), "TOP SCORES", RGBA(255,255, 255, 255)

For count_entry As Integer = 1 To num_of_entries
Draw String (270, 140 + count_entry * 12), hname(count_entry), RGBA(255,255, 255, 250-count_entry*10)
Draw String (340, 140 + (count_entry) * 12), hscore(count_entry), RGBA(255,255, 255, 250-count_entry*10)
Next count_entry

Draw String (245, 400), "Press ESCAPE to exit", RGBA(255,255, 255, 220)

workpage Xor = 1
ScreenUnlock

Sleep 10

Loop Until MultiKey(SC_ESCAPE)


Цикл Do ... Loop заканчивается, когда пользователь нажимает ESCAPE.
Я использовал Draw String для печати имен и баллов. Цикл FOR используется для перебора отдельных записей имен и баллов, и для отображения каждой записи строго под каждой (обратите внимание, как меняется параметр оси Y при использовании переменной 'count_entry'. Я увеличиваю на 12, чтобы получить пространство между отдельными записями по вертикали). Также я использовал небольшой трюк для отображения каждого следующего счета с более низкой полупрозрачностью (последний параметр в функции RGBA).

После размещения всего этого кода в процедуре "ReadHighScore ', вы можете скомпилировать и желаемый результат появится на экране.

Теперь, когда мы закончили с легкой частью, давайте реализуем внесение новых записей в нашу таблицу рекордов.

Я построил процедуру 'WriteHighScore':

Sub WriteHighScore (highscore_file As String, users_score As Integer)


Она будет вызываться с лучшим результатом, который мы хотим ввести. Если оценка будет ниже, чем самая низкая в таблице рекордов, код не будет выполнен.

Эта процедура должна начинаться следующим кодом:

Dim free_filehandle As Integer

Dim startwrite As Integer

free_filehandle = FreeFile

Open highscore_file For Input As #free_filehandle

For count_entry As Integer = 1 To num_of_entries
Input #free_filehandle, hname(count_entry)
Input #free_filehandle, hscore(count_entry)
' Если конец файла достигнут, выходим из цикла FOR.
If EOF(free_filehandle) Then Exit For
Next count_entry

Close #free_filehandle


Как вы видите, все начинается также, как и в процедуре 'ReadHighScore'. Для того чтобы оценить счет пользователя и изменить таблицу рекордов нам нужно открыть файл, содержащий записи оценок и сохранить их в выделенных переменных. 'startwrite' - переменная , где новая запись должна быть помещена внутри таблицы рекордов (на какой позиции).
Приведенный ниже код должен работать только если оценка пользователя выше, чем самый низкий балл в таблице рекордов:

If users_score > hscore(num_of_entries) Then

For check_score As Integer = 1 To num_of_entries

If users_score > hscore(check_score) Then
InputName
' Записываем положение, в котором размещается новая оценка
' и выходим из цикла.

startwrite = check_score
Exit For
End If

Next check_score


Цикл FOR "идет" от самой высокой оценки до самой низкой, и если наша запись будет больше чем один из сравниваемых баллов, то будет записан наш новый ввод . Например, в первой итерации цикла программа проверяет 'hscore (1)' - верхняя оценка в таблице рекордов. Если оценка пользователя выше, чем 'hscore(1)', то очевидно, оценка пользователя должна иметь новый наивысший балл и 'startwrite' должна быть равна 1. InputName - это процедура , которую мы создадим позже. Внутри ее пользователь будет вводить имя новой записи.

Далее следует код, который ставит новую запись в правильном положении, а все нижние записи смещает на одну позицию вниз.

Проверьте следующий код:

If startwrite = num_of_entries Then
hscore(startwrite) = users_score
hname(startwrite) = playername
Else
 
For write_pos As Integer = (num_of_entries - 1) To startwrite Step -1
hscore(write_pos + 1) = hscore(write_pos)
hname(write_pos + 1) = hname(write_pos)
Next write_pos
hscore(startwrite) = users_score
hname(startwrite) = playername
End If


Первое условие проверяет, является ли новая запись самым низким баллом в таблице рекордов. Если это так, то нам не нужно "перетряхивать" остальные записи, мы просто заменяем самую последнюю запись на новую.
Если это не так, то выполняется цикл от самого низкого балла до новой записи ('startwrite'), то есть снизу вверх.

Например, если наша таблица состоит из 10 записей, а новая запись должна быть сделана на 5 позиции, то цикл устанавливается с 9 до 5. Когда "write_pos" равно 9, значения из 'hscore (9)' и 'hname ( 9)' передаются в 'hscore (9+1)' и 'hname (9 +1)'. Когда 'write_pos' равен 8, значения из 'hscore (8)' и 'hname (8)' передаются в 'hscore (8 +1)' и 'hname (8 +1). И так далее.

После цикла FOR нам нужно ввести новую запись на соответствующую позицию (помеченную как 'startwrite'), Новая запись, устанавливается с 'users_score' и 'playername', где 'playername' будет введено внутри процедуры 'InputName'.

Последнее, в процедуре 'WriteHighScore' нам нужно сохранить новые записи обратно в файл:

free_filehandle = FreeFile

Open highscore_file For Output As free_filehandle
For count_entry As Integer = 1 To num_of_entries
Print #free_filehandle, hname(count_entry)
Print #free_filehandle, hscore(count_entry)
Next count_entry
Close free_filehandle


Обратите внимание , что FOR OUTPUT используется и PRINT для записи данных во внешний файл.
Все что нам нужно сейчас, это создать процедуру 'InputName' вроде этой:

Sub InputName

ScreenSet workpage, workpage Xor 1
ScreenSet 0,0
Line (0,0)-(639,479), RGBA(0, 0, 0, 255), BF
Locate 12, 17
Input ; "Please input your name: ", playername

End Sub


Конечно, это может выглядеть по другому в вашей игре. Возможно, Вы попросите, чтобы игрок ввел его/ее имя в другом месте в игре (например при старте игры). Просто имейте в виду , что это нужно.

Чтобы проверить все ли в порядке, просто напишите...

ReadHighScore "high_scores.dat"
WriteHighScore "high_scores.dat", 4500
End


... после первого SCREENSET (вне процедур). Измените второй параметр с вызовом 'WriteHighScore', чтобы ввести различные очки в вашу таблицу. Я уверен, что вы знаете, что, вызывая 'WriteHighScore' , второй параметр не должен быть жестко закодирован со статическим числом. Сделайте параметр с переменной, в которой вы будете сохранять счет игрока.

Что дальше?

Другие вещи, которые я хочу показать относительно этой темы, связаны с шифрованием счета и лучшим режимом ввода имени. Поскольку оба этих действия, которые я использую, написаны не мной, я только покажу их и обеспечу их в программе в качестве примера, который вы можете легко сможете использовать для своих собственных потребностей.
Шифрование сделано, используя две функции, 'neoENCpass' и 'neodeENCpass'. Одна для шифрования , другая для дешифровки. Их вызывают с паролем, который используется едино как для шифрования, так и для дешифровки.
Каждый раз, как вы получите данные из файла, вы расшифровываете их:

Input #free_filehandle, hname(count_entry)
neoENCdepass SAdd(hname(count_entry)), Len(hname(count_entry)), "yourpass"


Единственная раздражающая особенность этого метода , что вам нужно отдельный исходный код для шифрования / дешифрования своей таблицы, так как процедуры внутри проекта будут работать, только если таблица предварительно шифруется. Я предоставил небольшую программу, которая делает это шифрование для вас. Рекомендую сделать резервную копию вашего проекта  в отдельную папку.
Вместо шифрования вы можете использовать бинарные файлы, которые конечно не шифруют, но все таки значения в файле не так очевидны для пользователей. Зашифрованные файлы все таки надежнее, поскольку изменить значение можно только если знаешь пароль. Ах да, при предоставлении исходного кода публике, не забудьте изменить пароли шифрования внутри конечного EXE файла :).
Во всяком случае, вы можете вообще не шифровать файл. Но я лично предпочитаю, чтобы не каждый Дик и Том мог изменить их с помощью блокнота. Незашифрованные таблицы могут сломать реальную статистику игроков, при редактировании файла.
Ввод имени я не буду описывать. Если захотите, просмотрите примеры кода, думаю там не трудно понять. Это гораздо лучше, чем простой ввод, описанный выше (вы можете использовать его с пользовательскими шрифтами) и так же он позволяет ограничить количество символов в имени. Эти процедуры были сделаны 'Ryan Szrama', и все благодарности ему.
Скачать расширенный пример (с шифрованием и лучшим вводом имени и данных): http://lachie.phatcode.net/Downloads/Managing_A_High_Score_Table.zip

И это все для этого урока.
До следующего раза, удачи!

Учебник написан Lachie D. (mailto CHR$(58) lachie13 CHR$(64) yahoo CHR$(46) com ; http://lachie.phatcode.net - The Maker Of Stuff)