Четверг, 21.11.2024, 21:09


Главная
Регистрация
Вход
Welcome to Home Приветствую Вас Гость | RSS  
Меню сайта

Категории раздела
Мои статьи [25]

Мини-чат

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Главная » Статьи » Мои статьи

WarCraft 3: Ищем баги сами

Думаю, ни для кого не секрет, что отладка алгоритма, как правило, занимает времени не меньше, чем само его написание. Тем более многие триггерщики, с которыми я сталкивался, вообще не знают, как самостоятельно найти ошибку. Ситуацию отягощает также отсутствие программы-отладчика для языка jass (напомню что триггеры - это всего лишь визуальная оболочка jass-кода).

В данной статье я намерен ознакомить читателя с методом отладочной печати, или попросту вывода DeBug сообщений. Вопрос "синтаксических" ошибок, возникающих при написании jass-кода, я рассматривать не буду (а в GUI они кажется вообще отсутствуют).

Итак, ниже я приведу достаточно примитивный триггер:

Он срабатывает при смерти юнита, и если юнит принадлежит Игроку 12 (в jass) или 13 (Нейтрально враждебный в GUI), то прибавляет к целочисленной переменной i_CreepsCounter (дословно счетчик крипов) уровень убитого монстра. Далее идет проверка, не достигла ли данная переменная значения 30, и если да, то данный триггер выключается, выводиться сообщение игроку, и через некоторое время объявляется победа.

Смысл его в том, что когда игрок убьет достаточно монстров (а то, насколько ценный монстр определяется по его уровню) объявить игроку победу. Вроде все правильно. Однако, сыграв несколько раз в карту и убивая нейтралов мы обнаружим, что победа не объявляется. И тут пришло время искать ошибку...

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

Итак, мы точно знаем, что ошибка происходит именно в этом триггере. Мы можем сделать массу предположений, но как их проверить? Очень просто, мы будем выводить всего-навсего DeBug сообщения через действие Игра - Text Message. Начнем с того, запускается ли триггер вообще, несмотря на условие. Для этого я просто создам триггер, скопирую событие, а в действиях поставлю вывод сообщения:

Сразу скажу, что я пишу длинные сообщения только для того, что бы было яснее человеку, читающему статью, на практике же проще ввести несколько символов, главное -чтобы вам было понятно, из какого именно куска триггера вызвано сообщение.

* hint если бы у меня в триггере было несколько событий, я бы тестировал каждое из них отдельно.

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

* hint если бы ошибка находилась именно в событии, то мне пришлось бы просто найти другое.

Дальше, стоит проверить, выполняется ли условие, для этого я добавляю действие вывода сообщения уже в свой триггер:

* hint очень важно как-либо выделять то, что данная операция предназначена для поиска ошибок, т.к. их потом придется удалять, и если они никак не выделены, то придется снова вникать в алгоритм, чтобы понять, что относится к основному алгоритму, а что е DeBug'у

* hint если бы у меня было несколько условий - я бы тестировал каждое из них отдельно в том стороннем триггере (это позволит узнать, какое из них когда возвращает правду, а когда ложь), вот так:

Вернемся к нашему примеру (мы уже узнали, что условие также работает успешно) - очевидно ошибка где то в действии триггера. Тут у нас имеется условный оператор, действия, находящиеся в котором, сработают, когда переменная будет равна 30. Ее мы сейчас и проверим. Теперь мы будем выводить на экран не логические значения (либо алгоритм выполняется и сообщение выводится, либо он не выполняется и соответственно сообщение не выводиться) а значения нашей переменной:

* hint также мы можем выводить значения кроме переменных целочисленных (integer) также реальных (real), строчных (string - их даже не надо преобразовывать), логических (boolean - для их вывода стоит использовать условный оператор if, написав в условии <переменная> равно Да, а в действие вывод сообщения)

Запустив карту и убив в ней крипа, мы заметим и наши сообщения, к примеру ... 21, 23, 24, 27, 29, 32, 36 ... Смотрим на наше условие, и видим, что оно будет выполнено в случае, только если игрок "выполнит план по убийствам крипов", в случае же если он его "перевыполнит", то победы ему невидать. Это несправедливо, исправляем триггер:

* hint если вы одновременно выводите сообщения о значениях нескольких переменных, желательно использовать функцию сложения строк, в первой части которой указать намек на то, значение какой именно переменной вы выводите.

* hint не забывайте по окончанию проверок удалять все debug элементы.

Ну вот, теперь победа объявляется всегда, когда игрок наберет достаточно очков. Еще раз повторю, что пример достаточно примитивный (придумать не примитивный, но применимый к триггерам я не смог) но вполне четко описывает данный метод. По сути мы можем только догадываться, о чем думает компьютер (какие значения в данный момент имеют переменные и какие инструкции он в данный момент исполняет), но мы можем попросить его рассказать нам об этом с помощью DeBug сообщений ;)

 То же самое, только в jass'е

Все вышеизложенное имеет также применение и при написании скриптов на jass. Лично я предпочитаю писать следующим образом:

...
 call MyFunc(i_my, .0, ...
 set i_my=i_my+i_some+...
//
** call BJDebugMsg("i_my "+I2S(i_my)+" i_some "+I2S(i_some))**
//
 call DoSomething()
...

Т.е. во-первых я выделяю DeBug просто комментариями (ой, насколько это удобней делать на jass, в редакторе триггеров ввод комментариев сделан просто жутко), во-вторых использую функцию BJDebugMsg(string) - наверное единственная true BJ function ;)

Имеется еще несколько преимуществ, к примеру, мы можем "отключить" выполнение какой-либо инструкции/инструкций, сделав ее комментарием (еще Sergey писал про это):

...
 call SomeFunc(i_my, ...
 set x=x+...
**// call DoSemething() // temporarily off**
 set i_my=i_my+1
...

А также мы можем вывести на экран integer-значение handle обьекта, это также иногда бывает весьма полезно:

function H2I takes handle h returns integer
 return GetHandleId(h)
endfunction

...
 call DoSomething(12, ...
//
** call BJDebugMsg(I2S(H2I(u_myUnit))+" "+I2S(H2I(d_myDestructable)))**
//
...

 Заключение

И всё-таки, возможно, ваш алгоритм все-таки не будет работать именно так, как вы хотите - именно из-за того, что действие, которое вы используете, просто так устроено, а других подходящих нет, тогда... Если того, что бы вы хотели, нет в common.j (список всех native функций, грубо говоря, все возможные триггерные действия) - то стандартными средствами вы никак не добьетесь желаемого результата. Нет, конечно можно сторонней программой попробовать, но это уже немного другой разговор ;)

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

Вроде все, статья хоть и простая, но, думаю, весьма полезная.

Категория: Мои статьи | Добавил: Enemy1PK (02.04.2017)
Просмотров: 333 | Рейтинг: 0.0/0
Всего комментариев: 0
avatar
Вход на сайт

Поиск

Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • База знаний uCoz

  • Copyright MyCorp © 2024uCoz