Книга знаний

1С:Предприятие

Играем в

Начинающие программисты на языке одинСи часто задают вопрос: "Как работать с объектом "Таблица" в 1С?". В принципе данная тема достаточно хорошо раскрыта в ЖКК, и поэтому частота появление этого вопроса вызывает недоумение, во всяком случае, у меня. Тема сама по себе большая, и ее трудно уместить в рамках одной статьи. По этому остановимся на самом популярном вопросе: "Как раскрасить таблицу в 1С?". Самым лучшим способом разобраться, как работает не понятный тебе объект, это написать программу, которая работает с данным объектом. Вот и мы сейчас попробуем реализовать что-нибудь, используя агрегатный объект "Таблица". Если быть точнее, то мы с вами сейчас реализуем игру "Жизнь", придуманную в 1970 году математиком Дж. Конвеем. Почему игру, и почему именно "Жизнь". Во-первых, разбираться как кодиться игра все-таки интереснее, чем смотреть на сухой код вывода отчета на печать. Во-вторых, код ее достаточно прост и не будет загромождать суть разбираемого нами вопроса, то есть "раскрашивание" объекта "Таблица". Итак, начнем играть.Автор статьи: Волшебник | Редакторы:
Последняя редакция №4 от 09.01.06 | История
URL: http://kb.mista.ru/article.php?id=67

Правила игры


Действие игры происходит на некой плоскости, разделенной на клетки. Каждая клетка плоскости может принимать два состояния. Есть на ней жизнь или нет. На состояние любой клетки оказывает влияние состояние соседних клеток. С определенной дискретностью на плоскости происходит смена поколений. Состояние любой клетки под воздействием "Генетического закона Конвея" может измениться. Говоря по-русски, жизнь в любой момент может прекратиться либо наоборот появиться на свет. Законы развития просты как три копейки, точнее две, и состоят из двух пунктов:

    1. Рождение. На любой не занятой клетки может возникнуть жизнь, если в окрестности Мура, восемь соседних клетках, есть ровно три живых организма.
    2. Смерть. Любой живой организм вымирает от недостатка питания или общения, по этому, если в окрестности Мура менее чем два соседа или более трех, организм умирает.

Далее интерпретация правил несколько варьируется.

Первое различие касается плоскости, или мира. Мир может быть замкнутым, а может быть ограничен. В ограниченном мире у любой угловой клетки в окрестности только три соседа, а у любой боковой, но не угловой, их пять. В замкнутом мире у любой клетки соседей всегда восемь.

Второе различие. Когда игра считается законченной. Тут много разных вариантов, и цикличность поколений, переход в колебательное или устойчивое состояние. Все это нам не надо. Ведь мы на самом деле играть не собираемся, не так ли. Мы оставим один вариант конца игры - это вымирание колонии.

Теперь не много перелопатим правила под свои нужды. Игра будет происходить на плоскости 40х17 замкнутого пространства. Играющий может влиять только на расстановку живых сил первого поколения. Иначе говоря, игрок сам создает колонию. Колония может быть создана случайно, с заданием плотности населения. Либо руками, то есть когда бог сам говорит: "Да будет жизнь". После смены поколения изменить что-либо в колонии будет нельзя. Смена поколений происходит путем применения генетического закона Конвея. Игра заканчивается смертью колонии, как от естественных причин, так и от "милости" бога, путем всемирного потопа или иного Армагеддона.

Ну, с правилами разобрались, приступаем к дизайну.

Дизайн


Позвольте не много уклониться от темы и заметить, что многие программисту отбрасывают такую вещь как дизайн приложения напрочь. Это очень не мало важный фактор. Конечный пользователь вряд ли будет любоваться оригинальностью вашего кода. Просто по одной причине, он ни черта не поймет. А вот красявость вашей аппликации может стимулировать как интерес, так и наоборот, полное отрицание. Поэтому уделяйте дизайну приложения больше сил и возможностей.

