Посмотрите на Рисунок 6(б). Видно, что когда мы заменяем значение одной ячейки, эти изменения влияют, как на первый так и на второй круги ячеек. Возникает резонный вопрос: не случится ли такой ситуации, когда помещение на карту новой текстуры потребует перерисовки всей карты, так, словно кто-то бросил камень в воду? Если следовать принципам изложенным в этой статье, то не случится. Я проверял все варианты. Изменения касаются лишь первого и второго круга. Кто не верит, может проверить, посчитать, прикинуть, но это займет много времени. Теперь мы подходим к главному — по какому принципу рассчитывать новые значения изменяемых текстур. Возможно я Вас немного удивлю, но рассчитывать нам больше ничего не придется. Нам понадобится создать три массива (таблицы) 16 на 25 элементов, записать в них заранее расчитанные значения, а затем их считывать в ходе выполнения программы. Сейчас поясню.
Поскольку в общей сумме у нас по максимуму может измениться 25 элементов на карте (Рисунок 6(б)), мы создадим вспомогательную матрицу 5х5, куда будем считывать с карты значения соответствующих ячеек. Затем мы изменим значения в этой матрице и поместим ее снова на карту откуда взяли.
В каждой ячейке может быть следующее значение:
Index + GroundIndex*16 , где
Index — число от 0 до 15 указывающее на номер переходной текстуры. GroundIndex — число от 0 до 2 указывающее на тип земли — ВОДА, ТРАВА, КАМНИ
Итак мы знаем номер лежащей в ячейке переходной текстуры (GetAdditionalIndex), мы также знаем номер этой ячейки в матрице 5×5. Этого вполне достаточно. Мы создадим массив-таблицу ширина которого равна количеству возможных переходных текстур 16, а высота равна количеству ячеек в матрице 5×5=25. Дальше мы действуем следующим образом: Считываем в матрицу 5×5 участок карты центром которого является ячейка в которую мы "кладем" новую землю, в ячейку 12 кладем цельную текстуру той земли которой мы рисуем. Затем для всех ячеек матрицы 5×5 кроме 12-ой делаем следующее: Поучаем номер переходной текстуры (GetAdditionalIndex) и обращаемся к таблице 16×25. Где номер переходной текстуры это положение ячейки таблицы 16×25 по горизонтали, а номер ячейки в матрице 5×5 это положение ячейки таблицы 16×25 по вертикали. На рисунке 7, цифра 6 по горизонтали это GetAdditionalIndex от текстуры, которая прячется в матрице 5×5 в ячейке номер 17, а "Х" в красной клетке это тот самый новый номер для этой текстуры. Фактически смысл сводится к следующему: посмотрели какая была текстура — заглянув в таблицу, узнали какая стала.
Рисунок 7
Вы наверное спросите — а как узнать какие значения должны быть в таблице 16×25? Никак. Они рассчитываются в уме и записываются в таблицу ручками. Но вы можете не задумываться над этим, я уже рассчитал и записал их в своем примере. Смотрите в исходниках.
Кстати в тексте статьи я упоминал о том, что нам придется создать три таблицы 16×25. Я не оговорился. Дело в том, что у нас возможны три варианта, когда значения одной и той же ячейки в таблице должны быть разными:
1. Активная земля равняется прежней земле. Например, мы рисуем ТРАВОЙ, а в рассчитываемой ячейке тоже ТРАВА или ТРАВА с ЗЕМЛЕЙ.
2. Активная земля не равна прежней земле. Например, мы рисуем ТРАВОЙ, а в рассчитываемой ячейке ВОДА или ВОДА с ЗЕМЛЕЙ.
3. Рисуем переходным типом земли — ЗЕМЛЯ.
Если кому-нибудь еще что-то не понятно, то надеюсь после рассмотрения исходных текстов программы все встанет на свои места.
Пример написан на Delphi 3 Professional, с использованием компонент библиотеки DelphiX для DirectX 6.0
Модуль MapDat:
// Определение класса Matrix5
Type TMatrix5 = class(TObject)
private
Matrix : array[0..4,0..4] of byte;
Vector : array[0..24] of byte;
public
function GetBaseIndex( ElementIndex : Integer ): Integer;
Function GetAdditionalIndex( ElementIndex : Integer ): Integer;
procedure Fill(X,Y : Integer);
procedure Place(X,Y : Integer);
procedure Culculate(X,Y : Integer; BrushIndex : Integer );
procedure Draw(X,Y : Integer; BrushIndex : Integer );
end;
Внутри класса определены переменные в виде матрицы 5×5 и вектора. Некогда я думал, что это упростит написание программы, сейчас я думаю, что можно воспользоваться только вектором. Методы GetBaseIndex и GetAdditionalIndex мы уже рассматривали, рассмотрим остальные:
Метод Fill(x,y : Integer);
procedure TMatrix5.Fill(X,Y : Integer);
var i,j : Integer;
begin
for j := 0 to 4 do
for i := 0 to 4 do