Сначала глянем на форму. Почесав затылок, приходит к выводу. Тут дизайн на фих не нужен. Ведь мы будем работать с объектом "Таблица" и форма будет скрыта за ней. Поэтому ляпаем на форму, что-то вроде эбоута. И едем дальше, к таблице.

Тут уж есть, где разгуляться. И все будет зависеть от буйности вашей фантазии. Я буйствовать, сильно не буду. Нету на это время.

Запускаем пофигуратор и переключаемся на объект "Таблица". Для начала выставляем у 42 клеток ширину равную 3 у.е. в системе 1С. И у 23 строк выставляем высоту строки равной 15 единицам. После чего у двух строк, а именно второй и четвертой выставляем высоту строки 8,25. У строки 4 делаем высоту равной 20,25. Выделяем область таблицы с координатами верхней левой [R1C1] и нижней правой [R23C42], в дальнейшем области будем показывать так [R1C1:R23C42] и установим для ячеек этой области следующий формат используемого шрифта (см. рисунок №1).


Рис. 1

Далее, для области [R2C2:R22C41] используя свойства ячеек, сделаем жирную рамку (см. рисунок №2).


Рис.2

Теперь займемся раскраской. Для ячеек областей таблицы [R2C2:R4C41] и [R21C2:R21C41] установим в качестве фона светло-серый цвет (см. рисунок №3).


Рис.3

Теперь у нас получилась прямоугольная область с четко обозначенными границами живого пространства. Теперь немножко подумаем, как отделять нам клетки друг от друга. Можно конечно положиться на саму 1С, сказав ей выводить границы ячеек. Но мне кажется это не совсем красяво. Ведь тогда границы будут и там, где они сто лет не нунжны, а это не есть гуд. Выделяем область [R5C2:R21C41] и для ее ячеек устанавливаем следующие свойства (cм. рисунок №4)


Рис.4

Правда, после этого у нас стали не совсем кошерно отображаться границы справа и слева. Исправляем косяк. Для этого у области [R5C2:R21C2] выставляем свойства похожие на те, что показаны на рисунке №2, с одним различием. Будем не обводить, а рисовать только слева. Нам ведь только там нужна жирная граница. Для этого в группе "Рамка" вместо "Обвести" выбираем "Лево". Аналогично поступаем с областью [R5C41:R21C41], с той лишь разницей, что выставляем границу справа.

Надеюсь, понятно для чего я выкрасил верхнюю и нижнюю часть в светло-серый цвет. Да. Вверху будет у нас панель управления, внизу строка состояния.

Займемся панелью управления игрой. Для управления игрой нам понадобиться следующее кнопки:
    - запуск игры,
    - смена поколения,
    - Армагеддон,
    - команда генерации нового поколения,
    - и как необязательная опция кнопки для чтения и сохранения текущего состояния игры.

Путей тут много, я пошел одним из самых простых. Выделяем в нашей таблице следующие области: [R3C3:R3C7], [R3C9:R3C13], [R3C16:R3C20], [R3C22:R3C26], [R3C28:R3C32] и [R3C35:R3C39]. И делаем объединение ячеек. После чего, в цикле, у каждой из шести ячеек выставляем следующие свойства (см. рисунок №5)





Рис.5

У всех шести ячеек свойства совпадают за не большим различием. В том месте, где у меня написано Load, для каждой из ячеек надо написать свое значение (я использовал следующие наименования: "Load", "Save", "Begin", "Next", "Random", "Finish"). Так же вам надо обратить на поле "Расшифровка". В нем обязательно должно быть написано слово "Hook". Для чего, объясню чуть позже, когда займемся кодированием игры.

Теперь займемся строкой состояния. В ней нам надо будет выводить только два значения: количество живых существ и номер текущего поколения. Границ нам тут не надо. Единственное что я сделал (см. рисунок №5, пункт №2), правда, с небольшим отличием установил выравнивание по правому краю. Итак, поехали. Объединяем в таблице следующие области: [R22C3:R22C6], [R22C7:R22C10], [R22C12:R22C16] и [R22C17:R22C20]. В них пишем, по порядку, следующее: "Count life:", "0", "Period:", "0".

Наконец наведем несколько приятных штрихов. Для области [R5C2:R5C41] установим границу сверху, черным цветом, стилем непрерывная линия толщиной как у кнопок (см. рисунок №5, пункт №3). Аналогично, только снизу, проводим границу для области[R21C2:R21C41]. И на конец у ячеек [R22C11] и [R22C21] проводим такую же границу справа.

Вообще границы у объекта "Таблица" вещь рульная. Играя ими, можно добиться кучу поразительных визуальных эффектов. Например, такой:


Рис.6

С дизайном покончено. Прежде чем окончательно закрывать таблицу нам надо сделать две вещи. Первая это сформировать секции, в принципе не обязательно, но для избегания всяких несуразностей лучше сделать. Я сформировал две. Одну вертикальную, назвав ее "Width". Другую горизонтальную - "Height". Вторая вещь долгая и нудная. Как тут пошустрее выкрутиться описывать не буду. Вам надо, для всех ячеек области [R5C2:R21C41] в поле "Расшифровка" написать слово "Hook".

Если вы сделали все правильно, то у вас должно быть получиться, нечто, похожее на это:


Рис.7


Он сказал: "Поехали", и махнул рукой



Для начала, на всякий случай, позвольте вам напомнить структуру модуля отчета. То есть что куда и зачем писать.
    1. Блок описания глобальных переменных. То есть переменные, которые видны и доступны все модулю.
    2. Блок процедур и функций. Тут размещаются все процедуры и функции нашего модуля. Каких либо строгих правил нет. Кроме одного: все процедуры и функции, используемые другими процедурами или функциями, должны располагаться сверху от вызывающих их процедур или функций.
    3. Блок инициализации модуля. Здесь выполняются действия, которые необходимо выполнить в самом начале работы модуля, до того как форма обработки выведена на экран. В принципе данный блок можно опустить, используя вместо него переопределенную функцию OnOpen. Кому как удобнее.

Надеюсь вывод табличной части на экран, не вызывает не у кого усиленное сердцебиение. Поэтому останавливаться на этом подробно не будем. А подумаем над такой вещью. Как сделать так, что бы пользователь увидал нашу таблицу поверх формы. Правда, зачем пользователю смотреть на нее, все равно управление игрой будет происходить в таблице. Сразу напрашивается вариант с процедурой OnOpen. Но он не совсем подходит, так как форма выскакивает поверх таблицы, и для переключения на таблицу надо будет делать лишни телодвижения. Вариант с функцией на форме, тоже не прокатывает. Почему, спросите у 1С. Что делать. Делать не фих, снимаем трусы и начинаем бегать:

var Mode;
     var true, false;
     var Table;

     procedure OnClose()
          if Mode = true then
               Mode = false;
               Table = createobject("table");
               Table.SourceTable(Table);
               Table.PutSection("Height|Width");
               Table.Protection(true);
               Table.Show("game ""Life""");
               returnstatus(false);
          endif;
     endprocedure

     procedure OnOpen()
          true = 1;
          false = 0;
          Mode = true;
          Form.Close();
     endprocedure


Теперь глянем, что мы тут навыбегали. У нас тут объявлены две предопределенные процедуры. Одна из них вызывается при открытии, другая при закрытии формы. Суть всей фичи такова. В процедуре при открытии формы, пока она еще не отобразилась на экране, мы закрываем форму, предварительно выставив флаг Mode в положение true. Он нам нужен для того, что бы процедура OnClose знала, кто ее пытается закрыть, процедура OnOpen или нечто другое. Поскольку форма закрывается, наступает очередь процедуры OnClose. Та смотрит на флаг, и если он выставлен в true, начинает рисовать таблицу, предварительно опустив Mode в положение false. И как только таблица нарисована, с помощью процедуры returnstatus обламывает закрытие формы. В результате трюка Таблица рисуется поверх формы, что нам и требовалось получить.

У вас могут возникнуть вопросы, зачем я использовал true, false и Table, да к тому же вынес их в блок глобальных переменных. Глобально я их объявил, потому, как они будут нужны куче других процедур и функций. А использование true & false могу только объяснить наглядностью. Во всяком случае, мне кажется так понятнее, чем просто 1 и 0, как учит нас 1С.

Следующее, что бросается в глаза, так это отсутствие функции Random в языке 1С. А ведь нам она понадобиться. По условию задачи, жизнь может генерироваться случайно. И тут на помощь приходит avb он же Алексей Бажитов, надо заметить, что и трюк с выводом таблицы я узнал от него. И так функция Random:

// объявляем глобальную переменную
     var GlSeed;

     function Random(MaxValue)
          GlSeed = GlSeed * 1103515245 + 12345;
          return ((GlSeed / 65536) % 32768) % MaxValue + 1;
     endfunction

     // добавляем в процедуру OnOpen
     GlSeed = _getperformancecounter();


Объяснить тут мало, что можно. Стоит только сказать, что данный алгоритм был предложен, комитетом ANSI-C, для тех, кто не знает, он вырабатывает стандарты для языка Си. В том, что добавляется в процедуру OnOpen, используется не документированная функция языка 1С, которая возвращает число миллисекунд прошедших с момента включения компьютера. И в данном случае выполняет функцию, аналогичную паскалевской или бейсиковой функции randomize. Сама функция Random возвращает случайно число из диапазона от 1 до MaxValue. Все теперь рандом у нас есть, и поверьте мне не плохой. Из всех виденных мною вариантов, самый лучший, для одинЭс конечно.

Далее нам нужен двухмерный массив размером 40х17. Не спешите хлопать в ладоши, говоря, что у 1С нет двухмерных массивов. Спорим есть. Надо просто внимательно глянуть на агрегатный объект, слово то придумали в фирме 1С, "ТаблицаЗначений". Конечно, у него есть не которые минусы по сравнению с обычным массивом, но зато есть и куча плюсов. Останавливаться на них мы не будем, а просто объявим глобальную переменную и создадим для нее пустой агрегатный объект "ТаблицаЗначений", имеющий 40 колонок и 17 строк.

     var tabTemp;

     tabTemp = createobject("valuetable");
     for x = 1 to 40 do
          tabTemp.NewColumn("K" + string(x), "number", 1, 0);
     enddo;
     for x = 1 to 17 do
          tabTemp.NewLine();
     enddo;


Так еще, что первое бросается в глаза, из глобальных переменных, нам нужны две, в которых будем хранить количество жизней в текущем поколении и соответственно номер самого поколения. Объявляем их:

     var Life, Period;
 //и инициализируем:
     Life = 0;
     Period = 0;


Вроде все готово к штурму замка. Сохраняем обработку. Запускаем интерпретатор. Отрываем обработку там. Любуемся (см. рисунок №8).


Рис.8

Вроде все так, но что-то не так. Зачем нам в самом начале нужны активные кнопки "Save", "Begin", "Next" и "Finish". Правильно, все они нужны в тот момент, когда игра уже началась. Да и то не всегда. Вот тут-то ребята мы и подошли к первой части нашего вопроса о раскраске таблиц. Научимся менять цвет текста в таблице. Для изменения цвета текста ячейки таблицы служит функция для работы с агрегатным объектом "ОбластьТаблицы" TextColor. Данная функция может использоваться как для изменения цвета текста области таблицы, так и для получения информации о цвете текста у заданной области. Все это понятно, но что за новый объект "ОбластьТаблицы". В ЖКК про него ни гугу, и как нам его получить. И тут вы не правы. ЖКК упоминает о нем, конечно не так подробно как о "Таблице", но на безрыбье и рак рыба. А если положить лапу на сердце то можно утверждать, что в данной книжке достаточно материала об агрегатном типе данных "ОбластьТаблицы". Для того, что бы получить объект "ОбластьТаблицы" нам нужно использовать метод Area объекта "Таблица". Пока неясно, ни чего страшного, далее все будет понятнее. Продолжаем кодить игру. Итак, у нас есть пять кнопок, активностью которых мы хотим управлять. Если текст кнопки черный, значит кнопка - активна. Если же цвет текста темно-серый, значит кнопочка энейбл. Для этого объявим пять глобальных переменных:

     var flLoad, flSave, flBegin, flNext, flRandom, flFinish;

//установим им значения на начало игры:

     flLoad = true;
     flSave = false;
     flBegin = false;
     flNext = false;
     flRandom = true;
     flFinish = false;


а дальше делаем процедуру, которая будет менять цвет текста в зависимости от выставленных флагов:

     procedure RefreshTable()
          Table.Area("R3C9").TextColor(?(flSave = true, 0, 8421504));
          Table.Area("R3C16").TextColor(?(flBegin = true, 0, 8421504));
          Table.Area("R3C22").TextColor(?(flNext = true, 0, 8421504));
          Table.Area("R3C28").TextColor(?(flRandom = true, 0, 8421504));
          Table.Area("R3C36").TextColor(?(flFinish = true, 0, 8421504));
     endprocedure


Было бы неплохо заодно сообщать пользователю, о количестве живых существ в нашем мире. Да заодно и номер поколения. Для этого у уже известного нам объекта "ОбластьТаблицы" есть свойство, не путать с методом, Text. Данное свойство доступно как для чтения, так и для записи. Добавляем в нашу процедуру следующие строчки:

     Table.Area("R22C7").Text = string(Life);
     Table.Area("R22C17").Text = string(Period);


А теперь один нюанс. Для того чтобы пользователь увидел сделанные в таблице изменения, таблицу надо перерисовать, используя уже известный нам метод Show. Добавляем следующую строчку:

Table.Show();


Ну и теперь добавим вызов вновь созданной процедуры сразу же после первого вывода таблицы на экран, в процедуру OnOpen, следом за строкой "Table.Show("game ""Life""");". Сохраним изменения и полюбуемся на результат:


Рис.9

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

Раскраска



Наверное вы уже сами просекли как красить ячейки. Да? Правильно. Все то же самое с точностью наоборот. Только теперь мы будем устанавливать, а не брать. И на это раз рассмотрим фон области. Хотя тут вы можете сами позаниматься рукоблудством, сколько вашей душе будет угодно. А я пока накалякаю процедуру по отображению виртуального мирка на очке вашего монитора, или функцию, которая отобразит на карте текущее положение дел в нашей колонии строго режима.

Для работы с цветом фона области у объекта "ОбластьТаблицы" есть метод BackGroundColor(). Если ему в качестве параметра передать число из диапазона от 0 до 16777215, то он закрасит фон выделенной области в какой либо цвет. Вот только вопрос, ведь надо не в какой-либо, а который хочется мне. У данной проблемы есть несколько решений. Первый самый простой. В режиме конфигурирования закрасить ячейку нужным цветом, а потом программно считать, значение фона, для данной ячейки. У этого способа есть парочка минусов. Во-первых, в конфигураторе мы не можем выбрать любой цвет. Во-вторых, куча лишних телодвижений. Можно пойти другим путем. Используя возможность данной функции принимать в качестве параметра не одно число, а целых три. Да ребятишки из 1С реализовали для нашей раскраски так называемую цветовую схему RGB color(8 bit/Channel). Что сие значит. Да только то, что для кодирования любого цвета используется три составляющих. Это интенсивность красного, зеленого и синего цвета. Сама интенсивность задается 8 битами и может принимать значение от 0 до 255. То есть для отображения чистого красного цвета нам надо передать функции три байта интенсивность красного 255, синего и зеленого по нулям. Соответственно формируются синий и зеленый цвет. Все остальные цвета надуманы, и создаться за счет обмана зрения, из смеси этих трех цветов. Так черный цвет - это помесь красного, синего и зеленного с интенсивностью равной нулю. Белый цвет все то же самое, только интенсивность цветов равна 255. Интенсивность составляющих для других цветов можно узнать в любом графическом редакторе. Когда вы получите интенсивность трех составляющих, нужного вам цвета, вычислить его номер можно будет по следующей формуле:



Надеюсь доступно объяснил, чем вызван такой диапазон передоваемых значений. Количеством возможных цветов и их градаций, то есть от черного цвета ((0 * 65536) + (0 * 256) + 0) = 0, до 16777215, белого ((255 * 65536) + (255 * 256) + 255). Все выше сказанное справедливо и для других функций при работе с цветом.

Итак, с цветом разобрались, начинаем кодить. Как уже отмечалась, наша процедура должна раскрасить мир в зависимости от текущего состояния колонии. То есть она, процедура, должна знать, что в какой ячейки есть жизнь, а в какой нет. Для этого будем передавать ей, в качестве параметра, массив размером 40х17, в котором значение true для ячейки [x, y] сигнализирует о том, что в данной клетке жизнь есть. И тогда становиться все просто. Последовательно пробежать по всем ячейкам, просмотреть значение. И в зависимости оттого, что там есть, раскрасить соответствующую ячейку в нужный цвет. В конце не забыв перерисовать таблицу с помощью уже реализованной нами процедуры RefreshTable():

     procedure DrawTable(tabProg)
          for y = 1 to 17 do
               for x = 1 to 40 do
                    Address = "R" + string(4 + y) + "C" + string(1 + x);
                    Table.Area(Address).BackGroundColor(
                                        ?(tabProg.GetValue(y, x) = true, 16711680, 16777215));
               enddo;
          enddo;
          RefreshTable();
     endprocedure


Надеюсь, вас не смущает, что при вычислении адреса, мы делали приращение строк и колонок. Ведь в действительности ячейки нашего "мира" с адресом [R5C2] соответствует ячейка массива с адресом [1, 1].

Случайно конечно хорошо, но хотелось бы иметь возможность раскрашивать табличку ручками, да и тычки по "кнопкам" тоже надо ловить. Что для этого надо? Ответ напрашивается сам - будем делать "Хук".

Ловушка



Кое-что для реализации ловушки мы уже сделали. Помните когда занимались дизайном, в свойствах некоторых ячеек, в поле расшифровка ставили "Hook", а если вы уже запускали игру, то получали кучу ошибок (см. рисунок №11).


Рис. 11

Прибить ошибку просто. Для этого в блоке глобальных переменных надо описать переменную Hook:

   
var Hook;


Проверяем. Точно ошибка пропала. Курсор при наведении на ячейки принимает форму креста с лупой. Винда, если кликнуть по данной ячейке, издает нечленораздельные звуки, сигнализируя об ошибке, конечно, если у вас не прибита звуковая схема. И все, больше ничего. Нам надо немножко не это. Опять книжка от Федора Достоевского. Лезем в ЖКК и открываем главу №31 посвященную работе со специальным агрегатным типом данных "Таблица". Внематочно читаем. Тем более читать там не много, так что сильно не напрягайтесь. Фирма 1С с девизом: "Краткость - сестра таланта", делает всех доступно серьезными или серьезно доступными, уделив этому вопросу целых два абзаца в ЖКК. Могет и три, точно не помню. Суть их сводиться к следующему: "Поле Расшифровка, в свойствах Ячейки, служит для связи агрегатного объекта Таблица с модулем отчета или отображения различных объектов используемых в отчете, и никак не влияет на отображение самого объекта Таблица". Во как. Теперь понятненько, почему добавление глобальной переменной Hook прибивает ошибку, показанную на рисунке №11. Ну что же попробуем присвоить хуку какое-либо значение, например единицу. Для этого в процедуру OnOpen вставляем строку:

   
Hook = 1;


Краш-тест. Винда визжать перестала. А пендель по ячейкам с хуком, рисует стандартный мессадж бокс, показанный на рисунке:


Рис. 12

Это уже больше походит на правду и начинает радовать. Реагировать на клики 1С научили. Но нам же надо, что бы реагировала наш отчет. Копаем в ЖКК дальше. Копать придется далеко. До самого конца главы посвященной объекту "Таблица". Вот там то мы и найдем описание двух процедур, которые и обрабатывают пинки по ячейкам таблицы. Почему именно две, и чем они отличаются между собой, разгребайте сами. Я лишь скажу, что нам нужна SheetCellProcessing. Итак, шлепаем в модуль нашего отчета и добавляем следующий код:

     procedure SheetCellProcessing(Value, Flag, tabProg, Address)
          Flag = false;
     endprocedure;


Сначала поговорим о том, что мы передаем данной процедуре:
    1. Value - мы ее использовать в нашей игре не будем. Но так на всякий случай. Сюда толкается то значение, которое имела переменная, указанная в поле "Расшифровка", в момент формирования "таблицы". В нашем случае тут всегда будет единица.
    2. Flag - флаг стандартной обработки. Если флаг не сброшен, то после завершения процедуры SheetCellProcessing обработка пинка отдается самой одноэсине. А та, в зависимости от значения ячейки (Value) выводит всякие боксы, как частный случай (см. рисунок №12). Для разных типов данных выводятся разные боксы, для элемента справочника - форма элемента справочника, для документа - форма документа и т.д.
    3. tabProg - сам объект таблица, в ячейку которой пнул зверь.
    4. Address - адрес ячейки которую пнули.

Если с этим все ясно, то едем дальше, иначе ЖКК в руки и бога в помощь.

Как видно из примера мы сразу роняем флаг стандартной обработки. И в правду на фиха нам всякое не понятное во время игры. Проводим тест-драйв. Запускаем нашу обработочку и начинаем пинать ячейки.

Вери вел. Ячейки пинаются, Винда не пикает, и не выкидывает на экран всякие глюпые мессаги. То, что на данном этапе и нужно. Как говорят пилоты: "Высота - 10936 ярдов. Полет нормальный". Летим дальше.
Если быть точнее, то займемся планированием. Откинемся на спинку кресла и расслабимся, как советуют девелоперы из компании Майкрософт. У нас в таблице получилась куча ячеек клики, по которым нам надо ловить. И в зависимости от того, какую ячейку пнули выполнять те или иные действия. Первое, что приходит на ум - это адрес ячейки. В действительности данный способ не совсем рационален и мы его сразу списываем в помойку. Глянем внимательно на нашу таблицу, закурим и подумаем, чем наши ячейки отличаются друг от друга. Правильно - содержанием. То есть текстом, который отображается в ячейке.

"Постой, как же так. А ячейки на игровом пространстве вообще не имеют текста". Слышаться ропот наиболее внимательных читателей. И они правы. Вот только надо ли нам различать пинки между ними. Ведь в действительности действия по обработке пенделя по ним до-тупого однообразны. Глянуть есть ли в текущей клетке жизнь. Если да - то прибить ее, иначе - создать. Значит, нам надо просто разделить действия по обработке кликов по кнопкам управления игрой и кликам по игровому пространству.

Добавляем в процедуру следующий код:

     Text = tabProg.Area(Address).Text;
     if emptyvalue(Text) = false then
          message("Пнули кнопку управления");
     else
          message("Пнули ячейку мира");
     endif;


Попробуем разобрать, чего это мы тут понаделали. Первой строкой мы получаем значение свойство Текст у объекта "ОбластьТаблицы" с нужным нам адресом. Далее просто смотрим пустое оно или нет. Если значение не пустое - значит кликнули ячейку управления, иначе - игрового поля. Надеюсь вы догадались, что после отладки строчки с функцией message надо изничтожить. Итак пока все просто. Дальше будет еще проще, во всяком случае я на это надеюсь.
На время забудем про панель управления и займемся игровым полем и жизнью на нем. После того как мы поймали клик на ячейки игрового поля, нам надо узнать если в этой клетке жизнь. Как? По цвету фона. Глядим какой фон у данной ячейки таблицы:

   
Color = tabProg.Area(Address).BackGroundColor();


У меня в игре цвет может принимать два значения. Синенький (16711680) - есть жизнь, и беленький (16777215) - жизнь отсутствует. Добавляем нижеследующий код:

     if Color = 16711680 then
          // Здесь буду действия связанные с убийством.
     else
          // Здесь наоборот, с рождением.
     endif;


Для начала рассмотрим убийство. Сперва спросим игрока об осознанности его деяния, так на всякий случай, исходя из правил хорошего тона. Спрашивать будем используя функцию doquerybox (см. ЖКК). Получив подтверждение умышленности данного деяния уменьшаем показатель количества жизней на убитый организм. Смотрим количества оставшихся организмов, и если таковых не делаем не доступными кнопки управления игрой: "Begin" - смысл играть на пустом поле и "Save" - да еще и хранить его.
Все вышесказанное на языке 1С будет выглядеть примерно так:

     if doquerybox("You really want to kill a life?", 4) = 6 then
          Life = Life - 1;
          if Life = 0 then
               flSave = false;
               flBegin = false;
          endif;
          Color = 16777215;
     endif;


Смысл предпоследней строки станет ясен несколько ниже. А пока по аналогии займемся процессом обратным убийству, то бишь рождению жизни:

     if doquerybox("You really want to create a life?", 4) = 6 then
          Life = Life + 1;
          if Life = 1 then
               flSave = true;
               flBegin = true;
          endif;
          Color = 16711680;
     endif;


Как видим различий почти нет, а значить и жевать тут больше нечего. Едем дальше. А дальше вообще все просто. Нам осталось выкрасить ячейку в полученный цвет и перерисовать таблицу:

     tabProg.Area(Address).BackGroundColor(Color);
     RefreshTable();


Надеюсь вы вкурили для чего я использовал присвоение переменной Color во время убийства и рождения жизни.

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

     if flNext = false then
          // Здесь размещен код по созданию/убийству организмов
          // на игровом поле.
     endif;


Также тут можно было бы использовать флаг кнопки "Finish". Ну или воткнуть что-то свое. Как гриться: "Хозяин - барин". Вам решать, а мы пока переходим к управлению игрой.

Панель управления



Во время проектирования игры мы создали на панели управления шесть кнопок, для вызова тех или иных функций управления игрой. На всякий случай напомню вам их:

    1. Load - для чтения ранее сохраненной игры.
    2. Save - сохранения текущей игры.
    3. Begin - начать новую игру.
    4. Next - сменить поколение.
    5. Random - сгенерировать первое поколение случайным образом.
    6. Finish - прекратить текущую игру.

Теперь подумает о доступности тех или иных функциях на разных стадиях игры. Начнем пожалуй с определения количества стадий игры. А их у нас всего две: подготовка первого поколения и сама игра ;). Хотя первую можно разделить тоже на две: первоначальная инициализация - на поле нет ни одного живого организма, и готовность номер один - на поле появилась жизнь. Исходя из вышеизложенного, получаем следующую таблицу доступности функций:

РежимLoadSaveBeginNextRandomFinish
1a+---+-
1b+++-+-
2++-+-+

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2025 | Mista.ru

Яндекс.Метрика